CSS position: demo de static, relative, absolute, fixed y sticky | We.Inc
Cambia un elemento entre static, relative, absolute, fixed y sticky dentro de una demo con scroll. Explicación de los bloques contenedores, inset, los problemas de sticky y el centrado.
La propiedad position elige cómo se coloca un elemento. static es el flujo normal. relative lo desplaza un poco conservando su espacio. absolute lo saca del flujo y lo coloca respecto al ancestro posicionado más cercano. fixed lo fija al viewport. sticky se desplaza con normalidad hasta llegar a un umbral como top: 0 y entonces se queda ahí.
Los cinco valores
Valor
¿En el flujo?
Se coloca respecto a
Sigue en el flujo
Nada, top/left no tienen efecto
Sigue en el flujo y conserva su espacio original
Su propia posición normal
Sale del flujo
La caja de padding del ancestro posicionado más cercano
Sale del flujo
El viewport, salvo que un ancestro tenga transform, filter o algo similar
Sigue en el flujo
El ancestro con scroll más cercano, dentro de su padre
La demo de abajo tiene una caja naranja dentro de un área con scroll. Cambia su valor de position, mueve los desplazamientos, haz scroll y observa respecto a qué se mide. Después vienen una guía sobre los bloques contenedores, una lista de comprobación para cuando sticky no se queda fijo y la propiedad abreviada inset.
Elige un valor de position para la caja naranja.
Ajusta top y left, y activa o desactiva el padre posicionado para ver qué caja usa absolute.
Haz scroll dentro de la demo para comparar fixed y sticky.
Copia el CSS que corresponde a lo que ves.
Pon position: relative en una tarjeta y los hijos con absolute usarán esa tarjeta como marco. Ese único patrón cubre la mayoría de los diseños con insignias y superposiciones.
Usa inset: 0 en lugar de escribir top, right, bottom y left por separado.
Un elemento fixed dentro de cualquier cosa con transform, filter, perspective o contain: paint deja de estar fijo a la pantalla. Muévelo al final de body o renderízalo en un portal.
Los elementos posicionados se pintan por defecto encima de los static. Recurre a z-index solo cuando dos elementos posicionados se solapan.
Preguntas frecuentes
¿Qué diferencia hay entre absolute y relative?
relative desplaza un elemento visualmente, pero deja su espacio original en el diseño, así que nada a su alrededor se mueve. absolute saca el elemento del diseño por completo, los vecinos cierran el hueco, y top o left se miden desde el ancestro posicionado más cercano y no desde donde habría estado.
¿Por qué no funciona position: sticky?
Las causas habituales son que falta un valor top, que un ancestro tiene overflow hidden o auto, o que el padre no es más alto que el elemento sticky. Recorre en orden la lista de comprobación de esta página; los dos primeros puntos resuelven la mayoría de los casos.
¿Respecto a qué se posiciona absolute?
Respecto a su bloque contenedor: la caja de padding del ancestro más cercano cuyo position sea distinto de static. Los ancestros con transform, filter o ciertos valores de contain también cuentan. Si no hay ninguno, es el bloque contenedor inicial, que tiene el tamaño del viewport.
¿Por qué mi encabezado fixed se mueve con la página?
Algún ancestro tiene transform, perspective, filter, backdrop-filter, will-change: transform o contain: paint. Cualquiera de ellos convierte ese ancestro en el bloque contenedor de los descendientes fixed, así que el encabezado queda fijo a él en lugar de a la pantalla.
¿Qué es inset en CSS?
Una propiedad abreviada de top, right, bottom y left, con el mismo patrón de uno a cuatro valores que margin. inset: 0 estira un elemento posicionado sobre su bloque contenedor. inset-inline e inset-block definen los pares de cada eje.
El bloque contenedor, en palabras sencillas
Todo desplazamiento necesita un rectángulo de referencia. Para los elementos static y relative es la caja de contenido del padre. Para absolute es la caja de padding del ancestro posicionado más cercano, así que el padding de la tarjeta no empuja hacia dentro a un hijo con absolute.
Para fixed suele ser el viewport. Eso cambia cuando un ancestro tiene transform, perspective, filter, backdrop-filter, contain: paint o un valor de will-change equivalente: el ancestro pasa a ser la referencia y el elemento se desplaza con él. La demo de esta página usa justo eso para que su caja fixed se quede dentro del marco de la demo.
Centrar con posicionamiento absolute
Con un tamaño conocido, inset: 0 más margin: auto coloca la caja justo en el centro y mantiene nítidos los bordes de píxel. Con un tamaño desconocido, top: 50% y left: 50% seguidos de translate(-50%, -50%) funcionan con cualquier contenido.
Ambos sacan la caja del flujo, así que el padre no crecerá a su alrededor. Si el padre debe ajustarse al contenido, centra con grid o flex.
Cada valor en detalle
Valores de position
Valor
Flujo
Qué hacen los desplazamientos
Uso típico
Sigue en el flujo
Se ignoran
El valor por defecto. Todo empieza aquí.
Sigue en el flujo y conserva su espacio original
Lo desplazan desde donde estaría
Pequeños ajustes, y como ancla para hijos con absolute.
Barras de cookies, botones flotantes, modales a pantalla completa.
Sigue en el flujo
Umbral a partir del cual se queda fijo
Encabezados de tabla, títulos de sección, menús laterales.
Qué crea un bloque contenedor para absolute y fixed
Ancestros que capturan a los descendientes posicionados (según MDN)
El ancestro tiene
¿Lo usan los hijos absolute?
¿Lo usan los hijos fixed?
position: relative, absolute, fixed o sticky
Sí
No
transform, perspective, rotate, scale o translate distinto de none
Sí
Sí
filter o backdrop-filter distinto de none
Sí
Sí
contain: layout, paint, strict o content
Sí
Sí
will-change con alguna de esas propiedades, como transform o filter
Sí
Sí
content-visibility: auto
Sí
Sí
Patrones que usarás
Insignia en la esquina de una tarjeta
La tarjeta es el ancestro posicionado. Los desplazamientos negativos sacan la insignia hasta la mitad fuera de la esquina.
Encabezado de tabla sticky
El contenedor tiene scroll, así que th con top: 0 se queda fijo en él. El fondo evita que se transparenten las filas.
La propiedad abreviada inset
El mismo patrón que margin: un valor para todos los lados, dos para vertical y horizontal, cuatro en el sentido de las agujas del reloj empezando por arriba.
Has definido un umbral: top, bottom, left o right. Sin él, sticky se comporta como relative.
Ningún ancestro entre el elemento y el contenedor con scroll tiene overflow: hidden, auto o scroll. Cualquiera de ellos se convierte en el contenedor con scroll, a menudo uno que nunca se desplaza. overflow: clip no crea un contenedor con scroll.
El padre es más alto que el elemento sticky. Solo se queda fijo mientras su padre está en pantalla y después se va con él.
En una fila de flex o grid, el elemento no está estirado a toda la altura. Añade align-self: start para que tenga espacio para moverse.
El elemento sticky está por encima del contenido que se desplaza por debajo. Dale un fondo y un z-index si el contenido se transparenta.
El scroll ocurre de verdad en el ancestro que crees. Comprueba en DevTools qué elemento tiene la barra de desplazamiento.
Laboratorio de position
Valor de position
El padre tiene position: relative
Los desplazamientos se ignoran. La caja está en el flujo normal entre los párrafos.
La caja se dibuja desplazada, pero se conserva el hueco donde estaría. Los vecinos no se mueven.
Se mide desde el padre con borde discontinuo, que tiene position: relative.
No hay padre posicionado, así que se mide desde el área de la demo con scroll, el siguiente ancestro posicionado hacia arriba.
Fijo al marco de la demo mientras haces scroll. El marco tiene un transform, que lo convierte en el bloque contenedor en lugar de la ventana del navegador.
Se desplaza con el texto hasta quedar a top píxeles del borde superior del contenedor con scroll y luego se mantiene hasta que su padre desaparece.
Área de demo con scroll
Haz scroll dentro de este marco. El texto es de relleno para que haya algo que desplazar.
Padre
Primer párrafo antes de la caja.
Párrafo después de la caja. Observa si sube cuando la caja sale del flujo.
Línea {n} de contenido extra para hacer scroll.
Contenido después del padre, línea {n}.
Copiar CSS
Por qué fixed se queda en el marco: el marco tiene transform: translateZ(0). Cualquier transform convierte un elemento en el bloque contenedor de sus descendientes fixed. En una página real sin un ancestro así, fixed se ancla a la ventana del navegador.
Sticky no funciona: lista de comprobación
¿El encabezado sticky queda oculto tras otro contenido? Consulta