CSS Nesting nativo

Anidar selectores directamente en CSS sin preprocesadores. Aprende la sintaxis moderna de CSS Nesting: &, selectores relativos, media queries anidadas y diferencias con Sass.

Durante años, anidar selectores era exclusivo de Sass y LESS. Desde 2023, CSS nativo lo soporta en todos los navegadores modernos. Sin compilador, sin dependencias.


La sintaxis básica

El selector & representa el elemento padre:

/* Antes — sin nesting */
.card { padding: 1rem; border-radius: 8px; }
.card .title { font-size: 1.25rem; }
.card:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); }
.card:hover .title { color: var(--color-primary); }

/* Ahora — con CSS Nesting */
.card {
  padding: 1rem;
  border-radius: 8px;

  & .title {
    font-size: 1.25rem;
  }

  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);

    & .title {
      color: var(--color-primary);
    }
  }
}

El símbolo &

& es el selector del bloque padre. Su posición determina la relación:

.btn {
  background: var(--color-primary);

  /* .btn:hover */
  &:hover {
    background: var(--color-accent);
  }

  /* .btn.activo */
  &.activo {
    background: var(--color-success);
  }

  /* .btn + .btn (hermano adyacente) */
  & + & {
    margin-left: 0.5rem;
  }

  /* .contenedor .btn (dentro de .contenedor) */
  .contenedor & {
    width: 100%;
  }
}

Media queries anidadas

Una de las ventajas más prácticas: las media queries van junto al componente, no al final del archivo:

.hero {
  display: grid;
  grid-template-columns: 1fr;
  padding: 2rem 1rem;

  @media (min-width: 768px) {
    grid-template-columns: 1fr 1fr;
    padding: 4rem 2rem;
  }

  @media (min-width: 1200px) {
    grid-template-columns: 1fr 1fr 1fr;
  }

  & .hero-title {
    font-size: clamp(2rem, 5vw, 4rem);

    @media (min-width: 768px) {
      color: var(--color-primary);
    }
  }
}

@supports anidado

Igual que con media queries:

.elemento {
  display: flex;

  @supports (display: grid) {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
  }
}

Diferencias con Sass

CaracterísticaCSS NativoSass
Requiere compiladorNo
& en posición de selectorSí (a &)
& concatenado en nombreNo (&-title no funciona)
Media queries anidadas
@each, @for, @mixinNo
VariablesSí (custom properties)Sí (distintas)

La diferencia clave: en Sass puedes hacer &-title para generar .card-title. En CSS nativo, & solo puede ir donde iría un selector completo, no concatenado a texto.


Buenas prácticas

/* ✅ Bien: nesting para estados y variantes del mismo componente */
.btn {
  &:hover { ... }
  &:focus-visible { ... }
  &.btn--large { ... }
  &.btn--ghost { ... }
}

/* ✅ Bien: hijos directos relacionados */
.card {
  & .card__header { ... }
  & .card__body { ... }
  & .card__footer { ... }
}

/* ❌ Evitar: demasiados niveles de anidado */
.nav {
  & ul {
    & li {
      & a {
        & span { ... } /* 5 niveles — ilegible */
      }
    }
  }
}

Regla práctica: no más de 3 niveles de anidado. Si necesitas más, es una señal para dividir el componente.


¿Qué hemos aprendido?

  • CSS nativo ya soporta anidado con & sin Sass ni compiladores.
  • Las media queries y @supports se pueden anidar dentro del componente.
  • & representa el selector padre y debe usarse explícitamente para claridad.
  • Sass sigue siendo útil para funciones, bucles y concatenación de nombres con &-.

Siguiente paso

En la próxima lección veremos Cascade Layers con @layer: cómo controlar el orden de la cascada de forma explícita, especialmente con CSS de terceros.