Calabaza de Halloween animada en CSS

Calabaza de Halloween hecha en LESS CSS, usando gradient y keyframes

Aleix Martí
Blog de Interactius UX
7 min readOct 26, 2016

--

Ahora que se acerca el día de Todos los Santos, me he divertido un rato dibujando en CSS esta calabaza de Halloween, que aunque sea una tradición anglosajona siempre me ha atraído.

Cómo programar una calabaza de Halloween con CSS usando LESS

A continuación explicaré la estructura que he seguido usando LESS, pero si queréis ver el código compilado en CSS lo podéis hacer clicando en la pestaña “LESS” y posteriormente en el botón en la parte inferior “VIEW COMPILED”.

Lo primero ha sido definir los colores usados en los gradientes. El resto de colores he usado los predefinidos (orange, yellow, red y black)

@darkest-orange: #c77000;
@dark-orange: #f68c03;
@light-orange: #f3c510;
@green: #7f9020;

Para cada uno de los gajos de la calabaza he creado un mixin y unas clases para ahorrar unas líneas a la hora de posicionarlos.
Todos los gajos tienes estas propiedades:

.piece{
display: inline-block;
position: absolute;
}

En función de si son los del lado izquierdo o derecho, les aplico un gradiente linear de oscuro a claro y viceversa:

.bg-left{
background: -webkit-repeating-linear-gradient(to right, @dark-orange, @light-orange 90px);
background: -o-repeating-linear-gradient(to right, @dark-orange, @light-orange 90px);
background: -moz-repeating-linear-gradient(to right, @dark-orange, @light-orange 90px);
background: repeating-linear-gradient(to right, @dark-orange, @light-orange 90px);
}
.bg-right{
background: -webkit-repeating-linear-gradient(to right, @light-orange, @dark-orange 90px);
background: -o-repeating-linear-gradient(to right, @light-orange, @dark-orange 90px);
background: -moz-repeating-linear-gradient(to right, @light-orange, @dark-orange 90px);
background: repeating-linear-gradient(to right, @light-orange, @dark-orange 90px);
}

Este el mixin que recibe los parámetros para posicionar cada uno de los gajos. Se define el alto, ancho, posición y z-index. La forma se le da usando la propiedad border-radius para cada una de las esquinas:

.setup-piece(@w,@h,@l,@t,@tl,@tr,@bl,@br,@z){
width: @w;
height: @h;
left: @l;
top: @t;
border-top-left-radius: @tl;
border-top-right-radius: @tr;
border-bottom-left-radius: @bl;
border-bottom-right-radius: @br;
z-index: @z;
}

De esta forma, para definir cada uno de los gajos se puede hacer en sólo 3 líneas.

#piece1{
.piece;
.bg-left;
.setup-piece(70px,300px,0px,40px,100px,10px,100%,20px,1);
}

Los gajos del lado derecho se genera igual pero con la clase .bg-right y añadiendo la propiedad transform sobre el eje Y para conseguir un efecto espejo:

#piece7{
.piece;
.bg-right;
.setup-piece(70px,300px,320px,40px,100px,10px,100%,20px,1);
-ms-transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
transform: rotateY(180deg);
}

Para crear los ojos he aprovechado el mixin .setup-piece para posicionarlos y darles forma con border-radius en las esquinas. Después le aplico una ligera rotación de 15 grados. El color de fondo es un gradiente linear inclinado a 45 grados que pasa de amarillo a naranja y finalmente a rojo. Finalmente le aplico 10px de ancho en los border inferior y derecho para dar efecto de profundidad.

.eye{
position: absolute;
overflow: hidden;
}
.eye.right{
.setup-piece(80px,80px,235px,100px,100%,10px,10px,50%,5);
transform: rotate(15deg);
-ms-transform: rotate(15deg);
-webkit-transform: rotate(15deg);
background: -webkit-linear-gradient(45deg, yellow, orange, red 130px);
background: -moz-linear-gradient(45deg, yellow, orange, red 130px);
background: -o-linear-gradient(45deg, yellow, orange, red 130px);
background: linear-gradient(45deg, yellow, orange, red 130px);
border-bottom: 10px solid @darkest-orange;
border-right: 10px solid @darkest-orange;
}

Para crear el efecto de luz, inicialmente intenté hacer un efecto de transición entre gradientes, pero no se puede aplicar en CSS. Para hacerlo hay que hacer una pequeña trampa, que consiste en crear una capa semitransparente y en la animación ir cambiando la opacidad. He creado la capa usando el selector after, asignándole la animación que he creado con el nombre de fire. La propiedad overflow: hidden de la clase .eye (ver más arriba) impide que la capa semitransparente sobresalga del perfil del ojo.

.eye::after{
content:'';
display: block;
position: relative;;
top: 0;
width: 100%;
height: 100%;
background: black;
-webkit-animation: fire 3s infinite;
-moz-animation: fire 3s infinite;
-o-animation: fire 3s infinite;
animation: fire 3s infinite;
}

Esta misma animación se aplica en ojos, nariz, boca y sombra.

@keyframes fire {
0% { opacity: 0; }
10% { opacity: 0.2; }
25% { opacity: 0.5; }
50% { opacity: 0.2; }
75% { opacity: 0.4; }
90% { opacity: 0.2; }
100% { opacity: 0; }
}

La nariz se ha creado de forma similar a los ojos, se puede ver en el código.

La boca es la parte más compleja. El concepto es similar a los ojos, con la diferencia que en los ojos el background és un único gradiente linear, y en la boca hay 4 gradientes distintos para dibujar los dientes

#mouth {
position: absolute;
.setup-piece(250px,50px,260px,75px,90%,90%,30%,30%,5);
background: transparent no-repeat;
background-image: linear-gradient(to right, orange, orange 20px, @darkest-orange 20px, @darkest-orange 25px, transparent 25px, transparent 80px, orange 80px, orange 100px, @darkest-orange 100px, @darkest-orange 105px, transparent 105px, transparent 180px, orange 180px, orange 200px, @darkest-orange 200px, @darkest-orange 205px, transparent 205px), linear-gradient(to right, orange, orange 20px, @darkest-orange 20px, @darkest-orange 25px, transparent 25px, transparent 90px, orange 90px, orange 110px, @darkest-orange 110px, @darkest-orange 115px, transparent 115px),
linear-gradient(to right, @darkest-orange, @darkest-orange 20px, @darkest-orange 20px, @darkest-orange 25px, transparent 25px, transparent 90px, @darkest-orange 90px, @darkest-orange 110px, @darkest-orange 110px, @darkest-orange 115px, transparent 115px),
linear-gradient(to right, @darkest-orange, @darkest-orange);
background-position: 20px -25px, 60px 30px, 60px 28px, 0px 45px;
}

A continuación los desgloso y explico qué hace cada uno de los gradientes. En la imagen inferior se pueden ver por separado.

El primero son los 3 dientes superiores. Se generan gradientes de un color al mismo color para que no haya degradado. El orange dibuja el diente en sí. El color @darkest-orange se usa para dibujar el borde de los dientes para dar profundidad. La separación entre dientes es transparente para que puede verse el color de fondo.

linear-gradient(to right, orange, orange 20px, @darkest-orange 20px, @darkest-orange 25px, transparent 25px, transparent 80px, orange 80px, orange 100px, @darkest-orange 100px, @darkest-orange 105px, transparent 105px, transparent 180px, orange 180px, orange 200px, @darkest-orange 200px, @darkest-orange 205px, transparent 205px)

El segundo gradiente dibuja los dientes inferiores. Es el mismo principio que los dientes superiores.

linear-gradient(to right, orange, orange 20px, @darkest-orange 20px, @darkest-orange 25px, transparent 25px, transparent 90px, orange 90px, orange 110px, @darkest-orange 110px, @darkest-orange 115px, transparent 115px)

Uso un tercer gradiente para dibujar los bordes superiores de los dientes inferiores.

linear-gradient(to right, @darkest-orange, @darkest-orange 20px, @darkest-orange 20px, @darkest-orange 25px, transparent 25px, transparent 90px, @darkest-orange 90px, @darkest-orange 110px, @darkest-orange 110px, @darkest-orange 115px, transparent 115px)

Finalmente utilizo un último gradiente para dibujar el borde inferior de la boca. Es un gradiente de un solo color.

linear-gradient(to right, @darkest-orange, @darkest-orange)

Esta propiedad define las posiciones (left y top) de cada uno de los gradientes. Cada par de valores indica la posición de uno de los gradientes (en orden según como se hayan definido).

background-position: 20px -25px, 60px 30px, 60px 28px, 0px 45px;

Nota: aquí sólo he escrito la propiedad linear-gradient, pero en el código se puede ver que la propiedad se repite para cada uno de los navegadores -webkit-, -moz- y -o-.

Para completar la boca se necesita otro div con z-index inferior al de la boca dónde se le define un gradiente para la propiedad background. Es lo que se verá entre los espacios de los dientes.

#mouth-bg {
position: absolute;
z-index: 4;
.setup-piece(250px,50px,260px,75px,90%,90%,30%,30%,4);
background: -webkit-linear-gradient(0deg, red, orange, yellow);
background: -moz-linear-gradient(0deg, red, orange, yellow);
background: -o-linear-gradient(0deg, red, orange, yellow);
background: linear-gradient(0deg, red, orange, yellow);
}

De igual forma que en los ojos y la nariz, en el selector after se le aplica la animación para obtener el efecto de llama.

#mouth-bg::after{
content:'';
display: block;
position: relative;;
top: 0;
width: 100%;
height: 100%;
background: black;
-webkit-animation: fire 3s infinite;
-moz-animation: fire 3s infinite;
-o-animation: fire 3s infinite;
animation: fire 3s infinite;
}

El rabito de la calabaza se obtiene con la propiedad box-shadow para conseguir la curva. Se dibuja un círculo sin color, se posiciona y se le crea una sombra, situándola convenientemente para obtener una media luna, la parte inferior de la cual quedará oculta por la calabaza.

#tail{
width: 50px;
height: 50px;
border-radius: 100%;
top: -20px;
left: 195px;
display: block;
position: absolute;
box-shadow: -20px 5px 0px @green;
}

Finalmente, para dibujar la sombra de la calabaza se ha usado un div circular con un gradiente de naranja a negro y se ha aplicado la transición de forma similar a la boca, ojos y nariz.

#shadow{
position: relative;
top: 350px;
width: 400px;
height: 50px;
border-radius: 100%;
background: -webkit-radial-gradient( @dark-orange, black);
background: -moz-radial-gradient( @dark-orange, black);
background: -o-radial-gradient( @dark-orange, black);
background: radial-gradient( @dark-orange, black);
overflow: hidden;
z-index: 2;
}
#shadow-bg {
position: absolute;
top: 0;
width: 100%;
height: 100%;
border-radius: 100%;
z-index:1;
}
#shadow-bg::after{
content:'';
display: block;
position: relative;;
top: 0;
width: 100%;
height: 100%;
background: black;
-webkit-animation: fire 3s infinite;
-moz-animation: fire 3s infinite;
-o-animation: fire 3s infinite;
animation: fire 3s infinite;
}

Y eso es todo. A continuación se pueden ver algunos elementos sueltos y los distintos gradientes utilizados para formar la calabaza.

--

--