Cómo comenzar a usar las formas CSS

Cómo unir 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. Eso está a punto de cambiar con la introducción de las formas 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, lo que permite liberarse de las restricciones del rectángulo.

Las formas se pueden definir de forma manual o inferir a partir de imágenes.

Veamos un ejemplo muy sencillo.

Tal vez hayas sido tan ingenuo como yo cuando flotaste una imagen con partes transparentes esperando que el contenido se uniera y llenara los espacios, solo para decepcionarte con la forma rectangular que persiste alrededor del elemento. Las formas de CSS se pueden usar para resolver este problema.

Extraer una forma 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 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 superior 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é flota a la izquierda, el área de flotación se creará a la derecha de la taza. Si bien puedes diseñar una imagen con espacios en ambos lados, el contenido solo se unirá a la forma del lado opuesto designado por la propiedad de flotación, a la izquierda o a la derecha, nunca a ambos.

En el futuro, será posible usar shape-outside en elementos que no estén flotando con la introducción de las exclusiones de CSS.

Cómo crear formas manualmente

Además de extraer formas de las imágenes, también puedes codificarlas 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 vincula con un cuadro de referencia que establece el sistema de coordenadas. En un momento, hablaremos más sobre los cuadros de referencia.

La función circle()

Ilustración del valor de forma de Vertex()

La notación completa para un valor de 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 las coordenadas del centro del círculo en el eje X y el eje Y. Las coordenadas del centro del círculo son opcionales. Si los omites, se usará el centro del elemento (la intersección de sus diagonales) como predeterminado.

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

En el ejemplo anterior, el contenido se unirá alrededor del exterior de una ruta circular. El único argumento 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 afectará 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, una observación rápida: 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 usar propiedades de máscaras 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 reutilizar valores.

Ilustración de la forma `círculo()` + clip-path

En las ilustraciones de este documento, se usa el recorte para destacar 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 se calcula con una fórmula un poco más compleja: sqrt(width^2 + height^2) / sqrt(2). Es útil comprender esto porque te ayudará a imaginar cómo será la forma del círculo resultante si las dimensiones del elemento no son iguales.

Todos los tipos de unidades de CSS se pueden usar en las coordenadas de la función de forma: px, em, rem, vw, vh, etcétera. Puedes elegir la que sea lo suficientemente flexible o rígida para tus necesidades.

Para ajustar la posición del círculo, establece 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 de 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 apuntando hacia abajo.

Sistema de coordenadas para formas de CSS

Recuerda que shape-outside altera la forma del área flotante alrededor de la cual se unirá el contenido. El área flotante se extiende hasta los bordes exteriores 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 de forma explícita.

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, se puede asumir 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 hacia arriba y 100 px hacia la izquierda), al igual que el centro del círculo. El valor calculado del radio del círculo también aumenta para dar cuenta del aumento de la superficie del sistema de coordenadas establecido por el cuadro de referencia margin-box.

Sistema de coordenadas de margen de cuadro con y sin margen
Puedes elegir entre algunas opciones de cuadros de referencia: "margin-box", "borde-box", "padding-box" y "content-box". Sus nombres implican sus límites. Anteriormente, explicamos el "margin-box". El "border-box" está limitado por los bordes exteriores del elemento, el "padding-box" está limitado por el padding del elemento, mientras que el "content-box" es idéntico a la superficie real que usa el contenido dentro de un elemento.
Ilustración de todas las casillas de referencia

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

La función ellipse()

Ilustración del valor de la forma elipse()

Las elipses parecen círculos apretados. Se definen como ellipse(rx ry at cx cy), donde rx y ry son los radios de la elipse en el eje X y 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 de porcentaje se calcularán a partir de las dimensiones del sistema de coordenadas. No se requieren cálculos complicados. Puedes omitir las coordenadas del centro de la elipse, y se inferirán del centro del sistema de coordenadas.

El radio en los ejes X e Y también se puede definir con palabras clave: farthest-side proporciona un radio igual a la distancia entre el centro de la elipse y el lado del cuadro de referencia que se encuentra más lejos de él, mientras que closest-side significa lo opuesto exacto. 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 forma impredecible, pero deseas que la forma de elipse se adapte.

Las mismas palabras clave farthest-side y closest-side también se pueden usar 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 las elipses 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. La cantidad mínima de pares para especificar un polígono es tres (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 adaptables, puedes usar 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;
}

Hay un parámetro fill-rule opcional, importado desde SVG, que le indica al navegador cómo considerar la “interioridad” de un polígono en caso de rutas que se cruzan o formas cerradas. Joni Trythall explica muy bien cómo funciona la propiedad de la 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 se puede ajustar el contenido. Esto puede sonar contraintuitivo si tenemos en cuenta la premisa inicial de que las formas CSS deben proporcionar contenido web gratuito a partir de cuadros simples. Es muy probable. Aún no encontré un caso de uso para inset(), que aún no se puede lograr con flotantes y márgenes, o con 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 insertada es inset(top right bottom left border-radius). Los primeros cuatro argumentos de posición son compensaciones 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 a 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. Hasta ahora, nada exótico, así es como ya funcionan las carrozas. Sin embargo, si aplicas esta técnica, puedes volver a usar la geometría de un elemento. Veamos la propiedad border-radius.

Si lo usas 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 unir el contorno creado por border-radius.

Extracción de una forma del atributo border-radius de un elemento con el cuadro de referencia de border-box
.element{
  border-radius: 50%;
  shape-outside: border-box;
  float: left;
}

Por supuesto, puedes usar todos los cuadros de referencia de esta manera. Aquí hay otro uso para las formas derivadas: desplazamiento de comillas.

Cómo crear una cita destacada desplazada con el cuadro de referencia del cuadro de contenido

Es posible lograr el efecto de cita destacada desplazada solo con las propiedades de margen y flotación. Sin embargo, eso requiere que posiciones el elemento de comillas en el árbol HTML en el punto en el que deseas que se renderice.

A continuación, te mostramos cómo lograr el mismo efecto de cita destacada desplazada con mayor flexibilidad:

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

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

Margen de la forma

Notarás que unir el contenido alrededor de una forma puede hacer que se frote demasiado contra el elemento. Puedes agregar espacio 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 obtienes con la propiedad margin normal, pero shape-margin solo afecta el espacio alrededor del valor shape-outside. Agregará espacio alrededor de la forma solo si hay espacio para ello en el sistema de coordenadas. Por eso, en el ejemplo anterior, el radio del círculo se establece en 40%, no en 50%. Si el radio se hubiera establecido en el 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 se restringe en última instancia 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 solo acepta un valor positivo. No tiene una notación a mano alzada. ¿Qué es shape-margin-top para un círculo?

Cómo animar formas

Puedes combinar formas de CSS con muchas otras funciones de CSS, 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. Presta mucha atención a la experiencia si decides animar las formas.

Puedes animar los radios y los centros de las formas circle() y ellipse(), siempre y cuando estén definidos en valores 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 círculo animado

Se pueden lograr efectos más interesantes cuando se animan formas polygon(), con la nota importante de 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 agrupados en el estado de animación en el que 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

Cómo 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 ajustar el contenido

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

Sin embargo, con un poco de esfuerzo y compromiso, puedes lograr el efecto de unir el contenido dentro de una forma personalizada. El hack consiste en usar dos elementos flotantes con shape-outside, posicionados en lados opuestos de un contenedor. El compromiso es que debes usar uno o dos elementos vacíos que no tengan significado semántico, pero que sirvan como pilar para crear la ilusión de una forma en su interior.

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

  Lorem ipsum...
</div>

La posición de los elementos de soporte .left-shape y .right-shape en la parte superior del contenedor es importante porque 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 shape-inside para la demo de Alice

Este diseño hace que los dos elementos flotantes ocupen todo el espacio dentro del elemento, pero las propiedades shape-outside reservan espacio para el resto del contenido.

Si el navegador no admite las formas CSS, se producirán efectos feos, ya que se empujará todo el contenido hacia abajo. Por eso es importante usar la función de manera progresiva.

En los ejemplos anteriores de animación de formas, notarás que cambiar el texto puede ser molesto. No todos los casos de uso garantizan una forma animada. Sin embargo, puedes animar otras propiedades que interactúan con las formas de CSS para agregar efectos donde 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 coloca entre dos elementos flotantes. A medida que se mueve hacia abajo, debe volver a diseñarse según el shape-outside de los dos elementos flotantes. Esto da la impresión de que el texto se está yendo por el camino equivocado y agrega a la experiencia de contar historias. ¿Es un contenido gratuito en el límite? Tal vez. Pero se ve bien.

Debido a que el navegador realiza el diseño de 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 reordenamiento y pintura, lo que puede reducir notablemente el rendimiento. Usar con precaución. Sin embargo, el uso de formas de CSS sin animarlas no genera un impacto perceptible en el rendimiento.

Mejora progresiva

Comienza por suponer que el navegador no es compatible con las formas de CSS y, luego, desarrolla esa idea 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 CSS en la sección 'Non-core detects'.

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

.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 sobre cómo usar la regla @supports de CSS.

Desambiguación de las exclusiones de CSS

Lo que hoy conocemos como formas de CSS solía llamarse exclusiones y formas de CSS en los primeros días de la especificación. El cambio en los nombres puede parecer un matiz, pero en realidad es muy importante. Las exclusiones de CSS, ahora una especificación independiente, permiten unir el contenido alrededor de elementos posicionados de forma arbitraria, sin necesidad de una propiedad de flotación. Imagina unir contenido alrededor de un elemento con posicionamiento absoluto. Ese es un caso de uso para las exclusiones de CSS. Las formas de CSS solo definen la ruta alrededor de la cual se unirá el contenido.

Por lo tanto, las formas y las exclusiones no son lo mismo, pero se complementan. Las formas CSS están disponibles en los navegadores en la actualidad, mientras que las exclusiones de CSS aún no se implementan con la interacción de formas.

Herramientas para trabajar con formas de CSS

Puedes crear rutas de acceso en las 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 las formas CSS. Incluso si lo hicieran, trabajar de esa manera no sería demasiado práctico.

Las formas de 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 editar la forma en el contenido que la rodea. Existen algunas herramientas que te ayudarán con este flujo de trabajo:

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

Google Chrome: La extensión Editor de formas de CSS para Google Chrome extiende 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 a partir de imágenes: Si prefieres generar imágenes y que el navegador extraiga formas de ellas, Rebecca Hauck escribió un buen instructivo para Photoshop.

Relleno de espacios: Google Chrome es el primer navegador importante en enviar formas CSS. Pronto estará disponible la compatibilidad con esta función en iOS 8 y Safari 8 de Apple. Otros proveedores de navegadores pueden considerarlo en el futuro. Hasta entonces, hay un polyfill de formas de CSS para proporcionar compatibilidad básica.

Conclusión

En una Web en la que el contenido está atrapado en su mayoría en cuadros simples, las formas CSS proporcionan una forma de crear un diseño expresivo que cierra la brecha de fidelidad entre el diseño web y el impreso. Por supuesto, se puede abusar de las formas y crear distracciones. Sin embargo, cuando se aplican con buen gusto y prudencia, las formas pueden mejorar la presentación del contenido y enfocar la atención del usuario de una manera única.

Te dejo una colección de trabajos de otros diseñadores, en su mayoría impresos, que demuestran usos interesantes para el diseño no rectangular. Espero que esto te inspire a probar las formas 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.