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

Deux syntaxes concurrentes ont besoin de votre aide pour déterminer laquelle doit être retenue en tant que candidate de spécification.

Adam Argyle
Adam Argyle
Miriam Suzanne
Miriam Suzanne

L'imbrication CSS est un ajout de syntaxe pratique qui permet d'ajouter du CSS dans un ensemble de règles. Si vous avez déjà utilisé SCSS, Less ou Stylus, vous avez certainement vu quelques variantes de ce code:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Une fois compilé en CSS standard par le préprocesseur, il se transforme en CSS standard comme suit:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Nous envisageons sérieusement une version CSS officielle de cette syntaxe. Nous avons des préférences divergentes et nous aimerions demander l'aide de la communauté pour nous aider à trancher. Le reste de cet article présente les options de syntaxe afin que vous puissiez répondre à une enquête à la fin.

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

Plusieurs raisons expliquent pourquoi la syntaxe d'imbrication la plus populaire ne peut pas être utilisée telle quelle:

  1. Analyse ambiguë
    Certains sélecteurs imbriqués peuvent resssembler 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'auront pas les mêmes affordances. Les sélecteurs ne doivent jamais être interprétés de manière vague.

  2. Conflits d'analyse du préprocesseur
    La méthode CSS d'imbrication ne doit pas endommager les préprocesseurs ni les workflows d'imbrication existants des développeurs. Cela serait perturbateur et irrespectueux pour ces écosystèmes et communautés.

  3. En attente de :is()
    L'imbrication de base n'a pas besoin de :is(), mais une imbrication plus complexe en a besoin. Consultez l'exemple 3 pour une présentation succincte des listes de sélecteurs et de l'imbrication. Imaginons que la liste de sélecteurs se trouvait au milieu d'un sélecteur au lieu du début. Dans ce cas, :is() est nécessaire pour regrouper les sélecteurs au milieu d'un autre sélecteur.

Présentation de ce que nous comparons

Nous voulons que l'imbrication CSS soit correcte, et dans cet esprit, nous incluons la communauté. Les sections suivantes décrivent les trois versions possibles que nous évaluons. Nous allons ensuite examiner quelques exemples d'utilisation à des fins de comparaison. À la fin, vous devrez répondre à une courte enquête pour nous indiquer ce que vous avez préféré dans l'ensemble.

Option 1: @nest

Il s'agit de la syntaxe actuellement spécifiée dans CSS Nesting 1. Il offre un moyen pratique d'imbriquer des styles d'ajout en commençant de nouveaux sélecteurs imbriqués avec &. Il propose également @nest pour placer le contexte & n'importe où dans un nouveau sélecteur, par exemple lorsque vous n'ajoutez pas seulement des sujets. Il est flexible et minimal, mais vous devez vous souvenir de @nest ou de & en fonction de votre cas d'utilisation.

Option 2: @nest restricted

Il s'agit d'une alternative plus stricte, dans le but de réduire les dépenses mentionnées liées au rappel de deux méthodes d'imbrication. Cette syntaxe restreinte ne permet que l'imbrication après @nest. Il n'existe donc pas de modèle pratique d'ajout uniquement. Il élimine l'ambiguïté de choix, créant un moyen facile à retenir d'imbriquer, mais sacrifie la concision au profit de la convention.

Option 3: Crochets

Pour éviter la double syntaxe ou l'encombrement supplémentaire associé aux propositions @nest, Miriam Suzanne et Elika Etemad ont proposé une syntaxe alternative qui repose sur des accolades supplémentaires. Cela permet de clarifier la syntaxe, avec seulement deux caractères supplémentaires et aucune nouvelle règle at. Il 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 : Nidification directe

@nest

.foo {
  color: #111;

  & .bar {
    color: #eee;
  }
}

@nest always

.foo {
  color: #111;

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

crochets

.foo {
  color: #111;

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

CSS équivalent

.foo {
  color: #111;
}

.foo .bar {
  color: #eee;
}

Exemple 2 : Imbrications composées

@nest

.foo {
  color: blue;

  &.bar {
    color: red;
  }
}

@nest always

.foo {
  color: blue;

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

crochets

.foo {
  color: blue;

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

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 always

.foo, .bar {
  color: blue;

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

crochets

.foo, .bar {
  color: blue;

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

CSS équivalent

.foo, .bar {
  color: blue;
}

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

Exemple 4 : Plusieurs niveaux

@nest

figure {
  margin: 0;

  & > figcaption {
    background: lightgray;

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

@nest always

figure {
  margin: 0;

  @nest & > figcaption {
    background: lightgray;

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

crochets

figure {
  margin: 0;

  {
    & > figcaption {
      background: lightgray;

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

CSS équivalent

figure {
  margin: 0;
}

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

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

Exemple 5 : imbrication de parents ou modification de l'objet

@nest

.foo {
  color: red;

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

@nest always

.foo {
  color: red;

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

crochets

.foo {
  color: red;

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

CSS équivalent

.foo {
  color: red;
}

.parent .foo {
  color: blue;
}

Exemple 6 : Mélange de l'imbrication directe et de l'imbrication parent

@nest

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    &.baz {
      color: green;
    }
  }
}

@nest always

.foo {
  color: blue;

  @nest .bar & {
    color: red;

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

crochets

.foo {
  color: blue;

  {
    .bar & {
      color: red;

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

CSS équivalent

.foo {
  color: blue;
}

.bar .foo {
  color: red;
}

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

Exemple 7 : Encapsulage de requêtes multimédias

@nest

.foo {
  display: grid;

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

ou explicitement / étendue

.foo {
  display: grid;

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

@nest always (est 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 / étendue

.foo {
  display: grid;

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

CSS équivalent

.foo {
  display: grid;
}

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

Exemple 8 : Enchâssement de 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 always

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

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 always

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

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

Nous espérons que vous avez trouvé cette comparaison et cet échantillon des options de syntaxe que nous évaluons justes. Veuillez les examiner attentivement et nous indiquer ci-dessous celle que vous préférez. Merci de nous avoir aidés à faire évoluer l'imbrication CSS vers une syntaxe que nous allons tous connaître et apprécier.

Répondez à l'enquête !