Skip to main
Article
A stepped gradient of a pink hue in 2% lightness increments from 100% to 58%, labeled 'spec'

CSS Working Group Updates for June & July

What I’ve been working on as an Invited Expert

The CSS Working Group has regular face-to-face meetings (hybrid online/in-person) throughout the year, and they always result in a flurry of activity! Here’s a rundown of some highlights from the last few months, with a focus on the features I maintain.

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.

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)) {
  /* styles inside larger containers */
}

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.

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!

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) {
  /* these styles apply to the :scope (main) */
  /* but have no specificity */
  color: teal;
  border: thick solid;
}

This is equivalent to using :where(:scope):

@scope (main) {
  /* these styles apply to the :scope (main) */
  /* but have no specificity */
  :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:

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:

  1. 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.
  2. 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 > &) {
    /* the '&' above refers to 'header' */

    & h2 { /* this '&' refers to 'main > header' */ }
  }
}

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 { /* should we get an implied 'header' scope? */ }
}

That’s potentially a nice shortcut to have?

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!

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.

Upcoming Workshop

Mia from behind,
standing at a laptop -
speaking to a conference audience
and gesturing to one side

Cascading Style Systems

A workshop on resilient & maintainable CSS

New CSS features are shipping at an unprecedented rate – cascade layers, container queries, the :has() selector, subgrid, nesting, and so much more. It’s a good time to step back and understand how these tools fit together in a declarative system – a resilient cascade of styles.

Register for the October workshop »

Recent Articles

  1. see all Article posts
  2. Article post type

    Partial Feature Queries, Relaxed Layout Containment, and More

    CSS Working Group updates from July

    Over the last month, the CSS Working Group has determined we can loosen containment restrictions for query containers, and agreed on a syntax for special-case support queries (like support for the gap property in a flex context, or support for align-content in a block flow context).

    see all Article posts
  3. A dog zooming by the camera, up-close, body twisted and eyes wide as it circles a grass yard
    Article post type

    Zoom, zoom, and zoom

    The three types of browser (and CSS!) magnification

    I’m working on an article about fluid typography, and relative units. But instead, I fell down this rabbit hole – or a cleverly-disguised trap? – trying to understand ‘zoom’ in the browser (not Zoom™️ the software). Since I couldn’t find any up-to-date articles on the subject, I thought I shoul…

    see all Article posts