wandres.dev
POSICIONAMIENTO · De static a fixed, y el bloque contenedor

inset, las propiedades lógicas y el sistema sobrerestringido

El atajo inset y sus variantes lógicas, cómo resuelve el motor un conjunto de desplazamientos incompatibles, y el centrado con márgenes automáticos.

⏱ 17 min

top, right, bottom y left son propiedades físicas: presuponen que el texto va de izquierda a derecha y de arriba abajo. En una interfaz que se traduce al árabe o al japonés vertical, esa presuposición es un error de diseño. inset y su familia lógica resuelven el problema, y de paso dan el atajo que faltaba. Además, hay un algoritmo silencioso que decide qué ocurre cuando declaras cuatro desplazamientos y un tamaño que no caben juntos, y conocerlo desbloquea el centrado más limpio que existe.

🎯 Al terminar esta lección sabrás
  • Usar inset y sus variantes lógicas en lugar de las cuatro propiedades físicas.
  • Explicar cómo se resuelve un sistema de desplazamientos sobrerestringido.
  • Centrar un elemento absoluto con inset: 0 y márgenes automáticos.
  • Estirar un absoluto en un eje y anclarlo en el otro con una sola declaración.

inset y la familia lógica

inset es un atajo de las cuatro propiedades de desplazamiento y sigue exactamente la misma sintaxis de una a cuatro partes que margin y padding.

.a { inset: 0; }                  /* los cuatro a cero */
.b { inset: 1rem 2rem; }          /* bloque 1rem, en línea 2rem */
.c { inset: 0 auto auto 0; }      /* arriba e izquierda a cero, el resto auto */

Un detalle importante y contraintuitivo: inset es un atajo de las propiedades lógicas, no de las físicas, pero sus valores se escriben en el orden físico arriba-derecha-abajo-izquierda. En la práctica, en un documento horizontal de izquierda a derecha las dos formas coinciden. En un documento vertical o de derecha a izquierda, inset sigue el modo de escritura.

Las variantes con nombre son las que de verdad usas cuando quieres que la interfaz se adapte:

lógica física en horizontal-tb y ltr
inset-block-start top
inset-block-end bottom
inset-inline-start left
inset-inline-end right
inset-block: 0 1rem top: 0; bottom: 1rem
inset-inline: 1rem left: 1rem; right: 1rem

El caso donde la diferencia se paga en producción es el desplegable. Un menú anclado con right: 0 a un botón queda anclado por la derecha; en árabe, el botón está a la izquierda de su etiqueta y el menú debería colgar por el otro lado. Con inset-inline-end: 0 el cambio es automático al declarar dir="rtl", sin una sola regla adicional.

/* se adapta solo al modo de escritura */
.desplegable { position: absolute; inset-block-start: 100%; inset-inline-end: 0; }

Cuando los desplazamientos no caben

Un elemento absoluto tiene, en cada eje, tres incógnitas y una ecuación. En el eje en línea: el desplazamiento inicial, la anchura y el desplazamiento final tienen que sumar exactamente la anchura del bloque contenedor, contando también los márgenes. Si declaras las tres, el sistema puede quedar sobrerestringido, y hay que descartar una.

La regla que aplica el motor:

  • Si una de las tres es auto, se resuelve para satisfacer la ecuación. Es el caso normal.
  • Si dos son auto, entre ellas los márgenes automáticos se ponen a cero y el elemento adopta su tamaño según el contenido, colocándose en el desplazamiento declarado.
  • Si ninguna es auto, el sistema está sobrerestringido y se ignora el desplazamiento final: en un documento de izquierda a derecha se descarta right, y en uno de derecha a izquierda se descarta left. El eje de bloque descarta siempre bottom.

Esto explica un comportamiento que parece un bug: declaras left: 0; right: 0; width: 200px y el elemento se queda pegado a la izquierda con 200 píxeles, sin respetar el right. No es que se ignore el right por capricho: es que el sistema no tenía solución y la especificación decidió que la propiedad sacrificada fuera esa.

El centrado con inset y márgenes automáticos

Aquí está la parte útil. Si en lugar de dejar auto una de las tres incógnitas dejas auto los dos márgenes, el motor reparte el espacio sobrante a partes iguales entre ellos. Y ese es el centrado perfecto de un absoluto, sin transformaciones y sin conocer las medidas.

.centrado {
  position: absolute;
  inset: 0;
  inline-size: 20rem;
  block-size: 12rem;
  margin: auto;
}

Las cuatro esquinas a cero definen el rectángulo disponible; el tamaño está declarado; los márgenes automáticos absorben el sobrante por igual en los dos ejes. El elemento queda centrado en los dos ejes de forma exacta.

Sus ventajas sobre inset-block-start: 50%; translate: 0 -50% son concretas. Primera: no hay transformación, así que no crea contexto de apilamiento ni bloque contenedor para descendientes fijos. Segunda: el resultado cae en píxeles enteros, así que no hay borrosidad de subpíxel en el texto. Tercera: translate queda libre para animaciones.

Su limitación: hay que declarar el tamaño en el eje que quieras centrar. Si el elemento debe dimensionarse por su contenido, este método no sirve en ese eje y hay que volver a translate, o mejor, colocar el elemento con una rejilla de una celda y place-items: center.

flowchart TB
A[Quieres centrar un elemento absoluto] --> B{Conoces su tamano en los dos ejes}
B -->|Si| C[inset cero mas tamano declarado mas margin auto]
C --> D[Sin transformacion y sin borrosidad]
B -->|No, se dimensiona por contenido| E{Puedes usar el contenedor como rejilla}
E -->|Si| F[display grid mas place-items center]
E -->|No| G[inset del cincuenta por ciento mas translate del menos cincuenta]
G --> H[Crea contexto de apilamiento y ocupa translate]
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:#f9e2af,color:#11111b

El mismo mecanismo sirve para anclar en un eje y centrar en el otro, que es la geometría de la mayoría de los tooltips:

.tooltip {
  position: absolute;
  inset-block-end: 100%;                    /* justo encima del ancla */
  inset-inline: 0;                          /* rectángulo del ancho del ancla */
  inline-size: max-content;
  max-inline-size: 20rem;
  margin-inline: auto;                      /* centrado horizontal exacto */
}
Los márgenes automáticos son un solucionador de restricciones, no una regla de centrado

Merece la pena reencuadrar margin: auto porque casi todo el mundo lo tiene archivado como “el truco para centrar” y eso oculta lo que de verdad es. CSS resuelve el layout planteando una ecuación por eje: la suma de márgenes, bordes, padding y tamaño tiene que igualar la medida del bloque contenedor. auto no significa “céntrame”: significa “esta es la incógnita, despéjala”. Cuando hay una sola incógnita, el sistema tiene solución única y auto toma exactamente el sobrante; por eso margin-inline-start: auto empuja un elemento a la derecha en lugar de centrarlo. Cuando hay dos incógnitas simétricas, el sistema está infradeterminado y la especificación añade la restricción que falta: repartir a partes iguales. De ahí sale el centrado, como caso particular. Esta lectura explica de golpe tres cosas que de otro modo hay que memorizar por separado: por qué margin: auto no centra verticalmente un bloque en flujo normal —porque en el eje de bloque no hay ecuación que resolver, la altura del contenedor no está fijada por el contenido de arriba—, por qué centra verticalmente un absoluto con inset: 0 —porque ahí la ecuación existe y las cuatro esquinas la cierran—, y por qué en flexbox margin: auto absorbe el espacio libre antes que justify-content: en los tres casos es el mismo solucionador respondiendo a distintas cantidades de información.

⚔️ Resuelve el sistema a mano
  1. Declara inset-inline: 0 y inline-size: 200px en un absoluto y comprueba cuál de los dos desplazamientos se ignora.
  2. Repite con direction: rtl en el ancestro y verifica que ahora se ignora el otro.
  3. Centra un absoluto con inset: 0 y margin: auto y comprueba que la posición cae en píxeles enteros.
  4. Construye el tooltip centrado sobre su ancla con inset-inline: 0 y margin-inline: auto.
  5. Cambia las propiedades físicas de un desplegable por lógicas y comprueba que se adapta al poner dir="rtl".