question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How do we plan to handle inconsistencies with the new CSS Nesting specification?

See original GitHub issue

The CSS Nesting specification is moving quickly towards a First Public Working Draft, and potential implementation. We have some time before it will stabilize and land in browsers, but there is a lot of syntax overlap with some key differences in output. I wanted to document some of the ways it will conflict with (and give different results compared to) Sass nesting.

The spec is based very much on our feature, using the same & syntax, but adjusted to ensure that browsers only need single-character look-ahead. That means:

  1. Every nested selector requires the & parent reference
  2. Our syntax is supported directly when the nested selector starts with &

Sass-only

There are a few use cases we support, but CSS does not:

.block {
  p { … } /* CSS requires the implied & prefix for a descendant combinator */
  main & { … } /* CSS requires & to be the first character for this syntax */
  &_element { … } /* CSS does not support string-concatenation */
}

CSS-only

CSS provides a second syntax that has no current meaning with Sass. The @nest rule allows more complex combinations, while still avoiding infinite-lookahead.

/* required in cases such as… */
.block {
  @nest main & { … }
  @nest :not(&) { … }
}

/* optional in some cases… */
.block {
  @nest & p { … }
  @nest &.active { … }
}

CSS/Sass overlap

Then there are some rules supported by both Sass & CSS syntax, such as:

.foo, .bar {
  color: blue;
  & + .baz, &.qux { color: red; }
}

.foo {
  color: blue;
  & .bar & .baz & .qux { color: red; }
}

While the syntax is the same, these cases are interpreted in slightly different ways. Sass generates all the possible combinations:

.foo, .bar { color: blue; }
.foo + .baz, 
.foo.qux, 
.bar + .baz, 
.bar.qux { color: red; }

.foo { color: blue; }
.foo .bar .foo .baz .foo .qux { color: red; }

While CSS relies on the :is() selector for a simpler reading:

   .foo, .bar { color: blue; }
   :is(.foo, .bar) + .baz,
   :is(.foo, .bar).qux { color: red; }

   .foo { color: blue; }
   .foo .bar .foo .baz .foo .qux { color: blue; }

In those examples the output has a different structure, but generally the same meaning – both in terms of matching and specificity. But there are some cases where the specificity is significantly different. The :is() pseudoclass takes on the highest specificity of any selector inside it, even if that selector is not matched. Compiled selector lists, on the other hand, take the specificity of the highest matching selector. For example:

.error, #e404 {
  & > .baz { color: red; }
}

In CSS, this is equivalent to:

:is(.error, #e404) > .baz /* specificity: [1, 1, 0] */
{ color: red; }

While Sass generates:

.error > .baz, /* specificity: [0, 2, 0] */
#e404 > .baz /* specificity: [1, 1, 0] */
{ color: red; }

Thoughts…

We don’t generally add internal functionality around new CSS features before they have been implemented by several browsers – but the overlapped-syntax-with-different-meaning use-cases could cause a problem here.

  • Uses of @nest can likely be passed through without modifications
    • It might be possible short-term to require @nest as a marker for accessing the native CSS feature?
    • Long-term we will need to define how this syntax integrates (or doesn’t) with our selector functions
  • At some point we’ll need to change our output to use :is() or pass-through/generate the appropriate CSS nesting syntax
    • In some cases that can happen smoothly without any change for authors
    • In some cases that will be a specificity-breaking change
  • For Sass-only syntax, we have to decide what CSS is generated in each case
    • Implicit ancestor & could be added to the output or compiled according to the spec
    • Non &-prefixed selectors could add @nest to the output, or be compiled according to spec
    • We could continue to compile concatenation basically as-is?

I’ve likely missed some things here, but thought it was worth starting the conversation.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:16
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

12reactions
nex3commented, Apr 6, 2021

This is certainly a complex and difficult issue. Generally speaking, I support trying to converge on the specified CSS semantics, but even generating :is() selectors is fraught—many browsers still don’t support it at all, any many more have only partial prefixed support with likely incorrect specificity rules. So we’re stuck between a rock and a hard place: we can either support the newest CSS semantics and break compatibility with older browsers; or we can continue to support our existing semantics and be subtly incompatible with standard CSS.

I think we should follow a two-phase plan:

  1. For the time being, we keep the Sass semantics as-is. As you point out, users can opt into the latest CSS behavior using @nest which we will always pass through as-is. It is unfortunate to violate our CSS-superset policy here, but preserving backwards-compatibility for such a widely-used feature is more important.

  2. Once more than 98% of the global browser market supports unprefixed :is() (as per our browser compatibility policy), switch & to generate :is() rather than multiple different selectors. Ideally, we can align this with a 2.0.0 release, but that may or may not be realistic based on timings.

1reaction
nex3commented, Dec 17, 2022

Assuming the CSSWG moves forward with Option 3, we’re in a tricky situation because it doesn’t provide an obvious way for users to explicitly signal “I intend to use plain CSS syntax here”. @mirisuzanne and @stubbornella, let’s chat about this next time we meet.

Read more comments on GitHub >

github_iconTop Results From Across the Web

CSS Nesting Module - W3C
This module introduces the ability to nest one style rule inside another, with the selector of the child rule relative to the selector...
Read more >
Native CSS nesting: What you need to know - LogRocket Blog
Native CSS will support CSS nesting. What will that look like? What are its advantages? Learn more about native CSS nesting in this...
Read more >
The PostCSS Guide to Improving Selectors and Media Queries
The new CSS extensions specification introduces a way to store selectors in variables and reference them from other parts of the stylesheets.
Read more >
Handling common accessibility problems - MDN Web Docs
CSS tends to provide a lot fewer fundamental accessibility features than HTML, but it can still do just as much damage to accessibility...
Read more >
Top 10 most common CSS mistakes made by new and ...
If you decide to change the structure of the HTML, then you'll have to go back and update every combinator chain you've made...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found