Patrones Teselados en CSS

En el desafío de la tercera semana de julio de 2021 de Codepen: #CodePenChallenge Tessellation, proponían crear patrones teselados.

Los teselados hacen referencia a una regularidad o patrón de figuras que recubren o pavimentan completamente una superficie plana que cumple con dos requisitos:

  • Que no queden espacios.
  • Que no se superpongan las figuras.

Los teselados se crean usando copias isométricas de una figura inicial, es decir, copias idénticas de una o diversas piezas o teselas con las cuales se componen figuras para recubrir enteramente una superficie.

Distintas culturas a lo largo de la historia han utilizado esta técnica para formar pavimentos o muros de mosaicos en catedrales y palacios. Algunos mosaicos sumerios con varios miles de años de antigüedad contienen regularidades geométricas. + info en Wikipedia.

Después de informarme extensamente sobre el tema en este artículo de Ana Tudor en CSSTricks , empecé con los experimentos…

Concretamente lo que me hizo «ver la luz» en el referido artículo fue esta demo de Ana Tudor donde muestra los cambios de ángulos en vivo:

See the Pen Changing ‘conic-gradient()’ start angle (click buttons/ drag slider) by Ana Tudor (@thebabydino) on CodePen.

En la demo Ana ha añadido un borde amarillo que marca el tamaño de cada tesela, y también un deslizador que cambia el ángulo del gradiente cónico utilizado. Con un poco de imaginación y pensando que se pueden poner múltiples conic-gradient , unos encima de otros, con diferentes puntos de origen, usar partes transparentes, degradados, y además se pueden combinar con otros tipos de gradientes como radial-gradient o linear-gradient … da la impresión de que se puede hacer cualquier cosa. Incluso, como puede ver más adelante, desalinear las filas de teselas para intercalarlas y crear patrones increíbles con unas pocas líneas de CSS.

Empezando

Vamos a empezar con un patrón muy simple, un cubo 3D, que formaremos usando tres colores diferentes para dar volumen a las tres caras visibles del cubo. La siguiente imagen muestra los pasos necesarios para conseguir la tesela que buscamos, son solo tres pasos con tres conic-gradient superpuestos unos encima de otros.

Repitiendo la tesela final lograremos el patrón de cubos deseado:

Custom Properties

Lo primero es crear algunas custom properties (propiedades personalizadas) de CSS para poder reutilizarlas en nuestro código y ahorrar tiempo y espacio. Hace tiempo que son soportadas por la mayoría de navegadores y yo ya me he acostumbrado a usarlas, pero siempre puedes hacerlo a la vieja usanza, sin usar las custom properties, reemplazándolas por el código correspondiente en cada caso. Si aún no te has puesto con las custom properties te recomiendo que no lo dejes más, y añado aquí algunos enlaces con información sobre el tema en CSS Tricks , en Lenguaje CSS ó en W3C.

:root {
  --sz: 10px;   /* controla el tamaño */
  --c1: #d6c3ea; /* color 1 */
  --c2: #a98ec3; /* color 2 */
  --c3: #6a587f; /* color 3 */
  --ts: 50%/ calc(var(--sz) * 10) calc(var(--sz) * 17.5); /* tamaño de la tesela */
}

--sz controla el tamaño de todo del patrón.

--c1 , --c2 y --c3 son los tres colores que vamos a utilizar.

--ts delimita el tamaño de cada tesela.

conic-gradient()

El siguiente paso es crear los diferentes gradientes con la función integrada de CSS conic-gradient() en la propiedad background , aunque ahora revisándolo en W3C veo que ellos lo hacen en background-image.

Vamos con el primer gradiente:

.teselado-cubo {
  background:
    conic-gradient(
      from 0deg at 50% 50%, 
      var(--c2) 0 60deg, 
      var(--c1) 0 120deg, 
      var(--c3) 0 180deg, 
      var(--c2) 0 240deg, 
      var(--c1) 0 300deg, 
      var(--c3) 0 360deg
    )
  ;
}

Definimos el ángulo de inicio del gradiente: 0deg y la posición del mismo: 50% en el eje x y 50% en el eje y.

Después empezamos con los gradientes:

  • de 0 a 60deg con el color 2
  • de 0 a 120deg con el color 1
  • de 0 a 180deg con el color 3
  • de 0 a 240deg con el color 2
  • de 0 a 300deg con el color 1
  • de 0 a 360deg con el color 3

Nos ha faltado una cosa, que es especificar el tamaño de la tesela para ese gradiente, ya que ahora mismo el gradiente ocupa todo el espacio posible dentro del elemento div. Lo haremos justo después del paréntesis de cierre de la función conic-gradient y como lo tenemos almacenado y disponible en la custom property --ts solo tenemos que añadirlo de la siguiente manera:

.teselado-cubo {
  background:
    conic-gradient(
      from 0deg at 50% 50%, 
      var(--c2) 0 60deg, 
      var(--c1) 0 120deg, 
      var(--c3) 0 180deg, 
      var(--c2) 0 240deg, 
      var(--c1) 0 300deg, 
      var(--c3) 0 360deg
    ) 
    var(--ts) /*añadimos aqui el tamaño de la tesela*/
  ;
}

Ya tenemos algo que medio encaja, de alguna forma, pero nos faltan los dos gradientes que rematan el cubo por arriba y por abajo. Hay que tener en cuenta que las funciones integradas de CSS conic-gradient se superponen unas encima de otras, las de más arriba estarán por encima de las de abajo, de esta manera usando el valor de color transparente podremos abarcar solo un trozo del ángulo total y lo de debajo seguirá siendo visible. Primero vamos con el gradiente de arriba:

.teselado-cubo {
  background:
    /***segundo conic gradient***/
    conic-gradient(
      from -60deg at 50% 16.5%, 
      var(--c1) 0 120deg, 
      #fff0 0 360deg
    ) 
    var(--ts), /*añadimos tambien el tamaño de la tesela*/
    /***primer conic gradient***/
    conic-gradient(
      from 0deg at 50% 50%, 
      var(--c2) 0 60deg, 
      var(--c1) 0 120deg, 
      var(--c3) 0 180deg, 
      var(--c2) 0 240deg, 
      var(--c1) 0 300deg, 
      var(--c3) 0 360deg
    ) 
    var(--ts)
  ;
}

Definimos el ángulo de inicio del gradiente: -60deg y la posición del mismo: 50% en el eje x y 16.5% en el eje y.

Y ahora los gradientes, que en este caso son solo dos colores:

  • de 0 a 120deg con el color 1
  • de 0 a 360deg con color transparente

Esto ya casi está, solo falta el último conic-gradient para la parte de abajo:

.teselado-cubo {
  background:
    /***tercer conic gradient***/
    conic-gradient(
      from 120deg at 50% 83.5%, 
      var(--c1) 0 120deg, 
      #fff0 0 360deg
    ) 
    var(--ts), /*añadimos tambien aqui el tamaño de la tesela*/
    /***segundo conic gradient***/
    conic-gradient(
      from -60deg at 50% 16.5%, 
      var(--c1) 0 120deg, 
      #fff0 0 360deg
    ) 
    var(--ts),
    /***primer conic gradient***/
    conic-gradient(
      from 0deg at 50% 50%, 
      var(--c2) 0 60deg, 
      var(--c1) 0 120deg, 
      var(--c3) 0 180deg, 
      var(--c2) 0 240deg, 
      var(--c1) 0 300deg, 
      var(--c3) 0 360deg
    ) 
    var(--ts)
  ;
}

Es prácticamente una copia del gradiente anterior, solo tenemos que cambiar el ángulo de inicio del gradiente, en lugar de empezar en -60deg empezará en 120deg, y la posición también cambia para el eje y , de 16.5% pasará a ser 83.5%. Los valores de los colores son los mismos.

Y ya está, ya tenemos nuestro patrón teselado de cubos 3d solo con CSS.

Opciones

Ya que las tenemos creadas y disponibles podemos usar las custom properties para modificar rápidamente algunas características del patrón, como el tamaño o el color.

Tamaño

Si queremos cambiar el tamaño del patrón solo hay que modificar la custom property --sz y como el tamaño de la tesela --ts está creada con el valor de --sz cambiará de tamaño todo el patrón. Se puede usar cualquier medida para el tamaño, yo empecé usando vmin pero últimamente me he pasado a usar px porque muestra menos errores en la uniones de la teselas. Es uno de los pequeños problemas de usar esta técnica, de vez en cuando se muestran rayas en los bordes de las teselas debido a los gradientes de las capas inferiores y hay que jugar con el tamaño para que sean menos visibles al ojo humano.

Un par de ejemplos del cambio de tamaño:

:root {
  --sz: 6px;
}

Se puede usar cualquier tipo de medida, pero no recomiendo usar porcentajes %:

:root {
  --sz: 1.25vmin;
}

Color

Con los colores podemos hacer lo mismo, modificando las custom properties --c1 , --c2 y --c3 podemos modificar los colores de las tres caras del cubo. Se puede simplemente cambiar el tono de color o también se puede modificar la saturación o la luminosidad para cambiar el ángulo de iluminación.

Unos ejemplos:

:root {
  --c1: #c3eac7;
  --c2: #8ec39d;
  --c3: #587f61;
}
:root {
  --c1: #c33838; 
  --c2: #812f2f; 
  --c3: #ef6565;
}

Complicándolo

Las posibilidades son infinitas, dependiendo de lo que queramos complicarnos.

Por ejemplo, este patrón teselado que estuve intentando hacer durante meses hasta que por fin encontré la manera de conseguirlo. El problema era que eran necesarios demasiados conic-gradient y era esencial ordenarlos de manera que no interfirieran unos con otros. El patrón es el de la mítica alfombra del clásico Hotel Overlook de la película El Resplandor, del gran Stanley Kubrick.

En la siguiente demo cada recuadro representa un paso (un gradiente) en la creación del patrón. Empezando por un fondo negro, después tenemos 22 conic-gradient superpuestos uno encima de otro. Si se pasa por encima de cada uno de los recuadros se verá el ángulo usado para cada paso (marcado en color verde), el resto del gradiente se completa con color transparente. El último recuadro muestra el patrón teselado final.

Este es un ejemplo un poco exagerado creado para conseguir un patrón muy concreto, pero se pueden hacer cosas muy chulas con solo un puñado de gradientes.

Veamos un par de ejemplos más:

Un teselado triangular que da la impresión de que ha sido cortado en ángulo:

.teselado-triangular {
  --sz: 10px;
  --c1: #eacda5;
  --c2: #62928c;
  --c3: #cf2844;
  --c4: #404355;
  --c5: #331530;
  --ts: 50%/ calc(var(--sz) * 8.5) calc(var(--sz) * 14.7);
  height: 30vh;
  background: 
    conic-gradient(from 210deg at 23% 60%, var(--c3) 0 60deg, #fff0 0 100%) var(--ts), 
    conic-gradient(from 90deg at 77% 60%, var(--c3) 0 60deg, #fff0 0 100%) var(--ts), 
    conic-gradient(from 118deg at 50.5% 85%, var(--c2) 0 125deg, #fff0 0 100%) var(--ts), 
    conic-gradient(from 30deg at 50% 50%, var(--c5) 0 30deg, var(--c2) 0 60deg, #fff0 0 120deg,  var(--c4) 0 150deg, var(--c5) 0 180deg, #fff0 0 241deg, var(--c2) 0 270deg, var(--c4) 0 300deg, #fff0 0 100%) var(--ts), 
    linear-gradient(180deg, var(--c1) 0 10%, #fff0 0 100%) var(--ts), 
    conic-gradient(from -30deg at 50% 33%, var(--c3) 0 60deg, #fff0 0 100%) var(--ts), 
    var(--c1);
}

O este otro con unos cubos sombreados, algo más elaborado pero en el que solamente se utilizan tres conic-gradient. Aquí también usamos otra técnica de superposición de teselas que ya veremos próximamente en otro artículo, pues merece la pena explicarlo bien para que se entienda, aunque de momento podéis ir echando un vistazo al código y al resultado:

.teselado-cubo-sombreado {
  --sz: 11px;
  --c1: #fafafa;
  --c2: #cacaca;
  --c3: #9c9c9c;
  --c4: #76767680;
  --ts: 50%/ calc(var(--sz) * 10) calc(var(--sz) * 17.1);
  --bp: calc(var(--sz) * -5) calc(var(--sz) * -8.55);
  height: 30vh;
  --bg: 
    conic-gradient(from 0deg at 74% 42%, #fff0 0 19.5deg, #50505080 20deg 60deg, var(--c4) 0 99.5deg, #fff0 101deg 360deg) var(--ts),
    conic-gradient(from -60deg at 50% 16.66%, var(--c1) 0 120deg, #fff0 0 360deg) var(--ts), 
    conic-gradient(from 0deg at 50% 50%, var(--c2) 0 60deg, var(--c1) 0 90deg, #fff0 0 270deg, var(--c1) 0 279.75deg, var(--c4) 280.5deg 300deg, #7c7c7c 0, var(--c3), #adadad 360deg) var(--ts);
  background: var(--bg), var(--bg);
  background-position: var(--bp), var(--bp), var(--bp), 0 0, 0 0, 0 0;
}

Flipándolo

Aparte de toda la versatilidad que nos da la función conic-gradient, como ya comentábamos al principio del artículo, si se combina con las otras dos funciones integradas de CSS de gradientes radial-gradient y linear-gradient podemos hace casi cualquier patrón que se nos pase por la cabeza, planificando bien los pasos y las posiciones y el orden de los diferentes gradientes.

Un ejemplo muy simple es de la pieza de puzzle, creada combinando las funciones radial-gradient y conic-gradient :

.teselado-puzzle {
  --sz: 13px;	
  --c1: #009688;
  --c2: #d8db24;
  --pz: calc(var(--sz) * 0.65), #fff0 calc(calc(var(--sz) * 0.65) + 1px);
  --ts: 50%/ calc(var(--sz) * 10) calc(var(--sz) * 10);
  height: 30vh;
  background:
    radial-gradient(circle at 55% 25%, var(--c2) var(--pz)) var(--ts), 
    radial-gradient(circle at 75% 45%, var(--c2) var(--pz)) var(--ts),
    radial-gradient(circle at 5% 75%, var(--c2) var(--pz)) var(--ts), 
    radial-gradient(circle at 25% 55%, var(--c2) var(--pz)) var(--ts),
    radial-gradient(circle at 55% 75%, var(--c1) var(--pz)) var(--ts), 
    radial-gradient(circle at 75% 95%, var(--c1) var(--pz)) var(--ts),
    radial-gradient(circle at 5% 25%, var(--c1) var(--pz)) var(--ts), 
    radial-gradient(circle at 25% 5%, var(--c1) var(--pz)) var(--ts),
    conic-gradient(from 0deg at 50% 50%, var(--c1) 0 25%, var(--c2) 0 50%, var(--c1) 0 75%, var(--c2) 0 100%) var(--ts);
}

Y un último ejemplo usando las tres funciones; linear-gradient , radial-gradient y conic-gradient :

.teselado-placa-agujeros {	
  --sz: 8px;
  --r1: calc(var(--sz) * 4);
  --r2: calc(var(--sz) * 4.1);
  --r3: calc(var(--sz) / 12);
  --r4: calc(var(--sz) / 2.5);
  --r5: calc(var(--sz) / 2.25);
  --r6: calc(var(--sz) / 2);
  --c0: #050505;
  --c1: #1c1c1c;
  --c2: #404040;
  --c3: #525252;
  --c4: #808080;
  --ts: 50%/calc(var(--sz) * 10) calc(var(--sz) * 16.8);
  --border-fix: var(--c0) 0 25%, #fff0 0 100%;
  --screw1: var(--c4) var(--r3), #fff0 var(--r4);
  --screw2: var(--c0), var(--c1) var(--r5), #fff0 var(--r6);
  --hole1: var(--c0) var(--r1), #fff0 var(--r2) 100%;
  --hole2: var(--c1) var(--r1), #fff0 var(--r2) 100%;
  --hole3: var(--c4) var(--r1), #fff0 var(--r2) 100%;
  height: 30vh;
  background: 
    /* arreglo borde */
    conic-gradient(from 225deg at 40% 50%, var(--border-fix)) var(--ts), 
    conic-gradient(from 45deg at 60% 50%, var(--border-fix)) var(--ts), 
    conic-gradient(from -45deg at 50% 24%, var(--border-fix)) var(--ts), 
    conic-gradient(from 135deg at 50% 76%, var(--border-fix)) var(--ts),
    /* tornillos */
    radial-gradient(circle at 99.5% 83.5%, var(--screw1)) var(--ts),
    radial-gradient(circle at 100% 84%, var(--screw2)) var(--ts),
    radial-gradient(circle at -0.5% 83.5%, var(--screw1)) var(--ts),
    radial-gradient(circle at 0% 84%, var(--screw2)) var(--ts),
    radial-gradient(circle at 99.5% 16.5%, var(--screw1)) var(--ts),
    radial-gradient(circle at 100% 17%, var(--screw2)) var(--ts),
    radial-gradient(circle at -0.5% 16.5%, var(--screw1)) var(--ts),
    radial-gradient(circle at 0% 17%, var(--screw2)) var(--ts),
    radial-gradient(circle at 49.5% 67.5%, var(--screw1)) var(--ts),
    radial-gradient(circle at 50% 68%, var(--screw2)) var(--ts),
    radial-gradient(circle at 49.5% 31.5%, var(--screw1)) var(--ts),
    radial-gradient(circle at 50% 32%, var(--screw2)) var(--ts),
    /* agujeros */
    radial-gradient(circle at 100% 50%, var(--hole1)) var(--ts),
    radial-gradient(circle at 98% 48%, var(--hole2)) var(--ts),
    radial-gradient(circle at 102% 52%, var(--hole3)) var(--ts),
    radial-gradient(circle at 0% 50%, var(--hole1)) var(--ts),
    radial-gradient(circle at -2% 48%, var(--hole2)) var(--ts),
    radial-gradient(circle at 2% 52%, var(--hole3)) var(--ts),
    radial-gradient(circle at 50% 100%, var(--hole1)) var(--ts),
    radial-gradient(circle at 48% 98%, var(--hole2)) var(--ts),
    radial-gradient(circle at 52% 102%, var(--hole3)) var(--ts),
    radial-gradient(circle at 50% 0%, var(--hole1)) var(--ts),
    radial-gradient(circle at 48% -2%, var(--hole2)) var(--ts),
    radial-gradient(circle at 52% 2%, var(--hole3)) var(--ts),
    /* fondo */
    linear-gradient(45deg, var(--c2) 33%, #ffffff20 55% 65%, var(--c3) 70% );
    background-color: var(--c2);
}

Además de estas tres funciones también podemos usar las funciones de repetición de cada degradado, a saber: repeating-linear-gradient , repeating-radial-gradient y repeating-conic-gradient , por lo que realmente las posibilidades pueden llegar a ser infinitas…

En definitiva, que es adictivo esto de los teselados en CSS, yo empecé con el Desafío de Codepen de julio de 2021 y no he dejado de crear nuevos patrones, convirtiéndolo a veces en retos personales, como el caso del patrón del Overlook Hotel.

En la Colección de Codepen Tessellated Patterns podéis ver los más de 70 diseños que he creado hasta ahora. El objetivo principal es llegar a 100, pero sé que cuando alcance esa cifra surgirá otro diseño… y seguiré a por los 150, y después a por 200…

Próximamente explicaré con detalle la otra técnica de superposición de teselas de la que hablábamos anteriormente y con la que podemos crear intrincados diseños en nuestros patrones teselados en CSS.

Deja un Comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *