Note:
This is hopefully the first post in a series,
providing updates around my work
on the CSS Working Group.
These contributions take a lot of time and effort.
If you’re interested in supporting
our open-source work around CSS,
consider becoming a GitHub sponsor,
or contributing to our
Open Collective.
Container Queries & Units
spec:
Containment, Level 3
Conditional Rules, Level 5
Moving to a new specification
Issue:
Reorganizing the Containment specs (#10433)
While Container Queries rely heavily
on the idea of ‘containment’,
the two features are not tightly intertwined.
We’re working to
loosen the containment restrictions
even more if we can.
To help clarify that relationship,
and simplify maintenance of these distinct features,
Container Queries are moving
from the Containment module (level 3)
into the Conditional Rules module (level 5).
During this transition,
Container Queries disappeared from any public spec –
basically moving back into Editor’s Draft status,
after already shipping across browsers.
Maybe it was all a dream!
But a new Working Draft
was published this week.
Container Queries are back!
Querying the shadow DOM
After many issues were raised (thanks for making noise!),
we were able to revert the limitation on
querying containers across shadow-DOM boundaries.
Moving forward, slotted elements should be able to query
containers that are defined by a parent shadow-DOM.
Container units will also
get their sizing from
relevant shadow-DOM containers.
Viewport units and scrollbars
For years, the viewport units (vi
/vw
etc)
have been based on the full dimensions of the viewport,
ignoring any possible scrollbars.
That can lead to accidental overflow
if you size something to 100vw
,
and there’s a vertical scrollbar present.
While sometimes frustrating,
that behavior is designed to avoid loop conditions.
Elements sized with viewport units can cause scrollbars,
which would change the size of the unit,
potentially removing the need for scrollbars.
But we don’t have that issue when scrollbar spacing is stable –
using overflow: scroll
or
scrollbar-gutter: stable
–
so the working group resolved to loosen the restriction.
Moving forward, stable scrollbars
will be removed from the viewport measurements.
I requested we do the same
for container-relative cq*
units,
so that will also be fixed.
See the Pen Stable scroll bars & CQ units by @miriamsuzanne on CodePen.
Note that container units
(and container queries generally)
are relative to the
content-box
of a container.
So adding/removing padding
from the container
also/already
changes the size of our cq*
units.
Variables in container (size) queries
Issue: Can we allow custom properties in dimensional container queries?
(#8088)
A bit farther back, but still exciting:
we agreed that
variables should be allowed in container size queries.
In the future,
you’ll be able to write:
@container (width > var(--small)) {
}
The variable will resolve on the container.
So you won’t be able to make the variable different
for each element inside the query,
but it can be different for each container
being queried:
.container-1 { --small: 20em; }
.container-2 { --small: 600px; }
Zoom and container queries
Issue: Zoom and container queries (#10268)
We clarified the
impact of zoom
on resolving container queries.
Zoom
increases the size of a CSS pixel
in relation to the surrounding layout –
so things appear larger,
but maintain their internal dimensions.
A 50px
box is still 50px
,
we’ve just changed the size of our px
unit.
To keep things simple and consistent,
we determined that containers
should report their own size as they see it.
So a 50px
container at 200%
zoom
will continue to report 50px
in a container query.
This is similar to how relative units
and custom properties resolve
in a container query.
See the Pen Container Queries and Zoom by @miriamsuzanne on CodePen.
If you want to learn more about
all the variations of
browser and CSS magnification,
I’ve got you covered.
Is container query adoption ‘too low’?
I’ve seen a wide range of people worried that
“people aren’t using container queries” –
but we don’t actually have a way of tracking that!
I was curious where this concern comes from,
what we’re comparing to,
and what expectations we should have
about new features showing up in production.
I wrote up my thoughts,
summarized by the title of the article:
Learn Grid Now, Container Queries Can Wait.
We’ll be doing a live stream
with Stephanie Eckles
(ModernCSS.dev and SmolCSS)
about the reasons to use grid,
and some of the quick ways to get started.
Cascade Layers
Spec: Cascading and Inheritance, Level 5
Imposing layers on linked styles in HTML
We need
a layer
attribute for the html <link>
element.
We’ve known this for years,
but the issue has been stalled in the WHATWG,
as we try to sort out feature-testing new attributes
(also important!).
While I’d love to have feature queries
in html links as well,
I hope that
separating these two features
might unblock the essential progress on Cascade Layers.
I wrote an explainer,
and asked the w3c
Technical Architecture Group
for review,
as a step towards getting more formal
implementor feedback.
When I have time,
I will follow-up with a similar explainer
on the need for HTML feature queries.
Explicit placement of unlayered styles in the cascade
There’s been a very active discussion
around handling unlayered styles in the cascade.
By default,
unlayered styles have priority over layered styles.
This is an essential default in my mind,
but there are many use-cases
where we would want override the default.
The mega-thread discussion
is now eating it’s own tail,
but it seems like we have three basic approaches
to choose from
(and then small variations on each).
I have a clear favorite there,
but feel free to leave your thoughts as well.
If possible,
try not to get lost in alternative naming details –
and focus first on the overall behavior of the feature!
Scope
Spec: Cascading and Inheritance, Level 6
Scope specificity when nesting
We recently resolved
a series of issues
related to scope specificity and nesting.
In the future,
you’ll be able to put style declarations
directly into an @scope
rule,
and they will apply to the scope root with no added specificity:
@scope (main) {
color: teal;
border: thick solid;
}
This is equivalent to using :where(:scope)
:
@scope (main) {
:where(:scope) {
color: teal;
border: thick solid;
}
}
The number of option here –
and the interactions between features –
can be a bit confusing,
since each has different implications.
While the options and interactions are complex,
this resolution keeps the rules consistent for each feature:
- The
@scope
rule itself
does not add specificity to nested styles.
We can think of @scope (<selector>)
as similar to :where(<selector>)
,
which also hides the specificity selectors inside.
- The
@scope
rule can have directly-nested styles,
and they apply to the scoping root.
That doesn’t change anything about the selector specificity.
- Like
:root
, the :scope
selector is a pseudo-class,
and always has the same specificity as any other class
(0,1,0
).
- We can always think of the
&
selector
as being replaced by :is(<parent-selector>)
.
In this case, the parent selector is the scope root selector (main
),
with a specificity of [0,0,1]
.
Scope roots are no longer forgiving
‘Forgiving selector lists’
are handy in many situations,
but are now limited to :is()
and :where()
.
I don’t love that limitation,
but it seems like we’re stuck with it.
For that reason,
scope-start and scope-end selectors
also need to be un-forgiving.
Of course,
we can use :is()
and :where()
inside the scope start/end selectors –
so this is an inconvenience,
but not a change in functionality.
Implicit scopes in a nested context
Issue: Can we support implicit scopes in nested settings? (#10497)
Scope and nesting
obviously need to work together,
since they overlap in some major ways.
The basic rules for that interaction are:
- When a selector is directly inside a scope rule
(that’s the whole point!),
those selectors are ‘nested’ relative
to the scope root element,
as defined by the scope-start selector.
- When a scope rule is directly inside another selector,
the scope-start and scope-end selectors
act as ‘nested’ selectors relative to the parent.
header {
@scope (main > &) {
& h2 { }
}
}
I’ve opened a new issue to discuss
implicit scopes when nesting.
If we leave off the scope-start,
can we treat it like (&)
?
header {
@scope { }
}
That’s potentially a nice shortcut to have?
Mixins & Functions
Spec: Mixins & Functions, Level 1 (Editor’s Draft)
A draft spec for functions
The Editor’s Draft is underway!
It isn’t complete (mixins are missing),
but it lays out our current plan for custom CSS functions.
Thinking about mixins
I wrote an article about
how mixins should interact with the cascade.
I’d be happy for your thoughts!
I’ve heard some browsers
expressing doubt about the need for mixins.
I plan to start documenting use-cases
where CSS mixins would have a large impact.
If you have examples,
please reach out!
Wide-gamut colors
Issue: Channel clipping breaks author expectations, especially when using ‘perceptually uniform’ spaces (#9449)
I’m not an editor on the Color spec,
but I have some opinions anyway.
Browsers currently use ‘channel clipping’
to render out-of-gamut colors,
and the results can be wild.
I made a comparison pen, to see the difference between browser rendering and what the spec calls for:
See the Pen Color Clipping v Mapping by @miriamsuzanne on CodePen.
Good news!
We now have
a tentative compromise solution
that all browsers agree on.
It’s not perfect,
but it’s a big improvement
over the current state of things.
You can test how it impacts the linked pen
by turning on the css gamut mapping
feature flag
in some Chromium browser versions (go to chrome://flags
).
We talked about this issue
(and the potential solution)
on our Winging It Live stream
last month –
along with details of our
OddContrast
tool for wide-gamut contrast checking.