Columnas y formas en CSS: columns, shape-outside y writing-mode
En esta lección
- Texto en columnas: columns
- Separación y línea: column-gap y column-rule
- Un título que cruza todas las columnas: column-span
- Cómo se rellenan: column-fill
- Evitar cortes feos: break-inside y compañía
- Cuándo usar columnas (y cuándo no)
- Envolver el texto con formas: shape-outside
- Texto en vertical: writing-mode
- ¿Cuál uso?
- Errores frecuentes
- 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:
| Propiedad | Qué fija | Ejemplo |
|---|---|---|
column-count | Cuántas columnas quieres | column-count: 3 |
column-width | El ancho ideal de cada columna | column-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-gapes el hueco entre columnas. El valor por defecto es1em, que suele quedar justo. Con2remrespira mejor.column-ruledibuja una línea en medio del hueco. Se escribe como unborder: grosor, estilo y color (column-rule-width,column-rule-style,column-rule-color). No ocupa espacio: se dibuja encima delcolumn-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 */
}
| Propiedad | Para qué sirve |
|---|---|
break-inside: avoid | No partir el elemento entre columnas |
break-before: column | Forzar que empiece en una columna nueva |
break-after: avoid | No separar el elemento de lo que viene después |
orphans / widows | Lí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:
- Solo funciona sobre elementos con
float. Sin flotar, no hace nada. border-radiuses lo que ves,shape-outsidees 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-outsidesolo cambia por dónde pasa el texto, no recorta la imagen. Para recortar el dibujo, usaborder-radiusoclip-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 */
}
| Propiedad | Valor | Efecto |
|---|---|---|
writing-mode | horizontal-tb | Normal: horizontal, de arriba abajo |
vertical-rl | Vertical, las líneas nuevas van a la izquierda | |
vertical-lr | Vertical, las líneas nuevas van a la derecha | |
text-orientation | mixed | Letras latinas giradas 90° (por defecto) |
upright | Todas 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 columnas | columns |
| Que una galería de tarjetas de distinta altura parezca ladrillos | columns + break-inside: avoid |
| Un título que cruce las columnas | column-span: all |
| Evitar que una tarjeta se parta entre columnas | break-inside: avoid |
| Que el texto rodee un círculo o una diagonal | float + shape-outside |
| Una etiqueta o un lomo en vertical | writing-mode |
| Colocar bloques de una página | Grid o Flexbox |
Errores frecuentes
- Usar
shape-outsidesinfloat. No hace nada: el elemento tiene que flotar. - Pensar que
shape-outsiderecorta la imagen. Solo mueve el texto; el dibujo se recorta conborder-radiusoclip-path. - Poner
columns: 3y olvidar el móvil. Tres columnas de 100px no se leen. Prefierecolumns: 16em. - Un
<h2>dentro de un contenedor con columnas sincolumn-span. Queda encajado en una sola columna. - Tarjetas partidas entre columnas. Añade
break-inside: avoida cada tarjeta. - Columnas de altura fija con mucho texto. Con
column-fill: autoy poca altura, aparecen columnas nuevas a la derecha y un scroll horizontal. Deja que la altura sea automática. - Girar texto con
rotatepara ponerlo en vertical. La caja no cambia de tamaño; usawriting-mode.
Resumen
| Propiedad | Qué hace |
|---|---|
columns | Atajo de column-count y column-width |
column-gap | Hueco entre columnas |
column-rule | Línea entre columnas (no ocupa espacio) |
column-span: all | Un elemento cruza todas las columnas |
column-fill | balance (equilibrado) o auto (rellena en orden) |
break-inside: avoid | No partir un bloque entre columnas |
break-before / break-after | Forzar o evitar cortes |
shape-outside | Forma que sigue el texto (con float) |
shape-margin | Margen entre la forma y el texto |
writing-mode | Texto horizontal o vertical |
text-orientation | Letras 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).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.