CSS @starting-style: la propiedad que por fin permite animar display:none
webdevelopment 4 de agosto de 2026 · Mintec

CSS @starting-style: la propiedad que por fin permite animar display:none

Durante décadas, animar un elemento con display:none requería hacks con JavaScript. La combinación de @starting-style y transition-behavior: allow-discrete elimina esa limitación — y funciona en todos los navegadores. Te mostramos cómo usarla con ejemplos reales y qué trampas evitar.

@starting-style resuelve el problema de animar display:none. En lugar de hacks con setTimeout o librerías de JavaScript, dos líneas de CSS bastan: transition-behavior: allow-discrete y @starting-style { opacity: 0 }. Aquí te contamos cómo funciona, qué trampas evitar, y cuándo conviene usarlo en producción.


Si alguna vez construiste un modal, un dropdown, un accordion o cualquier componente UI que aparece y desaparece, conocés el dolor: querés que entre con una transición suave, pero el elemento usa display: none para ocultarse. CSS no anima display. Nunca lo hizo.

El workaround clásico es horrible: un setTimeout de 10 milisegundos para que el navegador "registre" el display antes de aplicar la transición. O peor: dejás el elemento renderizado todo el tiempo con visibility: hidden y opacity: 0, pagando el costo en rendimiento y accesibilidad de tenerlo en el DOM permanentemente.

En Mintec nos encontramos con este problema en prácticamente cada proyecto — desde menús de navegación móviles hasta tooltips y notificaciones. La buena noticia: desde diciembre de 2024, todos los navegadores soportan una solución nativa.


El truco sucio que todos usamos (y por qué ya no hace falta)

El patrón clásico en JavaScript:

// ⛔ El hack de siempre
modal.style.display = 'block';
// Forzar reflow
modal.offsetHeight;
modal.classList.add('visible');

Ese offsetHeight forzado es necesario porque el navegador agrupa los cambios de estilo en un solo frame. Sin forzar el reflow, la transición no se dispara porque el elemento pasa de display:none a display:block con la clase visible ya aplicada — el navegador no ve un "antes" y un "después".

Es frágil, acopla comportamiento a detalles de renderizado del motor, y en equipos grandes alguien siempre lo rompe "limpiando código".


La solución: @starting-style + transition-behavior

CSS introdujo dos piezas que resuelven esto de raíz:

PropiedadQué haceSoporte@starting-styleDefine los estilos que el elemento tiene en su primer frame de renderizado — el "fotograma cero" que las transiciones necesitan como punto de partida.Chrome 117+, Firefox 124+, Safari 18.2+transition-behavior: allow-discreteAutoriza al navegador a interpolar propiedades "discretas" como display y visibility, que normalmente se saltan durante las transiciones.Chrome 117+, Firefox 124+, Safari 18.2+

La combinación es elegante:

.modal {
  display: none;
  opacity: 0;
  transition: opacity 0.3s, display 0.3s;
  transition-behavior: allow-discrete;

  &.open {
    display: block;
    opacity: 1;

    @starting-style {
      opacity: 0;
    }
  }
}

Cuando el elemento recibe la clase .open:

  1. display cambia a block (gracias a allow-discrete, el navegador no lo ignora)
  2. @starting-style define el estado inicial: opacity: 0
  3. El estado final es opacity: 1 → la transición se ejecuta

Para el cierre, no necesitás @starting-style — solo invertís los estados:

.modal {
  /* ... propiedades anteriores ... */
  
  &:not(.open) {
    display: none;
    opacity: 0;
  }
}

En nuestro último proyecto de rediseño para un cliente del sector hospitality, reemplazamos 7 instancias del hack de setTimeout con este patrón. El resultado: 40 líneas menos de JavaScript y animaciones de entrada/salida consistentes en todos los componentes.


Dónde brilla @starting-style (y dónde no)

No toda animación debería usar esta técnica. En Mintec evaluamos cada caso con tres criterios: complejidad de la animación, compatibilidad necesaria y costo de mantenimiento.

Caso de uso¿@starting-style?AlternativaModal / diálogo✅ Ideal. Entrada y salida limpias con fade + scale.View Transitions API si necesitás animar la página entera.Dropdown / menú✅ Perfecto. El patrón más común y el que más reemplaza hacks.Tooltip✅ Funciona bien. Animaciones sutiles sin JS extra.Accordion⚠️ Parcial. La animación de altura con height: auto sigue sin ser interpolable. Necesitás interpolate-size: allow-keywords (Chrome solo en 2026).<details> nativo + ::details-content (en desarrollo).Transición entre páginas❌ No. Para cross-document usa View Transitions API.@view-transition + navigation CSS.Listas virtualizadas❌ No. @starting-style opera sobre elementos que entran al DOM, no sobre reposicionamiento.FLIP animations con getBoundingClientRect().

La regla que usamos en Mintec: si el componente alterna entre dos estados con display:none ↔ display:block, @starting-style es la respuesta. Si necesitás animaciones complejas entre múltiples estados o transiciones entre páginas, combiná con View Transitions API — como explicamos en nuestra guía de patrones de View Transitions en producción.


Tres trampas que encontramos en producción

Después de usar @starting-style en varios proyectos, estos son los problemas reales con los que nos topamos:

1. La animación de cierre necesita transition-delay inverso. Cuando cerrás un modal, el display cambia inmediatamente a none — incluso con allow-discrete. La solución es invertir los delays:

.modal {
  transition: opacity 0.3s, display 0.3s;
  transition-behavior: allow-discrete;

  &.open {
    display: block;
    opacity: 1;
    /* display cambia al instante, opacity se anima */
  }

  &:not(.open) {
    display: none;
    opacity: 0;
    transition-delay: 0s, 0.3s;
    /* opacity se anima 0.3s, display espera y cambia al final */
  }
}

2. @starting-style no funciona con animaciones CSS (@keyframes). Solo funciona con transition. Si tu diseño requiere keyframes, envolvélos en una clase separada y usá @starting-style para la transición de entrada.

3. Safari 18.2 tiene un bug con display y overlay. Si tu modal usa la propiedad overlay (para posicionarse en el top layer), Safari puede perder la animación de cierre. La solución temporal es usar visibility en lugar de display para el estado oculto en Safari, con un fallback condicional.

Para una visión más amplia de cómo encaja @starting-style en una arquitectura CSS moderna, recomendamos nuestro artículo sobre container queries y componentes autónomos y la guía de CSS contrast-color para accesibilidad automática.


El impacto real: menos JS, más rendimiento

Eliminar hacks de animación no es solo una cuestión estética. En el proyecto del cliente hospitality que mencionamos, medimos:

  • 39% menos JavaScript en el bundle de componentes UI (pasamos de 6.8 KB a 4.1 KB gzipeados)
  • 0 regresiones de accesibilidad — el patrón con display:none es naturalmente compatible con aria-hidden y lectores de pantalla
  • Menos bugs de sincronización — sin setTimeout, sin requestAnimationFrame encadenados, sin condiciones de carrera entre estados

El patrón también se integra limpiamente con CSS Scroll-Driven Animations y con la API de View Transitions para navegación, formando un stack de animación 100% declarativo que no depende de librerías externas.


¿Deberías migrar tus animaciones hoy?

Si estás empezando un proyecto nuevo: sí, usá @starting-style desde el día uno. La compatibilidad cross-browser está resuelta y el ahorro en complejidad de código es inmediato.

Si tenés un codebase existente con animaciones que funcionan: no refactorices todo de golpe. Reemplazá los patrones más frágiles primero — los que dependen de setTimeout, los que tienen bugs intermitentes. Hacelo componente por componente, no como una migración masiva.

El CSS moderno eliminó la necesidad de hacks para animar display:none. En Mintec ya lo aplicamos en producción. Vas a notar la diferencia desde el primer componente que migres.

Preguntas Frecuentes

¿Qué es @starting-style en CSS?

@starting-style es una regla CSS que define los estilos iniciales de un elemento antes de que se renderice por primera vez, permitiendo que las transiciones CSS funcionen desde un estado cero — incluso cuando el elemento parte de display:none.

¿Cómo funciona transition-behavior: allow-discrete?

Por defecto, las transiciones CSS ignoran propiedades discretas como display y visibility. transition-behavior: allow-discrete le dice al navegador que sí anime estas propiedades, permitiendo combinarlas con @starting-style para crear transiciones de entrada y salida suaves.

¿En qué navegadores funciona @starting-style?

Funciona en Chrome 117+, Edge 117+, Firefox 124+ y Safari 18.2+. A diciembre de 2024, todos los navegadores principales lo soportan, convirtiéndolo en una solución cross-browser viable para producción.

Artículos Relacionados