w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 666 forks source link

[css-contain-3] "container width" and "container height" units #5888

Closed una closed 3 years ago

una commented 3 years ago

Viewport-relative units (such as vh and vw) 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 in srcset similarly to how one would use vw units in srcset. 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 existing ch (character) unit

dbaron commented 3 years ago

The ch unit already exists with a different meaning (short for "character"), so this will at least need a different name.

jimmyfrasche commented 3 years ago

ew and eh

nigelmegitt commented 3 years ago

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.

una commented 3 years ago

@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

chrishtr commented 3 years ago

@mirisuzanne

mirisuzanne commented 3 years ago

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.

lilles commented 3 years ago

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.

fantasai commented 3 years ago

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.

css-meeting-bot commented 3 years ago

The CSS Working Group just discussed [css-conditional] [css-contain] "container width" and "container height" units.

The full IRC log of that discussion <astearns> topic: [css-conditional] [css-contain] "container width" and "container height" units
<astearns> github: https://github.com/w3c/csswg-drafts/issues/5888
<dlibby> una: this issue is regarding a new sizing unit
<dlibby> una: one for container width and one for height, how inline vs. block is used
<dlibby> una: could be something useful for ui designed, similar to vw/vh
<dlibby> una: could be related to srcset based on container width, probably want different images based on container size
<florian> q+
<emilio> q+
<jensimmo_> A big +1 from me for CW and CH. Or even just a unit in the inline direction if that's only what's possible. Such a thing will be very useful.
<dlibby> una: would like to get consensus on whether this concept (container-sized units) makes sense
<dlibby> astearns: clarify - these units would key off of container size we discussed before?
<astearns> ack florian
<dlibby> una: yes, could use for widths or similar to other viewport unit usage
<fremy> q?
<dlibby> florian: viewport units are useful, this sounds useful too. perhaps we already have this via percentage
<dlibby> florian: if this is direct parent, then % gives you this - examples where this is not true?
<bkardell_> % doesn't work if it isn't your parent tho
<dlibby> leaverou: font-size is one example
<dlibby> florian: realize there are cases where it doesn't work, but are those real use cases
<astearns> ack emilio
<iank_> re: only your direct parent -> except in quirks mode :P
<dlibby> emilio: this would resolve at computed value time?
<dlibby> una: yes
<dlibby> emilio: the thing with srcset is that it might not quite work - how do they evaluate in media queries?
<dlibby> emilio: assume these would resolve against viewport size. don't want to wait until layout to start loading images, since it's a bit circular
<dlibby> astearns: (this is the next issue)
<leaverou> +1 for adding these units, if they are implementable
<dlibby> emilio: sounds workable as long as we have a strong definition of what a container is
<jensimmo_> q?
<leaverou> perhaps cnw and cnh for names? (since ch is taken)
<dlibby> astearns: other comments/concerns?
<fremy> also +1 from me
<dlibby> jensimmons: big +1, when teaching viewport units people like them but not quite what they want
<dlibby> jensimmons: could really be useful for font-sizing
<dlibby> jensimmons: percent could work for margins, but this seems like something people will get excited for
<dlibby> jensimmons: if we only get inline direction, maybe a minor limitation, but better than today
<dlibby> astearns: could see vw convert to this quickly
<jensimmo_> border thickness is another thing that cannot be done with %
<dlibby> astearns: see enthusiasm and support - resolve to pursue container units along with container queries?
<dlibby> florian: we know we can have 2d containment, we suspect 1d, not sure on block - should this influence which units we want to expose?
<leaverou> also is the container width unit essentially a container inline unit? Does it change with writing mode?
<leaverou> (if so the unit could just be ci and cb)
<fantasai> leaverou, I think we should pick a prefix that can be expanded in the future, so probably a 2-letter prefix
<dlibby> astearns: not quite sure followed previous discussion, though we were going to have a draft with 2d and everything, see what we can get
<leaverou> fantasai: fair point
<dlibby> florian: i think we can do 2d and inline, but not sure if we can do block
<dlibby> florian: or we won't do block yet, at least
<dlibby> iank: highly unlikely to be able to do block only
<dlibby> iank: would like to make sure we have use cases for block direction
<astearns> ack fantasai
<dlibby> jensimmons: border could be interesting. could set it 1/10th of inline, 1/10 of block on rectangular box
<dlibby> fantasai: if we add block or things that may or may not exist need to define if they don't
<dlibby> fantasai: also should have a consistent prefix, 'c' and 'r' are problematic currently
<dlibby> fantasai: pick one we're unlikely to use or use a two letter combo
<dlibby> una: rch sound good
<dlibby> fantasai: we have to be careful as existing letters at the beginning won't work
<dlibby> una: let's bikeshed in the issue
<fantasai> r is particularly problematic since we're using it as a prefix for root
<dlibby> jensimmons: liked leaverou's thought of only allowing inline/block instead of height width
<dlibby> una: i'd like to keep symmetry of vw/vh to this proposal
<dlibby> una: but do like inline/block
<dlibby> fantasai: viewport units have logical units already
<fantasai> (vi and vb)
<dlibby> astearns: let's bikeshed in the issue, get examples of what the CSS really looks like
<dlibby> astearns: any objections to adding units, name tbd
<dholbert> it's also worth thinking / defining what happens when the item & the container have orthogonal writing modes
astearns commented 3 years ago

I got ahead of the bot:

Also should consider 4 names for height/width and inline/block RESOLVED: add new units to express container dimensions
matthew-dean commented 3 years ago

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?)

matthew-dean commented 3 years ago

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 ?)

fantasai commented 3 years ago

Important comment from @dholbert in those minutes: need to consider what happens when the element and container writing modes don't match.

LoganDark commented 3 years ago

cx and cy

davewallace commented 3 years ago

bw for box width, bh for box height 📦

Martinspire commented 3 years ago

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.

mirisuzanne commented 3 years ago

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.

mirisuzanne commented 3 years ago

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.

  1. if there is no ancestor container
  2. if the nearest ancestor container does not have the appropriate dimension contained (eg a block-dimension unit in an inline-container)

For normal @container queries, we've concluded that:

  1. the query fails when no container is available
  2. the query fails when the queried (nearest ancestor) container has improper containment (This hasn't been discussed officially, but the alternative isn't viable to begin with. Browsers would have to parse each query condition before knowing what container to resolve against, and conditions could either resolve against different containers, or impact the meaning of other conditions)

But relative units don't usually "fail" in the same way as query-blocks. My immediate instinct is that they would:

  1. Resolve against the viewport, if no container is present
  2. Resolve based on the nearest relevant container

But that could have some unexpected behavior:

  1. the viewport is likely much larger than any expected layout containers (this is why we rejected the fallback for @container)
  2. inline and block units could resolve against different containers in confusing ways

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.)

matthew-dean commented 3 years ago

@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!)

mirisuzanne commented 3 years ago

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:

lilles commented 3 years ago

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?

andruud commented 3 years ago

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).

dvdherron commented 3 years ago

a suggestion for units I don't think I've seen yet: qw,qh ( query-width, query-height? )

una commented 3 years ago

I like qw and qh to not clash with ch (more than rcw and rch)

mirisuzanne commented 3 years ago

Just to flesh that out into the six value we need:

(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)

matthew-dean commented 3 years ago

(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.

Crissov commented 3 years ago

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.

mirisuzanne commented 3 years ago

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;
}
andruud commented 3 years ago

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:

mirisuzanne commented 3 years ago

@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.

andruud commented 3 years ago

(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.

mirisuzanne commented 3 years ago

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.

andruud commented 3 years ago

@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?

mirisuzanne commented 3 years ago

@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.

mirisuzanne commented 3 years ago

Agenda+ to discuss and get resolution on the the specified approach.

jensimmons commented 3 years ago

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:

css-meeting-bot commented 3 years ago

The CSS Working Group just discussed [css-contain-3] "container width" and "container height" units, and agreed to the following:

The full IRC log of that discussion <dael> Topic: [css-contain-3] "container width" and "container height" units
<dael> github: https://github.com/w3c/csswg-drafts/issues/5888
<dael> miriam: earlier resolved we wanted them and went back to give names. Can't give 'c'. Best I saw was 'q' units. q-width, etc.
<florian> I like "q*"
<dael> astearns: Some people in issue have concerns about mix with q typographical, but I don't think we should worry
<dael> TabAtkins: same
<dael> astearns: was cq- considered?
<dael> miriam: I don't remember seeing that
<dael> astearns: I don't know I'm going to argue in favor of it
<dael> jensimmons: We did have vh and now we have lvh. Don't know if that leads us anywhere with this
<dael> jensimmons: Kontainer :)
<dael> astearns: fantasai on irc says c doesn't add much
<dael> jensimmons: Something with q. Agree it's a little awk but q just as a letter is nice
<dael> astearns: Anyone that wants to argue against the spec approach of q?
<fremy> I feel like qw is liked becaue people use QWERTY keyboards ^_^
<dael> astearns: prop: Go with specified approach using q
<heycam> "queried width" reads well
<dael> plinss: We do have q unit
<fremy> Doesn't feel as special on AZERTY ^_^
<dael> astearns: We do. Was discussion in issue about possibility of confusion. I think people that understand what q unit is will not be confused. People who don't know won't know it exists
<dael> plinss: Concern with future name collisions
<dael> emeyer: That was my concer with future names
<dael> astearns: Future that spawn their own units
<dael> plinss: Last time we went through I raised concern on explosion of unit types
<RRSAgent> logging to https://www.w3.org/2021/10/06-css-irc
<dael> astearns: Do you have a solution? We seem to be proliferating unit types. I agree 2 letter names are sometimes difficult
<dael> plinss: Function
<emeyer> q+
<dael> fantasai: If people type function names in parens when they want to use a unit they'll be unhappy. We have units for a reason and abbreviate for a reason so we should continue. Agree we should avoid collisions but at least following a pattern
<dael> plinss: Want to make sure this really need to be a unit and not that we're doing it because we did it before. not saying that's case here but need guidance for next one
<emeyer> My muting has gone crazyz.
<emeyer> I do want to advocate for cq. It’s more mnemonic.
<astearns> ack emeyer
<dael> astearns: emeyer advocates cq as the mnemonic. Does protect against possible future query units.
<emeyer> It would set a precedent of being more mnemonic with any future units.
<emeyer> That’s all from me.
<dael> astearns: Anyone that would object to using cq instead of merely q?
<TabAtkins> cq fine with me
<miriam> also ok with me
<fremy> cqw cqh sound fine
<dael> astearns: plinss you're okay adding these units because see need for them to be units but would like more guidlines of when units are necessary?
<dael> plinss: I don't have opinion on if these should be units. Haven't given much thought. I do want clear guidelines as to what should be a unit and why.
<dael> astearns: I think we need units b/c will use in same way as viewport units today.
<dael> astearns: I see comment from jensimmons ?
<dael> jensimmons: I wanted to see what it looked like
<dael> astearns: And what do you think?
<dael> jensimmons: Dunno
<dael> astearns: I think cq looks weird but may be because I looked at q for a while
<florian> I like the shorter version, but not to the point of blocking if we want to go the other way
<dael> astearns: I am swayed by future proofing to keep from future query units. cq provides distance from q unit
<dael> astearns: Prop: Use cq as the prefix
<dael> astearns: miriam okay with you?
<dael> miriam: Yeah
<dael> astearns: Objections to spec ch?
<dael> RESOLVED: Use cq as the prefix