Tab Completion

I'm Tab Atkins Jr, and I wear many hats. I work for Google on the Chrome browser as a Web Standards Hacker. I'm also a member of the CSS Working Group, and am either a member or contributor to several other working groups in the W3C. You can contact me here.
Listing of All Posts

2014 State of Element Queries

Last updated:

About a year ago, I talked about Element Queries and why they were difficult to make work. It's about time for an update.

Short version: Not much has changed. Naive element queries still suffer from circularity issues, and there's been no real effort to specify anything to get around it. However, Shadow DOM seems well-placed as a way to get an EQ feature into the web.

What Are Element Queries Again?

An "element query" is like a media query, specifically the 'width'/'height' MQs, but they apply to the width/height of the element's container, rather than of the screen or device.

Why Were Element Queries Problematic Again?

Element Queries (let's just call them EQs from here on) suffer from basic circular dependency issues.

Here's a simple example: Say you have an element which is normally 100px wide, but it uses an EQ to say that if its container is less than 200px wide, the element becomes 500px wide. (This is silly, but bear with me.) If the container is explicitly sized (width: 150px; or the like), this is fine, but if the container is sized to its contents (float: left;, for example), then you have a circular dependency: if the element is 100px wide, the container is 100px wide, but that trigger the EQ, so the element is 500px wide, so the container is 500px wide, but that disables the EQ, so the element is 100px wide, so the container is 100px wide, etc.

What About Them <iframe>s?

A funny detail of this is that EQs already exist, at least in some form. If you use a MQ on a page displayed in an <iframe>, the MQ is resolved against the size of the <iframe> element. This is exactly an EQ, if you treat the <iframe> as the "container" in the above explanations.

This avoids the problems outlined above because an <iframe> cannot depend on the size of its contents. An <iframe> is only ever explicitly sized; if it doesn't have anything explicit, it defaults to being 300px wide and 150px tall. The contents never affect its size in any way, and so the circularity problem never comes into effect - if you get super wide because the <iframe> is skinny, nobody cares; the <iframe> just sprouts a scrollbar.

How Can We Get Around This?

Of course, you don't want to use <iframe>s all over your page. It would be nice if we could somehow establish the same sort of situation while keeping all our content in the page together.

The first thought people usually have is to create some CSS value, perhaps a display value or something like that, which makes the element no longer pay attention to its contents. Unfortunately, this doesn't work well - since you don't know which elements are "independent" until you apply CSS, you might do too much work on initial layouts before you figure out that you're all wrong. Also, since the boundaries aren't immediately clear (and in fact can shift over time, if the property starts/stops applying to various elements), it's somewhat difficult to understand what elements are allowed to use EQs.

Better would be something that would alert browsers to the "independent" nature of the element before they start applying CSS. For example, an HTML attribute would work for this. Unfortunately, it's annoying to have to sprinkle presentation-based attributes over your page just to get your styling to work right.

I think that we've got a good opportunity to make this less bad with Web Components, though. They already represent a useful "boundary" in the form of the shadow DOM, so the potential confusion of not knowing which element your EQ is applying to would go away - it always applies to your host element, assuming it's marked as "independent".

While Level 1 of the Shadow DOM specs are stabilizing right now, I think this is something reasonable to aim for as a Level 2 feature. You'd be able to set some switch when defining your custom element, or perhaps set it per ShadowRoot, which would make your component's layout independent of its contents, just like <iframe>. Then, EQs would work inside the shadow, referring to the size of the host element. Outside of a shadow, or in a non-"independent" shadow, EQs would just always be false.

So that's the state of Element Queries in 2014. We're not really any closer to having them than we were in 2013, but there's light on the horizon for a possible good solution.

Basic Category Theory Terms

Last updated:

Magma: A set of objects that can be "added" or "combined" in some way, so that if you add any two of them the result is also in the set. That's it.

Semigroup: A magma whose "addition" operation obeys associativity: (a + b) + c must give the same answer as a + (b + c). For example, strings form a semigroup with concatenation as their "addition" operator: ("foo" + "bar") + "baz" = "foobarbaz", etc. So do the integers under the min() operation.

Monoid: A semigroup with a "zero" element: something that, when "added" to any element in the group, produces that other element again. For example, the empty string is the "zero" element in the string/concat monoid. The integers with min() aren't a monoid, but they become one if you add infinity as the "zero" element. Lots of interesting things are monoids; it's a very simple and useful constraint to satisfy. If you satisfy "monoid", you get a free version of the reduce function using your operation.

Group: A monoid where every element has an opposite or "negative" version, so that if you "add" an element and its opposite, you get the "zero" element. Strings with concatenation are not a group - concatenation only ever produces larger strings, not smaller ones, so you can't ever take a string like "foo" and add another string to it to get the empty string. On the other hand, integer/addition is a group - the opposite of each member is just the negative integer corresponding to it.

An Alternate Path to a Group

There's an alternate way to get from magma to group. You add different properties along the way, but when they're all combined they add up to the same thing that the previous path did. However, these properties are harder for most things to satisfy, and so they're not generally as useful; most of the things you use that'll satisfy any of these do so because they're all the way to a group already.

Quasigroup: This one's a little subtle. It's a magma, but the operation has the property that you can transform any element of the set to any other element by combining it with an appropriate third element. It has to do so whether the first element is the left or right argument, but the element you choose to combine it with can be different in the two cases.

The integers with subtraction form a quasigroup - if I want to transform "2" into "5", I can do it with 2 − -3 = 5 and 7 − 2 = 5. (So do the integers with addition, but they satisfy so many of these definitions that they're not very interesting to talk about.) On the other hand, strings with concatenation are not a quasigroup - while you can transform some strings, like "foo" to "foobar", you can't generally do it both ways (nothing satisfies the equation X + "foo" = "foobar"), and there are some strings you can't transform between at all.

Loop: A quasigroup with a "zero" element, which transforms an element into itself regardless of which side it starts on. Integer/subtraction is not a loop: X − 0 = X, which is what we want, but 0 − X = -X, which isn't what we want (and no other number satisfies either side, obviously.) I can't come up with a good example of something that's a loop but not a group right now.

Group: Ah, back to group. Along this path, a group is a loop with associativity.

Going Further

Abelian or Commutative X: Any of the above types might have an operation which is commutative - it doesn't matter which order the arguments are. Integer/min is an abelian monoid, because min(1, 10) = 10 and min(10, 1) = 10.

CSSOM Value API Proposal Dump

Last updated:

This is a basic sketch of a CSSOM Values API, where rather than returning strings for everything, we use proper JS objects. No more parsing of "5px" for you!

Requirements

  1. Must faithfully and directly reflect the property grammar in an obvious way. While specialized APIs might be useful to expose on the side, we want to be able to interact with properties in a generic way to help tooling and teaching.
  2. Must gracefully handle the kinds of property changes that the CSSWG commonly makes. DOM Level 2 Style failed this, for example, because it made shorthands return null - we pretty regularly make complex properties into shorthands, and this would break code.
  3. Must respect JS idioms. No bullshitty Java-style APIs, even if they would be more efficient. Those are just horrible, and people rightfully complain whenever we do them.
  4. Must be efficient, particularly when used in a rAF animation - we shouldn't need to create several objects (which become garbage) each iteration just to update a property.

Common Types of Property Changes

As CSS grows and changes, we alter and extend existing properties in various ways. There are a handful of common ways that have emerged over the years, though, that we must be sure to handle well in the API:

  1. Breaking apart a complex property into a shorthand with sub-properties.
  2. Turning a property with a single value into a list-valued property that accepts a comma-separated list.
  3. Adding more terms to the grammar of an existing property, such as taking a property that accepts a <length> and making it <length> || keyword, so it goes from 1 value to 1 or 2 values.

Proposal Sketch

We define an additional accessor for style data, provisionally called .css. Hanging off of this are mutable properties, one for each CSS property. (Dunno if we want to allow both camelCase and dash-case, or stick with only camelCase.)

The value of each property is an array containing one or more CSSValue objects. This has to be an array at all times, even for properties that only take a single value, like 'width', because 'width' might take two values in the future and we have to be ready for that to happen. The ordering of what values go in what indexes are defined by the spec and stable over time. Call this array a value list.

If you assign a CSSValue directly to the property, rather than to an index in the array, it's treated identically to if you'd wrapped it in an array. That is, el.css.width = 5px; and el.css.width = [5px]; are the same thing, for convenience.

The CSSValue objects (described more below) are going to be "value objects" from JS, which is a new kind of thing halfway between primitives (like numbers and strings) and objects. They're fully immutable, and it's planned that you can get syntax support for simple numeric ones, so that just appending a suffix to your number automatically makes a value object - using var x = 10px; in your JS will be equivalent to saying var x = CSS.px(10);.

For example, here's box-shadow:

el.style.boxShadow = "none";
print(el.css.boxShadow);
// []
el.style.boxShadow = "1px 1px 3px red";
print(el.css.boxShadow);
// [1px, 1px, 3px, 0px, CSS.color(red)]

If a property is list-valued, the array also has a .l property on it, which contains an array of those value lists. If you operate directly on the indexes of the value list, it's identical to operating on the first element of the .l property + unsetting all the others.

That is:

el.css.boxShadow[0] = 5px;

sets the horizontal offset of the first boxShadow and unsets all the other box shadows, identical to:

el.css.boxShadow.l[0][0] = 5px;
el.css.boxShadow.l.length = 1;

Issue: using a value list is kinda clumsy. It would be better to use named values, but that requires even more work for every property, plus more bikeshedding, and is still weird for things with a single value currently like 'width'. Maybe we can do both? Expose the value as an array, and some properties also hang individual components off of named values. Drawing from Anne's old proposal, maybe hang the named-components object off of a .m (for map) property.

Issue: .l is a crappy name. We can probably come up with better. But if we use named subvalues, we want to make sure that anything we attach to every value won't be clash with a useful name. A single letter like l seems promising for that.

Issue: Should the length of the value list and the lists of value lists be frozen? So you can manipulate values, but can't assign to indexes that don't exist? That would make things a bit saner, probably. After all, how do we serialize things if you just assign straight to el.css.backgroundImage.l[50] = ...;? Some list-valued properties may have a "null value" to use in that situation, but not all of them do. On the other hand, if we ever do the "all list-valued properties are shorthands for indexed longhands" thing, you'll be able to directly set "background-image-50" to something, so maybe we'll just have to add null values to everything anyway.

Issue: How should we handle partial updates that are invalid until completed? Just rely on the style flushing machinery to batch changes and only validate when they're flushed?

CSSValue Objects

CSSValue objects hold all of the terminal values, like <length> or <color>. The most important thing to know about them is that they're immutable. (This'll be accomplished either by deep-freezing them upon construction, or hopefully by making them from JS value objects.)

Immutability helps with efficiency, because it means the engine can cache values and reuse them in different places without you knowing - rather than your script creating a thousand different object that all represent "10px", it creates a single immutable one and uses it in a thousand places. (JS value objects let us override == and make this explicitly work.)

Immutability also cuts out a ton of potential problems from mutability. Every length, for example, is relative to the element its on, the property its on, and even the index of the value list it's in. For example, the length value in el.css.width is sometimes relative to the element's own width, for resolving percentages. What happens when you rip it off and assign it to a different property? This, and lots of related issues, disappear completely when you have immutable values.

As a consequence of this, assigning to a property value doesn't use the actual object you assigned (unless it can undetectably share). So:

el.css.width = 10em;
print(el.css.width.px); // null
print(el.css.width.em); // 10
print(el.computedCSS.width.px); // 160

el.css.width = el.computedCSS.width;
print(el.css.width.px); // 160
print(el.css.width.em); // null

(In this case, computed lengths are always "absolutized" in CSS, so we take the absolute length as the appropriate intent. Alternately, we could perhaps have computed values and beyond remember what unit their associated declared value was created with, when that's relevant. But that's a lot of difficulty and has some weird edge cases.)

CSSValue Subclasses

Each value type in CSS has a CSSValue subclass, like CSSLengthValue, CSSTimeValue, or CSSColorValue. Here's some example IDL for CSSLengthValue, one of the more complicated ones:

[Constructor(DOMString dimension),
 Constructor(double val, DOMString unit)]
interface CSSLengthValue : CSSValue { 
  readonly attribute double? px;
  readonly attribute double? pt;
  readonly attribute double? in;
  readonly attribute double? cm;
  readonly attribute double? mm;
  readonly attribute double? em;
  readonly attribute double? ex;
  readonly attribute double? vw;
  readonly attribute double? vh;
  …
};

(In addition to the direct constructors, there will be short constructor functions on the CSS object as well. Rather than new CSSLengthValue(5, 'px'), you can just write CSS.px(5).)

Depending on exactly how you obtain a length, some of the properties will be null, because the browser doesn't know how to interconvert between them.

For example, constructing a pixel length directly will let you ask for the size in pt or in, but not in em:

var l = 400px;
print(l.px) // 400
print(l.pt) // 300
print(l.in) // approximately 4.16666666
print(l.em) // null
print(l.vw) // null
print(l.percent) // null

Pulling a value directly off of a declared value is the same:

el.css.width = 400px;
print(el.css.width.px) // 400
print(el.css.width.em) // null
el.css.width = 25em;
print(el.css.width.px) // null
print(el.css.width.em) // 25

However, a computed value would have more fields filled in:

el.css.width = 400px;
print(el.computedCSS.width.px) // 400
print(el.computedCSS.width.em) // 25 (assuming font-size:16px)
print(el.computedCSS.width.vw) // 40 (assuming 1000px viewport)
print(el.computedCSS.width.percent) // null (needs layout info)

We'll need at least the following interfaces:

  • CSSLengthValue (.px, .em, .percent, etc)
  • CSSTimeValue (.s, .ms)
  • CSSAngleValue (.deg, .rad, .turn, etc)
  • CSSFrequencyValue (.hz, .khz)
  • CSSResolutionValue (.x, .dpi, etc)
  • CSSKeywordValue (.value + a stringifier?)
  • Strings are just strings
  • CSSURLValue (Would be cool if we could just use URLValue, the planned immutable/value object version of the URL interface)
  • CSSColorValue (.red, .green, .hue, etc)

Dealing With Functions

After some though, I think functions should be special-cased for each instance. We could expose a generic interface for them, similar to what we do for properties, but I don't think it'll be very useful, and the argument for a shared generic "shape" among functions is weak, unlike for properties, because other types of values are already custom.

So this means each function probably maps to a unique interface. They don't have to - all the color functions (rgb(), hsl(), etc) will all map to the CSSColorValue interface, for example - but in general they will.

They should expose their values in whatever way makes the most sense for that function. For example, the image() function will expose a list of urls/colors, plus some metadata things.

They'll also be immutable, for all the same reasons as the other values. This might call for an easy way to “mutate” one piece of a function (provide a “keypath” and a value, and return a new object with the indicated piece replaced?), but we can skip that until we're sure it's needed.

For example, here's what 'attr()' might look like:

interface CSSAttrFunctionValue : CSSValue {
  readonly attribute DOMString attribute;
  readonly attribute DOMString type;
  readonly attribute sequence<CSSValue> fallback;
};

And how it could be used:

el.dataset.width = "100";
var attr = CSS.attr('data-width', 'px', [50px])
el.css.width = attr;
print(el.css.width) // [CSS.attr(...)]
print(el.computedCSS.width) // [100px]

print(CSS.convert(attr, el)) // 100px

Note how the attr() function is preserved in the declared value, but disappears in the computed value, or when associated with an element.

Drawbacks

There are a few drawbacks to this approach.

Unless we use JS value objects (which don't exist yet), we can't do math directly on values. We can't add together two lengths:

var x = 5px;
var y = 10px;
var z = x + y; // NaN
var z = CSS.px(x.px + y.px); // success!

Nor can we do in-place updating:

el.css.width += 5px; // wrong
el.css.width = CSS.px(el.css.width.px + 5); // success!

If we wait for value objects, which I'm told we'll get Real Soon Now®, then both of these will work nicely.

Either way, though, we still won't be able to do something like:

el.css.width.px += 5;

Since the length is immutable, you can't ever assign directly to one of its members. We can't fix this without going fully mutable, which I'm not willing to do. I think it's easy enough to just construct a px value and add that to the 'width' property directly that I'm not too concerned about this.

Converting Between Values

Like I said earlier, if you construct a CSS.px(5) value, the .em property will be null, because you can't convert between px and em unless you know the font-size. In general, full interconversion requires associating the value with an element, a property, and in some cases an index into the property. (For example, a percentage in background-position is relative either to the width or the height, depending on where it appears.)

So, we need a convenient way to obtain these conversions. I think we can do something like:

CSS.convert(value, element, optional property, optional index)

This would return a new value of the same type, with all possible things filled in, based on the information you give. Example:

var x = 32px;
print(x.px);     // 32
print(x.em);     // null
print(x.percent) // null

el.css.fontSize = 16px;
var y = CSS.convert(x, el);
print(y.px)      // 32
print(y.em)      // 2
print(y.percent) // null

el.parentElement.css.width = 400px;
var z = CSS.convert(x, el, 'width');
print(z.px)      // 32
print(z.em)      // 2
print(z.percent) // 8

Single Line Comments (//) in CSS

Last updated:

Hi haters! This article is half informative, half tongue-in-cheek. Remember that I'll delete your ass like it wasn't a thing if you're rude!

CSS uses the same "block comment" syntax as the C-like languages - you start a comment with /*, and end it with */.

However, CSS is missing the "line comment" syntax that those languages have, where everything from // to the end of the line is commented out.

People have asked for this syntax to be added repeatedly, but unfortunately our hands our mostly tied - CSS minifiers don't know about line comments, so if we added it and the minifier removed all the linebreaks (as they tend to do), the line comment would accidentally comment out the entire rest of your stylesheet!

That said, CSS does actually already allow you to use //, after a fashion. It's not quite a line comment, but a next construct comment.

That is, whenever you use //, the next CSS construct - either declaration or block - will be "commented out". For example:

.foo {
  width: auto;
  //height: 500px;
  background: green;
}

Here, the // commented out the height declaration.

Similarly:

//@keyframes foo {
  from, to { width: 500px; }
  50% { width: 400px; }
}
@keyframes bar {
  from, to { height: 500px; }
  50% { height: 400px; }
}

Here, the // commented out the first @keyframes declaration.

Note, though, that if you try to use // just for writing comments into your stylesheet, you have to be careful - raw text isn't a CSS construct, so it'll look past that and comment out the next construct in your page:

// Do some stuff.
.foo { animation: bar 1s infinite; }
/* Whoops, the .foo block is commented out! */

Update: Whoops, got my own CSS syntax wrong. Sorry, winkybarf ({};), you're no good.

You can avoid this by ending your text comment with a {} (if you're outside of a rule) or a ; (if you're around some declarations), to let the CSS engine know that you're just kidding around:

// Do some stuff {}
.foo { animation: bar 1s infinite; }
/* Success! */

The astute among you will have noticed (or already known) that using // like this isn't really "commenting out" anything at all. Instead, it's just putting an invalid value into the stylesheet and relying on CSS's error-recovery rules to kill the next construct on the page and then recover gracefully. Because CSS's error recovery is well-defined, you can rely on every browser that implements it correctly to work in the expected way.

That said, this is still potentially a useful trick for those who really don't like having to go to the end of a line to add those */ markers, like me. ^_^

Black/Unresponsive Screen in ChromeOS Crouton

Last updated:

(This is a PSA to make it easier for me to find this information later, and hopefully help others.)

If you're running Crouton on a ChromeOS device, particularly a Pixel, you might run into trouble with version 32, which is currently in beta.

When you log into your chroot, the screen will be black, but the cursor will appear and be responsive. If you're lucky, you might even notice the cursor change in some areas of the screen. If you flip back to ChromeOS and then back to your chroot, the screen will have updated, but will still be unresponsive.

This is a bug with ChromeOS’s updated handling of some hardware acceleration. There is a temporary fix that you can activate by turning off a feature before logging in. Just run the following command in your shell:

echo 0 | sudo tee /sys/kernel/debug/dri/0/i915_fbc

This needs to be redone every time you reboot the machine, as the flag resets.