Saltar al contenido

CSS moderno en 2026: container queries, :has() y anchor positioning

Andrés Ujpán
Fecha de publicación:
2 min de lectura

Durante años, la respuesta a “¿cómo hago X en CSS?” era “usa JavaScript”. En 2026 esa respuesta ya no se aplica en la mayoría de los casos. Tres características en particular redibujaron el mapa del diseño en el navegador.

Tabla de contenido

Container Queries: responder al contenedor, no a la pantalla

Las media queries responden al ancho del viewport (la pantalla). El problema: un componente puede vivir en una columna estrecha o en una ancha según el diseño del padre. Las container queries resuelven esto.

/* Declare the container */
.card-wrapper {
  container-type: inline-size; 
  container-name: card;
}

/* The component responds to its container */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
  }

  .card__image {
    grid-row: 1 / 3;
  }
}

@container card (max-width: 399px) {
  .card {
    display: flex;
    flex-direction: column;
  }
}card.css
<!-- The same component works in any context -->
<aside class="card-wrapper" style="width: 300px">
  <article class="card">...</article>
  <!-- vertical layout -->
</aside>

<main class="card-wrapper" style="width: 700px">
  <article class="card">...</article>
  <!-- horizontal layout -->
</main>card.html

Unidades de container queries

Las consultas también exponen unidades relativas al contenedor:

.card__title {
  font-size: clamp(1rem, 4cqi, 2rem); /* cqi = container query inline size */
}typography.css

La pseudoclase :has() — el selector de padre que siempre quisimos

:has() selecciona un elemento en función de sus descendientes. Es el “selector de padre” que CSS negó durante décadas.

/* Form with empty required field */
form:has(input:required:invalid) .submit-btn {
  opacity: 0.5;
  pointer-events: none;
}

/* Card containing an image: different layout */
.card:has(img) {
  display: grid;
  grid-template-columns: 150px 1fr;
}

.card:not(:has(img)) {
  padding: 1.5rem;
}

/* Navbar with open menu: disable scroll on body */
body:has(.nav-menu[aria-expanded="true"]) {
  overflow: hidden;
}styles.css

:has() tiene soporte en todos los navegadores modernos desde 2023. Puedes usarlo en producción hoy mismo sin polyfills.

Anchor Positioning: tooltips y popovers sin JavaScript

Antes de Anchor Positioning, colocar un tooltip de forma relativa a su disparador requería calcular posiciones con JavaScript. Ya no:

/* Declare the anchor */
.btn-trigger {
  anchor-name: --my-button; 
}

/* Position the tooltip relative to the anchor */
.tooltip {
  position: absolute;
  position-anchor: --my-button; 
  bottom: calc(anchor(top) + 8px); 
  left: anchor(center); 
  transform: translateX(-50%);

  /* Flip automatically if it doesn't fit */
  position-try-fallbacks: flip-block; 
}tooltip.css
<button class="btn-trigger" popovertarget="tip">Hover me</button>
<div id="tip" class="tooltip" popover>
  This tooltip positions itself, without JS.
</div>tooltip.html

position-try-fallbacks: lógica de colisiones declarativa

.tooltip {
  position-try-fallbacks:
    flip-block,
    /* try top if it doesn't fit below */ flip-inline,
    /* try left if it doesn't fit right */ flip-start; /* combine both */
}tooltip.css

¿Qué pasa con el soporte?

CaracterísticaChromeFirefoxSafari
Container Queries105+ ✓110+ ✓16+ ✓
:has()105+ ✓121+ ✓15.4+ ✓
Anchor Positioning125+ ✓131+ ✓18+ ✓

En 2026, con la distribución actual de navegadores, puedes usar las tres en producción para la mayoría de los proyectos. Considera polyfills solo si tu audiencia incluye navegadores muy antiguos.

El CSS actual es declarativo y expresivo

La revolución silenciosa de CSS no fue Grid ni Flexbox. Fue el cambio de mentalidad: el navegador razona sobre las restricciones, tú declaras el resultado deseado. Las container queries, :has() y el posicionamiento de anclajes (anchor positioning) son la culminación de ese paradigma.

Artículo Anterior
Docker Compose en 2026: buenas prácticas que realmente importan
Fecha de publicación:
2 min de lectura
Lectura Recomendada
React 19: useActionState, useOptimistic y el fin de los estados de carga manuales
Fecha de publicación:
1 min de lectura
100%