CSS Anchor Position: guía práctica y patrones

Ilustración colorida en estilo plano de un pequeño velero flotando sobre el mar turquesa, sujeto por una gran ancla en el fondo marino — metáfora visual del funcionamiento de CSS Anchor Position.

CSS Anchor Position: guía práctica y patrones

Durante años, position: absolute/relative/fixed/sticky ha sido una fuente constante de dudas: ¿por qué absolute necesita un contenedor con relative?, ¿por qué mi tooltip “salta” fuera del viewport?, ¿cómo anclo algo a otro elemento… si no es su contenedor? En mis clases lo veía a diario: sin JavaScript, construir un popup o un menú contextual era casi misión imposible, y en WordPress el problema crecía por la profundidad del HTML de muchas plantillas, que dificulta identificar el contenedor real y los stacking contexts.

El nuevo CSS Anchor Positioning cambia la conversación: puedes nombrar un elemento como ancla y posicionar otro respecto a él, sin exigir relación de contenedor. Resultado: menos JS, menos hacks y una carga mental más baja para el equipo. En esta guía verás cuándo usarlo, cómo aplicarlo paso a paso, patrones productivos (tooltips, popovers, menús) y anti-patrones que te ahorrarán horas de depuración, con foco especial en WordPress.

Qué es CSS Anchor Positioning y por qué importa

CSS Anchor Positioning permite anclar el posicionamiento de un elemento a otro elemento arbitrario, usando:

  • anchor-name: nombra el elemento ancla.
  • position-anchor: indica a qué ancla se adhiere el elemento posicionado.
  • Funciones como anchor() y anchor-size() para leer posiciones y tamaños del ancla.
  • Reglas como @position-try y propiedades position-try, position-try-order, position-visibility para recolocar automáticamente el elemento si la posición preferida no cabe (p. ej., inversión arriba/abajo).

Referencia oficial: MDN – CSS anchor positioning y anchor().

Ventajas clave:

  • Menos dependencia de JS para UI flotante (tooltips/popovers).
  • Independencia del contenedor: el ancla puede estar “lejos” en el DOM.
  • Patrones declarativos: reposicionamiento automático con try tactics.
  • Mejor DX (Developer Experience) y menor layout thrashing cuando el sistema recoloca.

Consideración de soporte: hoy su disponibilidad aún es limitada y conviene aplicarlo con progressive enhancement (ver patrón más abajo).

Antes vs. ahora: del position clásico al anclaje moderno

El problema clásico

Con absolute, el sistema calcula posiciones respecto al contenedor posicionado (el primer ancestro con position distinto de static). Si tu tooltip depende de un botón que no es ese contenedor, llega el festival de wrappers, transform que crean nuevos contextos, o event listeners para calcular offsets con JS. En themes de WordPress, donde el HTML es profundo, entender quién es el contenedor real se convierte en una arqueología DOM.

El giro de Anchor Positioning

Ahora nombras el botón como ancla y pegas el tooltip al botón, no al contenedor. No necesitas reestructurar el DOM ni añadir position: relative en una cadena de contenedores.

Primer ejemplo: tooltip mínimo con anchor() (con fallback)

HTML

<button class="btn" aria-describedby="t1">Comprar</button>
<div id="t1" role="tooltip" class="tooltip">Añadir al carrito</div>

CSS (progressive enhancement)

/* 1) Estilos base (funciona en todos los navegadores) */
.btn { inline-size: auto; }
.tooltip {
    position: absolute;
    /* Fallback clásico: debajo del botón con JS ligero o posición aproximada */
    opacity: 0;
    pointer-events: none;
    transform: translateY(0.5rem);
}

/* 2) Activación por enfoque/hover (accesible) */
.btn:focus + .tooltip,
.btn:hover + .tooltip { opacity: 1; }

/* 3) Anchor Positioning si hay soporte */
@supports (position-anchor: --x) {
  /* Nombramos el ancla en el TRIGGER */
  .btn { anchor-name: --trigger; }

  /* El tooltip se ancla al trigger */
  .tooltip {
      position-anchor: --trigger;
      /* Colocar bajo el ancla, centrado horizontal */
      top: anchor(bottom);
      left: anchor(center);
      transform: translate(-50%, 0.5rem);
  }
}

css anchor position, tooltip alineado bajo un botón con anchor(bottom) y centrado con anchor(center)

Qué pasa aquí: el botón define anchor-name: --trigger. El tooltip usa position-anchor: --trigger y consulta anchor(bottom/center) para alinearse bajo y centrado respecto al botón. Si el navegador no soporta la feature, el bloque @supports no aplica y te quedas con un fallback (p. ej., un pequeño script que posicione el tooltip o una colocación aproximada).

Reposicionamiento automático: @position-try y position-try

Cuando el tooltip no cabe debajo, la UI debe recolocarse arriba. Con Anchor Positioning lo declaras:

@supports (position-anchor: --x) {
  /* Definimos estrategias nombradas */
  @position-try --below {
      /* "Área" inferior del ancla */
      inset-area: bottom;
  }
  @position-try --above {
      inset-area: top;
  }

  .btn { anchor-name: --trigger; }

  .tooltip {
      position-anchor: --trigger;

      /* Estrategia preferida y alternativas */
      position-try: --below, --above;

      /* Ajustes finos apoyados en anchor() */
      left: anchor(center);
      transform: translate(-50%, 0.5rem);
  }
}

Con esto, el navegador intenta colocar el tooltip debajo; si no hay espacio, prueba arriba automáticamente. Puedes añadir más variantes (izquierda/derecha) y ordenar intentos con position-try-order.

Ejemplo 2: popover alineado al borde con anchor-size()

Para menús que deben igualar el ancho del botón:

@supports (position-anchor: --x) {
  .btn { anchor-name: --menu-trigger; }

  .menu {
      position: absolute;
      position-anchor: --menu-trigger;

      /* Alinear borde izquierdo y copiar el ancho del trigger */
      left: anchor(left);
      width: anchor-size(width);
      top: anchor(bottom);
      transform: translateY(0.25rem);
  }
}

anchor-size(width) toma el ancho del ancla y lo aplica al menú, evitando cálculos JS y reflows manuales.

Anti-patrones frecuentes (y cómo evitarlos)

  1. Olvidar el progressive enhancement

    • Envuélvelo con @supports (position-anchor: --x) y no rompas la UI básica.
  2. Confiar en transformaciones del ancla

    • transform en el ancla puede crear nuevos contextos; comprueba que la medición no se vuelva contra ti.
  3. Ignorar accesibilidad

    • Tooltips/popovers necesitan roles ARIA, gestión de foco y escapado con Esc. El posicionamiento no soluciona A11y por sí solo.
  4. Profundidad DOM en WordPress

    • No “pelees” con el contenedor: nombra el ancla en el componente trigger (botón, icono del menú) y posiciona desde cualquier parte del árbol.
  5. No probar overflows reales

    • Usa @position-try con variantes para pantallas pequeñas y contenedores con scroll.

WordPress en producción: truco para themes con HTML profundo

En block themes, el botón puede estar dentro de varios wrappers. No pasa nada: el ancla sigue siendo el botón.

<!-- Bloque de Botón -->
<div class="wp-block-buttons">
   <div class="wp-block-button is-style-fill">
      <a class="wp-element-button btn--cta">Comprar</a>
   </div>
</div>

<!-- En otra zona del template -->
<div class="popover" id="buy-popover">Producto añadido ✓</div>

 

/* 1) Etiqueta el trigger como ancla */
.btn--cta { anchor-name: --buy; }

/* 2) El popover puede vivir en otro wrapper: no importa */
@supports (position-anchor: --x) {
  .popover {
      position: fixed; /* o absolute: depende de tu diseño */
      position-anchor: --buy; /* se ancla al botón */
      top: anchor(bottom);
      right: anchor(right); /* alineado al borde derecho del botón */
      transform: translateY(0.5rem);
  }
}

Claves:

  • Puedes usar position: fixed si el patrón lo requiere (HUD flotante, barra fija) manteniendo el anclaje semántico al botón real.
  • En Gutenberg, añade la clase btn--cta desde el panel avanzado del bloque.
  • Para diseño responsivo, combina position-try con media queries.

Accesibilidad mínima y JS opcional (solo para mejorar)

  • Roles y relaciones: aria-describedby (tooltip) o aria-controls/aria-expanded (popover).
  • Foco y teclado: abre con Enter/Espacio, cierra con Esc, devuelve el foco al trigger.
  • JS Light: usa JS solo para gestión de estado; deja el posicionamiento a CSS. Un patrón típico:
const trigger = document.querySelector('.btn');
const tooltip = document.getElementById('t1');

trigger.addEventListener('focus', () => tooltip.dataset.open = 'true');
trigger.addEventListener('blur', () => tooltip.dataset.open = 'false');
trigger.addEventListener('mouseenter', () => tooltip.dataset.open = 'true');
trigger.addEventListener('mouseleave', () => tooltip.dataset.open = 'false');
.tooltip { opacity: 0; }
.tooltip[data-open="true"] { opacity: 1; }

Checklist de implementación rápida

  • Definir anchor-name en el elemento trigger.
  • Aplicar position-anchor en el flotante.
  • Posicionar con anchor() y, si procede, dimensionar con anchor-size().
  • Añadir @position-try para recolocar en caso de overflow.
  • Encapsular en @supports y dejar un fallback funcional.
  • Revisar A11y (roles, foco, teclado) y z-index/stacking.
  • Probar en plantillas profundas (WordPress) y en móviles.

CSS Anchor Position trae un enfoque declarativo y robusto para UI flotante: anclas semánticas, menos JS, y recolocación inteligente. En escenarios reales —sobre todo en WordPress con HTML profundo— reduce el acoplamiento al contenedor y simplifica la experiencia de desarrollo. Si vienes del “mundo absolute/relative”, pensar en anclar a un elemento (y no a un contenedor) te abrirá patrones más limpios y mantenibles. Empieza por tooltips y popovers, añade @position-try y verás cómo la UI deja de luchar contra el layout.

Si te interesa llevar estos conceptos al siguiente nivel y aprender a crear interfaces dinámicas con HTML, CSS y JavaScript desde cero, inscríbete en el Curso de Programación Web de Escuela Espai. Es una formación completa donde practicarás posicionamiento, animaciones, responsive design y las bases del desarrollo front-end moderno.

Escribe un comentario