Ayuda a elegir una sintaxis para anidar CSS

Dos sintaxis en competencia necesitan tu ayuda para determinar cuál se debe defender a 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 agregar CSS dentro de un conjunto de reglas. Si usaste SCSS, Less o Pluma stylus, seguramente viste algunas variantes:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Una vez que el preprocesador la compila en 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 encarecidamente una versión oficial de CSS de esta sintaxis, y tenemos una división en la cual nos gustaría emplear la ayuda de la comunidad para evitar empates. El resto de esta publicación presentará las opciones de sintaxis para que puedas sentirte informado para completar una encuesta al final.

¿Por qué el ejemplo exacto de anidación que se muestra arriba no puede ser la sintaxis para la anidación de CSS?

Existen algunos motivos por los que la sintaxis de anidación más popular no se puede usar, tal como se muestra a continuación:

  1. Análisis ambiguo
    Algunos selectores anidados pueden lucir exactamente como las propiedades y los preprocesadores pueden resolverlos y administrarlos en el momento de la compilación. Los motores del navegador no tendrán las mismas posibilidades, por lo que los selectores nunca deben interpretarse de manera sutil.

  2. Conflictos de análisis del preprocesador
    La forma de anidar CSS no debería dañar los preprocesadores ni los flujos de trabajo existentes que anidan los desarrolladores. Esto sería molesto y desconsiderado para esos ecosistemas y comunidades.

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

Descripción general de lo que comparamos

Queremos que la integración de CSS sea correcta y, con ese espíritu, incluimos a la comunidad. Las siguientes secciones ayudarán a describir las tres versiones posibles que estamos evaluando. Luego, repasaremos algunos ejemplos de uso a modo de comparación 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 Nesting 1 de CSS. Ofrece una manera conveniente de anidar estilos adjuntos iniciando nuevos selectores anidados 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 sujetos. Es flexible y mínimo, pero se usa para recordar @nest o &, según el caso de uso.

Opción 2: @nest restringido

Esta es una alternativa más estricta, en un intento de reducir el gasto mencionado en 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. Quitar la ambigüedad de elección y crear una forma fácil de recordar de anidar, pero sacrifica la terciencia en favor de lo convencional.

Opción 3: Corchetes

Para evitar la sintaxis doble o el exceso de desorden relacionados con las propuestas de @nest, Miriam Suzanne y Elika Etemad propusieron una sintaxis alternativa que, en su lugar, se basa en llaves adicionales. Esto proporciona claridad a la sintaxis, con solo dos caracteres adicionales y sin nuevas reglas at. También permite que se agrupen las reglas anidadas por el tipo de anidamiento 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: Anidamiento compuesto

@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 anidación

@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: Anidamiento principal o cambio de sujeto

@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 explícitamente / ampliadas

.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 explícitamente / ampliadas

.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 anidación complejo "Fregadero de la 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 consideres que fue una comparación justa y un ejemplo de las opciones de sintaxis que evaluamos. Revísalos detenidamente y cuéntanos cuál prefieres a continuación. Agradecemos que nos ayudes a llevar la anidación de CSS a una sintaxis que todos conoceremos y amaremos.

Responder la encuesta