Cómo comenzar a usar las formas CSS

Une el contenido alrededor de rutas personalizadas

Durante mucho tiempo, los diseñadores web se vieron obligados a crear dentro de las limitaciones del rectángulo. La mayor parte del contenido en la web sigue atrapado en cuadros simples porque la mayoría de los emprendimientos creativos en un diseño no rectangular termina en frustración. Esto está a punto de cambiar con la introducción de formas de CSS, disponibles a partir de Chrome 37. Las formas de CSS permiten a los diseñadores web unir el contenido alrededor de trazados personalizados, como círculos, elipses y polígonos, y así liberarse de las restricciones del rectángulo.

Las formas se pueden definir manualmente o se pueden inferir de las imágenes.

Veamos un ejemplo muy sencillo.

Quizás fuiste tan ingenuo como yo cuando flotaste por primera vez una imagen con partes transparentes, esperando que el contenido se envolviera y llene los espacios, pero te decepcionarás por la forma de envoltorio rectangular que se mantenía alrededor del elemento. Las formas de CSS se pueden usar para resolver este problema.

Extrae una forma a partir de una imagen
<img class="element" src="image.png" />
<p>Lorem ipsum…</p>

<style>
.element{
  shape-outside: url(image.png);
  shape-image-threshold: 0.5;
  float: left;
}
</style>

La declaración de CSS shape-outside: url(image.png) le indica al navegador que extraiga una forma de la imagen.

La propiedad shape-image-threshold define el nivel mínimo de opacidad de los píxeles que se usarán para crear la forma. Su valor debe estar entre 0.0 (completamente transparente) y 1.0 (completamente opaco). Por lo tanto, shape-image-threshold: 0.5 significa que solo se usarán píxeles con una opacidad del 50% o más para crear la forma.

La propiedad float es clave aquí. Si bien la propiedad shape-outside define la forma del área alrededor de la cual se unirá el contenido, sin el número de punto flotante, no verás los efectos de la forma.

Los elementos tienen un área flotante en el lado opuesto de su valor float. Por ejemplo, si un elemento con la imagen de una taza de café se flota hacia la izquierda, el área flotante se creará a la derecha de la taza. Aunque puedes diseñar una imagen con espacios vacíos en ambos lados, el contenido solo envolverá la forma en el lado opuesto designado por la propiedad de número de punto flotante, a la izquierda o a la derecha, nunca a ambos.

En el futuro, se podrá utilizar shape-outside en elementos que no aparezcan flotantes con la introducción de las exclusiones de CSS.

Cómo crear formas manualmente

Además de extraer formas de imágenes, también puedes programarlas de forma manual. Puedes elegir entre algunos valores funcionales para crear formas: circle(), ellipse(), inset() y polygon(). Cada función de forma acepta un conjunto de coordenadas y se sincroniza con un cuadro de referencia que establece el sistema de coordenadas. Más información sobre los cuadros de referencia en un momento.

La función círculo()

Ilustración del valor de la forma círculo()

La notación completa del valor de una forma de círculo es circle(r at cx cy), en la que r es el radio del círculo, mientras que cx y cy son coordenadas del centro del círculo en el eje X y el eje Y. Las coordenadas del centro del círculo son opcionales. Si las omites, se usará el centro del elemento (la intersección de sus diagonales) como opción predeterminada.

.element{
  shape-outside: circle(50%);
  width: 300px;
  height: 300px;
  float: left;
}

En el ejemplo anterior, el contenido rodeará el exterior de una ruta circular. El argumento único 50% especifica el radio del círculo, que, en este caso específico, equivale a la mitad del ancho o la altura del elemento. Cambiar las dimensiones del elemento influirá en el radio de la forma del círculo. Este es un ejemplo básico de cómo las formas CSS pueden ser responsivas.

Antes de continuar, un apartado rápido: es importante recordar que las formas de CSS solo influyen en la forma del área flotante alrededor de un elemento. Si el elemento tiene un fondo, la forma no lo recortará. Para lograr ese efecto, debes utilizar las propiedades del enmascaramiento de CSS, ya sea clip-path o mask-image. La propiedad clip-path resulta muy útil porque sigue la misma notación que las formas CSS, por lo que puedes volver a usar valores.

Ilustración de la forma `círculo()` + ruta de recorte

Las ilustraciones de este documento usan recortes para resaltar la forma y ayudarte a comprender los efectos.

Volvamos a la forma de círculo.

Cuando se usan porcentajes para el radio del círculo, el valor en realidad se calcula con una fórmula un poco más compleja: qrt(width^2 + altura^2) / sqrt(2). Es útil comprender esto porque te ayudará a imaginar cuál será la forma de círculo resultante si las dimensiones del elemento no son iguales.

Todos los tipos de unidades CSS se pueden utilizar en coordenadas de funciones de forma: px, em, rem, vw, vh, etc. Puedes elegir uno que sea lo suficientemente flexible o rígido para tus necesidades.

Puedes ajustar la posición del círculo estableciendo valores explícitos para las coordenadas de su centro.

.element{
  shape-outside: circle(50% at 0 0);
}

Esto posiciona el centro del círculo en el origen del sistema de coordenadas. ¿Qué es el sistema de coordenadas? Aquí es donde presentamos los cuadros de referencia.

Cuadros de referencia para formas CSS

El cuadro de referencia es un cuadro virtual alrededor del elemento, que establece el sistema de coordenadas que se usa para dibujar y posicionar la forma. El origen del sistema de coordenadas se encuentra en la esquina superior izquierda, con el eje X apuntando hacia la derecha y el eje Y hacia abajo.

Sistema de coordenadas para las formas CSS

Recuerda que shape-outside altera la forma del área flotante alrededor de la que se unirá el contenido. El área flotante se extiende hasta los bordes externos del cuadro definido por la propiedad margin. Esto se denomina margin-box y es el cuadro de referencia predeterminado de una forma si no se menciona ninguna explícitamente.

Las siguientes dos declaraciones de CSS tienen resultados idénticos:

.element{
  shape-outside: circle(50% at 0 0);
  /* identical to: */
  shape-outside: circle(50% at 0 0) margin-box;
}

Aún no configuramos un margen en el elemento. En este punto, es seguro suponer que el origen del sistema de coordenadas y el centro del círculo se encuentran en la esquina superior izquierda del área de contenido del elemento. Esto cambia cuando estableces un margen:

.element{
  shape-outside: circle(50% at 0 0) margin-box;
  margin: 100px;
}

El origen del sistema de coordenadas ahora se encuentra fuera del área de contenido del elemento (100 px de arriba y 100 px a la izquierda), al igual que el centro del círculo. El valor calculado del radio del círculo también aumenta para tener en cuenta la mayor superficie del sistema de coordenadas establecido por la casilla de referencia margin-box.

Sistema de coordenadas de cuadro de márgenes con y sin margen
Puedes elegir entre algunas opciones de cuadros de referencia: "margin-box", "Border-box", "padding-box" y "content-box". Sus nombres indican sus límites. Anteriormente, explicamos el `margin-box`. El `Border-box` está restringido por los bordes externos de los bordes del elemento, el `padding-box` está restringido por el padding del elemento, mientras que `content-box` es idéntica al área de superficie real que usa el contenido dentro de un elemento.
Ilustración de todos los cuadros de referencia

Solo se puede usar una casilla de referencia a la vez con una declaración shape-outside. Cada cuadro de referencia influirá en la forma de una manera diferente y, a veces, sutil. Hay otro artículo que profundiza en el contenido y te ayuda a comprender los cuadros de referencia de las formas CSS.

La función elipse()

Ilustración del valor de la forma elipse()

Los puntos suspensivos parecen círculos aplastados. Se definen como ellipse(rx ry at cx cy), donde rx y ry son los radios de la elipse en el eje X y en el eje Y, mientras que cx y cy son las coordenadas del centro de la elipse.

.element{
  shape-outside: ellipse(150px 300px at 50% 50%);
  width: 300px;
  height: 600px;
}

Los valores porcentuales se calcularán a partir de las dimensiones del sistema de coordenadas. No se requieren cálculos matemáticos divertidos. Puedes omitir las coordenadas del centro de la elipse y se inferirán desde el centro del sistema de coordenadas.

Los radios de los ejes X e Y también se pueden definir con palabras clave: farthest-side produce un radio igual a la distancia entre el centro de la elipse y el lado del cuadro de referencia que está más alejado de él, mientras que closest-side significa exactamente lo opuesto. Utiliza la distancia más corta entre el centro y un lado.

.element{
  shape-outside: ellipse(closest-side farthest-side at 50% 50%);
  /* identical to: */
  shape-outside: ellipse(150px 300px at 50% 50%);
  width: 300px;
  height: 600px;
}

Esto puede resultar útil cuando las dimensiones del elemento (o el cuadro de referencia) pueden cambiar de manera impredecible, pero quieres que la forma de elipse se adapte.

También se pueden utilizar las mismas palabras clave farthest-side y closest-side para el radio en la función de forma circle().

La función Polygon()

Ilustración del valor de forma de Polygon()

Si los círculos y los puntos suspensivos son demasiado limitantes, la función de forma de polígono abre un mundo de opciones. El formato es polygon(x1 y1, x2 y2, ...), en el que se especifican pares de coordenadas x y para cada vértice (punto) de un polígono. El número mínimo de pares para especificar un polígono es tres, es decir, un triángulo.

.element{
  shape-outside: polygon(0 0, 0 300px, 300px 600px);
  width: 300px;
  height: 600px;
}

Los vértices se colocan en el sistema de coordenadas. En el caso de los polígonos responsivos, puedes utilizar valores porcentuales para algunas o todas las coordenadas.

.element{
  /* polygon responsive to font-size*/
  shape-outside: polygon(0 0, 0 100%, 100% 100%);
  width: 20em;
  height: 40em;
}

Existe un parámetro fill-rule opcional, importado desde SVG, que le indica al navegador cómo considerar el “interior” de un polígono en caso de rutas que se intersectan o formas cerradas. Joni Trythall explica muy bien cómo funciona la propiedad de regla de relleno en SVG. Si no está definido, el valor predeterminado de fill-rule es nonzero.

.element{
  shape-outside: polygon(0 0, 0 100%, 100% 100%);
  /* identical to: */
  shape-outside: polygon(nonzero, 0 0, 0 100%, 100% 100%);
}

La función inset()

La función de forma inset() te permite crear formas rectangulares alrededor de las cuales unir contenido. Esto puede sonar contraintuitivo si se tiene en cuenta la premisa inicial de que las formas de CSS liberan el contenido web de cuadros simples. Quizás muy bien. Aún no encontré un caso de uso para inset() que aún no se puede lograr con números de punto flotante y márgenes, ni con un polygon(). Sin embargo, inset() proporciona una expresión más legible para las formas rectangulares que polygon().

La notación completa de una función de forma de inserción es inset(top right bottom left border-radius). Los primeros cuatro argumentos de posición son desplazamientos hacia adentro desde los bordes del elemento. El último argumento es el radio del borde de la forma rectangular. Es opcional, por lo que puedes omitirla. Sigue la notación abreviada border-radius que ya usas en CSS.

.element{
  shape-outside: inset(100px 100px 100px 100px);
  /* yields a rectangular shape which is 100px inset on all sides */
  float: left;
}

Cómo crear formas a partir de cuadros de referencia

Si no especificas una función de forma para la propiedad shape-outside, puedes permitir que el navegador derive una forma del cuadro de referencia del elemento. El cuadro de referencia predeterminado es margin-box. Nada exótico hasta el momento, así es como funcionan las carrozas. Pero, si aplicas esta técnica, puedes reutilizar la geometría de un elemento. Veamos la propiedad border-radius.

Si la utilizas para redondear las esquinas de un elemento flotante, se obtiene el efecto de recorte, pero el área flotante sigue siendo rectangular. Agrega shape-outside: border-box para ajustar el contorno creado por border-radius.

Extraer una forma del radio de borde de un elemento mediante el cuadro de referencia del cuadro de borde
.element{
  border-radius: 50%;
  shape-outside: border-box;
  float: left;
}

Por supuesto que puedes usar todos los cuadros de referencia de esta manera. Este es otro uso para las formas derivadas: comillas simples desplazadas.

Crea una comilla desplegable desplazada mediante el cuadro de referencia del cuadro de contenido

Es posible lograr el efecto de la cotización inicial del desplazamiento usando solo las propiedades de margen y de número de punto flotante. Sin embargo, esto requiere que coloques el elemento de comillas en el árbol HTML en el punto en el que deseas que se renderice.

A continuación, se muestra cómo lograr el mismo efecto de comillas simples de desplazamiento con mayor flexibilidad:

.pull-quote{
  shape-outside: content-box;
  margin-top: 200px;
  float: left;
}

Configuramos de manera explícita el cuadro de referencia content-box para el sistema de coordenadas de la forma. En este caso, la cantidad de contenido entre comillas simples define la forma alrededor de la cual se ajustará el contenido externo. La propiedad margin-top se usa aquí para posicionar (desplazar) la comilla de extracción, independientemente de su posición en el árbol HTML.

Margen de la forma

Notarás que ajustar el contenido alrededor de una forma puede hacer que se toque demasiado cerca del elemento. Puedes agregar espaciado alrededor de la forma con la propiedad shape-margin.

.element{
  shape-outside: circle(40%);
  shape-margin: 1em;
  float: left;
}

El efecto es similar al que estás acostumbrado a usar la propiedad margin normal, pero shape-margin solo afecta el espacio alrededor del valor shape-outside. Agregará espaciado alrededor de la forma solo si hay espacio en el sistema de coordenadas. Por eso, en el ejemplo anterior, el radio del círculo está configurado en 40%, no en 50%. Si el radio se hubiera establecido en 50%, el círculo habría ocupado todo el espacio en el sistema de coordenadas sin dejar espacio para el efecto de shape-margin. Recuerda que la forma está limitada al margin-box del elemento (el elemento más su margin circundante). Si la forma es más grande y se desborda, se recortará a la margin-box y obtendrás una forma rectangular.

Es importante comprender que shape-margin acepta solo un valor positivo único. No tiene una notación a largo plazo. En todo caso, ¿qué es el margen superior de la forma para un círculo?

Cómo animar formas

Puedes combinar formas de CSS con muchas otras funciones, como transiciones y animaciones. Sin embargo, debo enfatizar que a los usuarios les resulta muy molesto cuando el diseño del texto cambia mientras leen. Si decides priorizar la animación de formas, presta mucha atención a la experiencia.

Puedes animar los radios y los centros de las formas circle() y ellipse(), siempre y cuando se definan en valores con los que el navegador pueda interpolar. Es posible ir de circle(30%) a circle(50%). Sin embargo, animar entre circle(closest-side) y circle(farthest-side) bloqueará el navegador.

.element{
  shape-outside: circle(30%);
  transition: shape-outside 1s;
  float: left;
}

.element:hover{
  shape-outside: circle(50%);
}
GIF de un círculo animado

Se pueden lograr efectos más interesantes cuando se animan formas polygon(), teniendo en cuenta que el polígono debe tener la misma cantidad de vértices entre los dos estados de animación. El navegador no puede interpolar si agregas o quitas vértices.

Un truco es agregar la cantidad máxima de vértices que necesitas y posicionarlos en clústeres en el estado de animación, donde deseas que la forma tenga menos bordes percibidos.

.element{
  /* four vertices (looks like rectangle) */
  shape-outside: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  transition: shape-outside 1s;
}

.element:hover{
  /* four vertices, but second and third overlap (looks like triangle) */
  shape-outside: polygon(0 0, 100% 50%, 100% 50%, 0 100%);
}
GIF de triángulo animado

Unir contenido dentro de una forma

Captura de pantalla de la demostración de Alicia en el país de las maravillas con formas de CSS para unir contenido

El borrador inicial de la especificación de formas CSS incluía una propiedad shape-inside que te permitía unir contenido dentro de una forma. Incluso hubo implementaciones en Chrome y Webkit por un tiempo. Sin embargo, unir el contenido de ubicación arbitraria dentro de una ruta personalizada requiere mucho más esfuerzo e investigación para abarcar todas las situaciones posibles y evitar errores. Es por eso que la propiedad shape-inside se postergó al nivel de formas de CSS 2 y se retiraron las implementaciones correspondientes.

Sin embargo, con un poco de esfuerzo y un poco de compromiso, aún puedes lograr el efecto de unir el contenido dentro de una forma personalizada. El truco es usar dos elementos flotantes con shape-outside, posicionados en lados opuestos de un contenedor. El compromiso es usar uno o dos elementos vacíos que no tienen significado semántico, sino que sirven como pivotes para crear la ilusión de una forma en el interior.

<div>
  <div class='left-shape'></div>
  <div class='right-shape'></div>

  Lorem ipsum...
</div>

La posición de los elementos de Strut .left-shape y .right-shape en la parte superior del contenedor es importante porque se flotarán hacia la izquierda y la derecha para flanquear el contenido.

.left-shape{
  shape-outside: polygon(0 0, ...);
  float: left;
  width: 50%;
  height: 100%;
}

.right-shape{
  shape-outside: polygon(50% 0, ...);
  float: right;
  width: 50%;
  height: 100%;
}
Ilustración de la solución alternativa para la demostración de la forma de Alice

Este estilo hace que los dos patrones flotantes ocupen todo el espacio dentro del elemento, pero las propiedades de shape-outside crean espacio para el resto del contenido.

Si el navegador no admite las formas CSS, esto producirá efectos negativos al desplazar todo el contenido hacia abajo. Por eso, es importante usar la función de una manera mejorada de forma progresiva.

En los ejemplos anteriores de animación de formas, notarás que el cambio de texto puede ser molesto. No todos los casos de uso garantizan una forma animada. Sin embargo, puedes animar otras propiedades que interactúen con las formas de CSS para agregar un efecto cuando tenga sentido.

En la demostración de Alicia en el país de las maravillas de las formas de CSS, usamos la posición de desplazamiento para cambiar el margen superior del contenido. El texto se comprime entre dos elementos flotantes. A medida que se mueve hacia abajo, se debe volver a diseñar en función del shape-outside de los dos elementos flotantes. Así da la impresión de que el texto se va por lo fácil y complementa la experiencia de narración. ¿Está en el límite de lo injustificado? Tal vez. Pero se ve genial.

Como el navegador realiza el diseño del texto de forma nativa, el rendimiento es mejor que usar una solución de JavaScript. Sin embargo, cambiar el margen superior durante el desplazamiento activa muchos eventos de rediseño y pintura, y eso puede reducir el rendimiento de forma notoria. Usar con precaución. Sin embargo, el uso de formas de CSS sin animarlas no implica un impacto de rendimiento perceptible.

Mejora progresiva

Comienza suponiendo que el navegador no admite formas de CSS y auméntala cuando detectes la función. Modernizr es una buena solución para realizar la detección de funciones, y hay una prueba para las formas de CSS en la sección “Detección no principal”.

Algunos navegadores proporcionan detección de funciones en CSS mediante la regla @supports sin la necesidad de bibliotecas externas. Google Chrome, que también admite formas de CSS, comprende la regla @supports. Así es como se utiliza para mejorar progresivamente:

.element{
  /* styles for all browsers */
}

@supports (shape-outside: circle(50%)){
  /* styles only for browsers which support CSS Shapes */
  .element{
    shape-outside: circle(50%);
  }
}

Lea Verou escribió más información sobre cómo usar la regla @supports de CSS.

Desambiguación de las exclusiones de CSS

Lo que conocemos hoy como las formas de CSS solían llamarse exclusiones y formas de CSS al principio de la especificación. El cambio en la denominación puede parecer un matiz, pero en realidad es muy importante. Las exclusiones de CSS, que ahora son una especificación independiente, permiten ajustar el contenido en torno a elementos posicionados de manera arbitraria, sin la necesidad de una propiedad de número de punto flotante. Imagina que encierras el contenido en un elemento en una posición absoluta. Ese es un caso de uso de las exclusiones de CSS. Las formas CSS simplemente definen la ruta alrededor de la cual se ajustará el contenido.

Por lo tanto, las formas y las exclusiones no son lo mismo, pero se complementan entre sí. Actualmente, las formas de CSS están disponibles en navegadores, mientras que las exclusiones de CSS aún no se implementaron con la interacción de formas.

Herramientas para trabajar con formas CSS

Puedes crear rutas de acceso en herramientas clásicas de creación de imágenes, pero ninguna de ellas, al momento de escribir este documento, exporta la sintaxis requerida para los valores de formas CSS. Incluso si lo hicieran, trabajar así no sería demasiado práctico.

Las formas CSS están diseñadas para usarse en el navegador, donde reaccionan a otros elementos de la página. Es muy útil visualizar los efectos de la edición de la forma en el contenido que la rodea. Existen algunas herramientas que te ayudarán con este flujo de trabajo:

Brackets: La extensión CSS Shapes Editor para Brackets usa el modo de vista previa en vivo del editor de código para superponer un editor interactivo y editar valores de formas.

Google Chrome: La extensión CSS Shapes Editor para Google Chrome amplía las herramientas para desarrolladores del navegador con controles para crear y editar formas. Coloca un editor interactivo sobre el elemento seleccionado.

El inspector de Google Chrome tiene compatibilidad integrada para destacar formas. Coloca el cursor sobre un elemento con una propiedad shape-outside y se iluminará para ilustrar la forma.

Formas de las imágenes: Si prefieres generar imágenes y que el navegador extraiga formas de ellas, Rebecca Hauck escribió un buen instructivo para Photoshop.

Polyfill: Google Chrome es el primer navegador importante en enviar formas CSS. Pronto se admitirá la función en iOS 8 y Safari 8 de Apple. Otros proveedores de navegadores podrían considerarlo en el futuro. Hasta entonces, existe un polyfill de formas de CSS para proporcionar asistencia básica.

Conclusión

En una web donde el contenido está principalmente atrapado en cuadros simples, las formas de CSS proporcionan una manera de crear un diseño expresivo, lo que reduce la fidelidad entre el diseño web y el de impresión. Por supuesto, se pueden abusar de las formas y crear distracciones. Sin embargo, cuando se aplican con buen gusto y buen juicio, las formas pueden mejorar la presentación del contenido y enfocar la atención del usuario de una manera que sea única para ellas.

Te dejo una colección de trabajos de otras personas, en su mayoría impresos, que demuestra usos interesantes para el diseño no rectagular. Espero que esto te inspire a probar formas de CSS y experimentar con nuevas ideas de diseño.

Muchas gracias a Pearl Chen, Alan Stearns y Zoltan Horvath por leer este artículo y proporcionar información valiosa.