wandres.dev
ANIMAR A AUTO · interpolate-size y calc-size

Por qué height: auto nunca fue interpolable

Qué es realmente auto en el modelo de CSS, por qué el problema no era de esfuerzo sino de arquitectura, y qué tenía que romperse para resolverlo.

⏱ 17 min

Animar un acordeón es probablemente la tarea más pedida y peor resuelta de la animación web. Lleva veinticinco años apareciendo en foros con la misma pregunta —“¿por qué no puedo hacer transition: height a auto?”— y con respuestas que se limitan a ofrecer un truco. La respuesta de verdad es que auto no es un valor de altura: es una instrucción para el algoritmo de layout, y no hay ningún punto intermedio entre “cero píxeles” y “una instrucción”. Entender eso explica de paso por qué la solución tardó tanto y qué tuvo que cambiar en el lenguaje para permitirla.

🎯 Al terminar esta lección sabrás
  • Explicar qué es auto en términos de valor computado y valor usado.
  • Justificar por qué la interpolación exige tipos iguales y qué se rompe si no lo son.
  • Enumerar los tres problemas técnicos que hacían difícil resolverlo.
  • Reconocer que el mismo problema afecta a más palabras clave de las que parece.

auto no es un número

CSS distingue varios estadios en la vida de un valor, y los dos que importan aquí son el valor computado y el valor usado.

El valor computado es el que sale de la etapa de estilo: la cascada ya está resuelta, las unidades relativas ya se han convertido a absolutas, var() ya se ha sustituido. Es el valor que devuelve getComputedStyle() para la mayoría de las propiedades y es el que las transiciones interpolan.

El valor usado es el que sale de layout: aquí ya se conocen los tamaños de los contenedores, los porcentajes se han resuelto a píxeles y las palabras clave se han evaluado.

height: auto computa a auto. No a un número: a la palabra auto, literalmente. Su significado —“tan alto como haga falta para el contenido, según el contexto de formato en que estés”— no se resuelve hasta layout.

Puedes comprobarlo en dos líneas:

const el = document.querySelector('.panel');
console.log(getComputedStyle(el).height);       // "184px" ...

Aquí hay una sutileza que confunde a mucha gente y conviene aclararla: getComputedStyle().height devuelve un número en píxeles aunque la propiedad esté en auto, porque para height la especificación pide devolver el valor usado cuando el elemento se está renderizando. Es una excepción histórica, no la regla general. Lo que el sistema de transiciones ve es el valor computado, que sigue siendo auto.

Con un elemento que no se renderiza, la máscara cae:

const oculto = document.querySelector('.panel-oculto');  // display: none
console.log(getComputedStyle(oculto).height);   // "auto"

Por qué la interpolación exige tipos iguales

Interpolar es calcular un valor intermedio, y para eso hace falta una operación de mezcla definida sobre un tipo. Entre 0px y 184px la mezcla es aritmética. Entre 0px y auto no hay ninguna mezcla posible, porque auto no vive en el mismo espacio: no es un punto en la recta de las longitudes, es una regla.

La pregunta natural es por qué el motor no resuelve auto a píxeles y luego interpola, ya que en layout acabará haciéndolo. Y ahí están los tres problemas técnicos que hicieron que esto tardara veinticinco años.

Problema uno: el valor destino no se conoce en la etapa de estilo. Las transiciones se establecen durante el cálculo de estilo, que ocurre antes de layout. Para conocer el valor en píxeles de auto habría que ejecutar layout, y layout depende del estilo. Es una inversión del orden del pipeline que estudiamos en el nivel 2.

Problema dos: el valor destino puede cambiar durante la animación. El contenido de un panel puede cambiar de tamaño mientras se anima —una imagen que carga, un texto que refluye al redimensionar la ventana—. Si auto se congelara a píxeles al empezar, la animación acabaría en un valor equivocado. Si se recalculara cada fotograma, cada fotograma exigiría un layout adicional.

Problema tres: cambiar el comportamiento por defecto habría roto sitios existentes. Millones de páginas dependen de que height: auto no transicione. Muchas tienen transition: all y un height: auto en algún estado; si de repente empezara a interpolar, esas páginas cambiarían de comportamiento sin que nadie tocara nada. Por eso la solución tenía que ser opcional, activada explícitamente, y eso condiciona su forma.

No es solo auto

El problema es más amplio de lo que sugiere la pregunta habitual, porque auto es solo una de las palabras clave de tamaño intrínseco. Todas comparten la misma naturaleza y el mismo obstáculo:

Palabra clave Significa
auto Lo que el contexto de formato determine
min-content El mínimo sin desbordar el contenido
max-content Lo que ocuparía sin ningún salto de línea
fit-content Entre las dos anteriores, acotado por el espacio disponible
stretch Todo el espacio disponible del contenedor

Y afecta a todas las propiedades de dimensionado: width, height, min-width, max-height, block-size, inline-size y sus equivalentes lógicas. Una solución que solo arreglara height: auto habría sido un parche; la que llegó cubre la familia entera.

El truco de max-height sobrevivió veinte años porque el problema real no es animar la altura: es animar la altura que no conoces

Merece la pena separar dos problemas que la pregunta “cómo animo a auto” mezcla, porque tienen soluciones distintas y confundirlos es lo que hace que la gente elija mal. El primer problema es animar hasta una altura que sí conoces. Si el panel mide 184 píxeles y lo sabes, transition: height 300ms con valores en píxeles funciona desde 1998 y siempre funcionó. Nadie tuvo nunca ese problema. El segundo es animar hasta una altura que depende del contenido, y por tanto no puedes escribir en la hoja de estilos porque cambia con el texto, con el idioma, con el tamaño de fuente del usuario y con el ancho de la ventana. Ese es el problema real, y por eso ninguna solución que exija escribir un número lo resuelve de verdad: max-height: 1000px no es una altura, es una apuesta, y una apuesta se pierde el día que alguien traduce el texto al alemán. Esto tiene una consecuencia que cambia cómo se evalúan las alternativas de este nivel: el criterio no es cuál se ve mejor, es cuál sigue funcionando cuando el contenido cambia sin que tú te enteres. La técnica de rejilla lo cumple porque nunca menciona una altura. La medición desde JavaScript lo cumple si vuelves a medir cuando el contenido cambia, y no lo cumple si mides una vez y guardas el número. Y max-height no lo cumple nunca: solo lo disimula mientras la apuesta se mantenga. Cuando elijas entre las alternativas de las lecciones siguientes, la pregunta correcta no es cuánto código ocupa cada una, es qué pasa el día que el contenido crece un 40 por ciento.

Qué tenía que cumplir la solución

De los tres problemas técnicos salen tres requisitos, y explican por qué la solución tiene la forma exacta que tiene y no otra.

Ser opcional. No puede cambiar el comportamiento por defecto. De ahí que sea una propiedad que hay que activar, y que su valor por defecto sea el comportamiento antiguo.

Poder aplicarse de forma amplia. Si hubiera que activarla propiedad por propiedad y elemento por elemento, sería inutilizable en la práctica. De ahí que sea una propiedad heredada, que se pone una vez en la raíz y afecta a todo el documento.

Permitir operar sobre el valor resuelto. No basta con interpolar hasta auto: hace falta poder decir cosas como “hasta la mitad de su tamaño intrínseco” o “hasta su tamaño intrínseco más dos rem”, porque los diseños reales lo piden. De ahí una función aparte que hace aritmética con el valor resuelto.

Las dos piezas resultantes son interpolate-size y calc-size(), y son el tema de la lección siguiente, con la advertencia importante de que su disponibilidad en 2026 no es la de las capacidades del nivel anterior.

⚔️ Comprueba la naturaleza de auto
  1. Lee getComputedStyle(el).height en un elemento visible con height: auto y en uno con display: none. Anota la diferencia y explícala.
  2. Declara transition: height 300ms y cambia de 0px a auto. Comprueba con getAnimations() si se crea alguna transición.
  3. Cambia de 0px a 184px y comprueba que sí se crea. Anota qué cambió exactamente.
  4. Repite con width y con min-content en lugar de auto, y verifica que el comportamiento es el mismo.