Closed una closed 3 years ago
The ch
unit already exists with a different meaning (short for "character"), so this will at least need a different name.
ew
and eh
rw
and rh
would match the identically named <length>
units in TTML2, in the case that the container is the root container region, which makes sense e.g. for playing captions over a video container, where it is normal to render text at a height proportional the height of the video.
@nigelmegitt I like the idea of using "root" in the name here. Potentially also "root container width" (rcw
) and "root container height" (rch
) to specify container from root. I also like rw
and rh
@mirisuzanne
Yeah, I'm totally in favor of this, and think it would be very useful. My understanding was that container-relative units would have an additional performance cost with every use, making them likely too expensive. I would love to find out that I mis-understood.
There has been some previous discussion of this in other container query threads.
Container units would be as costly as vh/vw for @media query changes, I suppose. Blink marks computed styles for an element with a flag saying that it relies on viewport units and such elements have styles recomputed when the viewport size changes. Same should work for container units/queries.
Note, we use 'r' as a prefix currently for the root element e.g. in rem
and rlh
so probably should avoid that as a prefix here.
The CSS Working Group just discussed [css-conditional] [css-contain] "container width" and "container height" units
.
I got ahead of the bot:
Yeah, I think I came up with aw
/ ah
/ ai
/ ab
(for allocated width, height, inline-axis, block-axis), but could also be: qw
/ qh
/ qi
/ qb
? (For queried units?)
Also relevant: https://github.com/WICG/container-queries/issues/12#issuecomment-587186810
https://github.com/WICG/container-queries/issues/12#issuecomment-587186810
So whatever the "prefix", should have [x]min and [x]max to correspond with viewport units? (So, 6 names, @astearns ?)
Important comment from @dholbert in those minutes: need to consider what happens when the element and container writing modes don't match.
cx and cy
bw
for box width, bh
for box height 📦
What if not CO was the shorthand, but CT instead? So CTW (container width) and CTH (container height) instead of COW and COH? In the past I've seen many projects using containers and ct to prefix stuff instead of co. It prevents making words that don't make sense (like cow) and still is similar enough to container...
Alternatively, one could imagine that character would get a different name here if it conflicts with easy container references, since that isn't widely used anyways and allowing like CHR instead of CH for character in other places would make it deprecated soon anyways. Looking at how widely it is used, might make sense to see if its up for replacement.
I also like the box one from @davewallace above. Since you can have containers in containers, its not much different from boxes in boxes. It would also help shave off a few characters to make filesize a little shorter.
Important comment from @dholbert in those minutes: need to consider what happens when the element and container writing modes don't match.
Since vi
and vb
resolve based on the root element’s writing mode, I expect we would use the container writing mode here. That has the advantage of ensuring that the containment axis and unit axis always match: when the container has inline containment, the inline units match the contained dimension.
Beyond the naming question (which should be considered in tandem with bikeshedding a new container property), there's a bigger question about how these units work.
For normal @container
queries, we've concluded that:
But relative units don't usually "fail" in the same way as query-blocks. My immediate instinct is that they would:
But that could have some unexpected behavior:
@container
)Maybe that's workable for authors, but it feels very fragile and unpredictable. Would we need some way to provide a more customizable fallback value in the "failed query" situations? Either by allowing them to "fail" and fallback on a previous value:
p {
padding: 1em;
padding: 10cw; /* ignored if no width-query is possible */
}
Or using some function fallback logic?
p {
/* container(<container-relative-value>, <fallback value>) */
padding: container(calc(1em + 1w), 1em);
}
I'm not sure either of those is quite right. Just trying to think through the options here.
(The cw
and w
units above represent a container width unit, but are not actual unit name proposals. I know that cw
will not work.)
@mirisuzanne I would think that if they don't apply, they simply don't apply (are not valid), and similar to other CSS behavior, would be as if the entire property were not declared. So your first option ignored if no width-query is possible
seems the most intuitive / consistent with CSS.
Or, another way to say this is that CSS already has a mechanism for "fallback" values (declaring the same property twice), so inventing a new syntax feels unnecessary. Plus, it allows the author to decide what that fallback behavior should be (because it might be to do nothing!)
Well, both my example fallbacks are based on existing CSS mechanisms. One is used for parse-time issues like invalid syntax, while the other is used for computed-value-time issues like invalid custom properties.
Looking back through the transcript, it seems like we plan resolve these units at computed value time, which means we're in custom-property (invalid-at-computed-value-time/IACVT) territory. So, having suggested it, I now think we can't use the parse-time fallback, even if we wanted to? So that complicates things a bit more.
I think ideally we might want:
As you mentioned, this won't work since the first declaration will be dropped at parse time as long as the 'cw' unit is supported at all:
p {
padding: 1em;
padding: 10cw; /* ignored if no width-query is possible */
}
If you resolve against 0 or the viewport dimensions, could one use calc with the existing min()/max() functions, or would that bee too hard to do predictably because you cannot predict if the fallback will be narrower or wider than a working container?
For that kind of fallback, just make it possible to query for a given container type?
p { padding: 1em; }
@container (inline-size) {
p { padding: 10cw; }
}
custom-property territory
I don't think we're automatically in custom-property territory here, we should first try to be in em
-territory (which is also resolved computed value time).
a suggestion for units I don't think I've seen yet: qw
,qh
( query-width, query-height? )
I like qw
and qh
to not clash with ch
(more than rcw
and rch
)
Just to flesh that out into the six value we need:
qw
: 1/100th of the container width
qh
: 1/100th of the container height
qi
: 1/100th of the container inline-size
qb
: 1/100th of the container block-size
qmin
: the smaller of qw
and qh
qmax
: the larger of qw
and qh
(the only potential confusion here might be with the Q
unit?)
In each case, the value can only be resolved inside a container of the appropriate type. As an initial draft, I'm proposing that unresolved container-units default to zero. Any other fallback seems dangerous and unpredictable. Zero may seem dangerous, but I think it can work well in practice.
The calc()
, min()
, max()
, and clamp()
functions already provide a fairly reliable way to specify fallback values. Consider these uses, assuming there is no container:
.examples {
font-size: calc(1em + 0.2qmin); /* 1em */
font-size: clamp(1em, 5qw, 3em); /* 1em */
font-size: clamp(1em, max(5qw, 3vw), 3em); /* clamp(1em, 3vw, 3em) */
padding: max(2qh, 1em) max(2qw, 1em); /* 1em 1em */
}
And, as mentioned by @andruud above, we can ensure they resolve by specifying them inside the appropriate @container queries. (I'll start with this as the draft spec, and we can revisit before publishing)
(the only potential confusion here might be with the Q unit?)
That.... is a very unusual unit. I would petition to have that one (Q
) removed before that proposal exits draft status, personally, partly for its unusualness (upper-casing? single letter? seems very strange), partly because I don't see the need, and partly because of this proposal.
q
could have been called qmm
to make it more transparent for people unfamiliar with it, but for those that are actually using it, q
is the appropriate symbol they are used to. (Some might say kyu
would have been fine as well.)
It’s been in CSS for a while now and is not going to be changed.
For that kind of fallback, just make it possible to query for a given container type?
p { padding: 1em; } @container (inline-size) { p { padding: 10cw; } }
@andruud This ensures that that there is a proper container for the unit, but it doesn't ensure it's the nearest ancestor container referenced by the unit. We would still need to say that units evaluate against the nearest container of the appropriate type. And that puts us back in a situation where units could evaluate against multiple containers:
<div style="container: size;">
<div style="container: block-size;">
<div style="container: inline-size;">
<div style="container: style;">
<p>how do container units evaluate on this paragraph?</p>
</div>
</div>
</div>
</div>
Which container is used for the various units?
p {
/* style (nearest)? block-size (of-type)? */
margin: 1qb;
/* style (nearest)? inline-size (of-type)? */
padding: 1qi;
/* style (nearest)? size (of-type)? or inline-size & block-size, individually per axis? */
font-size: 1qmax;
}
That's a different question, but OK, how about making the units evaluate against the selected container (see "container selection process") for the container-query/element pair?
/* (Syntax borrowed from https://github.com/w3c/csswg-drafts/issues/6393). */
@container type(inline-size) {
p { padding: 1qi; }
}
Here qi
evaluates against whatever is the current selected container is for the matched element. This way authors have full flexibility.
Otherwise, if there is no enclosing @container
rule, it evaluates against the nearest container (no mercy).
Worse ideas:
calc(1 * qi(<selection>))
(beautiful).@andruud lol, no mercy.
If I were to do something like this now, without container queries, I would likely set eg --qi
and --qb
custom properties on each container with inline or block containment respectively, using JS. Then I would inherit them, and determine the other units from there:
[data-container~="inline-size"],
[data-container~="size"] {
--qi: <set via JS>;
}
[data-container~="block-size"],
[data-container~="size"] {
--qb: <set via JS>;
}
[data-container] * {
--qmin: min(var(--qi, 0), var(--qb, 0));
--qmax: max(var(--qi, 0), var(--qb, 0));
}
When I think of it that way - it doesn't bother me that the two dimensions might come from different containers. I feel like each unit represents the best info we have about the given axis. So that's making me lean towards doing unit container selection for each axis individually, as though qi
represents @container type(inline-size)
and qb
represents @container type(block-size)
, and the other units are derived from those.
Then we only need the (merciless) fallback when there is no container in the requested dimension.
(Great, you already have a solution! :P)
While not impossible to have different units do their own "implicit container selection", it sounds like a mess to me. And of course it will add much-unneeded paper cuts to performance. (EDIT: Actually I'm not sure about that paper cut part).
@container foo (...) {
p { padding: 2qi; } /* Still resolves against nearest container with inline-size?! */
}
@property --qi {
syntax: "<length>";
initial-value: 0px;
inherits: false;
}
@property --qb {
syntax: "<length>";
initial-value: 0px;
inherits: false;
}
@container somelevel {
p { --qi: 2qi; }
}
@container anotherlevel {
p { --qb: 2qb; }
}
p { padding: max(var(--qi), var(--qb)); }
(EDIT: The above code assumes units resolve against the container selected by the enclosing container query).
But I can see how that's a bit verbose if it's a common thing.
I expect the vast majority of containers to be single-axis or style/state containers. Two-axis size containers are likely to be very rare. I think that makes your approach extremely limiting - these units would only be usable one at a time in very restricted ways. We'll almost never be able to combine inline & block units:
@container my-inline-container (...) {
/* only inline units can be used in here? */
}
We would rarely be able to use qmin
or qmax
, or anything like padding: 2qb 2qi
with both inline & block units in a single property. The min/max units would only be allowed when querying a 2d container, and the combined property would have to be split into individual queries.
This approach is also complicated by nested container rules:
@container my-inline-container (...) {
@container my-block-container (...) {
/* which container do we use for units in here? */
}
}
The only way we can make these units broadly available & combine-able is to make them self-select their containers. I don't see a viable way around that.
@mirisuzanne Ack. To be clear then, container units basically ignore the container selection provided by the enclosing @container
rule (if any), and do their own selection entirely?
@andruud yes, that's my proposal. The units work completely independent of @container
rules. So inside @container
they would behave more like a nested query, rather than a reference to the outer query. I expect in practice some of the units are likely to reference the same container, but not all of them.
I've updated the editor's draft with that behavior.
Agenda+ to discuss and get resolution on the the specified approach.
The unit names are:
qw
: 1/100th of the container width
qh
: 1/100th of the container height
qi
: 1/100th of the container inline-size
qb
: 1/100th of the container block-size
qmin
: the smaller of qw
and qh
qmax
: the larger of qw
and qh
Each unit is resolved in relation to the nearest ancestor with an appropriate container-type
for the given dimension (where logical units are resolved based on the container's writing mode). That means a qi
unit will resolve against the nearest ancestor with container-type
of size
or inline-size
, and a qb
unit will resolve against the nearest ancestor with container-type
of size
or block-size
-- so the different axis units can resolve against different containers, and the qmin
/qmax
units will use the smaller/larger value, even if they come from different containers.
If no eligible query container is available, then use the small viewport size for that axis.
Typing this out in the meeting so we can see it:
.examples {
font-size: calc(1em + 0.2qmin); /* 1em */
font-size: clamp(1em, 5qw, 3em); /* 1em */
font-size: clamp(1em, max(5qw, 3vw), 3em); /* clamp(1em, 3vw, 3em) */
padding: max(2qh, 1em) max(2qw, 1em); /* 1em 1em */
}
or
.examples {
font-size: calc(1em + 0.2cqmin); /* 1em */
font-size: clamp(1em, 5cqw, 3em); /* 1em */
font-size: clamp(1em, max(5cqw, 3svw), 3em); /* clamp(1em, 3svw, 3em) */
padding: max(2cqh, 1em) max(2cqw, 1em); /* 1em 1em */
}
Also we now have:
The CSS Working Group just discussed [css-contain-3] "container width" and "container height" units
, and agreed to the following:
RESOLVED: Use cq as the prefix
Viewport-relative units (such as
vh
andvw
) are commonly used among UI design for sizing and spacing of elements.With the new container queries spec, authors will want to use such units as container-height (
ch
) and container-width (cw
) for container-based sizing.An example is using
cw
units insrcset
similarly to how one would usevw
units insrcset
. Percentages would not work in that instance, as the value needs to be absolute or relative length.Edit:
ch
would not work as a unit name due to the existingch
(character) unit