Помогите выбрать синтаксис для вложенности CSS.

Двум конкурирующим синтаксисам нужна ваша помощь в определении того, какой из них следует отдать предпочтение кандидату на спецификацию.

Вложенность CSS — это удобное дополнение к синтаксису, позволяющее добавлять CSS-код внутри набора правил. Если вы использовали SCSS , Less или Stylus , то наверняка видели несколько разновидностей этого:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Который после компиляции в обычный CSS препроцессором превращается в обычный CSS следующим образом:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

Официальная версия CSS этого синтаксиса находится в стадии активного рассмотрения, и у нас возникло разделение мнений, поэтому мы хотели бы обратиться за помощью к сообществу, чтобы разрешить ситуацию. Остальная часть этой публикации будет посвящена вариантам синтаксиса, чтобы вы могли быть в курсе событий и пройти опрос в конце.

Почему точный пример вложения, показанный выше, не может быть синтаксисом вложения CSS?

Существует несколько причин, по которым самый популярный синтаксис вложенности нельзя использовать как есть:

  1. Неоднозначный синтаксический анализ
    Некоторые вложенные селекторы могут выглядеть точно так же, как свойства, и препроцессоры могут обрабатывать их и управлять ими во время сборки. Браузеры не обладают такими же возможностями, поэтому селекторы никогда не следует трактовать произвольно.

  2. Конфликты анализа препроцессора
    Вложенность, реализованная в CSS, не должна нарушать работу препроцессоров или существующие процессы разработки. Это было бы разрушительно и неуважительно по отношению к этим экосистемам и сообществам.

  3. Ожидание :is()
    Для базовой вложенности не требуется :is() , но для более сложной вложенности требуется. См. пример №3 для краткого введения в списки селекторов и вложенность. Представьте, что список селекторов находится в середине селектора, а не в начале. В этом случае :is() требуется для группировки селекторов в середине другого селектора.

Обзор того, что мы сравниваем

Мы хотим добиться правильной вложенности CSS, и в этом ключе мы привлекаем сообщество. В следующих разделах будут описаны три возможные версии, которые мы рассматриваем. Затем мы рассмотрим несколько примеров использования для сравнения, а в конце проведём небольшой опрос, чтобы узнать, какой вариант вам больше нравится.

Вариант 1: @nest

Это текущий синтаксис, указанный в CSS Nesting 1. Он предлагает удобный способ вложения стилей, начиная новые вложенные селекторы с помощью & . Он также предлагает @nest для размещения контекста & в любом месте внутри нового селектора, например, когда вы добавляете не только подлежащие элементы. Он гибкий и минималистичный, но за счёт необходимости запоминать @nest или & в зависимости от вашего варианта использования.

Вариант 2: @nest ограничен

Это более строгий вариант, призванный сократить упомянутые затраты на запоминание двух методов вложенности. Этот ограниченный синтаксис допускает вложение только после @nest , поэтому отсутствует удобный шаблон «только добавление». Это устраняет неоднозначность выбора, создавая один легко запоминающийся способ вложения, но жертвует краткостью ради соблюдения правил.

Вариант 3: Скобки

Чтобы избежать двойного синтаксиса и лишних сложностей, характерных для предложений @nest , Мириам Сюзанна и Элика Этемад предложили альтернативный синтаксис , основанный на дополнительных фигурных скобках. Это обеспечивает ясность синтаксиса, добавляя всего два символа и не добавляя новых at-правил. Кроме того, это позволяет группировать вложенные правила по требуемому типу вложенности, упрощая работу с несколькими селекторами с одинаковой вложенностью.

Пример 1 — Прямое вложение

@гнездо

.foo {
  color: #111;

  & .bar {
    color: #eee;
  }
}

@nest всегда

.foo {
  color: #111;

  @nest & .bar {
    color: #eee;
  }
}

скобки

.foo {
  color: #111;

  {
    & .bar {
      color: #eee;
    }
  }
}

Эквивалентный CSS

.foo {
  color: #111;
}

.foo .bar {
  color: #eee;
}

Пример 2 — Сложное вложение

@гнездо

.foo {
  color: blue;

  &.bar {
    color: red;
  }
}

@nest всегда

.foo {
  color: blue;

  @nest &.bar {
    color: red;
  }
}

скобки

.foo {
  color: blue;

  {
    &.bar {
      color: red;
    }
  }
}

Эквивалентный CSS

.foo {
  color: blue;
}

.foo.bar {
  color: red;
}

Пример 3 — Списки селекторов и вложенность

@гнездо

.foo, .bar {
  color: blue;

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

@nest всегда

.foo, .bar {
  color: blue;

  @nest & + .baz,
  &.qux {
    color: red;
  }
}

скобки

.foo, .bar {
  color: blue;

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

Эквивалентный CSS

.foo, .bar {
  color: blue;
}

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

Пример 4 — Несколько уровней

@гнездо

figure {
  margin: 0;

  & > figcaption {
    background: lightgray;

    & > p {
      font-size: .9rem;
    }
  }
}

@nest всегда

figure {
  margin: 0;

  @nest & > figcaption {
    background: lightgray;

    @nest & > p {
      font-size: .9rem;
    }
  }
}

скобки

figure {
  margin: 0;

  {
    & > figcaption {
      background: lightgray;

      {
        & > p {
          font-size: .9rem;
        }
      }
    }
  }
}

Эквивалентный CSS

figure {
  margin: 0;
}

figure > figcaption {
  background: hsl(0 0% 0% / 50%);
}

figure > figcaption > p {
  font-size: .9rem;
}

Пример 5 — Вложение родительских элементов или смена темы

@гнездо

.foo {
  color: red;

  @nest .parent & {
    color: blue;
  }
}

@nest всегда

.foo {
  color: red;

  @nest .parent & {
    color: blue;
  }
}

скобки

.foo {
  color: red;

  {
    .parent & {
      color: blue;
    }
  }
}

Эквивалентный CSS

.foo {
  color: red;
}

.parent .foo {
  color: blue;
}

Пример 6 — Смешивание прямого и родительского вложения

@гнездо

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    &.baz {
      color: green;
    }
  }
}

@nest всегда

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    @nest &.baz {
      color: green;
    }
  }
}

скобки

.foo {
  color: blue;

  {
    .bar & {
      color: red;

      {
        &.baz {
          color: green;
        }
      }
    }
  }
}

Эквивалентный CSS

.foo {
  color: blue;
}

.bar .foo {
  color: red;
}

.bar .foo.baz {
  color: green;
}

Пример 7 — Вложенность медиа-запросов

@гнездо

.foo {
  display: grid;

  @media (width => 30em) {
    grid-auto-flow: column;
  }
}

или явно / расширенно

.foo {
  display: grid;

  @media (width => 30em) {
    & {
      grid-auto-flow: column;
    }
  }
}

@nest всегда (всегда явный)

.foo {
  display: grid;

  @media (width => 30em) {
    @nest & {
      grid-auto-flow: column;
    }
  }
}

скобки

.foo {
  display: grid;

  @media (width => 30em) {
    grid-auto-flow: column;
  }
}

или явно / расширенно

.foo {
  display: grid;

  @media (width => 30em) {
    & {
      grid-auto-flow: column;
    }
  }
}

Эквивалентный CSS

.foo {
  display: grid;
}

@media (width => 30em) {
  .foo {
    grid-auto-flow: column;
  }
}

Пример 8 — Вложенные группы

@гнездо

fieldset {
  border-radius: 10px;

  &:focus-within {
    border-color: hotpink;
  }

  & > legend {
    font-size: .9em;
  }

  & > div {
    & + div {
      margin-block-start: 2ch;
    }

    & > label {
      line-height: 1.5;
    }
  }
}

@nest всегда

fieldset {
  border-radius: 10px;

  @nest &:focus-within {
    border-color: hotpink;
  }

  @nest & > legend {
    font-size: .9em;
  }

  @nest & > div {
    @nest & + div {
      margin-block-start: 2ch;
    }

    @nest & > label {
      line-height: 1.5;
    }
  }
}

скобки

fieldset {
  border-radius: 10px;

  {
    &:focus-within {
      border-color: hotpink;
    }
  }

  > {
    legend {
      font-size: .9em;
    }

    div {
      + div {
        margin-block-start: 2ch;
      }

      > label {
        line-height: 1.5;
      }
    }}
  }
}

Эквивалентный CSS

fieldset {
  border-radius: 10px;
}

fieldset:focus-within {
  border-color: hotpink;
}

fieldset > legend {
  font-size: .9em;
}

fieldset > div + div {
  margin-block-start: 2ch;
}

fieldset > div > label {
  line-height: 1.5;
}

Пример 9 – Сложная вложенная группа «Кухонная мойка»

@гнездо

dialog {
  border: none;

  &::backdrop {
    backdrop-filter: blur(25px);
  }

  & > form {
    display: grid;

    & > :is(header, footer) {
      align-items: flex-start;
    }
  }

  @nest html:has(&[open]) {
    overflow: hidden;
  }
}

@nest всегда

dialog {
  border: none;

  @nest &::backdrop {
    backdrop-filter: blur(25px);
  }

  @nest & > form {
    display: grid;

    @nest & > :is(header, footer) {
      align-items: flex-start;
    }
  }

  @nest html:has(&[open]) {
    overflow: hidden;
  }
}

скобки

dialog {
  border: none;

  {
    &::backdrop {
      backdrop-filter: blur(25px);
    }

    & > form {
      display: grid;

      {
        & > :is(header, footer) {
          align-items: flex-start;
        }
      }
    }
  }

  {
    html:has(&[open]) {
      overflow: hidden;
    }
  }
}

Эквивалентный CSS

dialog {
  border: none;
}

dialog::backdrop {
  backdrop-filter: blur(25px);
}

dialog > form {
  display: grid;
}

dialog > form > :is(header, footer) {
  align-items: flex-start;
}

html:has(dialog[open]) {
  overflow: hidden;
}

Время голосовать

Надеемся, вы считаете это сравнение справедливым и представляете пример рассматриваемых нами вариантов синтаксиса. Внимательно изучите их и сообщите нам ниже, какой из них вам больше нравится. Мы ценим вашу помощь в развитии вложенности CSS до синтаксиса, который мы все поймём и полюбим!

Примите участие в опросе!