Ayuda a elegir una sintaxis para anidar CSS

Dos sintaxis que compiten entre sí necesitan tu ayuda para determinar cuál debe ser defendida por un candidato de especificación.

Adam Argyle
Adam Argyle
Miriam Suzanne
Miriam Suzanne

El anidamiento de CSS es una adición conveniente de sintaxis que permite que se agregue CSS dentro de un conjunto de reglas. Si usaste SCSS, Less o Pluma stylus, sin duda habrás visto algunas variantes de esto:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

que, después de que el preprocesador lo compila en una CSS normal, se convierte en una CSS normal de la siguiente manera:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Se está considerando detenidamente una versión oficial de CSS de esta sintaxis, y tenemos una división de preferencia que queremos emplear la ayuda de la comunidad para romper el empate. En el resto de esta publicación, presentaremos las opciones de sintaxis para que te sientas informado de responder una encuesta al final.

¿Por qué el ejemplo exacto del anidamiento que se muestra arriba no puede ser la sintaxis para el anidado de CSS?

Existen algunos motivos por los que no se puede usar la sintaxis de anidación más popular de la siguiente manera:

  1. Análisis ambiguo
    Algunos selectores anidados pueden lucir exactamente como las propiedades y los preprocesadores pueden resolverlos y administrarlos en el tiempo de compilación. Los motores de navegadores no tendrán las mismas posibilidades, y los selectores nunca deben interpretarse de manera flexible.

  2. Conflictos de análisis del preprocesador
    La forma CSS de anidar no debe interrumpir los preprocesadores ni los flujos de trabajo existentes de anidamiento de desarrolladores. Esto sería disruptivo y desconsiderado para esos ecosistemas y comunidades.

  3. Esperando :is()
    El anidamiento básico no necesita :is(), pero el anidamiento más complejo sí. Consulta el Ejemplo n.o 3 para obtener una introducción breve a las listas de selectores y la anidación. Imagina que la lista de selectores estaba en el medio de un selector y no al principio; en esos casos, se requiere :is() para agrupar los selectores en el medio de otro selector.

Descripción general de lo que comparamos

Queremos que el anidamiento de CSS sea correcto y, con ese espíritu, incluimos a la comunidad. Las siguientes secciones te ayudarán a describir las tres versiones posibles que estamos evaluando. Luego, revisaremos algunos ejemplos de uso para compararlos y, al final, habrá una encuesta ligera en la que se te preguntará cuál prefieres en general.

Opción 1: @nest

Esta es la sintaxis especificada actualmente en el anidamiento de CSS 1. Ofrece una manera conveniente de anidar estilos de anexo mediante el inicio de selectores anidados nuevos con &. También ofrece @nest como una forma de colocar el contexto & en cualquier lugar dentro de un selector nuevo, como cuando no solo agregas temas. Es flexible y mínimo, pero a costa de la necesidad de recordar @nest o & según tu caso de uso.

Opción 2: @nest restringido

Esta es una alternativa más estricta, con el fin de reducir los gastos mencionados de recordar dos métodos de anidación. Esta sintaxis restringida solo permite que el anidamiento ocurra después de @nest, por lo que no hay un patrón de conveniencia para agregar solo. Esto elimina la ambigüedad de la elección y crea una forma fácil de recordar para anidar, pero sacrifica la perseverancia en favor de lo convencional.

Opción 3: Corchetes

Para evitar la sintaxis doble o el desorden adicional que implican las propuestas @nest, Miriam Suzanne y Elika Etemad propusieron una sintaxis alternativa que, en cambio, se basa en llaves adicionales. Esto proporciona claridad sintáctica, con solo dos caracteres adicionales y sin reglas atrasadas nuevas. También permite que las reglas anidadas se agrupen según el tipo de anidación requerido, como una forma de simplificar varios selectores anidados similares.

Ejemplo 1: Anidación directa

@nest

.foo {
  color: #111;

  & .bar {
    color: #eee;
  }
}

@nest siempre

.foo {
  color: #111;

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

corchetes

.foo {
  color: #111;

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

CSS equivalente

.foo {
  color: #111;
}

.foo .bar {
  color: #eee;
}

Ejemplo 2: Anidación compuesta

@nest

.foo {
  color: blue;

  &.bar {
    color: red;
  }
}

@nest siempre

.foo {
  color: blue;

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

corchetes

.foo {
  color: blue;

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

CSS equivalente

.foo {
  color: blue;
}

.foo.bar {
  color: red;
}

Ejemplo 3: Listas de selectores y anidamiento

@nest

.foo, .bar {
  color: blue;

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

@nest siempre

.foo, .bar {
  color: blue;

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

corchetes

.foo, .bar {
  color: blue;

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

CSS equivalente

.foo, .bar {
  color: blue;
}

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

Ejemplo 4: Varios niveles

@nest

figure {
  margin: 0;

  & > figcaption {
    background: lightgray;

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

@nest siempre

figure {
  margin: 0;

  @nest & > figcaption {
    background: lightgray;

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

corchetes

figure {
  margin: 0;

  {
    & > figcaption {
      background: lightgray;

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

CSS equivalente

figure {
  margin: 0;
}

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

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

Ejemplo 5: Anidación parental o cambio de tema

@nest

.foo {
  color: red;

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

@nest siempre

.foo {
  color: red;

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

corchetes

.foo {
  color: red;

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

CSS equivalente

.foo {
  color: red;
}

.parent .foo {
  color: blue;
}

Ejemplo 6: Combinación de anidamiento directo y superior

@nest

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    &.baz {
      color: green;
    }
  }
}

@nest siempre

.foo {
  color: blue;

  @nest .bar & {
    color: red;

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

corchetes

.foo {
  color: blue;

  {
    .bar & {
      color: red;

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

CSS equivalente

.foo {
  color: blue;
}

.bar .foo {
  color: red;
}

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

Ejemplo 7: Anidación de consultas de medios

@nest

.foo {
  display: grid;

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

o de forma explícita / extendida

.foo {
  display: grid;

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

@nest siempre (siempre es explícito)

.foo {
  display: grid;

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

corchetes

.foo {
  display: grid;

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

o de forma explícita / extendida

.foo {
  display: grid;

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

CSS equivalente

.foo {
  display: grid;
}

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

Ejemplo 8: Grupos de anidación

@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 siempre

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

corchetes

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 equivalente

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

Ejemplo 9: Grupo de anidamiento complejo "Fregadero de cocina"

@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 siempre

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

corchetes

dialog {
  border: none;

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

    & > form {
      display: grid;

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

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

CSS equivalente

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

Hora de votar

Esperamos que sientas que fue una comparación justa y una bandeja de muestra de las opciones de sintaxis que evaluamos. Revísalos detenidamente y cuéntanos cuál prefieres a continuación. Agradecemos que nos ayudes a desarrollar la anidación de CSS a una sintaxis que todos conozcamos y adoremos.

Responde la encuesta.