Saltar al contenido
multicolumna.css · devschool

Columnas y formas en CSS: columns, shape-outside y writing-mode

Lección 11 de 50 · 10 min de lectura · Actualizado el

En esta lección
  1. Texto en columnas: columns
  2. Separación y línea: column-gap y column-rule
  3. Un título que cruza todas las columnas: column-span
  4. Cómo se rellenan: column-fill
  5. Evitar cortes feos: break-inside y compañía
  6. Cuándo usar columnas (y cuándo no)
  7. Envolver el texto con formas: shape-outside
  8. Texto en vertical: writing-mode
  9. ¿Cuál uso?
  10. Errores frecuentes
  11. Resumen

Hasta ahora has colocado cajas con Flexbox y Grid, pero CSS también tiene herramientas para el propio texto: repartirlo en columnas como en una revista, hacer que rodee una forma que no sea un rectángulo y ponerlo en vertical. En esta lección verás las columnas (columns), cómo controlar dónde se cortan, el envolvimiento con formas (shape-outside) y un repaso de writing-mode. Al final sabrás cuál usar en cada caso y cuándo es mejor otra cosa.

Texto en columnas: columns

Con la propiedad columns conviertes un bloque en un contenedor de varias columnas. El contenido fluye de arriba abajo por la primera, sigue por la segunda, y así. No hace falta tocar el HTML: los hijos siguen siendo los mismos.

<article class="reportaje">
  <h2>Cómo elegir unas botas</h2>
  <p>...</p>
  <p>...</p>
  <p>...</p>
</article>
.reportaje {
  columns: 3;
}

columns es un atajo de dos propiedades que puedes escribir por separado:

PropiedadQué fijaEjemplo
column-countCuántas columnas quierescolumn-count: 3
column-widthEl ancho ideal de cada columnacolumn-width: 16em

Número fijo o ancho ideal

Con columns: 3 siempre habrá tres columnas, aunque en el móvil queden de 100px y no se pueda leer. Casi siempre es mejor fijar un ancho ideal y dejar que el navegador decida cuántas caben:

.reportaje {
  columns: 16em;    /* tantas columnas de 16em como quepan */
}

En un móvil habrá una sola columna; en un portátil, tres o cuatro. Sin media queries. Y si das las dos cosas (columns: 16em 3), el número es un máximo: nunca habrá más de tres, aunque quepan más.

Nota: el ancho se interpreta como un mínimo aproximado, y las columnas crecen un poco para ocupar todo el espacio. Por eso el resultado es de columnas algo más anchas que 16em, no más estrechas.

Separación y línea: column-gap y column-rule

.reportaje {
  columns: 16em;
  column-gap: 2rem;                 /* espacio entre columnas */
  column-rule: 1px solid #ccc;      /* línea entre columnas */
}
  • column-gap es el hueco entre columnas. El valor por defecto es 1em, que suele quedar justo. Con 2rem respira mejor.
  • column-rule dibuja una línea en medio del hueco. Se escribe como un border: grosor, estilo y color (column-rule-width, column-rule-style, column-rule-color). No ocupa espacio: se dibuja encima del column-gap, así que no cambia el ancho de las columnas.

Un título que cruza todas las columnas: column-span

Si un <h2> está dentro del contenedor, queda atrapado en la primera columna. Con column-span: all se estira por todas las columnas y el texto se reparte debajo:

.reportaje h2 {
  column-span: all;
}

Solo admite dos valores: all y none (por defecto). No existe “cruzar solo dos columnas”. Si hay un column-span: all a mitad del texto, el contenido se reparte antes y después por separado, como en un periódico con un titular intermedio.

Cómo se rellenan: column-fill

Por defecto (column-fill: balance) el navegador equilibra las columnas para que acaben aproximadamente igual de largas. Con auto, rellena una columna hasta la altura del contenedor y pasa a la siguiente, algo que solo tiene sentido si el contenedor tiene una altura fija.

.panel {
  height: 300px;
  columns: 2;
  column-fill: auto;
}

Evitar cortes feos: break-inside y compañía

Cuando el contenido se reparte en columnas, un bloque puede quedar partido entre dos: la mitad de una tarjeta al final de una columna y el resto arriba de la siguiente. Se evita con las propiedades de ruptura:

.reportaje figure,
.reportaje .tarjeta {
  break-inside: avoid;         /* no partas este bloque */
}

.reportaje h3 {
  break-after: avoid;          /* no dejes un subtítulo huérfano al final */
}
PropiedadPara qué sirve
break-inside: avoidNo partir el elemento entre columnas
break-before: columnForzar que empiece en una columna nueva
break-after: avoidNo separar el elemento de lo que viene después
orphans / widowsLíneas mínimas de un párrafo que pueden quedar solas abajo o arriba

break-inside: avoid es la más usada: con ella, una galería de tarjetas en columnas parece una cuadrícula tipo masonry (ladrillos de distinta altura sin huecos):

.galeria { columns: 12em; column-gap: 12px; }
.galeria figure { break-inside: avoid; margin: 0 0 12px; }

Estas mismas propiedades break-* sirven para impresión, para decidir dónde se corta una página; lo verás en CSS para imprimir.

Cuándo usar columnas (y cuándo no)

Las columnas de texto funcionan bien con textos cortos o medios: una introducción, una lista de enlaces, un listado de ingredientes, una lista de ciudades, el pie de una web. Fíjate en el problema que tienen: la persona lee hasta abajo de la primera columna y después tiene que volver arriba para leer la segunda. Si el texto es largo y en una pantalla las columnas no caben enteras, obligas a hacer scroll hacia abajo y hacia arriba una y otra vez.

  • Sí: listas largas de elementos cortos (que caben enteras en pantalla), pies de página con enlaces, galerías tipo masonry, texto de ancho muy grande donde una sola línea sería ilegible.
  • No: artículos largos, formularios, o colocar bloques de la página (para eso, Grid).

Una lista corta en columnas es un buen ejemplo:

.ciudades {
  columns: 10em;
  padding: 0;
  list-style: none;
}

Envolver el texto con formas: shape-outside

Cuando una imagen flota con float, el texto la rodea… siguiendo siempre su rectángulo, aunque la imagen sea un círculo. Con shape-outside le dices al texto que rodee otra forma:

.avatar {
  float: left;
  width: 120px;
  height: 120px;
  margin-right: 16px;
  border-radius: 50%;          /* la forma que se ve */
  shape-outside: circle(50%);  /* la forma que sigue el texto */
}

Dos cosas importantes:

  1. Solo funciona sobre elementos con float. Sin flotar, no hace nada.
  2. border-radius es lo que ves, shape-outside es lo que sigue el texto. Hay que poner los dos para que coincidan.

Formas disponibles

shape-outside: circle(50%);                          /* círculo */
shape-outside: ellipse(60% 40%);                     /* elipse */
shape-outside: inset(10px round 20px);               /* rectángulo con esquinas redondeadas */
shape-outside: polygon(0 0, 100% 0, 0 100%);         /* triángulo: el texto sigue la diagonal */
shape-outside: url(foto-recortada.png);              /* sigue la parte opaca de la imagen */
shape-outside: border-box;                           /* la caja del elemento */

Con polygon(), puedes hacer que el texto se ajuste a una diagonal o a un recorte irregular. Con url(), el texto sigue la silueta de una imagen con transparencia (PNG, WebP, SVG): el texto se acerca a la parte visible y entra en la transparente. El umbral se ajusta con shape-image-threshold.

shape-margin

Es el margen entre la forma y el texto:

.avatar {
  shape-outside: circle(50%);
  shape-margin: 12px;
}

Cuidado: shape-outside solo cambia por dónde pasa el texto, no recorta la imagen. Para recortar el dibujo, usa border-radius o clip-path. Y si la imagen viene de otro dominio, shape-outside: url() exige que se pueda leer (CORS); si no, el navegador la ignora.

Texto en vertical: writing-mode

Ya viste writing-mode en propiedades lógicas. Aquí lo resumimos desde el punto de vista del diseño. Cambia la dirección en la que se escribe y se apilan las líneas:

.lomo {
  writing-mode: vertical-rl;     /* líneas verticales, se apilan hacia la izquierda */
  text-orientation: mixed;       /* letras latinas giradas, escrituras asiáticas de pie */
}

.cartel {
  writing-mode: vertical-rl;
  text-orientation: upright;     /* todas las letras de pie, una debajo de otra */
}
PropiedadValorEfecto
writing-modehorizontal-tbNormal: horizontal, de arriba abajo
vertical-rlVertical, las líneas nuevas van a la izquierda
vertical-lrVertical, las líneas nuevas van a la derecha
text-orientationmixedLetras latinas giradas 90° (por defecto)
uprightTodas las letras de pie

Casos de uso reales: la etiqueta lateral de una sección, el lomo de un libro, la cabecera de una tabla estrecha, o un texto largo en japonés o chino. Para números cortos dentro de texto vertical existe text-combine-upright: all, que los junta en una sola casilla; es lo habitual al escribir años o fechas en japonés vertical.

A diferencia de transform: rotate(), writing-mode cambia el espacio que ocupa la caja: una etiqueta que gira con rotate seguiría ocupando su hueco horizontal y se solaparía con el resto.

¿Cuál uso?

Si quieres…Usa
Repartir una lista o un texto corto en varias columnascolumns
Que una galería de tarjetas de distinta altura parezca ladrilloscolumns + break-inside: avoid
Un título que cruce las columnascolumn-span: all
Evitar que una tarjeta se parta entre columnasbreak-inside: avoid
Que el texto rodee un círculo o una diagonalfloat + shape-outside
Una etiqueta o un lomo en verticalwriting-mode
Colocar bloques de una páginaGrid o Flexbox

Errores frecuentes

  • Usar shape-outside sin float. No hace nada: el elemento tiene que flotar.
  • Pensar que shape-outside recorta la imagen. Solo mueve el texto; el dibujo se recorta con border-radius o clip-path.
  • Poner columns: 3 y olvidar el móvil. Tres columnas de 100px no se leen. Prefiere columns: 16em.
  • Un <h2> dentro de un contenedor con columnas sin column-span. Queda encajado en una sola columna.
  • Tarjetas partidas entre columnas. Añade break-inside: avoid a cada tarjeta.
  • Columnas de altura fija con mucho texto. Con column-fill: auto y poca altura, aparecen columnas nuevas a la derecha y un scroll horizontal. Deja que la altura sea automática.
  • Girar texto con rotate para ponerlo en vertical. La caja no cambia de tamaño; usa writing-mode.

Resumen

PropiedadQué hace
columnsAtajo de column-count y column-width
column-gapHueco entre columnas
column-ruleLínea entre columnas (no ocupa espacio)
column-span: allUn elemento cruza todas las columnas
column-fillbalance (equilibrado) o auto (rellena en orden)
break-inside: avoidNo partir un bloque entre columnas
break-before / break-afterForzar o evitar cortes
shape-outsideForma que sigue el texto (con float)
shape-marginMargen entre la forma y el texto
writing-modeTexto horizontal o vertical
text-orientationLetras giradas o de pie en vertical

En la siguiente lección vas a ajustar el modelo de cajas con Modelo de caja. Si te interesan los recortes de formas, más adelante tendrás Recortes y máscaras.

Pruébalo tú

Cambia el código y pulsa Ejecutar (o Ctrl + Enter).

multicolumna.html
Resultado

Pon a prueba lo que has aprendido

[CSS] ¿Qué ventaja tiene columns: 16em frente a columns: 3?

[CSS] Un h2 dentro de un contenedor con columns queda encajado en la primera columna. ¿Cómo hacer que cruce todas?

[CSS] ¿Cuál es el requisito para que shape-outside afecte al texto?
.foto {
  float: left;
  shape-outside: circle(50%);
}

[CSS] Tarjetas de distinta altura en columnas, y alguna se parte entre dos columnas. ¿Qué propiedad lo evita?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.