Gå til farvevælgeren

CSS color-mix() og relative farver: lyse nuancer, hover og gennemsigtighed

color-mix() blander to farver i et farverum, du selv vælger, og relativ farvesyntaks lader dig ændre én kanal i en eksisterende farve, fx oklch(from var(--brand) calc(l - 0.1) c h). Begge dele kører i browseren, virker med CSS-variabler og erstatter de fleste farvefunktioner i Sass. Bland i oklab til lyse nuancer og hover og i srgb til gennemsigtighed.

6 min. læsetid

Sådan virker color-mix()

color-mix() tager et farverum og to farver med valgfrie procentsatser og returnerer farven midt imellem. color-mix(in oklab, var(--brand) 90%, black) betyder 90 % brandfarve og 10 % sort, blandet i Oklab. Angiver du kun én procentsats, får den anden farve resten. Angiver du ingen, får hver 50 %.

Browseren beregner resultatet, mens siden kører, så det virker med custom properties, currentColor og temaskift. Sass-funktioner kører én gang i byggetrinnet og kan ikke se værdier fra var().

Giver de to procentsatser tilsammen under 100 %, bliver resultatet også delvist gennemsigtigt: color-mix(in srgb, red 30%, blue 30%) er en ligelig blanding med 60 % opacitet. Hold summen på 100 %, medmindre det er det, du vil have.

CSS

.button {
    --brand: #2563eb;
    background: var(--brand);
}

.button:hover {
    background: color-mix(in oklab, var(--brand) 90%, black);
}

.badge {
    background: color-mix(in oklab, var(--brand) 10%, white);
    border-color: color-mix(in srgb, var(--brand) 30%, transparent);
}

Hvilket farverum skal du blande i: srgb, oklab eller oklch

Farverummet ændrer resultatet mere, end de fleste forventer. Nedenfor er ren blå #0000ff blandet 50/50 med ren gul #ffff00, beregnet med den samme matematik, som browseren bruger.

I srgb giver gennemsnittet af kanalerne præcis 128, 128, 128: blandingen er #808080, en grå uden kroma. Oklab blander også langs en ret linje, og blå og gul ligger på hver sin side af den, så de stort set ophæver hinanden og bliver en mat, grålig blå. Oklch interpolerer i stedet farvetonens vinkel og tager den korte vej fra 264° til 110°, så midtpunktet bliver en klar blågrøn ved farvetone 187°, der bevarer kroma fra begge farver.

FarverumBlå + gulOKLCH L, CBlue-600 + yellow-400OKLCH L, C
srgb#8080800.600, 0#9098800.665, 0.035
oklab#6cabc70.710, 0.077#90a3ad0.703, 0.026
oklch#00bdb00.710, 0.262#00bd9e0.703, 0.194

Det andet par er Tailwinds #2563eb og #facc15. Resultaterne i oklch ligger uden for sRGB, så kroma vises før tilpasningen til farveomfanget, og HEX-koden er den nærmeste farve, en sRGB-skærm kan vise.

To ting springer i øjnene. Srgb lander på lyshed 0,600, mens det perceptuelle midtpunkt mellem blå (0,452) og gul (0,968) er 0,710: det er den mørke, mudrede midte i srgb-gradienter. Og kun oklch holder blandingen klar, men til gengæld passerer den en farvetone, som ingen af de to farver har.

Tommelfingerregel: bland i oklab til lyse og mørke nuancer og hover-tilstande, hvor du bliver på én farvetone og vil have jævne trin i lyshed. Bland i oklch, når begge farver er mættede, og du vil have et mættet resultat. Bland i srgb til gennemsigtighed, hvor det svarer til, hvordan browseren lægger lag oven på hinanden.

Opskrifter: hover, lyse nuancer og gennemsigtige varianter

Alle værdier nedenfor bruger #2563eb som --brand. Hvid tekst på den har et kontrastforhold på 5,17:1.

Med 10 % sort bliver farven mørkere i oklab end i srgb: hvid tekst stiger til 6,50:1 mod 6,11:1. Oklab og oklch giver det samme resultat, når den anden farve er sort eller hvid, fordi de ikke har nogen farvetone at interpolere.

OpskriftCSSResultatKontrast
Hover, 10 % mørkerecolor-mix(in oklab, var(--brand) 90%, black)#1f55cc6,50:1 med hvid tekst
Active, 20 % mørkerecolor-mix(in oklab, var(--brand) 80%, black)#1947ae8,22:1 med hvid tekst
Hover blandet i srgbcolor-mix(in srgb, var(--brand) 90%, black)#2159d36,11:1 med hvid tekst
Lys baggrund, 10 %color-mix(in oklab, var(--brand) 10%, white)#e8f0ff4,51:1 med tekst i brandfarven
Lys baggrund, 20 %color-mix(in oklab, var(--brand) 20%, white)#d2e2ff3,95:1 med tekst i brandfarven
Gennemsigtig, 20 %color-mix(in srgb, var(--brand) 20%, transparent)rgba(37, 99, 235, 0.2)Ligner #d3e0fb på hvid

Hold øje med de lyse nuancer. Tekst i brandfarven har 5,17:1 på hvid, 4,51:1 på en lys nuance med 10 % og kun 3,95:1 på en med 20 %, og det dumper WCAG AA for normal tekst. Sætter du tekst i brandfarven på en lys nuance af den, så brug hover-nuancen til teksten.

Blander du med transparent, bevares farven, og kun alfa sænkes, fordi browseren blander præmultiplicerede værdier. Resultatet tilpasser sig det, der ligger bagved: den samme regel ligner #d3e0fb på hvid og #132651 på en mørk flade i #0f172a. En lys nuance blandet med hvid forbliver bleg på en mørk side, så til komponenter, der vises i begge temaer, er den gennemsigtige version som regel det bedste valg.

Relativ farvesyntaks: ret én kanal

Relativ farvesyntaks tager en eksisterende farve, deler den op i kanaler og lader dig skrive hver af dem om med calc(). I oklch(from var(--brand) l c h) indeholder nøgleordene l, c og h lyshed (0 til 1), kroma og farvetone for --brand.

I modsætning til color-mix() ændrer den præcis én egenskab: sænk lysheden uden at røre kroma, drej farvetonen uden at ændre lysheden, eller giv en farve gemt som HEX en alfakanal.

OpskriftCSSResultat fra #2563ebHvid tekst
Mørkereoklch(from var(--brand) calc(l - 0.1) c h)#0442c88.02:1
Lysereoklch(from var(--brand) calc(l + 0.1) c h)#4484ff3.51:1
Halv kromaoklch(from var(--brand) l calc(c * 0.5) h)#4e6faf4.98:1
Farvetone +30°oklch(from var(--brand) l c calc(h + 30))#7a49df5.46:1
Komplementæroklch(from var(--brand) l c calc(h + 180))#9567004.98:1
Grå, samme lyshedoklch(from var(--brand) l 0 h)#7070704.95:1
50 % alfaoklch(from var(--brand) l c h / 0.5)rgba(37, 99, 235, 0.5)Afhænger af baggrunden

CSS

:root {
    --brand: #2563eb;
    --brand-hover: oklch(from var(--brand) calc(l - 0.08) c h);
    --brand-muted: oklch(from var(--brand) l calc(c * 0.5) h);
    --brand-ring: oklch(from var(--brand) l c h / 0.4);
    --accent: oklch(from var(--brand) l c calc(h + 30));
}

Se på de fire rækker, der beholder l: ændrer du kroma eller farvetone, flytter kontrasten med hvid tekst sig kun mellem 4,95:1 og 5,46:1 mod 5,17:1 for originalen. Det er lysheden, der styrer kontrasten, så farvetonevarianter bygget på denne måde bevarer omtrent samme tilgængelighed. Den lysere og den komplementære farve ligger uden for sRGB ved fuld kroma, og browseren tilpasser dem til den nærmeste farve, den kan vise.

color-mix() vs relative farver vs Sass darken()

Brug color-mix(), når du tænker i »lidt sort« eller »lidt gennemsigtighed«. Brug relativ farvesyntaks, når du tænker i kanaler: »samme farve, 0,1 mørkere«. Begge erstatter de fleste farvefunktioner i Sass og følger med, når temaet skifter, uden at du skal bygge igen.

color-mix()Relativ farvesyntaksSass darken()
KørerI browserenI browserenI byggetrinnet
Virker med var() og currentColorJaJaNej
FarvemodelDet farverum, du angiver: srgb, oklab, oklch, hsl og flereEnhver farvefunktion: oklch(), rgb(), hsl(), lab()Kun HSL
Bedst tilLyse og mørke nuancer, gennemsigtighed, blanding af to tokensÆndring af én kanal: lyshed, kroma, farvetone eller alfaGammel kode med faste farver
TrinstørrelseEn andel af afstanden til den anden farvePræcis, i kanalens egne enhederFaste HSL-point, ujævne for øjet
Understøttet sidenChrome 111, Safari 16.2, Firefox 113Chrome 119, Safari 18, Firefox 128Alle browsere, outputtet er statisk

Hvorfor Sass darken() giver ujævne trin

darken($color, 10%) trækker 10 point fra lysheden i HSL. Lyshed i HSL er ikke oplevet lyshed, så den synlige ændring afhænger af farvetonen. Her står darken(10%) ved siden af oklch(from … calc(l - 0.1) c h) for fire almindelige UI-farver, målt med ΔE OK, hvor omkring 0,02 er den mindste forskel, du kan se.

FarveSass darken(10%)ΔE OKOKLCH l − 0.1ΔE OK
#2563eb#134cca0.078#0442c80.099
#facc15#d7ad050.099#d9ab000.101
#16a34a#1076360.136#00842b0.094
#dc2626#b21d1d0.090#b700000.089

Det samme darken(10%) ændrer grøn 1,7 gange mere end blå. OKLCH-versionen holder sig mellem 0,089 og 0,101 for alle farvetoner. Den lille spredning skyldes tilpasningen til farveomfanget, fordi nogle af de mørkere farver falder lige uden for sRGB. Sass anbefaler selv color.adjust() eller color.scale() frem for darken(), og i ren CSS har du ikke brug for nogen af dem.

Browsersupport og fallbacks

color-mix() virker i Chrome og Edge 111, Safari 16.2 og Firefox 113, som alle udkom i 2023. Relativ farvesyntaks kom i Chrome og Edge 119 og Firefox 128. Safari fik en tidlig version i 16.4 og den nuværende syntaks i Safari 18. Alle aktuelle browsere understøtter begge dele.

Det sædvanlige trick med en almindelig værdi på linjen før virker ikke, når den nye værdi indeholder var(). En erklæring med var() bliver altid godkendt ved parsingen, så browseren smider din fallback-linje væk. Kan den ikke beregne color-mix(), falder den tilbage til den nedarvede eller den oprindelige værdi. Pak i stedet den moderne version ind i @supports.

CSS

.button:hover {
    background: #1f55cc;
}

@supports (color: color-mix(in oklab, red, blue)) {
    .button:hover {
        background: color-mix(in oklab, var(--brand) 90%, black);
    }
}

@supports (color: oklch(from red l c h)) {
    .badge {
        border-color: oklch(from var(--brand) l c h / 0.3);
    }
}

Spørgsmål og svar

Hvad gør color-mix() i CSS?

color-mix() blander to farver i et farverum, du angiver, og returnerer resultatet, fx color-mix(in oklab, var(--brand) 90%, black) for en farve, der er 10 % mørkere end brandfarven. Den kører i browseren, så den virker med CSS-variabler og currentColor.

Hvilket farverum skal jeg bruge i color-mix()?

Brug oklab til lyse og mørke nuancer og hover-tilstande, fordi det giver jævne trin i lyshed og bevarer farvetonen. Brug oklch, når du blander to mættede farver og vil have et mættet resultat, og srgb, når du blander med transparent. Blå og gul blandet i srgb giver en flad grå #808080, mens oklch giver en klar blågrøn.

Hvordan gør jeg en farve mørkere ved hover i ren CSS?

Brug background: color-mix(in oklab, var(--brand) 90%, black) eller background: oklch(from var(--brand) calc(l - 0.1) c h). For #2563eb giver den første #1f55cc og den anden #0442c8, og kontrasten med hvid tekst stiger fra 5,17:1 til 6,50:1 og 8,02:1.

Hvordan gør jeg en farve i en CSS-variabel gennemsigtig?

Skriv color-mix(in srgb, var(--brand) 20%, transparent) eller oklch(from var(--brand) l c h / 0.2). Begge virker, når --brand er gemt som HEX, og lader selve farven være uændret, så kun opaciteten sænkes.

Understøtter alle browsere color-mix()?

Ja, alle aktuelle browsere understøtter den: Chrome og Edge fra 111, Safari fra 16.2 og Firefox fra 113. Til ældre browsere sætter du en statisk farve først og color-mix()-versionen inde i @supports, fordi en linje med var() ikke kan falde tilbage af sig selv.

Hvad er forskellen på color-mix() og relativ farvesyntaks?

color-mix() blander to farver efter en procentdel, og det passer til lyse og mørke nuancer og gennemsigtighed. Relativ farvesyntaks som oklch(from var(--brand) l c calc(h + 30)) skriver enkelte kanaler i én farve om, og det passer til præcise trin i lyshed, skift i farvetone og alfa.

Prøv det i PickShade

Relaterede guides