Jag har nyligen uppdaterat den animerade grafiken på min webbplats med ett nytt tema och en grupp banbrytande karaktärer, och praktiserat massor av teknikerna jag delade i den här serien. Några av mina animationer ändrar utseende när någon interagerar med dem eller vid olika tidpunkter på dagen.
Färgerna i grafiken på mina bloggsidor ändras från morgon till kväll varje dag. Sedan finns det snöläget, som lägger till kyliga färger och ett vinterligt tema, tack vare ett överläggsskikt och ett blandningsläge.
Under arbetet med detta började jag undra om CSS-relativa färgvärden kunde ge mig mer kontroll samtidigt som jag förenklade processen. Obs: I den här handledningen kommer jag att fokusera på relativa färgvärden och OKLCH-färgrymden för temagrafik och animationer. Om du vill dyka djupt in i relativ färg, skapade Ahmad Shadeed en fantastisk interaktiv guide. När det gäller färgrymder, omfång och OKLCH skrev vår egen Geoff Graham om dem.
Upprepad användning av element var nyckeln. Bakgrunder återanvändes när det var möjligt, med zoomningar och överlägg som hjälpte till att konstruera nya scener från samma konstverk. Det föddes av nödvändighet, men det uppmuntrade också att tänka i termer av serier snarare än enskilda scener. Problemet med att manuellt uppdatera färgpaletter Låt oss gå direkt till min utmaning. I Toon Titles som den här – baserad på Yogi Bear Show-avsnittet från 1959 “Lullabye-Bye Bear” – och mitt arbete generellt sett är paletter begränsade till ett fåtal utvalda färger.
Jag skapar nyanser och nyanser från vad jag kallar min "foundation"-färg för att utöka paletten utan att lägga till fler nyanser.
I Sketch arbetar jag i HSL-färgrymden, så denna process innebär att jag ökar eller minskar ljushetsvärdet för min foundationfärg. Ärligt talat, det är inte en mödosam uppgift - men att välja en annan grundfärg kräver att du skapar en helt ny uppsättning nyanser och nyanser. Att göra det manuellt, om och om igen, blir snabbt mödosamt.
Jag nämnde HSL - H (nyans), S (mättnad) och L (ljushet) - färgrymden, men det är bara ett av flera sätt att beskriva färg. RGB — R (röd), G (grön), B (blå) — är förmodligen den mest bekanta, åtminstone i sin Hex-form. Det finns också LAB - L (ljushet), A (grön-röd), B (blå-gul) - och den nyare, men nu allmänt stödda modellen LCH - L (ljushet), C (kroma), H (nyans) - i dess OKLCH-form. Med LCH — specifikt OKLCH i CSS — kan jag justera ljushetsvärdet för min foundationfärg.
Eller så kan jag ändra dess färg. LCH chroma och HSL saturation beskriver båda intensiteten eller rikedomen hos en färg, men de gör det på olika sätt. LCH ger mig ett bredare utbud och mer förutsägbar blandning mellan färger.
Jag kan också ändra nyansen för att skapa en palett av färger som delar samma ljushet och färgvärden. I både HSL och LCH börjar nyansspektrat vid rött, rör sig genom grönt och blått och återgår till rött.
Varför OKLCH ändrade hur jag tänker om färg Webbläsarstöd för OKLCH-färgrymden är nu utbrett, även om designverktyg – inklusive Sketch – inte har kommit ikapp. Lyckligtvis borde det inte hindra dig från att använda OKLCH. Webbläsare konverterar gärna Hex-, HSL-, LAB- och RGB-värden till OKLCH åt dig. Du kan definiera en anpassad CSS-egenskap med en grundfärg i vilket utrymme som helst, inklusive Hex: /* Foundation färg */ --grund: #5accd6;
Alla färger som härrör från det kommer att konverteras till OKLCH automatiskt: --foundation-light: oklch(från var(--foundation) [...]; } --foundation-mid: oklch(från var(--foundation) [...]; } --foundation-dark: oklch(från var(--foundation) [...]; }
Relativ färg som ett designsystem Tänk på relativ färg som att säga: "Ta den här färgen, justera den och ge mig sedan resultatet." Det finns två sätt att justera en färg: absoluta förändringar och proportionella förändringar. De ser likadana ut i kod, men beter sig väldigt olika när du börjar byta foundationfärger. Att förstå den skillnaden är det som kan göra att använda relativ färg till ett system. /* Foundation färg */ --grund: #5accd6;
Till exempel är ljushetsvärdet för min foundationfärg 0,7837, medan en mörkare version har ett värde på 0,5837. För att beräkna skillnaden subtraherar jag det lägre värdet från det högre och tillämpar resultatet med en calc() funktion: --foundation-mörk: oklch(från var(--foundation) beräknat (1 - 0,20) c h);
För att få en ljusare färg lägger jag till skillnaden istället: --foundation-light: oklch(från var(--foundation) beräknat (1 + 0,10) c h);
Chromajusteringar följer samma process. För att minska intensiteten på min grundfärg från 0,1035 till 0,0035, subtraherar jag ett värde från det andra: oklch(från var(--foundation) 1 beräknat (c - 0,10) h);
För att skapa en palett av nyanser, beräknar jag skillnaden mellan nyansvärdet för min foundationfärg (200) och min nya nyans (260): oklch(från var(--foundation) lc beräknat (h + 60));
Dessa beräkningar är absoluta. När jag subtraherar ett fast belopp, säger jag i praktiken, "Dra alltid av så mycket." Detsamma gäller när man lägger till fasta värden: beräkn.(c - 0,10) beräknat(c + 0,10)
Jag lärde mig gränserna för detta tillvägagångssätt på den hårda vägen. När jag förlitade mig på att subtrahera fasta färgvärden, kollapsade färgerna mot grått så fort jag bytte foundation. En palett som fungerade för en färg föll isär för en annan. Multiplikation beter sig annorlunda. När jag multiplicerar chroma säger jag till webbläsaren: "Minska den här färgens intensitet med en proportion." Förhållandet mellan färger förblir intakt, även när grunden förändras: beräkn.(c * 0,10)
Mina regler för flytta det, skala det, rotera det
Flytta lätthet (lägg till eller subtrahera), Skala kroma (multiplicera), Rotera nyans (lägg till eller subtrahera grader).
Jag skalar chroma eftersom jag vill att intensitetsförändringar ska förbli proportionella mot basfärgen. Nyansrelationer är roterande, så att multiplicera nyans är meningslöst. Lätthet är perceptuell och absolut – multiplicera den ger ofta udda resultat.
Från en färg till ett helt tema Relativ färg gör att jag kan definiera en grundfärg och generera alla andra färger jag behöver – fyllningar, streck, gradientstopp, skuggor – från den. Vid den tidpunkten slutar färg att vara en palett och börjar vara ett system. SVG-illustrationer tenderar att återanvända samma få färger över fyllningar, linjer och övertoningar. Relativ färg låter dig definiera dessa relationer en gång och återanvända dem överallt - ungefär som animatörer återanvände bakgrunder för att skapa nya scener.
Ändra grundfärgen en gång, och varje härledd färg uppdateras automatiskt, utan att räkna om något för hand. Utanför animerad grafik skulle jag kunna använda samma tillvägagångssätt för att definiera färger för tillstånden för interaktiva element som knappar och länkar. Grundfärgen jag använde i min "Lullabye-Bye Bear" Toon Title är en blå som ser cyan ut. Bakgrunden är en radiell gradient mellan min foundation och en mörkare version.
För att skapa alternativa versioner med helt olika stämningar behöver jag bara ändra grundfärgen: --grund: #5accd6; --grad-end: var(--foundation); --grad-start: oklch(från var(--foundation) beräknat (1 - 0,2357) beräknat (c * 0,833) h);
För att binda dessa anpassade egenskaper till min SVG-gradient utan att duplicera färgvärden, ersatte jag hårdkodade stoppfärgsvärden med inline-stilar:
Därefter behövde jag se till att min Toon Text alltid står i kontrast till vilken grundfärg jag än väljer. En 180 graders nyansrotation ger en kompletterande färg som verkligen poppar upp - men kan vibrera obehagligt: .text-light { fyll: oklch(från var(--foundation) lc beräknat (h + 180)); }
En 90° förskjutning ger en levande sekundärfärg utan att vara helt komplementär: .text-light { fyll: oklch(från var(--foundation) lc beräknat (h - 90)); }
Min återskapande av Quick Draw McGraws 1959 Toon-titel "El Kabong" använder samma teknik men med en mer varierad palett. Till exempel finns det en annan radiell gradient mellan grundfärgen och en mörkare nyans.
Byggnaden och trädet i bakgrunden är helt enkelt olika nyanser av samma grundfärg. För dessa vägar behövde jag ytterligare två fyllningsfärger: .bg-mid { fyll: oklch(från var(--foundation) beräknat (1 - 0,04) beräknat (c * 0,91) h); }
.bg-dark { fyll: oklch(från var(--foundation) beräknat (1 - 0,12) beräknat (c * 0,64) h); }
När grunderna börjar röra på sig
Hittills har allt jag visat varit statiskt. Även när någon använder en färgväljare för att ändra grundfärgen, sker den förändringen omedelbart. Men animerad grafik står sällan stilla - ledtråden ligger i namnet. Så om färg är en del av systemet finns det ingen anledning att den inte kan animera också.
För att animera grundfärgen måste jag först dela upp den i dess OKLCH-kanaler— ljushet, färg och nyans. Men det finns ett viktigt extra steg: jag måste registrera dessa värden som skrivna anpassade egenskaper. Men vad betyder det?
Som standard vet en webbläsare inte om ett anpassat CSS-egenskapsvärde representerar en färg, längd, nummer eller något helt annat. Det betyder ofta att de inte kan interpoleras smidigt under animering och hoppar från ett värde till ett annat.
Att registrera en anpassad egenskap berättar för webbläsaren vilken typ av värde den representerar och hur den ska bete sig över tid. I det här fallet vill jag att webbläsaren ska behandla mina färgkanaler som siffror så att de kan animeras smidigt.
@property --f-l {
syntax: "
@property --f-c {
syntax: "
@property --f-h {
syntax: "
När de väl är registrerade beter sig dessa anpassade egenskaper som inbyggd CSS. Webbläsaren kan interpolera dem bildruta för bildruta. Jag bygger sedan om grundfärgen från dessa kanaler: --foundation: oklch(var(--f-l) var(--f-c) var(--f-h));
Detta gör att grundfärgen blir animerbar, precis som alla andra numeriska värden. Här är en enkel "andnings"-animation som försiktigt ändrar lättheten över tiden: @keyframes andas { 0 %, 100 % { --f-l: 0,36; } 50% { --f-l: 0,46; } }
.toon-title { animation: andas 10s lätthet-in-ut oändligt; }
Eftersom alla andra färger i fyllningar, övertoningar och streck härrör från --foundation, animeras de tillsammans, och ingenting behöver uppdateras manuellt. En animerad färg, många effekter I början av denna process undrade jag om CSS relativa färgvärden kunde erbjuda fler möjligheter samtidigt som de skulle göra dem enklare att implementera. Jag har nyligen lagt till en ny guldgruvabakgrund på min hemsidas kontaktsida, och den första iterationen inkluderade oljelampor som lyser och svänger.
Jag ville utforska hur animering av CSS-relativa färger kan göra gruvans inre mer realistisk genom att tona den med färger från lamporna. Jag ville att de skulle påverka världen omkring dem, som verkligt ljus gör. Så istället för att animera flera färger byggde jag ett litet ljussystem som animerar bara en färg.
Min första uppgift var att placera ett överlagringsskikt mellan bakgrunden och mina lampor:
Jag använde mix-blend-mode: färg eftersom det färgar det som finns under det samtidigt som den underliggande luminansen bevaras. Eftersom jag bara vill att överlägget ska vara synligt när animationer är aktiverade, valde jag överlägget: .svg-mine #overlay { display: ingen; }
@media (prefers-reduced-motion: no-preference) { .svg-mine[data-animations=on] #overlay { display: block; opacitet: 0,5; } }
Överlägget var på plats, men ännu inte kopplat till lamporna. Jag behövde en ljuskälla. Mina lampor är enkla och var och en innehåller ett cirkelelement som jag suddade ut med ett filter. Filtret ger en mycket mjuk oskärpa över hela cirkeln.
Istället för att animera överlägget och lamporna separat, animerar jag en enda "flame"-färgsymbol och härleder allt annat från det. Först registrerar jag tre skrivna anpassade egenskaper för OKLCH-kanaler:
@property --fl-l {
syntax: "
Jag animerade de kanalerna och tryckte avsiktligt några ramar mot orange så att flimmern tydligt läses som eldljus:
@keyframes flame { 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; } }
Sedan omfångade jag den animationen till SVG, så att de delade variablerna är tillgängliga för både lamporna och mitt överlägg:
@media (prefers-reduced-motion: no-preference) { .svg-mine[data-animations=on] { animation: flame 3.6s oändligt linjär; isolering: isolera;
/* Bygg en flamfärg från animerade kanaler */ --flamma: oklch(var(--fl-l) var(--fl-c) var(--fl-h));
/* Lampfärg härledd från låga */ --lampkärna: oklch(från var(--flamma) calc(l + 0,05) calc(c * 0,70) h);
/* Överlagringsfärg härrörande från samma låga */ --overlay-tint: oklch(från var(--flame) beräknat(l + 0,06) beräknat(c * 0,65) beräknat (h - 10)); } }
Slutligen applicerade jag de här härledda färgerna på de glödande lamporna och överlägget som de påverkar: @media (prefers-reduced-motion: no-preference) { .svg-mine[data-animations=on] #mine-lamp-1 > cirkel, .svg-mine[data-animations=on] #mine-lamp-2 > cirkel { fylla: var(--lamp-kärna); }
.svg-mine[data-animations=on] #overlay { display: block; fylla: var(--overlay-tint); opacitet: 0,5; } }
När lågan skiftar mot orange värms lamporna upp och scenen värms upp med dem. När lågan svalnar lägger sig allt ihop. Det bästa är att ingenting skrivs manuellt. Om jag ändrar grundfärgen eller justerar flamanimationsintervallen uppdateras hela belysningssystemet samtidigt. Du kan se slutresultatet på min hemsida. Återanvändning, återanvändning, återbesökt Dessa Hanna-Barbera-animatörer tvingades återanvända element av nödvändighet, men jag återanvänder färger eftersom det gör mitt arbete mer konsekvent och lättare att underhålla. CSS relativa färgvärden tillåter mig att:
Definiera en enda grundfärg, Beskriv hur andra färger relaterar till det, Återanvänd dessa relationer överallt, och Animera systemet genom att ändra ett värde.
Relativ färg gör inte bara teman lättare. Det uppmuntrar ett sätt att tänka där färg, som rörelse, är avsiktlig – och där förändring av ett värde kan förvandla en hel scen utan att skriva om verket under det.