Aide au choix d'une syntaxe pour l'imbrication CSS

Deux syntaxes concurrentes ont besoin de votre aide pour déterminer celle qui doit être promue à un candidat à la spécification.

Adam Argyle
Adam Argyle
Miriam Suzanne
Miriam Suzanne

L'imbrication CSS est un ajout de syntaxe pratique qui permet d'ajouter du code CSS à l'intérieur d'un ensemble de règles. Si vous avez utilisé SCSS, Less ou Stylet, vous avez certainement vu quelques types de cette fonctionnalité:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Une fois compilée avec un CSS standard par le préprocesseur, elle devient un CSS standard comme ceci:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Une version CSS officielle de cette syntaxe fait l'objet d'un examen approfondi et nous préférons utiliser l'aide de la communauté pour mettre fin à la relation. Le reste de cet article présentera les options de syntaxe afin que vous puissiez vous sentir informé pour répondre à une enquête à la fin.

Pourquoi l'exemple d'imbrication exact présenté ci-dessus ne peut-il pas correspondre à la syntaxe d'imbrication CSS ?

Voici quelques raisons pour lesquelles la syntaxe d'imbrication la plus populaire ne peut pas être utilisée telle quelle:

  1. Analyse ambiguë
    Certains sélecteurs imbriqués peuvent ressembler exactement à des propriétés et à des préprocesseurs qui peuvent les résoudre et les gérer au moment de la compilation. Les moteurs de navigateur n'ont pas les mêmes affordances. Les sélecteurs ne doivent jamais être interprétés de façon vague.

  2. Conflits d'analyse des préprocesseurs
    La méthode d'imbrication CSS utilisée ne doit pas endommager les préprocesseurs ni les workflows d'imbrication existants des développeurs. Cela perturberait et négligeait ces écosystèmes et ces communautés.

  3. En attente de :is()
    L'imbrication de base n'a pas besoin de :is(), contrairement à une imbrication plus complexe. Consultez l'exemple n° 3 pour obtenir une présentation rapide des listes de sélecteurs et de l'imbrication. Imaginez que la liste de sélecteurs se trouve au milieu d'un sélecteur et non au début. Dans ce cas, :is() est requis pour regrouper les sélecteurs au milieu d'un autre sélecteur.

Présentation de ce que nous comparons

Nous voulons que l'imbrication des CSS soit efficace, et dans cet esprit, nous incluons la communauté. Les sections suivantes décrivent les trois versions possibles que nous évaluons. Nous examinerons ensuite quelques exemples d'utilisation à des fins de comparaison. À la fin, nous vous proposerons une enquête visant à vous demander quelle option vous avez privilégiée.

Option 1: @nest

Il s'agit de la syntaxe actuellement spécifiée dans Imbrication CSS 1. Il s'agit d'un moyen pratique d'imbriquer des styles d'ajout en démarrant de nouveaux sélecteurs imbriqués avec &. Il propose également @nest comme moyen de placer le contexte & n'importe où dans un nouveau sélecteur, par exemple lorsque vous ne vous contentez pas d'ajouter des sujets. Cette solution est flexible et minimale, mais nécessite de mémoriser @nest ou &, en fonction de votre cas d'utilisation.

Option 2: @nest restricted

Il s'agit d'une alternative plus stricte, qui vise à réduire les dépenses liées à la mémorisation de deux méthodes d'imbrication. Cette syntaxe limitée permet uniquement l'imbrication après @nest. Il n'y a donc pas de modèle pratique "Ajouter uniquement". Suppression de l'ambiguïté du choix, création d'une méthode d'imbrication facile à mémoriser, mais sacrifice au profit de la convention.

Option 3: crochets

Afin d'éviter la syntaxe double ou le désordre supplémentaire liés aux propositions de @nest, Miriam Suzanne et Elika Etemad ont proposé une autre syntaxe qui repose plutôt sur des crochets supplémentaires. Cela permet une clarté de la syntaxe, avec seulement deux caractères supplémentaires et aucun nouvel at-rules. Elle permet également de regrouper les règles imbriquées par type d'imbrication requis, afin de simplifier plusieurs sélecteurs imbriqués de manière similaire.

Exemple 1 : Imbrication directe

@nest

.foo {
  color: #111;

  & .bar {
    color: #eee;
  }
}

@nest, toujours

.foo {
  color: #111;

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

crochets

.foo {
  color: #111;

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

Code CSS équivalent

.foo {
  color: #111;
}

.foo .bar {
  color: #eee;
}

Exemple 2 : Imbrication composée

@nest

.foo {
  color: blue;

  &.bar {
    color: red;
  }
}

@nest, toujours

.foo {
  color: blue;

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

crochets

.foo {
  color: blue;

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

Code CSS équivalent

.foo {
  color: blue;
}

.foo.bar {
  color: red;
}

Exemple 3 : Listes de sélecteurs et imbrication

@nest

.foo, .bar {
  color: blue;

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

@nest, toujours

.foo, .bar {
  color: blue;

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

crochets

.foo, .bar {
  color: blue;

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

Code CSS équivalent

.foo, .bar {
  color: blue;
}

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

Exemple 4 : Niveaux multiples

@nest

figure {
  margin: 0;

  & > figcaption {
    background: lightgray;

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

@nest, toujours

figure {
  margin: 0;

  @nest & > figcaption {
    background: lightgray;

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

crochets

figure {
  margin: 0;

  {
    & > figcaption {
      background: lightgray;

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

Code CSS équivalent

figure {
  margin: 0;
}

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

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

Exemple 5 : Imbrication des parents ou changement de sujet

@nest

.foo {
  color: red;

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

@nest, toujours

.foo {
  color: red;

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

crochets

.foo {
  color: red;

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

Code CSS équivalent

.foo {
  color: red;
}

.parent .foo {
  color: blue;
}

Exemple 6 : Combiner l'imbrication directe et l'imbrication parente

@nest

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    &.baz {
      color: green;
    }
  }
}

@nest, toujours

.foo {
  color: blue;

  @nest .bar & {
    color: red;

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

crochets

.foo {
  color: blue;

  {
    .bar & {
      color: red;

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

Code CSS équivalent

.foo {
  color: blue;
}

.bar .foo {
  color: red;
}

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

Exemple 7 : Imbrication de requêtes média

@nest

.foo {
  display: grid;

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

ou explicitement / étendu

.foo {
  display: grid;

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

@nest toujours (toujours explicite)

.foo {
  display: grid;

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

crochets

.foo {
  display: grid;

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

ou explicitement / étendu

.foo {
  display: grid;

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

Code CSS équivalent

.foo {
  display: grid;
}

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

Exemple 8 : Imbriquer des groupes

@nest

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, toujours

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

crochets

fieldset {
  border-radius: 10px;

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

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

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

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

Code CSS équivalent

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

Exemple 9 : Groupe d'imbrication complexe "Évier de cuisine"

@nest

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, toujours

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

crochets

dialog {
  border: none;

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

    & > form {
      display: grid;

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

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

Code CSS équivalent

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

C'est l'heure de voter

J'espère que vous avez trouvé que ce comparatif et exemple des options de syntaxe que nous évaluons sont raisonnables. Veuillez les lire attentivement et nous indiquer ci-dessous celle que vous préférez. Nous vous remercions de nous aider à améliorer l'imbrication CSS dans une syntaxe que nous connaissons et apprécions tous.

Répondre à l'enquête !