Cómo comenzar a usar las formas de CSS

Ajustar el contenido alrededor de rutas de acceso 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 de la Web sigue atrapado en cuadros simples porque la mayoría de las iniciativas creativas de diseño no rectangular terminan en frustración. Eso está a punto de cambiar con la introducción de las formas de CSS, disponibles a partir de Chrome 37. Las formas de CSS permiten que los diseñadores web ajusten el contenido alrededor de rutas de acceso personalizadas, como círculos, elipses y polígonos, lo que rompe las restricciones del rectángulo.

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

Veamos un ejemplo muy sencillo.

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

Cómo 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 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 en el rango entre 0.0 (completamente transparente) y 1.0 (completamente opaco). Por lo tanto, shape-image-threshold: 0.5 significa que solo se usarán los 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 ajustará el contenido, sin el elemento 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 una imagen de una taza de café se desplaza hacia la izquierda, el área de desplazamiento se creará a la derecha de la taza. Aunque puedes diseñar una imagen con espacios en ambos lados, el contenido solo se ajustará alrededor de la forma en el lado opuesto designado por la propiedad float, izquierda o derecha, nunca ambas.

En el futuro, será posible usar shape-outside en elementos que no estén 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 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 circle()

La notación completa para el valor de una forma circular es circle(r at cx cy), donde r es el radio del círculo, mientras que cx y cy son las coordenadas del centro del círculo en los ejes X e 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 valor predeterminado.

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

En el ejemplo anterior, el contenido se ajustará 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. Si cambias las dimensiones del elemento, se modificará el radio de la forma circular. Este es un ejemplo básico de cómo las formas de CSS pueden ser responsivas.

Antes de continuar, un breve comentario: 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 las propiedades de Enmascaramiento de CSS, ya sea clip-path o mask-image. La propiedad clip-path es muy útil porque sigue la misma notación que las formas de CSS, por lo que puedes reutilizar los valores.

Ilustración de la forma `circle()` + clip-path

Las ilustraciones de este documento usan el recorte para destacar la forma y ayudarte a comprender los efectos.

Volvamos a la forma circular.

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 cuál será la forma circular 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.

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 de CSS

La caja de referencia es una caja 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 ajustará el contenido. El área flotante se extiende hasta los bordes exteriores del cuadro definido por la propiedad margin. Se denomina margin-box y es el cuadro de referencia predeterminado para una forma si no se menciona ninguno de manera 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 establecimos 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 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 tener en cuenta la mayor superficie del sistema de coordenadas establecido por el cuadro de referencia margin-box.

Sistema de coordenadas de la caja de margen con y sin margen
Hay algunas opciones de cuadro de referencia para elegir: `margin-box`, `border-box`, `padding-box` y `content-box`. Sus nombres implican sus límites. Anteriormente, explicamos el `margin-box`. El `border-box` está restringido por los bordes exteriores de los bordes del elemento, el `padding-box` está restringido por el padding del elemento, mientras que el `content-box` es idéntico 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 un cuadro 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 tema 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 ellipse()

Las elipses 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 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 matemáticos complejos. Puedes omitir las coordenadas del centro de la elipse, y se inferirán del 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 más alejado de él, mientras que closest-side significa exactamente lo contrario: usa 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 ser útil cuando las dimensiones del elemento (o el cuadro de referencia) pueden cambiar de forma impredecible, pero quieres que la forma de la 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 la forma 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 especificas pares de coordenadas x e 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 responsivos, puedes usar valores de porcentaje 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 de SVG, que indica al navegador cómo considerar la “interioridad” de un polígono en caso de rutas que se intersecan a sí mismas o formas cerradas. Joni Trythall explica muy bien cómo funciona la propiedad fill-rule en SVG. Si no se define, el fill-rule se establece de forma predeterminada en 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 parecer contradictorio si se tiene en cuenta la premisa inicial de que las formas de CSS liberan el contenido web de las cajas simples. Es muy probable que así sea. Aún no encontré un caso de uso para inset() que no se pueda lograr con elementos flotantes y márgenes, o con un polygon(). Sin embargo, inset() proporciona una expresión más legible para las formas rectangulares que polygon().

La notación completa para 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 de 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;
}

Crear formas a partir de cajas 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. Hasta ahora, nada fuera de lo común, así es como ya funcionan los números de punto flotante. Sin embargo, si aplicas esta técnica, puedes reutilizar la geometría de un elemento. Veamos la propiedad border-radius.

Si la usas para redondear las esquinas de un elemento flotante, obtendrás el efecto de recorte, pero el área flotante seguirá siendo rectangular. Agrega shape-outside: border-box para que se ajuste al contorno creado por border-radius.

Extraer una forma del radio del borde de un elemento con el cuadro de referencia 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: citas destacadas desplazadas.

Crea una cita destacada desplazada con el cuadro de referencia content-box

Es posible lograr el efecto de cita destacada desplazada usando solo las propiedades float y margin. Sin embargo, eso requiere que posiciones el elemento de cita 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 de forma explícita el cuadro de referencia content-box para el sistema de coordenadas de la forma. En este caso, la cantidad de contenido de la cita destacada define la forma alrededor de la cual se ajustará el contenido exterior. Aquí se usa la propiedad margin-top para posicionar (desplazar) la cita destacada, 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 acerque demasiado al 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 se obtiene con la propiedad margin normal, pero shape-margin solo afecta el espacio alrededor del valor de shape-outside. Agregará espacio alrededor de la forma solo si hay espacio para ella en el sistema de coordenadas. Por eso, en el ejemplo anterior, el radio del círculo se establece en un 40%, no en un 50%. Si el radio se hubiera establecido en 50%, el círculo habría ocupado todo el espacio del sistema de coordenadas, sin dejar espacio para el efecto de shape-margin. Recuerda que, en última instancia, la forma está restringida al margin-box del elemento (el elemento más el margin que lo rodea). Si la forma es más grande y se desborda, se recortará al margin-box y terminarás con una forma rectangular.

Es importante comprender que shape-margin solo acepta un valor positivo. No tiene una notación de formato largo. ¿Qué es un shape-margin-top para un círculo?

Cómo animar formas

Puedes mezclar las 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 que el diseño del texto cambie 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 que se definan en valores que el navegador pueda interpolar. Es posible pasar 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 de 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 quieres 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 un triángulo animado

Ajustar el 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 CSS Shapes incluía una propiedad shape-inside que te permitía ajustar el contenido dentro de una forma. Incluso hubo implementaciones en Chrome y Webkit durante un tiempo. Sin embargo, unir contenido posicionado de forma arbitraria dentro de una ruta de acceso 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 aplazó al nivel 2 de formas de CSS y se retiraron las implementaciones para ella.

Sin embargo, con un poco de esfuerzo y compromiso, puedes lograr el efecto de ajustar el contenido dentro de una forma personalizada. El truco consiste en usar dos elementos flotantes con shape-outside, ubicados en lados opuestos de un contenedor. La desventaja es que tienes que usar uno o dos elementos vacíos que no tienen un significado semántico, pero que sirven como puntales 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 puntal .left-shape y .right-shape en la parte superior del contenedor es importante porque flotarán hacia la izquierda y hacia 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 en la demostración de Alicia

Este diseño hace que los dos puntales 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 de CSS, se producirán efectos desagradables, ya que se desplazará todo el contenido hacia abajo. Por eso, es importante usar la función de manera progresivamente mejorada.

En los ejemplos anteriores de animación de formas, notarás que el texto que se desplaza puede ser molesto. No todos los casos de uso justifican 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 comprime entre dos elementos flotantes. A medida que se mueve hacia abajo, debe volver a diseñar según el shape-outside de los dos elementos flotantes. Esto da la impresión de que el texto se está adentrando en la madriguera del conejo y se suma a la experiencia de narración. ¿Es injustificado? Tal vez. Pero se ve genial.

Debido a que el diseño del texto lo realiza el navegador de forma nativa, el rendimiento es mejor que con una solución de JavaScript. Sin embargo, cambiar el margen superior al desplazarse activa muchos eventos de cambio de diseño y pintura, lo que puede reducir notablemente el rendimiento. Usar con precaución. Sin embargo, usar CSS Shapes sin animarlas no tiene un impacto perceptible en el rendimiento.

Mejora progresiva

Para empezar, asume que el navegador no admite las formas de CSS y, luego, agrega compatibilidad 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 "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 CSS Shapes, comprende la regla @supports. Así es como se usa 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 sobre cómo usar la regla @supports de CSS.

Desambiguación de las exclusiones de CSS

Lo que hoy conocemos como formas CSS solía llamarse exclusiones y formas CSS en los primeros días de la especificación. El cambio de nombre puede parecer un matiz, pero en realidad es muy importante. Exclusiones de CSS, que ahora es una especificación independiente, permite ajustar el contenido alrededor de elementos posicionados de forma arbitraria, sin necesidad de una propiedad float. Imagina que el contenido se ajusta alrededor de un elemento con posición absoluta; ese es un caso de uso para las exclusiones de CSS. Las formas de CSS solo 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í. Las formas de CSS están disponibles en los navegadores hoy en día, 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, al momento de escribir este artículo, ninguna de ellas exporta la sintaxis requerida para los valores de CSS Shapes. Incluso si lo hicieran, trabajar de esa manera no sería muy 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 CSS Shapes Editor para Brackets usa el modo de vista previa en vivo del editor de código para superponer un editor interactivo que permite editar los valores de las formas.

Google Chrome: La extensión CSS Shapes Editor para Google Chrome extiende las Herramientas para desarrolladores del navegador con controles para crear y editar formas. Se 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.

Polyfill: Google Chrome es el primer navegador importante que incluye formas de CSS. Pronto se admitirá la función en iOS 8 y Safari 8 de Apple. Es posible que otros proveedores de navegadores lo consideren en el futuro. Hasta entonces, hay un polyfill de CSS Shapes 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 de CSS proporcionan una manera de crear un diseño expresivo, lo que cierra la brecha de fidelidad entre el diseño web y el diseño de impresión. Por supuesto, se puede abusar de las formas y crear distracciones. Sin embargo, cuando se aplican con gusto y buen criterio, las formas pueden mejorar la presentación del contenido y centrar la atención del usuario de una manera única para él.

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

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