Los cinco valores de position y qué cambia cada uno
Qué hace exactamente static, relative, absolute, fixed y sticky con el flujo, la caja y las propiedades de desplazamiento.
position es una propiedad de cinco valores que la mayoría usa en dos modos: quitar del flujo y no quitar del flujo. Pero cada valor cambia tres cosas distintas —si el elemento sigue ocupando sitio, contra qué caja se resuelven sus desplazamientos, y si crea contexto de apilamiento— y esas tres decisiones son independientes. Ordenarlas es lo que convierte el posicionamiento en algo predecible.
- Describir para cada valor si el elemento sigue ocupando espacio en el flujo.
- Identificar qué valores hacen que las propiedades de desplazamiento tengan efecto.
- Saber cuáles crean contexto de apilamiento y cuáles bloque contenedor.
- Elegir el valor mínimo suficiente para un problema dado.
La tabla de las tres decisiones
| valor | ¿ocupa sitio? | inset hace efecto |
crea contexto de apilamiento | bloque contenedor de absolutos |
|---|---|---|---|---|
static |
sí | no | no | no |
relative |
sí, en su hueco original | sí, desplaza la pintura | solo con z-index no auto |
sí |
absolute |
no | sí, define la caja | solo con z-index no auto |
sí |
fixed |
no | sí, define la caja | siempre | sí |
sticky |
sí | sí, como umbral | siempre | sí |
Cuatro columnas y cinco filas resumen casi todo. Los dos casos que rompen la simetría son los que hay que memorizar: fixed y sticky crean contexto de apilamiento sin necesidad de z-index, y relative y sticky siguen ocupando su sitio original aunque se dibujen desplazados.
Los cinco, uno a uno
static. El valor inicial. El elemento se coloca según el flujo normal y las propiedades top, right, bottom, left, inset y z-index no tienen ningún efecto. Se ignoran, no es que se apliquen y den cero.
Merece la pena recordar que static no es lo mismo que no declarar nada: escribir position: static explícitamente sirve para cancelar un posicionamiento heredado de una regla más genérica, y es la forma correcta de deshacer un relative puesto por una utilidad.
relative. El elemento se coloca en el flujo normal, se reserva su hueco, y después se desplaza visualmente según sus propiedades de desplazamiento. Lo importante es que el hueco no se mueve: los hermanos siguen colocados como si el elemento estuviera en su sitio, y donde se pinta puede solaparse con ellos. relative es la forma más barata de convertir un elemento en referencia para descendientes absolutos, y la más barata de subirlo al paso 6 del orden de pintado sin tocar z-index.
absolute. El elemento sale del flujo: su hueco desaparece y los hermanos se recolocan como si no existiera. Su caja se dimensiona y se coloca respecto a su bloque contenedor, que es el tema de la siguiente lección. Si no declaras ningún desplazamiento, se queda en su posición estática hipotética, es decir, donde habría estado en el flujo, lo cual es un comportamiento sorprendentemente útil.
fixed. Como absolute, pero el bloque contenedor es el viewport, y por tanto el elemento no se mueve al hacer scroll. Crea contexto de apilamiento siempre. Y tiene una vulnerabilidad grande: cualquier ancestro con transform, filter y compañía le roba el bloque contenedor y lo convierte de facto en un absolute. Es el tema de la última lección de este nivel.
sticky. El híbrido: se comporta como relative mientras su umbral no se cruza, y como fixed respecto a su contenedor de scroll cuando se cruza. Sigue ocupando su hueco todo el tiempo, lo que evita el salto de contenido característico de convertir un elemento a fixed con JavaScript. Tiene condiciones estrictas para funcionar, y ese es el contenido completo del nivel siguiente.
Qué valor elegir
La regla que evita la mayoría de los problemas: usa el valor mínimo que resuelva el problema, porque cada escalón añade consecuencias.
Si solo necesitas que un elemento sea la referencia de un descendiente absoluto, relative sin desplazamientos es suficiente y no crea contexto de apilamiento. Si además le pones z-index: 0 estás encerrando el subárbol sin necesidad.
Si necesitas superponer dos elementos, muchas veces no hace falta posicionar nada: una rejilla de una sola celda con los dos hijos en grid-area: 1 / 1 los superpone sin sacar a ninguno del flujo, y el contenedor se dimensiona con el mayor de los dos. Es el patrón que evita el clásico problema de un absoluto que no puede dar altura a su padre.
.superpuestos { display: grid; }
.superpuestos > * { grid-area: 1 / 1; }
Si necesitas que algo flote sobre el resto de la página —modal, menú, tooltip, toast— la respuesta ya no es fixed con un z-index grande, es la capa superior con dialog o popover. Recuérdalo antes de escribir un position: fixed; z-index: 9999, que es el patrón que la lección por qué tu z-index de 9999 sigue quedando debajo diagnostica.
flowchart TB
A[Que necesitas] --> B{Debe seguir ocupando su sitio}
B -->|Si, y solo desplazarse| C[relative]
B -->|Si, y pegarse al llegar a un umbral| D[sticky]
B -->|No, sale del flujo| E{Respecto a que se coloca}
E -->|A un ancestro| F[absolute]
E -->|Al viewport| G[fixed]
E -->|Debe flotar sobre toda la pagina| H[Capa superior con dialog o popover]
A --> I{Solo quieres superponer dos cajas}
I -->|Si| J[Una rejilla de una celda sin position]
style A fill:#89b4fa,color:#11111b
style C fill:#a6e3a1,color:#11111b
style D fill:#a6e3a1,color:#11111b
style F fill:#a6e3a1,color:#11111b
style G fill:#f9e2af,color:#11111b
style H fill:#a6e3a1,color:#11111b
style J fill:#cba6f7,color:#11111bHay una asimetría en absolute que casi nunca se cuenta y que explica un comportamiento aparentemente contradictorio. Un elemento absoluto sale del flujo para el layout de sus hermanos: no ocupa sitio, no empuja a nadie, no contribuye a la altura del padre. Pero sí participa en dos cosas del entorno. La primera es su posición estática hipotética: si no declaras ningún desplazamiento, el motor tiene que calcular dónde habría estado el elemento de haber seguido en el flujo, lo que significa que su posición depende del contenido que lo rodea, y por tanto cambia si cambia ese contenido. Un absoluto sin desplazamientos dentro de un párrafo se mueve cuando el párrafo se reflowea, aunque tú creyeras haberlo sacado del flujo. La segunda es la resolución de porcentajes: los del elemento se resuelven contra el bloque contenedor, que es la caja de relleno del ancestro posicionado y no su caja de contenido, a diferencia de lo que ocurre con un elemento en flujo normal. Un inline-size: 100% en un absoluto dentro de un padre con padding: 2rem mide dos remes más de lo que mediría el mismo elemento en flujo. Estas dos asimetrías son la causa de la mayoría de los “pero si está posicionado en absoluto, ¿por qué se mueve?” y de los “¿por qué se sale exactamente el padding?”.
- Aplica
position: relative; inset-block-start: 40pxa un elemento en una lista y comprueba que el hueco original sigue ahí. - Cambia a
absolutesin declarar ningún desplazamiento y observa que no se mueve pero los hermanos sí. - Verifica en DevTools que un
position: fixedsinz-indexya crea contexto de apilamiento. - Superpón dos cajas con la rejilla de una celda y confirma que el contenedor toma la altura de la más alta.
- Cancela un
position: relativeheredado conposition: staticexplícito y comprueba que el descendiente absoluto salta a otro ancestro.