wandres.dev
RECORTE Y MÁSCARAS · clip-path, mask, shape-outside

shape-outside: que el texto fluya alrededor de una forma

La única propiedad de CSS que modifica el contorno de exclusión de un flotante, cómo se deriva una forma del canal alfa de una imagen, y por qué necesita float para existir.

⏱ 16 min

Que el texto rodee una forma en lugar de un rectángulo es de las cosas más viejas de la maquetación impresa y de las últimas en llegar a la web. shape-outside lo resuelve, y lo hace con una restricción que decepciona a casi todo el mundo la primera vez: solo funciona sobre elementos flotados. Entender por qué es así explica también por qué no llegará nunca a grid ni a flex.

🎯 Al terminar esta lección sabrás
  • Aplicar shape-outside con formas básicas y con imágenes.
  • Explicar por qué la propiedad exige float.
  • Ajustar el contorno derivado de una imagen con shape-image-threshold.
  • Combinar la exclusión con el recorte visual del elemento.

La sintaxis y la restricción

.foto {
  float: left;
  inline-size: 220px;
  aspect-ratio: 1;
  shape-outside: circle(50%);
  shape-margin: 1rem;
  clip-path: circle(50%);
}

shape-outside acepta los mismos valores que clip-path en cuanto a formas básicas —inset(), circle(), ellipse(), polygon()—, además de las palabras de caja y de una imagen.

shape-margin añade una separación medida desde la forma, no desde la caja. Es la propiedad que hace que el texto no se pegue a la curva, y es distinta de margin, que sigue empujando la caja rectangular.

Y la restricción: la propiedad solo tiene efecto en elementos flotados. En un elemento en flujo normal, en una celda de rejilla o en un ítem de flexbox no hace absolutamente nada.

La razón es estructural y merece entenderse. Todo el mecanismo por el que un texto evita a otra cosa en CSS es el de los flotantes: un flotante se saca del flujo, se coloca a un lado, y las cajas de línea del contenido siguiente se acortan para no solaparse con él. Ese acortamiento se calcula línea a línea, contra el rectángulo del flotante. shape-outside no inventa un mecanismo nuevo: se limita a sustituir ese rectángulo por otro contorno en el mismo cálculo.

Grid y flexbox no tienen nada equivalente. Ahí las cajas se colocan en pistas o en líneas y no hay ningún paso donde una caja de línea se acorte por la presencia de otra cosa. Por eso la propiedad no puede extenderse a ellos sin inventar un modelo de exclusiones completo, que es una especificación aparte que lleva años parada.

Formas desde una imagen

El valor más interesante no es una función geométrica sino una imagen:

.recorte {
  float: right;
  inline-size: 280px;
  shape-outside: url(silueta.png);
  shape-margin: 12px;
}

El navegador lee el canal alfa de la imagen y construye el contorno con las zonas cuyo alfa supere un umbral. Ese umbral es shape-image-threshold, con valor inicial 0.0, lo que significa que cualquier píxel con alfa mayor que cero cuenta como parte de la forma.

Subirlo endurece el criterio y es lo que quieres con imágenes que tienen bordes suaves o sombras:

.recorte { shape-image-threshold: 0.4; }

Con 0.4, el halo semitransparente alrededor de una figura recortada deja de contar y el texto se acerca más.

Un degradado también vale como imagen de forma, y ahí shape-image-threshold se vuelve el control principal:

.diagonal {
  float: left;
  inline-size: 200px;
  block-size: 300px;
  shape-outside: linear-gradient(to bottom right, #000 50%, transparent 50%);
  shape-image-threshold: 0.5;
}

Eso produce una exclusión triangular sin escribir un solo polígono.

🛑
Las imágenes de forma están sujetas a CORS

Una imagen usada en shape-outside tiene que ser del mismo origen o servirse con las cabeceras de CORS adecuadas. Si no, el navegador ignora la forma en silencio y el texto vuelve a rodear el rectángulo. No hay error en consola en todos los motores, así que es un fallo difícil de diagnosticar: si la forma no aparece y el CSS es correcto, mira de dónde viene la imagen.

La forma no recorta

Un error de primera vez: shape-outside no cambia el aspecto del elemento. Solo cambia por dónde fluye el texto de alrededor. Un elemento con shape-outside: circle(50%) sigue viéndose como un rectángulo, con el texto rodeando un círculo invisible.

Para que se vea como se comporta hay que recortarlo también, y por eso los ejemplos llevan siempre las dos propiedades con el mismo valor:

.foto {
  shape-outside: circle(50%);
  clip-path: circle(50%);
}

Que sean dos propiedades separadas no es un descuido: son dos cosas distintas y hay casos donde interesa que no coincidan. Un elemento con una forma de exclusión mayor que su forma visible deja aire alrededor sin que la separación sea uniforme, cosa que shape-margin no puede hacer porque es constante.

Cuándo vale la pena

La honestidad obliga a acotar. shape-outside exige un flotante, exige un texto lo bastante largo como para que rodear signifique algo, y exige que el resultado siga leyéndose bien cuando el contenedor se estreche. En una columna de móvil, un texto que rodea un círculo produce líneas de tres palabras que se leen fatal.

El patrón que funciona es aplicarla solo por encima de cierto ancho:

.articulo { container-type: inline-size; }

@container (min-width: 40rem) {
  .articulo .figura {
    float: left;
    shape-outside: circle(50%);
    shape-margin: 1.5rem;
    clip-path: circle(50%);
  }
}

Por debajo de ese ancho la figura vuelve al flujo, ocupa el ancho completo y el texto va debajo. Es una degradación limpia y no necesita nada más.

El contorno se aplica a las cajas de línea, no a los elementos, y de ahí sale todo lo que sorprende

El modelo mental que hace que shape-outside deje de parecer caprichosa es entender contra qué se calcula la exclusión: no contra los elementos vecinos sino contra cada caja de línea, una por una, en el momento de maquetar el bloque que contiene el flotante. Para cada línea, el motor mira a qué altura vertical cae, corta la forma a esa altura, y acorta la línea hasta el borde de la forma. De ahí caen tres consecuencias que se descubren siempre por accidente. La primera: la exclusión se aplica a la línea entera, así que un contorno con una entrante estrecha no produce una palabra encajada dentro de ella; produce una línea acortada hasta el punto más saliente de la forma a esa altura. Una forma cóncava no se aprovecha, y un polígono con dientes se comporta como su envolvente por filas. La segunda: como el corte se hace a la altura de cada línea, la resolución vertical de la exclusión es la altura de línea. Con line-height grande, una curva suave se convierte en una escalera de escalones altos y el efecto se pierde; con line-height pequeño la curva se aprecia. Es el único caso de CSS donde la tipografía cambia la calidad de una forma. Y la tercera: los elementos que no generan cajas de línea ignoran la exclusión por completo. Un div con fondo, una tabla, una imagen en bloque, cualquier cosa que no sea texto en línea, pasa por debajo del flotante sin acortarse; solo el contenido en línea respeta la forma. Eso explica el desconcierto de ver un párrafo rodeando bien el círculo y el bloque siguiente metiéndosele encima. La regla operativa: shape-outside es una propiedad de composición tipográfica y solo funciona sobre texto corrido. Para todo lo demás sigue haciendo falta reservar el hueco con layout de verdad.

⚔️ Haz fluir el texto
  1. Aplica shape-outside: circle(50%) sin clip-path y observa que el elemento sigue siendo rectangular.
  2. Rodea una imagen recortada con shape-image-threshold a 0 y a 0.5 y compara la separación.
  3. Construye una exclusión triangular con un degradado y umbral 0.5.
  4. Sube el line-height del texto hasta que la curva se convierta en una escalera.
  5. Mete un bloque con fondo entre dos párrafos y comprueba que ignora la exclusión.