- From: Dael Jackson <daelcss@gmail.com>
- Date: Wed, 24 Sep 2025 19:11:43 -0400
- To: www-style@w3.org
=========================================
These are the official CSSWG minutes.
Unless you're correcting the minutes,
please respond by starting a new thread
with an appropriate subject line.
=========================================
CSS Values 5
------------
- RESOLVED: Make type(<url>) invalid in the attr() function (for now)
(Issue #5079: attr()'s url type seems wrong)
- RESOLVED: Simplify as much as possible (combining items when it
can) for computed value (Issue #12290: Computed (vs used)
value of mix() functions)
- RESOLVED: Work on this in the next level (in consideration of all
the complications outlined above) (Issue #11941: Allow
`if()` in descriptors)
- RESOLVED: type() values are parsed just as in custom properties.
Other attr() type values (units % etc) are literals. Add
a 'number' keyword for literal numbers (Issue #12479:
Clarify how `<attr-unit>` should be parsed)
- RESOLVED: Add color-scheme() test to both @container queries and
if(). Open issue on what color-scheme() returns when used
in color-scheme property (Issue #10577: Using `if()` to
do dark/light switching of image urls)
- RESOLVED: Explore options for making a syntactic distinction
between input and interpolation options (Issue #12348:
*-interpolate() grammar can lead to confusing values)
===== FULL MEETING MINUTES ======
Agenda: https://lists.w3.org/Archives/Public/www-style/2025Sep/0015.html
Present:
Tab Atkins-Bittner
Kevin Babbitt
Oriol Brufau
Emilio Cobos Álvarez
Yehonatan Daniv
Elika Etemad
Roman Komarov
Alan Stearns
Miriam Suzanne
Anne van Kesteren
Lea Verou
Scribe: fantasai
Scribe's scribe: emilio, TabAtkins
CSS Values Level 5
==================
attr()'s url type seems wrong
-----------------------------
github: https://github.com/w3c/csswg-drafts/issues/5079
annevk: attr() started supporting types allowing to parse attributes
according to specified CSS types
annevk: Some issues here.
annevk: One was about passing the base URL along. Some discussion
about that, forget the outcome.
astearns: Munira suggested making the type invalid
annevk: That is my preferred solution as well
annevk: Resolving as document stylesheet maybe works. But if you
resolve and serialize, it ends up as all ASCII, which is all
Western-centric. Not what you want to print.
annevk: like domain name would become pyunicode
annevk: If you want it as a formatting feature, you'd need to add a
converter function
annevk: which we haven't exposed previously, and has security
implications
annevk: Browsers have not yet converged on a single algorithm for
that operation
emilio: Multiple base URL is not an issue.
emilio: That said, I don't disagree with making it invalid.
annevk: So you'd parse it for each document separately?
emilio: attr() is substituted at computed value time, so you need to
have the element and the document
annevk: So when you compute the value, you parse the attribute value
emilio: Yes
emilio: You can optimize some cases but basically yes
annevk: So you go into CSSOM, you get attr()
emilio: The stylesheet has a base URL of its own, but it's not the
base URI we're talking about here.
annevk: Does the CSSOM have an object representing attr() you can
query?
emilio: Maybe with typed OM
emilio: But you don't know which element you're applying the
function to
emilio: It's likely that idk about typed OM internals much
emilio: but I imagine it presents as a function value with a keyword
inside
emilio: Might be a string or whatever
emilio: But it's tangential. You can share a stylesheet across
multiple docs without an issue
astearns: Problem is that making invalid breaks print formatters
annevk: Yeah, some want to do things like print URL of the link. But
then you run into the i18n problem. Only works well for ASCII
inputs, and that doesn't feel responsible to standardize
fantasai: was it the case that the existing formatters use a
different syntax? In which case making type(<url>) invalid
wouldn't change anything?
fantasai: presumably they are stuck on whatever syntax they support
fantasai: seems we could try to standardize what they support but
dropping type() from the spec shouldn't change it
fantasai: but agreed we shouldn't break their existing content
fantasai: that'd be bad
annevk: Unclear if they support the current syntax
astearns: Could take a resolution that `type(<url>)` is invalid,
hoping print formatters are not using that syntax yet?
TabAtkins: I'm happy with that.
TabAtkins: We can also put an allowance for e.g. non-security-
sensitive uses such as printing
annevk: Not so much security, but i18n concern
annevk: If the idea of this function is to display the URL, then we
should have a good way of displaying
annevk: If we just echo the value, then it will work for Western docs
but fall apart everywhere else
annevk: In which case I'd rather avoid
annevk: making something that works for a subset of the world
astearns: By making invalid now, not blocking a future where we
figure out how to make it work well for i18n
annevk: Yes. We should probably get there eventually. Just not a
priority to figure out for anyone atm
astearns: Ok, let's take a resolution and see how Mike responds to it.
PROPOSED: Make type(<url>) invalid in the attr() function (for now).
<TabAtkins> +1
RESOLVED: Make type(<url>) invalid in the attr() function (for now).
Computed (vs used) value of mix() functions
-------------------------------------------
scribe: TabAtkins
github: https://github.com/w3c/csswg-drafts/issues/12290
fantasai: we have mix() that compute a weighted average from a list
of items and percents
fantasai: sometimes you can compute it to a final value, others you
need to keep values distinct until used value time
fantasai: we have two ways of simplifying we could take
fantasai: one is to simplify as much as possible, including
collapsing some items in the list
fantasai: or we can just simplify each argument, preserving the
number of items
fantasai: so your computed value serialization would be either a
single value, or a list with same number of items as
previous. no halfway point like in the first option
fantasai: currently we've specced the second option since it seems
simpler, wanted opinions
<emilio> I think whatever calc() does?
<TabAtkins> calc() does first options
astearns: doesn't first option lose info?
astearns: how would I be able to look at list of values and get an
average if I didn't know how many items where there?
fantasai: each item has a weight, so your combined item has the
combined weight
fantasai: [gives example]
astearns: ah, makes sense
<lea> no opinion either
astearns: slight preference for second, it preserves more of author
intent
astearns: and this is hopefully an edge case
<lea> +1
<kbabbitt> also lean slightly toward option 2
<lea> What does min()/max() do? That might be a better precedent than
calc() as it has multiple arguments too
TabAtkins: Emilio commented in chat, seemed to lean option 1
emilio: is there a reason to not be consistent with calc? max
simplification?
fantasai: calc() is hierarchical, you kinda combine things that are
adjacent until they can't be combined any more
fantasai: in mix(), if first and last can be combined, middle can't,
you'll get a combined item that has to go first or last...
emilio: I think we should simplify nested mix() if possible, and that
bubbles
fantasai: yes, if you can collapse a mix() into one value, you do
that. this is if at least one item can't be combined
emilio: ah kk, thought you were never simplifying
fantasai: nah, just if one item can't be simplified
TabAtkins: min()/max() simplify by combining items, so that's a
precedent
TabAtkins: from some feedback from smfr back in the day
astearns: should we go with option 1 then, for precedent?
fantasai: it's a little easier for min/max since you just drop some,
while here you have to change values, but it's certainly
doable
astearns: so proposed is to simplify as much as possible (combining
items when it can) for computed value
fantasai: I feel like manipulating the result will be odd, but...
RESOLVED: simplify as much as possible (combining items when it can)
for computed value
Allow `if()` in descriptors
---------------------------
scribe: fantasai
github: https://github.com/w3c/csswg-drafts/issues/11941
TabAtkins: We have if() function which allows conditional tests. But
only works in properties, because substitution functions
are only defined in that context
TabAtkins: but there are good use cases for being able to use if()
with global tests like MQ
TabAtkins: It could be done by using conditional at-rules today
TabAtkins: if() is a nice syntactic convenience.
TabAtkins: Anders says it's doable as long as we're clear which tests
can be used outside an element context
TabAtkins: I agree, should be useful. Right now just style query and
color scheme query that we'd have to turn off. But doable
TabAtkins: So I suggest we accept this feature, spec text TBD
<lea> +1, many upsides, little to no downsides
fantasai: I think it makes sense, but rather than excluding we should
use an allowlist
TabAtkins: agree
fantasai: like there's a lot of variable stuff we can't do outside of
an element context
emilio: relatedly, there might be descriptors (e.g. @font-face) ...
emilio: even in the case in the OP, @font-face is exposed to the
FontFace API
emilio: It's fairly different from media case
emilio: I'm not sure how we should expose these values when you ask
for them
emilio: Seems weird to do the substitution when you do the getter
emilio: requires updating style information, it's a bit annoying
TabAtkins: currently those return strings
TabAtkins: so I think it's fine?
TabAtkins: we'd have to define how substitutions are reflected
TabAtkins: and I think it's fine to say that the OM doesn't reflect
substitutions
emilio: That means the programmatic functions need to accept these,
and those are exposed to workers, which is annoying
emilio: There's a lot of edge cases to think about
emilio: And that's just the @font-face ones
emilio: And then @page stuff might affect MQ?
astearns: So seems useful thing, but complications to work through.
lea: Do we need to exclude variables syntactically?
lea: Could define that variables resolve to their initial value.
lea: idk if web compatible
lea: but as an author, that's what I would expect
TabAtkins: I would say that those tests, not on allow list, would
just resolve to false
TabAtkins: that would potentially allow us to turn on some tests in
the future
fantasai: given all that, I think we should maybe defer this to
later... I don't want us to spend tons of times going down
the rabbit holes and delaying if() on elements
fantasai: so maybe say this is a good idea, but for the next level.
sounds like it'll be a bunch of work on the spec and impl
side, for something that is technically doable right now in
other ways
<emilio> +1
fantasai: so I worry if we batch it all together it'll be slower to
get the core feature interoperable
astearns: so we could resolve to accept it but punt to the next
level. no more delay needed
astearns: agree we shouldn't gate the existing if() on this
<TabAtkins> +1
PROPOSED: Work on this in the next level.
RESOLVED: Work on this in the next level (in consideration of all the
complications outlined above).
Clarify how `<attr-unit>` should be parsed
------------------------------------------
github: https://github.com/w3c/csswg-drafts/issues/12479
TabAtkins: Right now stuff for <attr-unit> production says "take this
numerical attribute and pretend it's a pixel value"
TabAtkins: says to parse it as a <number>
TabAtkins: How literal are we about that? Is it a NUMBER token from
Syntax? Or a <number> value from values?
TabAtkins: the latter allows all the math functions
TabAtkins: we need to clarify the spec in either direction
TabAtkins: an earlier version of extended attr() was very
specifically only looking for number tokens
TabAtkins: for implementation simplicity reasons
TabAtkins: it solved the use cases and was easy to do
TabAtkins: as you allow more complicated things, it becomes more
complicated
TabAtkins: probably not worse than allowing more complexity in other
values?
TabAtkins: originally we added calc(), and then a jillion more math
functions
fantasai: this brings up interesting question
fantasai: do we want to distinguish between CSS types (which should
parse as full CSS) but have a different syntax for simple
things
fantasai: like "just a number"
fantasai: for some core types like number... for string type we have
a special type that doesn't do CSS parsing and just uses
the attr value, so we already have cases for this
distinction
fantasai: so maybe this is a case where we distinguish
fantasai: if you want CSS length values, use type(<length>), if you
want CSS numbers, use type(<number.)
fantasai: but if you just want a number interpreted as px, use this
other syntax
fantasai: and maybe that syntax is just the syntax we had before,
with the keywords
emilio: What is the behavior with custom properties?
emilio: I would assume you ? parse calc() there
emilio: consistency with that would be fair
fantasai: This would mean any type() annotation is consistent with
properties, but attr() would allow some extra things
emilio: Agree that a raw number makes sense
fantasai: Yeah, raw number with a unit appended, and a raw string,
those are the special things we should be able to do
astearns: We could take a resolution to define the type() parsing and
have a new issue for extending the attr() for these raw
types
TabAtkins: This issue is about the unit keywords, not type() function
TabAtkins: I'm happy with the proposal, where type keywords work for
raw values, and anything with type() function is a CSS
parse.
TabAtkins: Probably means we want to add one more keyword for number,
for raw numbers
TabAtkins: currently we have raw-string, all the units, and %
fantasai: So you're proposing raw-number?
<TabAtkins> proposed: all the unit/% keywords are "raw number" - only
a literal number. Add raw-number that's the same without
a unit. All type() values are CSS-parsed, so
type(<number>) allows calc()/etc
PROPOSED: type() values are parsed just as in custom properties.
Other attr() type values (units % etc) are literals.
emilio: seems fair. I wish the keyword was just 'number'. It's a
fairly common use cases, and 90% of use cases would be
covered by it.
emilio: raw-string is the default, so right now you don't have to
type it
TabAtkins: I'm fine with plain 'number'.
PROPOSED: type() values are parsed just as in custom properties.
Other attr() type values (units % etc) are literals. Add a
'number' keyword for literal numbers.
RESOLVED: type() values are parsed just as in custom properties.
Other attr() type values (units % etc) are literals. Add a
'number' keyword for literal numbers.
Using `if()` to do dark/light switching of image urls
-----------------------------------------------------
github: https://github.com/w3c/csswg-drafts/issues/10577
astearns: proposed to add a color-scheme() test to if()
<TabAtkins> add color-scheme() test to if(), which tests the used
color scheme on the element
fantasai: is that cyclic?
emilio: should we look at inherited value?
TabAtkins: not cyclic because wouldn't work on color-scheme property
TabAtkins: Looking at parent color-scheme wouldn't be useful.
TabAtkins: It's not a complicated dependency, so should be
straightforward.
TabAtkins: we can make color-scheme() test fail in color-scheme
property.
emilio: There are typed custom properties as well...
TabAtkins: We already have spec mechanisms to handle arbitrarily
complex dependency chains accurately
TabAtkins: this would add a dependency in
TabAtkins: same as using a style test depends on custom property
TabAtkins: thing to be concerned about is if likely that future
language editions would create cycles that aren't already
present
TabAtkins: but I think it's safe to say that color-scheme won't
depend on anything in the future
fantasai: I defer to impls on implementability
fantasai: but if we'll have special behavior on color-scheme prop to
have it work, we should do the same as font-size/em
fantasai: it also makes a somewhat useful thing - you can do the
opposite color scheme of your parent
astearns: Question of whether to add color-scheme() to container
queries as well
fantasai: if we're doing the hard one we should do the easy one :)
<lea> +1 for color-scheme() on color-scheme resolving to parent
color-scheme. I wish we had done that with var() too
TabAtkins: Agree we should keep them in sync.
PROPOSED: Add color-scheme() test to both @container queries and
if(). Open question on what color-scheme() checks when in the
color-scheme property.
fantasai: Can we just resolve on the color-scheme() checks parent
color-scheme?
TabAtkins: unsure, let's leave it unresolved and discuss in other
issue
RESOLVED: Add color-scheme() test to both @container queries and
if(). Open issue on what color-scheme() returns when used
in color-scheme property.
*-interpolate() grammar can lead to confusing values
----------------------------------------------------
github: https://github.com/w3c/csswg-drafts/issues/12348
TabAtkins: kbabbitt noted that the interpolate puts the value we're
interpolating first, then options, then a list of stops
TabAtkins: he points out that this looks confusing because some of
the options can be affecting output values
<TabAtkins> background-color: color-interpolate(300px in hsl, 200px:
red, 500px: green, 600px: blue);
TabAtkins: but are placed next to the input value
TabAtkins: 300px in hsl loos like it's modifying 300px
TabAtkins: wanted to see if we could rearrange syntax to make it less
confusing
TabAtkins: oriol's suggestion was to put the options first
TabAtkins: this puts the options close to the 'interpolate(' word
<TabAtkins> color-interpolate(in hsl / 300px, 200px: red, 500px:
green, 600px: blue)
TabAtkins: uses a slash to separate the value out, to avoid syntactic
ambiguity
lea: Not a strong objection, but I would prefer to avoid mixing
slashes vs commas, because CSS is inconsistent in how it
handles it
lea: can we use a keyword?
<lea> color-interpolate(in hsl at 300px, 200px: red, 500px: green,
600px: blue)
fantasai: I haven't looked at this issue, but here's a wild idea. all
the other values have a colon, what if you said `300px:
in hsl`
fantasai: so the first value is what we're interpolating, and options
for the values
oriol: Another point is, I believe we can just use a comma as a
separator as there's no grammar overlap
oriol: agree with Lea that mixing slash and comma is a bit weird. so
I think we can just use a comma
lea: Syntactically we want to distinguish prelude from list
lea: So we want it to not look the same
fantasai: I don't love having them in a different order. putting the
value, the core thing you're acting on, last...
<lea> `color-interpolate(at 300px in hsl, 200px: red, 500px: green,
600px: blue)` seems ok to me?
fantasai: it's also often not '300px', it's something complicated
like a var()
fantasai: it's one of the most important parts of the function, the
first thing you'll think about
astearns: Sounds like we don't have consensus on a direction yet.
RESOLVED: Explore options for making a syntactic distinction between
input and interpolation options.
Received on Wednesday, 24 September 2025 23:12:14 UTC