CSS shape(): recortes responsivos sin coordenadas fijas (adiós a path() y a las máscaras SVG frágiles)
shape() es la nueva función CSS de clip-path que acepta curvas complejas con unidades CSS: porcentajes, rem, calc() e incluso unidades mixtas que mantienen el grosor de una curva mientras la forma escala. Es Baseline desde febrero de 2026 (Chrome 135+, Safari 18.4+, Firefox). Esto es lo que cambia para recortes orgánicos en imágenes y video, con el framework de decisión que usamos en los proyectos de Mintec.
CSS shape(): recortes responsivos sin coordenadas fijas (adiós a path() y a las máscaras SVG frágiles)
Sí: desde febrero de 2026 existe una función CSS que permite recortar imágenes y video con curvas orgánicas que escalan de verdad con el elemento — se llama shape(), es parte de clip-path, y elimina los dos workarounds que usábamos hasta ahora: path() con coordenadas fijas en píxeles y las máscaras SVG que se rompen en mobile. Chrome 135 y Safari 18.4 la trajeron en abril de 2025; Firefox completó el soporte a inicios de 2026 y la plataforma la declaró Baseline en el digest de febrero 2026 de web.dev. Es decir: ya se puede usar en producción sin excusas.
Lo escribimos desde el trabajo, no desde la teoría. En los últimos proyectos de Mintec, los recortes orgánicos —máscaras tipo blob, bordes ondulados, siluetas curvas sobre imágenes de producto y sobre video— eran la fuente número uno de bugs responsive que no aparecían en desktop. Cada vez que un diseñador exportaba una máscara SVG y el desarrollador la pegaba con clip-path: url(#...), el resultado se veía perfecto en el artboard y se deformaba en la vista móvil. shape()` resuelve exactamente ese problema.
El problema: la falsa elección de antes de shape()
Antes de shape(), si querías un recorte con curvas (no un polígono de bordes rectos), tenías tres opciones, y las tres mentían:
polygon(): responsivo de verdad (acepta porcentajes), pero solo líneas rectas. Cualquier curva se convertía en 20 puntos que crujían al escalar.path(): curvas reales con comandos SVG, pero solo coordenadas fijas en píxeles. Un path que se veía bien a 600px se descuadraba a 320px.- Máscara SVG con
<clipPath>: escalaba, pero siempre de forma uniforme — no podías decir "la forma crece pero el grosor de la onda se queda igual". Y arrastrabas un archivo SVG, unviewBoxy una referenciaurl(#...)que se rompía si el diseñador tocaba el archivo.
La consecuencia práctica: recortes que requerían JavaScript (listeners de resize recalculando paths), SVG inline pegados a mano, o directamente "el blob se ve distinto en móvil, lo dejamos así".
Qué es shape(): path SVG con unidades CSS
shape() es una función del tipo <basic-shape> que acepta comandos similares a los de un path SVG —from, line to, hline to, vline to, curve to, smooth to, arc to, close— pero con la diferencia que cambia todo: cada coordenada admite cualquier unidad CSS, incluyendo porcentajes, rem, calc() y custom properties.
El ejemplo canónico lo documentó el equipo de Chrome en su artículo "Use shape() for responsive clipping": una bandera con bordes en curva que con path() quedaba así:
.flag {
clip-path: path("M 0 20 C 25 0 75 40 100 20 V 80 C 75 100 25 60 0 80 z");
}
Todo en píxeles, congelado al tamaño para el que se dibujó. La misma bandera con shape():
.flag {
clip-path: shape(
from 0% 20%,
curve to 100% 20% with 25% 0% / 75% 40%,
vline to 80%,
curve to 0% 80% with 75% 100% / 25% 60%,
close
);
}
Mismos comandos, misma curva — pero ahora en porcentajes. El recorte escala con el elemento. Y aquí viene lo interesante: las unidades se pueden mezclar dentro de la misma forma. Quieres que la bandera escale pero que la onda mantenga 20px de altura constante:
.flag {
clip-path: shape(
from 0% 20px,
curve to 100% 20px with 25% 0% / 75% 40px,
vline to calc(100% - 20px),
curve to 0% calc(100% - 20px) with 75% 100% / 25% calc(100% - 40px),
close
);
}
Eso —un % y un px conviviendo en la misma coordenada— era literalmente imposible con path() y con SVG <clipPath>. Es la feature que mata el problema de las máscaras orgánicas en mobile.
Animaciones de recorte sin JavaScript
Como shape() vive en CSS, se puede animar con custom properties registradas vía @property. El equipo de Chrome muestra una bandera ondeando con puro CSS: registras --wave-height como <length>, la animas con @keyframes y clip-path la consume:
@property --wave-height {
syntax: "<length>";
inherits: false;
initial-value: 40px;
}
@keyframes wave {
from { --wave-height: 0px; }
to { --wave-height: 180px; }
}
.flag {
animation: wave 1s infinite alternate;
clip-path: shape(
from 0px var(--wave-height),
curve to 100% var(--wave-height) with 25% 0px / 75% calc(var(--wave-height) * 2),
close
);
}
Antes, animar una máscara orgánica significaba JavaScript manipulando un path SVG frame a frame. Ahora es un @keyframes y una custom property. Para el tipo de piezas animadas que producimos en la parte de producción de medios de Mintec, esto recorta tiempo de desarrollo y, sobre todo, elimina la capa de JS que se rompía cuando el cliente cambiaba el copy del hero.
Comparativa: qué técnica usar (y cuándo)
| Técnica | Curvas complejas | Escala con el elemento | Unidades mixtas | Animable en CSS | Estado en 2026 |
|---|---|---|---|---|---|
polygon() | No (solo líneas rectas) | Sí | Sí (%, calc()) | Sí | Baseline, ideal para formas simples |
path() | Sí | No (píxeles fijos) | No | No (JS) | Legado para trabajo responsivo |
SVG <clipPath> | Sí | Solo escala uniforme | No | Difícil | Legado; útil como fallback |
shape() | Sí | Sí | Sí (%, px, rem, calc(), variables) | Sí (@property) | Baseline desde feb 2026 |
El framework que aplicamos en los proyectos:
- Bordes rectos →
polygon(). Simple, Baseline, cero fricción. - Curvas orgánicas que deben escalar →
shape(). Este es el caso del 90% de las máscaras blob de producto. - Grosor de curva constante mientras la forma crece →
shape()con unidades mixtas. El caso que ningún otro método resuelve. - Navegadores antiguos → fallback con
@supports (clip-path: shape(from 0 0, line to 100% 0, close))y unpolygon()o máscara SVG antes del bloque moderno. - Bordes suaves (feathering) →
mask-imagecon gradientes, noclip-path. Un recorte duro no difumina bordes.
Lo que hicimos en un ecommerce real
En un rediseño de ecommerce reciente, los bloques de imagen de producto usaban máscaras blob exportadas como SVG. El problema aparecía exactamente como describe el equipo de Chrome en la referencia de CSS-Tricks sobre clip-path: lo que se veía en desktop no era lo que se veía en móvil, porque la máscara escalaba el grosor de las curvas junto con todo lo demás. Migramos los seis bloques a shape() con unidades mixtas: el cuerpo del blob escala con %, y el grosor de las ondas queda fijo en px. Resultado: se eliminaron ~40 líneas de SVG inline, un listener de resize que recalculaba paths en JavaScript, y el bug de mobile dejó de existir porque el recorte ya no depende de un tamaño concreto.
En un sitio de un cliente de medios, aplicamos lo mismo sobre video: clip-path: shape(...) directamente sobre un elemento <video> en el hero, para lograr una silueta orgánica que antes exigía renderizar el video con máscara en el editor de video. El recorte ocurre en tiempo de pintado, no afecta el layout ni la decodificación — el mismo principio de rendimiento de video que ya documentamos. Si además combinas esto con las pseudo-clases :playing y :paused y con @starting-style para animar la entrada, tienes un hero de video recortado y animado sin una línea de JavaScript.
Nuestra opinión (sin rodeos)
path() sigue siendo la respuesta equivocada para recortes responsivos, y el pipeline de "el diseñador exporta una máscara SVG" es legado: cada vez que veas un clip-path: path("M 0 0 ...") o un clip-path: url(#mask) en un proyecto nuevo, es deuda técnica recién creada. shape() no es un reemplazo cosmético: cambia dónde vive la responsabilidad del recorte — del archivo SVG congelado al CSS que ya escala con tu diseño. Es el mismo movimiento que ya vimos con contrast-color() y reading-flow: la plataforma absorbiendo trabajo manual que hacíamos con herramientas de otro paradigma.
Dos advertencias honestas. Primero, hoy shape() aplica a clip-path; la especificación también lo contempla para offset-path, pero en producción úsalo para recortes. Segundo, no recortes contenido que necesite ser legible: una máscara sobre texto ya era mala idea con SVG y sigue siéndolo con shape() — el clipping no arregla contraste, solo geometría. Y si vas a animar, registra las variables con @property en lugar de animar la propiedad completa: animar clip-path: shape() entero es caro; animar una <length> registrada es barato.
La próxima vez que un diseñador te pase un blob exportado de Figma, pídele la intención, no el archivo: qué curva, qué grosor, qué proporción. Con eso escribes un shape() de cinco líneas que funciona en cualquier viewport.
Preguntas Frecuentes
¿Qué es la función shape() en CSS?
shape() es una función del tipo <basic-shape> de CSS que define trayectorias de recorte para clip-path usando comandos similares a los de un path SVG (from, line to, curve to, arc to, close) pero con cualquier unidad CSS: porcentajes, rem, px, calc() y variables. A diferencia de path(), que solo acepta coordenadas fijas en píxeles, shape() crea formas orgánicas que escalan con el elemento.
¿shape() funciona en todos los navegadores?
Sí, desde febrero de 2026 es Baseline: Chrome 135+ y Safari 18.4+ lo soportan desde abril de 2025, y Firefox se sumó en su lanzamiento de enero/febrero de 2026. Para navegadores anteriores se usa progressive enhancement con @supports y un recorte fallback (por ejemplo, polygon() o una máscara SVG).
¿Cuál es la diferencia entre shape() y path() en clip-path?
path() acepta una cadena de comandos SVG con coordenadas fijas en píxeles: si el elemento cambia de tamaño, el recorte no se adapta salvo que lo recalcules. shape() acepta las mismas curvas pero con unidades relativas (porcentajes, cqw, calc()), y permite mezclar unidades dentro de la misma forma, por ejemplo escalar el cuerpo del recorte con % y mantener el grosor de una curva fijo en px.



