Recientemente actualicé los gráficos animados de mi sitio web con un nuevo tema y un grupo de personajes pioneros, poniendo en práctica muchas de las técnicas que compartí en esta serie. Algunas de mis animaciones cambian de apariencia cuando alguien interactúa con ellas o en diferentes momentos del día.
Los colores del gráfico encima de las páginas de mi blog cambian desde la mañana hasta la noche todos los días. Luego, está el modo nieve, que agrega colores fríos y un tema invernal, cortesía de una capa superpuesta y un modo de fusión.
Mientras trabajaba en esto, comencé a preguntarme si los valores de color relativos de CSS podrían darme más control y al mismo tiempo simplificar el proceso. Nota: En este tutorial, me centraré en los valores de color relativos y el espacio de color OKLCH para temas de gráficos y animaciones. Si desea profundizar en el color relativo, Ahmad Shadeed creó una excelente guía interactiva. En cuanto a los espacios de color, las gamas y OKLCH, nuestro propio Geoff Graham escribió sobre ellos.
El uso repetido de elementos fue clave. Los fondos se reutilizaron siempre que fue posible, con zooms y superposiciones que ayudaron a construir nuevas escenas a partir de la misma obra de arte. Nació por necesidad, pero también impulsó a pensar en términos de series más que de escenas individuales. El problema de actualizar manualmente las paletas de colores Vayamos directamente a mi desafío. En títulos de dibujos animados como este, basados en el episodio “Lullabye-Bye Bear” de Yogi Bear Show de 1959, y en mi trabajo en general, las paletas se limitan a unos pocos colores seleccionados.
Creo sombras y tintes a partir de lo que llamo mi color “base” para expandir la paleta sin agregar más tonos.
En Sketch, trabajo en el espacio de color HSL, por lo que este proceso implica aumentar o disminuir el valor de luminosidad de mi color base. Honestamente, no es una tarea ardua, pero elegir un color de base diferente requiere crear un conjunto completamente nuevo de tonos y tintes. Hacerlo manualmente, una y otra vez, rápidamente se vuelve laborioso.
Mencioné el espacio de color HSL (H (tono), S (saturación) y L (luminosidad), pero esa es solo una de varias formas de describir el color. RGB (R (rojo), G (verde), B (azul) es probablemente el más familiar, al menos en su forma hexadecimal. También está LAB - L (luminosidad), A (verde-rojo), B (azul-amarillo) - y el modelo LCH - L (luminosidad), C (croma), H (tono), más nuevo, pero ahora ampliamente compatible, en su forma OKLCH. Con LCH (específicamente OKLCH en CSS) puedo ajustar el valor de luminosidad del color de mi base.
O puedo alterar su croma. El croma LCH y la saturación HSL describen la intensidad o riqueza de un color, pero lo hacen de diferentes maneras. LCH me ofrece una gama más amplia y una combinación de colores más predecible.
También puedo alterar el tono para crear una paleta de colores que comparten los mismos valores de luminosidad y croma. Tanto en HSL como en LCH, el espectro de tonos comienza en rojo, pasa por verde y azul y regresa a rojo.
Por qué OKLCH cambió mi forma de pensar sobre el color La compatibilidad del navegador con el espacio de color OKLCH ahora está muy extendida, incluso si las herramientas de diseño, incluido Sketch, no se han puesto al día. Afortunadamente, eso no debería impedirle utilizar OKLCH. Los navegadores estarán encantados de convertir los valores Hex, HSL, LAB y RGB en OKLCH por usted. Puede definir una propiedad personalizada de CSS con un color base en cualquier espacio, incluido Hex: /*Color de base*/ --fundación: #5accd6;
Todos los colores derivados de él se convertirán a OKLCH automáticamente: --fundación-luz: oklch(de var(--fundación) [...]; } --fundación-mid: oklch(de var(--fundación) [...]; } --fundación-oscura: oklch(de var(--fundación) [...]; }
El color relativo como sistema de diseño Piense en el color relativo como si dijera: "Toma este color, modifícalo y luego dame el resultado". Hay dos formas de ajustar un color: cambios absolutos y cambios proporcionales. Se ven similares en código, pero se comportan de manera muy diferente una vez que comienzas a intercambiar colores de base. Comprender esa diferencia es lo que puede convertir el uso del color relativo en un sistema. /*Color de base*/ --fundación: #5accd6;
Por ejemplo, el valor de luminosidad de mi color de base es 0,7837, mientras que una versión más oscura tiene un valor de 0,5837. Para calcular la diferencia, resto el valor más bajo del más alto y aplico el resultado usando una función calc(): --fundación-oscura: oklch(de var(--fundación) calc(l - 0,20)ch);
Para lograr un color más claro, agrego la diferencia: --base-luz: oklch(de var(--fundación) calc(l + 0,10)ch);
cromaLos ajustes siguen el mismo proceso. Para reducir la intensidad del color de mi base de 0,1035 a 0,0035, resto un valor del otro: oklch(de var(--fundación) l calc(c - 0,10)h);
Para crear una paleta de tonos, calculo la diferencia entre el valor del tono de mi color de base (200) y mi nuevo tono (260): oklch(de var(--fundación) lc calc(h + 60));
Esos cálculos son absolutos. Cuando resto una cantidad fija, en realidad estoy diciendo: "Siempre resta esta cantidad". Lo mismo se aplica al agregar valores fijos: cálculo(c - 0,10) cálculo(c + 0,10)
Aprendí los límites de este enfoque de la manera más difícil. Cuando confié en restar valores cromáticos fijos, los colores colapsaron hacia el gris tan pronto como cambié la base. Una paleta que funcionó para un color se vino abajo para otro. La multiplicación se comporta de manera diferente. Cuando multiplico el croma, le digo al navegador: "Reduce la intensidad de este color en una proporción". La relación entre colores permanece intacta, incluso cuando cambia la base: cálculo(c * 0,10)
Mis reglas de moverlo, escalarlo, rotarlo
Mover luminosidad (sumar o restar), Croma de escala (multiplicar), Rotar tono (sumar o restar grados).
Escala el croma porque quiero que los cambios de intensidad sean proporcionales al color base. Las relaciones de tono son rotacionales, por lo que multiplicar el tono no tiene sentido. La ligereza es perceptiva y absoluta; multiplicarla a menudo produce resultados extraños.
De un color a un tema completo El color relativo me permite definir un color de base y generar todos los demás colores que necesito (rellenos, trazos, paradas de degradado, sombras) a partir de él. En ese momento, el color deja de ser una paleta y pasa a ser un sistema. Las ilustraciones SVG tienden a reutilizar los mismos pocos colores en rellenos, trazos y degradados. El color relativo le permite definir esas relaciones una vez y reutilizarlas en todas partes, de forma muy parecida a como los animadores reutilizaban fondos para crear nuevas escenas.
Cambie el color de base una vez y cada color derivado se actualizará automáticamente, sin tener que volver a calcular nada manualmente. Fuera de los gráficos animados, podría utilizar este mismo enfoque para definir colores para los estados de elementos interactivos como botones y enlaces. El color de base que utilicé en el título de mi caricatura “Lullabye-Bye Bear” es un azul de apariencia cian. El fondo es un degradado radial entre mi base y una versión más oscura.
Para crear versiones alternativas con estados de ánimo completamente diferentes, sólo necesito cambiar el color de la base: --fundación: #5accd6; --grad-end: var(--fundación); --grad-start: oklch(de var(--fundación) calc(l - 0,2357) calc(c * 0,833)h);
Para vincular esas propiedades personalizadas a mi degradado SVG sin duplicar valores de color, reemplacé los valores de color de parada codificados con estilos en línea:
A continuación, necesitaba asegurarme de que mi Toon Text siempre contrastara con cualquier color de base que eligiera. Una rotación de tono de 180 grados produce un color complementario que ciertamente resalta, pero que puede vibrar de manera incómoda: .texto-luz { llenar: oklch(de var(--fundación) lc calc(h + 180)); }
Un cambio de 90° produce un color secundario vívido sin ser completamente complementario: .texto-luz { llenar: oklch(de var(--fundación) lc calc(h - 90)); }
Mi recreación del título del dibujo animado de 1959 de Quick Draw McGraw, “El Kabong”, utiliza las mismas técnicas pero con una paleta más variada. Por ejemplo, hay otro degradado radial entre el color de la base y un tono más oscuro.
El edificio y el árbol del fondo son simplemente tonos diferentes del mismo color de base. Para esos caminos, necesitaba dos colores de relleno adicionales: .bg-medio { llenar: oklch(de var(--fundación) calc(l - 0,04) calc(c * 0,91) h); }
.bg-oscuro { llenar: oklch(de var(--fundación) calc(l - 0,12) calc(c * 0,64) h); }
Cuando los cimientos empiezan a moverse
Hasta ahora, todo lo que he mostrado ha sido estático. Incluso cuando alguien usa un selector de color para cambiar el color de la base, ese cambio ocurre instantáneamente. Pero los gráficos animados rara vez se quedan quietos: la clave está en el nombre. Entonces, si el color es parte del sistema, no hay razón para que no pueda animarse también.
Para animar el color de la base, primero necesito dividirlo en sus canales OKLCH.- luminosidad, croma y tono. Pero hay un paso adicional importante: necesito registrar esos valores como propiedades personalizadas escritas. ¿Pero qué significa eso?
De forma predeterminada, un navegador no sabe si el valor de una propiedad personalizada de CSS representa un color, una longitud, un número o algo completamente distinto. Esto a menudo significa que no se pueden interpolar suavemente durante la animación y saltar de un valor al siguiente.
El registro de una propiedad personalizada le indica al navegador el tipo de valor que representa y cómo debe comportarse con el tiempo. En este caso, quiero que el navegador trate mis canales de color como números para que puedan animarse sin problemas.
@propiedad --fl {
sintaxis: "
@propiedad --fc {
sintaxis: "
@propiedad --fh {
sintaxis: "
Una vez registradas, estas propiedades personalizadas se comportan como CSS nativo. El navegador puede interpolarlos cuadro por cuadro. Luego reconstruyo el color de base a partir de esos canales: --fundación: oklch(var(--f-l) var(--f-c) var(--f-h));
Esto hace que el color de base se vuelva animable, como cualquier otro valor numérico. Aquí hay una animación simple de "respiración" que cambia suavemente la luminosidad con el tiempo: @keyframes respira { 0%, 100% { --fl: 0,36; } 50% { --fl: 0,46; } }
.título-toon { animación: respira 10 segundos, entra y sale infinitamente; }
Debido a que todos los demás colores en rellenos, degradados y trazos se derivan de --foundation, todos se animan juntos y no es necesario actualizar nada manualmente. Un color animado, muchos efectos Al comienzo de este proceso, me preguntaba si los valores de color relativos de CSS podrían ofrecer más posibilidades y al mismo tiempo hacerlos más sencillos de implementar. Recientemente agregué un nuevo fondo de mina de oro a la página de contacto de mi sitio web, y la primera versión incluía lámparas de aceite que brillan y se balancean.
Quería explorar cómo animar colores relativos CSS podría hacer que el interior de la mina fuera más realista al teñirlo con los colores de las lámparas. Quería que afectaran el mundo que los rodeaba, como lo hace la luz real. Entonces, en lugar de animar varios colores, construí un pequeño sistema de iluminación que anima solo un color.
Mi primera tarea fue colocar una capa superpuesta entre el fondo y mis lámparas:
Utilicé mix-blend-mode: color porque tiñe lo que hay debajo y preserva la luminosidad subyacente. Como solo quiero que la superposición sea visible cuando las animaciones están activadas, opté por la superposición: .svg-mío #superposición { pantalla: ninguna; }
@media (prefiere movimiento reducido: sin preferencia) { .svg-mine[data-animations=on] #overlay { mostrar: bloquear; opacidad: 0,5; } }
La funda estaba colocada pero aún no conectada a las lámparas. Necesitaba una fuente de luz. Mis lámparas son simples y cada una contiene un elemento circular que difuminé con un filtro. El filtro produce un desenfoque muy suave en todo el círculo.
En lugar de animar la superposición y las lámparas por separado, animo una única ficha de color de "llama" y obtengo todo lo demás a partir de ahí. Primero, registro tres propiedades personalizadas escritas para los canales OKLCH:
@propiedad --fl-l {
sintaxis: "
Animé esos canales, empujando deliberadamente algunos fotogramas hacia el naranja para que el parpadeo se lea claramente como la luz del fuego:
@keyframes llama { 0%, 100% { --fl-l: 0,86; --fl-c: 0,12; --fl-h: 95; } 6% { --fl-l: 0,91; --fl-c: 0,10; --fl-h: 92; } 12% { --fl-l: 0,83; --fl-c: 0,14; --fl-h: 100; } 18% { --fl-l: 0,88; --fl-c: 0,11; --fl-h: 94; } 24% { --fl-l: 0,82; --fl-c: 0,16; --fl-h: 82; } 30% { --fl-l: 0,90; --fl-c: 0,12; --fl-h: 90; } 36% { --fl-l: 0,79; --fl-c: 0,17; --fl-h: 76; } 44% { --fl-l: 0,87; --fl-c: 0,12; --fl-h: 96; } 52% { --fl-l: 0,81; --fl-c: 0,15; --fl-h: 102; } 60% { --fl-l: 0,89; --fl-c: 0,11; --fl-h: 93; } 68% { --fl-l: 0,83; --fl-c: 0,16; --fl-h: 85; } 76% { --fl-l: 0,91; --fl-c: 0,10; --fl-h: 91; } 84% { --fl-l: 0,85; --fl-c: 0,14; --fl-h: 98; } 92% {--fl-l: 0,80; --fl-c: 0,17; --fl-h: 74; } }
Luego ajusté esa animación al SVG, por lo que las variables compartidas están disponibles tanto para las lámparas como para mi superposición:
@media (prefiere movimiento reducido: sin preferencia) { .svg-mine[data-animations=on] { animación: llama 3.6s infinito lineal; aislamiento: aislar;
/* Crea un color de llama a partir de canales animados */ --flame: oklch(var(--fl-l) var(--fl-c) var(--fl-h));
/* Color de lámpara derivado de la llama */ --núcleo de lámpara: oklch(de var(--llama) calc(l + 0,05) calc(c * 0,70) h);
/* Tinte superpuesto derivado de la misma llama */ --overlay-tint: oklch(de var(--llama) calc(l + 0,06) calc(c * 0,65) calc(h - 10)); } }
Finalmente, apliqué esos colores derivados a las lámparas brillantes y la superposición a la que afectan: @media (prefiere movimiento reducido: sin preferencia) { .svg-mine[data-animations=on] #mine-lamp-1 > círculo, .svg-mine[data-animations=on] #mine-lamp-2 > círculo { llenar: var(--lamp-core); }
.svg-mine[data-animations=on] #overlay { mostrar: bloquear; relleno: var(--overlay-tint); opacidad: 0,5; } }
Cuando la llama se vuelve naranja, las lámparas se calientan y la escena se calienta con ellas. Cuando la llama se enfría, todo se calma. La mejor parte es que nada se escribe manualmente. Si cambio el color de base o modifico los rangos de animación de las llamas, todo el sistema de iluminación se actualiza simultáneamente. Puedes ver el resultado final en mi web. Reutilizar, reutilizar, revisitado Esos animadores de Hanna-Barbera se vieron obligados a reutilizar elementos por necesidad, pero yo reutilizo los colores porque hace que mi trabajo sea más consistente y más fácil de mantener. Los valores de color relativos de CSS me permiten:
Definir un único color de base, Describe cómo se relacionan otros colores con él, Reutilice esas relaciones en todas partes y Anime el sistema cambiando un valor.
El color relativo no sólo facilita la temática. Fomenta una forma de pensar en la que el color, como el movimiento, es intencional y en la que cambiar un valor puede transformar una escena entera sin tener que reescribir la obra que hay debajo.