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

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

Адам Аргайл
Adam Argyle
Мириам Сюзанна
Miriam Suzanne

Вложенность 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;
  }
}

@гнездо всегда

.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;
  }
}

@гнездо всегда

.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;
  }
}

@гнездо всегда

.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;
    }
  }
}

@гнездо всегда

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;
  }
}

@гнездо всегда

.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;
    }
  }
}

@гнездо всегда

.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;
    }
  }
}

@гнездо всегда

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;
  }
}

@гнездо всегда

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 до синтаксиса, который мы все узнаем и полюбим!

Проводить исследование!