Saltar al contenido principal
Leyendo: Layout y texto avanzados
Apuntes de clase

Clase 32 · Layout y texto avanzados

Las piezas que faltan para dominar Flex y Grid, el control del texto largo (puntos suspensivos, guiones, titulares equilibrados), las unidades y funciones nuevas (dvh, hsl(), color-mix()) y subgrid. Cada bloque termina con su demo.

01 · Flex y Grid: lo que falta

Completando Flex y Grid

Repasamos lo que vimos en las clases 7, 8 y 28 y añadimos las propiedades que más se usan y aún no conocemos.

  • Flex: align-self, order, flex-flow.
  • Atajos de alineación: place-items, place-content, place-self.
  • Grid: grid-template-areas, grid-auto-rows, grid-auto-flow.
01 · Flex y Grid: lo que falta

align-self, order y flex-flow

Propiedades de Flex que se aplican a los hijos.

align-self sobrescribe el align-items del contenedor solo en un hijo. Acepta los mismos valores (flex-start, center, flex-end, stretch).

style.css · align-self
.dos {
  align-self: flex-end;
}

order cambia el orden visual sin tocar el HTML. Por defecto todos valen 0; un número menor va antes, uno mayor va después.

style.css · order
.cuatro {
  order: -1;
}

order solo cambia lo que se ve; el teclado y los lectores de pantalla siguen el orden del HTML.

flex-flow resume flex-direction y flex-wrap en una sola línea.

style.css · flex-flow
/* Equivale a: flex-direction: row; flex-wrap: wrap; */
.fila {
  flex-flow: row wrap;
}
01 · Flex y Grid: lo que falta

Flex en funcionamiento

Cada caja se alinea de forma distinta y la 4 se coloca la primera.

index.html
<div class="fila">
  <div class="caja uno">1</div>
  <div class="caja dos">2</div>
  <div class="caja tres">3</div>
  <div class="caja cuatro">4</div>
</div>
style.css
.fila {
  display: flex;
  flex-flow: row wrap;
  align-items: flex-start;
  gap: 12px;
  height: 170px;
  padding: 12px;
  background-color: #f2f4ee;
  border-radius: 12px;
}
.caja {
  width: 90px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  color: #fff;
  background-color: #3a8dde;
  border-radius: 8px;
}
.dos {
  align-self: flex-end;
  background-color: #e07a3c;
}
.tres {
  align-self: center;
  background-color: #4caf7a;
}
.cuatro {
  order: -1;
  background-color: #8e5bd6;
}
Resultado: align-self y order
01 · Flex y Grid: lo que falta

place-items, place-content y place-self

Atajos que combinan los dos ejes en una sola propiedad.

Con Grid centrar algo es de una sola línea: display: grid; place-items: center;.

style.css
/* align-items + justify-items */
.contenedor {
  display: grid;
  place-items: center;
}

/* align-content + justify-content */
.contenedor {
  display: grid;
  place-content: center;
}

/* align-self + justify-self (en un solo hijo) */
.hijo {
  place-self: end;
}
index.html
<div class="celda inicio"><span></span></div>
<div class="celda centro"><span></span></div>
<div class="celda final"><span></span></div>
style.css
.celda {
  display: grid;
  width: 120px;
  height: 120px;
  background-color: #f2f4ee;
  border-radius: 12px;
}
.celda span {
  width: 36px;
  height: 36px;
  background-color: #3a8dde;
  border-radius: 8px;
}
.inicio {
  place-items: start;
}
.centro {
  place-items: center;
}
.final {
  place-items: end;
}
Resultado: place-items
01 · Flex y Grid: lo que falta

grid-template-areas

Dibujar el diseño con palabras.

En lugar de numerar filas y columnas, nombramos las zonas en grid-template-areas, dibujando el diseño tal y como se ve. Después cada hijo se coloca con grid-area: nombre.

  • Cada texto entre comillas es una fila.
  • Cada palabra es una columna.
  • Repetir una palabra hace que la zona ocupe varias celdas.
  • Un punto . deja la celda vacía.

Es lo que más facilita el diseño responsive: en el @media solo redibujas las áreas.

01 · Flex y Grid: lo que falta

grid-template-areas en funcionamiento

Una página completa; reduce el ancho de la ventana (menos de 600px) y las zonas se apilan.

index.html
<div class="pagina">
  <header>Cabecera</header>
  <nav>Menú</nav>
  <main>Contenido</main>
  <aside>Lateral</aside>
  <footer>Pie</footer>
</div>
style.css
.pagina {
  display: grid;
  grid-template-columns: 150px 1fr 130px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas: "cabecera cabecera cabecera"
                       "menu contenido lateral"
                       "pie pie pie";
  gap: 10px;
  min-height: 300px;
}
header {
  grid-area: cabecera;
  background-color: #3a8dde;
}
nav {
  grid-area: menu;
  background-color: #e07a3c;
}
main {
  grid-area: contenido;
  background-color: #4caf7a;
}
aside {
  grid-area: lateral;
  background-color: #8e5bd6;
}
footer {
  grid-area: pie;
  background-color: #364f6b;
}

@media (max-width: 600px) {
  .pagina {
    grid-template-columns: 1fr;
    grid-template-areas: "cabecera"
                         "menu"
                         "contenido"
                         "lateral"
                         "pie";
  }
}
Resultado: grid-template-areas
01 · Flex y Grid: lo que falta

grid-auto-rows y grid-auto-flow

Qué hace el grid con los elementos que sobran.

  • grid-auto-rows: alto de las filas que el grid crea automáticamente.
  • grid-auto-flow: dense: rellena los huecos con elementos posteriores, útil cuando hay celdas de distinto tamaño.
style.css
.galeria {
  /* alto de las filas que se crean solas */
  grid-auto-rows: 70px;
  /* rellena los huecos con los elementos siguientes */
  grid-auto-flow: dense;
}
index.html
<div class="galeria">
  <div class="ancho">1</div>
  <div>2</div>
  <div>3</div>
  <div class="ancho">4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
</div>
style.css
.galeria {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 70px;
  grid-auto-flow: dense;
  gap: 8px;
}
.galeria div {
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  color: #fff;
  background-color: #3a8dde;
  border-radius: 8px;
}
.ancho {
  grid-column: span 2;
  background-color: #e07a3c;
}
Resultado: grid-auto-flow: dense
02 · Texto y desbordamiento

Cuando el texto no cabe

Títulos largos, descripciones enormes y direcciones que rompen la caja: cómo controlarlo.

En una web real nunca sabemos cuánto texto va a haber. Estas propiedades evitan que el contenido rompa el diseño:

  • white-space y text-overflow: una línea con «…».
  • line-clamp: varias líneas con «…».
  • overflow-wrap, word-break y hyphens: partir palabras largas.
  • text-wrap: repartir mejor las líneas.
02 · Texto y desbordamiento

Una línea con puntos suspensivos

Tres propiedades que van siempre juntas.

  • white-space: nowrap: el texto no salta de línea.
  • overflow: hidden: oculta lo que se sale.
  • text-overflow: ellipsis: añade «…» al cortar.
style.css
.una-linea {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
02 · Texto y desbordamiento

Varias líneas con puntos suspensivos

Limitar un texto a un número de líneas.

style.css
.tres-lineas {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

Esta forma con prefijo -webkit- funciona en todos los navegadores actuales. Existe también la propiedad estándar line-clamp, que aún no se soporta en todos.

02 · Texto y desbordamiento

Partir palabras largas

Direcciones, correos y palabras kilométricas.

  • overflow-wrap: anywhere: parte una palabra solo si no cabe.
  • word-break: break-all: parte en cualquier letra (más agresivo).
  • hyphens: auto: parte con guion según el idioma; hay que indicar lang en el HTML.
style.css
.url {
  overflow-wrap: anywhere;
}

/* alternativa más agresiva: rompe en cualquier letra */
.url {
  word-break: break-all;
}

/* guiones automáticos (necesita lang="es" en el HTML) */
.guiones {
  hyphens: auto;
}
02 · Texto y desbordamiento

text-wrap: balance y pretty

Titulares equilibrados y párrafos sin «viudas».

balance reparte el texto de un titular de forma pareja entre sus líneas. pretty evita que la última línea de un párrafo quede con una sola palabra.

style.css
h1,
h2 {
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}
02 · Texto y desbordamiento

Control del texto en funcionamiento

Dos tarjetas con todas las técnicas aplicadas.

index.html
<article class="card">
  <h3 class="una-linea">Un título larguísimo que no cabe en una sola línea de la tarjeta</h3>
  <p class="tres-lineas">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque dignissim sapien sapien, eu dictum sapien euismod non. Nulla nec erat pulvinar, finibus urna ut, iaculis nisi. Mauris rutrum, lacus a vehicula aliquet, ex felis magna.</p>
  <p class="url">https://www.ejemplo.com/una/direccion/muy/larga/que/rompe/la/caja/si/no/hacemos/nada</p>
</article>

<article class="card">
  <h3 class="equilibrado">Un titular largo que se reparte de forma equilibrada en varias líneas</h3>
  <p class="guiones" lang="es">Electroencefalografista, paralelepípedo y desproporcionadamente: palabras largas que se parten con guion al final de la línea.</p>
</article>
style.css
.card {
  width: 250px;
  padding: 16px;
  border: 1px solid #d8e0e9;
  border-radius: 12px;
}
.card h3 {
  margin: 0 0 8px;
  font-size: 1.05rem;
}
.card p {
  margin: 0 0 8px;
  font-size: 0.9rem;
}

/* una sola línea con puntos suspensivos */
.una-linea {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* varias líneas (3) con puntos suspensivos */
.tres-lineas {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* las palabras muy largas se parten */
.url {
  overflow-wrap: anywhere;
  color: #3a8dde;
}

/* reparte el titular de forma equilibrada */
.equilibrado {
  text-wrap: balance;
}

/* parte las palabras con guion */
.guiones {
  hyphens: auto;
  text-align: justify;
}
Resultado: control del texto
03 · Unidades y funciones nuevas

Unidades y funciones modernas

Lo nuevo que completa a px, rem, vw y rgb().

  • dvh, svh, lvh: alturas de pantalla fiables en móvil.
  • hsl() y color-mix(): colores más fáciles de modificar.
  • margin-inline, padding-block e inset: propiedades lógicas.
  • object-position: qué parte de una imagen se ve al recortarla.
03 · Unidades y funciones nuevas

dvh, svh y lvh

El problema de 100vh en el móvil.

En el móvil, la barra del navegador aparece y desaparece al hacer scroll, y 100vh no la tiene en cuenta: una sección «a pantalla completa» queda tapada por abajo.

style.css
/* 100vh = altura de la ventana "más grande" (sin barras del navegador móvil) */
.hero-antiguo {
  height: 100vh;
}

/* svh = altura con las barras visibles (la más pequeña) */
.hero-seguro {
  height: 100svh;
}

/* dvh = altura dinámica: se adapta cuando las barras aparecen o se ocultan */
.hero {
  height: 100dvh;
}
03 · Unidades y funciones nuevas

hsl()

Tono, saturación y luminosidad.

Con rgb() es difícil saber qué número cambiar para «hacerlo más oscuro». Con hsl() el tono es el color (0–360), la saturación su intensidad y la luminosidad lo claro u oscuro que es. Combinado con las variables de la clase 14 permite generar una paleta entera.

style.css
.boton {
  /* hsl(tono 0-360, saturación %, luminosidad %) */
  background-color: hsl(210 70% 55%);
}
.boton:hover {
  /* solo bajamos la luminosidad */
  background-color: hsl(210 70% 45%);
}

/* una paleta entera con una variable */
.muestra {
  background-color: hsl(var(--tono) 70% 55%);
}
03 · Unidades y funciones nuevas

color-mix()

Mezclar dos colores directamente en CSS.

Mezcla dos colores en el porcentaje indicado. Con blanco o negro sirve para crear versiones más claras y oscuras de un color de marca.

style.css
.claro {
  background-color: color-mix(in srgb, #3a8dde 30%, white);
}
.medio {
  background-color: color-mix(in srgb, #3a8dde, #e07a3c);
}
03 · Unidades y funciones nuevas

Propiedades lógicas e inset

inline es el eje horizontal y block el vertical.

Son atajos y se adaptan a idiomas que se escriben de derecha a izquierda. inset: 0 sustituye a escribir top, right, bottom y left a 0 (clase 13).

style.css
.caja {
  /* izquierda y derecha */
  margin-inline: auto;
  /* arriba y abajo */
  margin-block: 12px;
  padding-inline: 24px;
  padding-block: 10px;
}

/* inset: top, right, bottom y left a la vez */
.capa {
  position: absolute;
  inset: 0;
}
03 · Unidades y funciones nuevas

object-position

Qué parte de la imagen se ve con object-fit: cover.

Con object-fit: cover (clase 21) la imagen se recorta. object-position decide qué parte se queda visible: esquina, centro…

style.css
img {
  width: 100%;
  height: 110px;
  object-fit: cover;
  object-position: left top;
}
03 · Unidades y funciones nuevas

Unidades y funciones en funcionamiento

Todo junto: pantalla, paleta, mezclas, márgenes lógicos y recortes de imagen.

index.html
<section class="hero">
  <h1>Pantalla completa</h1>
  <p>Alto: 50dvh</p>
</section>

<div class="paleta">
  <div class="muestra" style="--tono: 10"></div>
  <div class="muestra" style="--tono: 60"></div>
  <div class="muestra" style="--tono: 140"></div>
  <div class="muestra" style="--tono: 210"></div>
  <div class="muestra" style="--tono: 290"></div>
</div>

<div class="mezclas">
  <div class="mezcla claro"></div>
  <div class="mezcla medio"></div>
  <div class="mezcla oscuro"></div>
</div>

<div class="caja">margin-inline: auto · padding-inline · padding-block</div>

<div class="fotos">
  <img src="../dinamicos/img1.svg" alt="" class="izq">
  <img src="../dinamicos/img1.svg" alt="" class="der">
  <img src="../dinamicos/img1.svg" alt="" class="centro">
</div>
style.css
.hero {
  height: 50dvh;
  display: grid;
  place-content: center;
  text-align: center;
  background-color: #e6f5d6;
  border-radius: 12px;
}
.paleta,
.mezclas,
.fotos {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
.muestra {
  flex: 1;
  height: 50px;
  border-radius: 8px;
  background-color: hsl(var(--tono) 70% 55%);
}
.mezcla {
  flex: 1;
  height: 50px;
  border-radius: 8px;
}
.claro {
  background-color: color-mix(in srgb, #3a8dde 30%, white);
}
.medio {
  background-color: color-mix(in srgb, #3a8dde, #e07a3c);
}
.oscuro {
  background-color: color-mix(in srgb, #3a8dde 60%, black);
}
.caja {
  width: 60%;
  margin-block: 12px;
  margin-inline: auto;
  padding-block: 10px;
  padding-inline: 24px;
  text-align: center;
  background-color: #f2f4ee;
  border-radius: 8px;
}
.fotos img {
  flex: 1;
  min-width: 0;
  height: 110px;
  object-fit: cover;
  border-radius: 8px;
}
.izq {
  object-position: left top;
}
.der {
  object-position: right bottom;
}
.centro {
  object-position: center;
}
Resultado: unidades y funciones
04 · Subgrid

El problema de las tarjetas

Cada tarjeta tiene su propio grid interior y no se alinean entre sí.

Si tres tarjetas tienen un título, un texto y un botón, y un título ocupa dos líneas, el texto de esa tarjeta empieza más abajo que el de sus vecinas. Cada tarjeta es un mundo aparte.

04 · Subgrid

grid-template-rows: subgrid

Las tarjetas comparten las filas del grid padre.

  1. Cada tarjeta ocupa varias filas del grid padre: grid-row: span 3.
  2. Es a su vez un grid que no define filas propias, sino que usa las del padre: grid-template-rows: subgrid.

Así el título, el texto y el botón de todas las tarjetas quedan alineados fila a fila.

style.css
/* cada tarjeta ocupa 3 filas del grid padre y las comparte con sus vecinas */
.con .card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  row-gap: 0;
}

También funciona con columnas:

style.css · columnas
.contenedor {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.fila-completa {
  display: grid;
  /* hereda las 4 columnas del padre */
  grid-template-columns: subgrid;
  grid-column: span 4;
}
04 · Subgrid

Subgrid en funcionamiento

Arriba, sin subgrid; abajo, con subgrid.

index.html
<p class="hint">Sin subgrid</p>
<div class="tarjetas sin">
  <article class="card">
    <h3>Título corto</h3>
    <p>Texto de la primera tarjeta.</p>
    <a class="open-btn" href="#">Ver</a>
  </article>
  <article class="card">
    <h3>Un título bastante más largo que ocupa dos líneas</h3>
    <p>Texto de la segunda tarjeta, que es algo más largo que el de la primera.</p>
    <a class="open-btn" href="#">Ver</a>
  </article>
  <article class="card">
    <h3>Título medio</h3>
    <p>Texto de la tercera.</p>
    <a class="open-btn" href="#">Ver</a>
  </article>
</div>

<p class="hint">Con subgrid</p>
<div class="tarjetas con">
  <article class="card">
    <h3>Título corto</h3>
    <p>Texto de la primera tarjeta.</p>
    <a class="open-btn" href="#">Ver</a>
  </article>
  <article class="card">
    <h3>Un título bastante más largo que ocupa dos líneas</h3>
    <p>Texto de la segunda tarjeta, que es algo más largo que el de la primera.</p>
    <a class="open-btn" href="#">Ver</a>
  </article>
  <article class="card">
    <h3>Título medio</h3>
    <p>Texto de la tercera.</p>
    <a class="open-btn" href="#">Ver</a>
  </article>
</div>
style.css
.tarjetas {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.card {
  padding: 14px;
  background-color: #f6f8fa;
  border: 1px solid #d8e0e9;
  border-radius: 12px;
}
.card h3 {
  margin: 0 0 8px;
  font-size: 1rem;
}
.card p {
  margin: 0 0 10px;
  font-size: 0.9rem;
}
/* cada tarjeta ocupa 3 filas del grid padre y las comparte con sus vecinas */
.con .card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  row-gap: 0;
}
Resultado: subgrid