learning-front

Nivel 1 · HTML y CSS: la estructura y la piel

Posicionamiento: position, z-index y overflow

Sacar elementos del flujo para superponerlos, fijar barras al hacer scroll y controlar el contenido que se desborda: position, z-index y overflow.

Ya sabes apilar cajas en el flujo, medirlas con el box model y distribuirlas con Flexbox y Grid. Todo eso coloca los elementos unos junto a otros. Falta la última pieza del layout: superponerlos (un badge sobre una carta), fijarlos (una barra que no se va al hacer scroll) y controlar lo que se desborda (un nombre demasiado largo). Eso es position, z-index y overflow.

Salir del flujo: position#

Por defecto, cada elemento está en el flujo normal: position: static. La propiedad position te deja cambiarlo. Sus valores:

ValorQué hace
staticEl flujo normal (por defecto). top/left no tienen efecto.
relativeSe desplaza desde su sitio con top/right/bottom/left, pero conserva su hueco.
absoluteSale del flujo y se coloca respecto al ancestro posicionado más cercano.
fixedSale del flujo y se ancla a la ventana: no se mueve al hacer scroll.
stickyNormal hasta que el scroll lo sacaría; entonces se “pega” al borde indicado.

relative: desplazar sin perder el sitio#

Con relative, el elemento se mueve respecto a donde estaría, pero su hueco original se mantiene (los vecinos no se recolocan):

css
.aviso {
  /* sale del flujo normal sin perder su hueco */
  position: relative;
  /* baja medio rem desde donde estaría normalmente */
  top: 0.5rem;
}

Su uso más importante, sin embargo, no es desplazar: es servir de marco de referencia para un hijo absolute. Eso nos lleva al siguiente valor.

absolute: anclar a un ancestro#

Un elemento absolute sale del flujo (deja de ocupar sitio) y se coloca con top/right/bottom/left respecto a su ancestro posicionado más cercano: el primer padre o abuelo con position distinto de static. Si ninguno lo tiene, se ancla a la página entera.

Por eso, para poner un badge en la esquina de una carta, el patrón es siempre el mismo —el más usado del posicionamiento—:

css
.carta {
  /* marco de referencia, no la mueve */
  position: relative;
}
.carta .badge {
  /* sale del flujo y se ancla al ancestro posicionado (.carta) */
  position: absolute;
  /* a 0.5rem del borde superior de .carta */
  top: 0.5rem;
  /* respecto a la esquina de .carta */
  right: 0.5rem;
}

El relative de la carta no la desplaza (no le damos top/left): solo la convierte en el sistema de coordenadas del badge.

fixed y sticky: barras que se quedan#

Las dos mantienen un elemento visible al hacer scroll, pero de forma distinta:

  • fixed lo ancla a la ventana y lo saca del flujo. Útil para un botón flotante, pero ojo: como deja de ocupar sitio, el contenido se mete debajo y suele haber que compensar con un margen o padding.
  • sticky es híbrido: el elemento ocupa su sitio normal y solo se “pega” al borde (top: 0) cuando el scroll lo alcanzaría. No tapa nada. Es lo habitual para una cabecera que debe quedarse arriba.
css
/* Ejemplo de position: fixed — botón que flota sobre el contenido */
.boton-flotante {
  /* sale del flujo y se ancla a la ventana: no se mueve al hacer scroll */
  position: fixed;
  /* a 1.5rem del borde inferior de la ventana */
  bottom: 1.5rem;
  /* a 1.5rem del borde derecho de la ventana */
  right: 1.5rem;
  /* por encima de todo lo demás */
  z-index: 100;
}
css
.barra {
  /* híbrido: normal hasta que el scroll lo sacaría, entonces se pega */
  position: sticky;
  /* el borde al que se pega: el superior */
  top: 0;
  /* la mantiene por encima del resto al superponerse */
  z-index: 10;
}

Trampa frecuente con sticky: para que funcione hacen falta dos cosas. Primera, un offset declarado (top, bottom, left o right): sin él, el elemento nunca sabe a qué borde pegarse y se comporta como relative. Segunda, que ningún ancestro tenga overflow: hidden, overflow: auto u overflow: scroll; si lo tiene, el sticky queda “atrapado” dentro de ese ancestro y deja de pegarse. Son las dos causas más comunes de “he puesto sticky y no funciona”.

z-index: quién va por encima#

Cuando dos elementos se superponen, ¿cuál se ve delante? Lo decide z-index: a mayor número, más cerca del usuario. La regla que más despista: solo funciona en elementos posicionados (con position distinto de static). En una caja normal no tiene ningún efecto.

css
/* por encima */
.barra { position: sticky; z-index: 10; }
/* por debajo de la barra */
.badge { position: absolute; z-index: 2; }

Cuando dos elementos posicionados no llevan z-index, gana el que va más tarde en el HTML. Declararlo a mano evita sorpresas.

Hay una trampa que cuesta horas la primera vez: subes el z-index a 9999 y el elemento sigue apareciendo por detrás de otro con un número mucho menor. La causa es el contexto de apilamiento. Ciertas propiedades crean una “burbuja” de apilamiento, y dentro de ella los z-index de los hijos solo compiten entre ellos, no con el resto de la página: por alto que pongas el número, ese elemento no saldrá por encima de algo que esté fuera de su burbuja. Las que la crean más a menudo son position con z-index (que ya conoces) y un par que verás en el capítulo de acabado visual, como transform y opacity por debajo de 1. La regla práctica: cuando un z-index enorme no funcione, no lo subas más; busca un ancestro con una de esas propiedades y sube el z-index de ese ancestro.

overflow: contenido que no cabe#

overflow decide qué pasa cuando el contenido es más grande que su caja:

  • visible (por defecto): se sale y se dibuja fuera.
  • hidden: se recorta lo que sobra.
  • auto / scroll: aparece una barra de desplazamiento dentro de la caja.

Su uso estrella en interfaces es recortar un texto largo en una sola línea, con puntos suspensivos. Hacen falta tres propiedades juntas:

css
.hero-name {
  /* no parte en varias líneas */
  white-space: nowrap;
  /* recorta lo que se sale */
  overflow: hidden;
  /* y lo sustituye por «…» */
  text-overflow: ellipsis;
}

Quita cualquiera de las tres y el efecto desaparece: sin nowrap el texto salta de línea, sin overflow: hidden no se recorta, y sin text-overflow se corta en seco sin avisar.

Pruébalo#

Haz scroll en el preview para ver la barra sticky pegarse arriba. Luego cambia la position del .badge en styles.css entre absolute, relative y static y observa la diferencia: absolute lo ancla a la esquina de la carta, relative lo mueve pero deja su hueco, static lo devuelve al flujo.

Comprueba lo que sabes#

Pregunta 1 de 5

¿Qué hace `position: relative` en un elemento?

Tu turno#

La plantilla tiene tres detalles sin resolver. Coloca el badge en la esquina de su carta, fija la barra al hacer scroll y recorta el nombre largo. Cuando lo tengas, estrecha el preview a 375px para comprobar que aguanta, y despliega las soluciones.

Ejercicio · en esta página

Coloca el badge, fija la barra y recorta el nombre

La plantilla del Team Builder ya tiene su grid y sus cartas. Faltan tres detalles de posicionamiento: el badge "Nuevo" en la esquina de su carta, la barra superior pegada al hacer scroll y el nombre largo recortado con puntos suspensivos. Completa los TODO de starter/styles.css con position, z-index y overflow.

Paso 1: Que funcione

  • El badge "Nuevo" aparece superpuesto en la esquina de su carta.
  • La barra se mantiene arriba al hacer scroll (aunque sea con la herramienta no ideal).
  • El nombre largo no rompe la carta.
Ver soluciones
/* SOLUCIÓN OK — funciona, pero con las herramientas equivocadas.
   El badge sí se coloca, pero la barra usa `fixed` (y tapa el contenido) y el
   nombre se recorta sin puntos suspensivos, cortado en seco. */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family:
    system-ui,
    -apple-system,
    "Segoe UI",
    Roboto,
    sans-serif;
  background: #f7f6fb;
  color: #1c1b22;
  line-height: 1.5;
}

.barra {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 0.85rem 1.25rem;
  background: #1c1b22;
  color: #fff;
  /* `fixed` la saca del flujo y la fija a la ventana: se queda arriba, sí, pero
     el contenido se mete DEBAJO y el título queda tapado. Habría que compensar
     con un padding-top en el body. Para algo que solo debe pegarse al borde
     superior, la herramienta correcta es `sticky`. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
}

.barra-marca {
  font-weight: 700;
}

.barra-info {
  font-size: 0.85rem;
  color: #c9c7d6;
}

.contenido {
  max-width: 60rem;
  margin: 0 auto;
  padding: 1.5rem;
}

.titulo {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #5b5966;
  margin: 0 0 1rem;
}

.rejilla {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1rem;
}

.hero-card {
  background: #fff;
  border: 1px solid #e6e3ee;
  border-radius: 12px;
  padding: 1.25rem;
  /* La carta es el marco de referencia del badge: esto sí está bien. */
  position: relative;
}

.hero-portrait {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: #b8336a;
  color: #fff;
  font-weight: 700;
  display: grid;
  place-items: center;
  margin-bottom: 0.75rem;
}

.hero-name {
  font-size: 1.05rem;
  font-weight: 700;
  margin: 0;
  /* Recorta, pero sin avisar: el nombre se corta en seco a mitad de palabra.
     Falta `text-overflow: ellipsis` para los puntos suspensivos. */
  overflow: hidden;
  white-space: nowrap;
}

.hero-role {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #5b5966;
  margin: 0.2rem 0 0;
}

.badge {
  background: #b8336a;
  color: #fff;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.2rem 0.5rem;
  border-radius: 999px;
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
}

Por qué este nivel

  • El badge se coloca bien: la carta es `relative` y el badge `absolute` en su esquina. Esa parte está resuelta.
  • La barra usa `position: fixed`: se queda arriba, pero sale del flujo y el contenido se mete debajo, tapando el título. Para pegarse al borde superior, lo correcto es `sticky`.
  • El nombre se recorta con `overflow: hidden` y `nowrap`, pero sin `text-overflow: ellipsis`: se corta a mitad de palabra, sin los puntos suspensivos que avisan de que hay más.