تابع color-mix() و رنگ نسبی در CSS: هاور، نسخه روشنتر و شفافیت
تابع color-mix() دو رنگ را در فضای رنگی دلخواه شما ترکیب میکند و سینتکس رنگ نسبی اجازه میدهد یک کانال از رنگی موجود را تغییر دهید، مثلاً oklch(from var(--brand) calc(l - 0.1) c h). هر دو در مرورگر اجرا میشوند، با متغیرهای CSS کار میکنند و جای بیشتر توابع رنگ Sass را میگیرند. برای نسخههای روشنتر و هاور در oklab ترکیب کنید و برای شفافیت در srgb.
7 دقیقه مطالعه
تابع color-mix() چطور کار میکند
تابع color-mix() یک فضای رنگی و دو رنگ با درصدهای اختیاری میگیرد و رنگ میان آنها را برمیگرداند. عبارت color-mix(in oklab, var(--brand) 90%, black) یعنی 90% رنگ برند و 10% سیاه، ترکیبشده در Oklab. اگر فقط یک درصد بدهید، رنگ دیگر باقیمانده را میگیرد. اگر هیچ درصدی ندهید، سهم هرکدام 50% است.
مرورگر نتیجه را هنگام اجرا حساب میکند، پس با متغیرهای سفارشی (custom properties)، currentColor و تغییر تم کار میکند. توابع Sass فقط یک بار هنگام بیلد اجرا میشوند و مقدارهای var() را نمیبینند.
اگر جمع دو درصد کمتر از 100% باشد، نتیجه تا حدی شفاف هم میشود: color-mix(in srgb, red 30%, blue 30%) ترکیبی مساوی با کدری 60% است. جمع را روی 100% نگه دارید، مگر اینکه دقیقاً همین را بخواهید.
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);
}در کدام فضای رنگی ترکیب کنیم: srgb، oklab یا oklch
فضای رنگی بیش از آنچه بیشتر افراد انتظار دارند نتیجه را تغییر میدهد. در زیر، آبی خالص #0000ff به نسبت 50/50 با زرد خالص #ffff00 ترکیب شده است، با همان محاسباتی که مرورگر انجام میدهد.
در srgb میانگین کانالها دقیقاً 128، 128، 128 میشود: نتیجه #808080 است، خاکستریای با کرومای صفر. Oklab هم در امتداد یک خط راست ترکیب میکند و آبی و زرد در دو طرف مقابل آن قرار دارند، پس تا حد زیادی همدیگر را خنثی میکنند و به یک آبی خاکستری کدر میرسند. اما Oklch زاویه فام را درونیابی میکند و مسیر کوتاهتر از 264° به 110° را میرود؛ پس نقطه میانی یک سبزآبی زنده با فام 187° است که کرومای هر دو رنگ ورودی را حفظ میکند.
- آبی #0000ff#0000ff
- زرد #ffff00#ffff00
- در srgb: #808080#808080
- در oklab: #6cabc7#6cabc7
- در oklch: #00bdb0#00bdb0
| فضای رنگی | آبی + زرد | OKLCH L, C | blue-600 + yellow-400 | OKLCH L, C |
|---|---|---|---|---|
| srgb | #808080 | 0.600, 0 | #909880 | 0.665, 0.035 |
| oklab | #6cabc7 | 0.710, 0.077 | #90a3ad | 0.703, 0.026 |
| oklch | #00bdb0 | 0.710, 0.262 | #00bd9e | 0.703, 0.194 |
جفت دوم رنگهای #2563eb و #facc15 از Tailwind است. نتیجههای oklch بیرون از sRGB هستند؛ برای همین کروما پیش از نگاشت گاموت نشان داده شده و کد HEX نزدیکترین رنگی است که صفحه sRGB میتواند نشان دهد.
دو نکته جلب توجه میکند. srgb روی روشنایی 0.600 مینشیند، در حالی که نقطه میانی ادراکی بین آبی (0.452) و زرد (0.968) برابر 0.710 است: این همان میانه تیره و گلآلود گرادیانهای srgb است. و فقط oklch ترکیب را زنده نگه میدارد، به این قیمت که از فامی عبور میکند که هیچکدام از دو رنگ ورودی ندارند.
قاعده سرانگشتی: برای نسخههای روشنتر، تیرهتر و حالت هاور در oklab ترکیب کنید، جایی که روی یک فام میمانید و گامهای روشنایی یکنواخت میخواهید. وقتی هر دو رنگ پراشباعاند و نتیجه پراشباع میخواهید، در oklch ترکیب کنید. برای شفافیت در srgb ترکیب کنید، چون با روش مرورگر در روی هم گذاشتن لایهها یکی است.
نمونه کدها: هاور، نسخه روشنتر و نسخه شفاف
همه مقدارهای زیر #2563eb را بهعنوان --brand به کار میبرند. متن سفید روی آن نسبت کنتراست 5.17:1 دارد.
ترکیب با 10% سیاه در oklab بیشتر از srgb تیره میکند: کنتراست متن سفید به 6.50:1 میرسد، در برابر 6.11:1. وقتی رنگ دوم سیاه یا سفید باشد، oklab و oklch نتیجه یکسانی میدهند، چون این دو رنگ فامی برای درونیابی ندارند.
| نمونه | CSS | نتیجه | کنتراست |
|---|---|---|---|
| هاور، 10% تیرهتر | color-mix(in oklab, var(--brand) 90%, black) | #1f55cc | 6.50:1 با متن سفید |
| حالت فعال، 20% تیرهتر | color-mix(in oklab, var(--brand) 80%, black) | #1947ae | 8.22:1 با متن سفید |
| هاور با ترکیب در srgb | color-mix(in srgb, var(--brand) 90%, black) | #2159d3 | 6.11:1 با متن سفید |
| پسزمینه روشن، 10% | color-mix(in oklab, var(--brand) 10%, white) | #e8f0ff | 4.51:1 با متن به رنگ برند |
| پسزمینه روشن، 20% | color-mix(in oklab, var(--brand) 20%, white) | #d2e2ff | 3.95:1 با متن به رنگ برند |
| شفاف، 20% | color-mix(in srgb, var(--brand) 20%, transparent) | rgba(37, 99, 235, 0.2) | روی سفید شبیه #d3e0fb |
مراقب نسخههای روشن باشید. متن به رنگ برند روی سفید 5.17:1 است، روی نسخه روشن 10% برابر 4.51:1 و روی نسخه روشن 20% فقط 3.95:1، که برای متن معمولی در WCAG AA رد میشود. اگر متن به رنگ برند را روی پسزمینه روشن همان برند میگذارید، برای متن از رنگ هاور استفاده کنید.
ترکیب با transparent خود رنگ را نگه میدارد و فقط آلفا را کم میکند، چون مرورگر مقدارهای پیشضربشده (premultiplied) را ترکیب میکند. نتیجه با آنچه پشتش قرار دارد هماهنگ میشود: همان قاعده روی سفید شبیه #d3e0fb است و روی سطح تیره #0f172a شبیه #132651. نسخهای که با سفید ترکیب شده روی صفحه تیره همچنان رنگپریده میماند؛ پس برای کامپوننتهایی که در هر دو تم ظاهر میشوند، نسخه شفاف معمولاً انتخاب بهتری است.
سینتکس رنگ نسبی: تغییر یک کانال
سینتکس رنگ نسبی یک رنگ موجود را میگیرد، آن را به کانالهایش تجزیه میکند و اجازه میدهد هرکدام را با calc() بازنویسی کنید. در oklch(from var(--brand) l c h)، کلیدواژههای l، c و h روشنایی (0 تا 1)، کروما و فام --brand را در خود دارند.
برخلاف color-mix()، این روش دقیقاً یک ویژگی را تغییر میدهد: روشنایی را بدون دست زدن به کروما کم کنید، فام را بدون تغییر روشنایی بچرخانید یا به رنگی که بهصورت HEX ذخیره شده آلفا اضافه کنید.
| نمونه | CSS | نتیجه از #2563eb | متن سفید |
|---|---|---|---|
| تیرهتر | oklch(from var(--brand) calc(l - 0.1) c h) | #0442c8 | 8.02:1 |
| روشنتر | oklch(from var(--brand) calc(l + 0.1) c h) | #4484ff | 3.51:1 |
| نصف کروما | oklch(from var(--brand) l calc(c * 0.5) h) | #4e6faf | 4.98:1 |
| فام +30° | oklch(from var(--brand) l c calc(h + 30)) | #7a49df | 5.46:1 |
| مکمل | oklch(from var(--brand) l c calc(h + 180)) | #956700 | 4.98:1 |
| خاکستری، همان روشنایی | oklch(from var(--brand) l 0 h) | #707070 | 4.95:1 |
| آلفای 50% | oklch(from var(--brand) l c h / 0.5) | rgba(37, 99, 235, 0.5) | به پسزمینه بستگی دارد |
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));
}به چهار ردیفی نگاه کنید که l را نگه میدارند: تغییر کروما یا فام کنتراست متن سفید را فقط بین 4.95:1 و 5.46:1 جابهجا میکند، در برابر 5.17:1 برای رنگ اصلی. کنتراست را روشنایی تعیین میکند، پس نسخههایی با فام دیگر که اینطور ساخته میشوند تقریباً همان دسترسپذیری را حفظ میکنند. رنگهای روشنتر و مکمل با کرومای کامل بیرون از sRGB هستند و مرورگر آنها را به نزدیکترین رنگی که میتواند نشان دهد نگاشت میکند.
مقایسه color-mix()، رنگهای نسبی و darken() در Sass
وقتی به «کمی سیاه» یا «کمی شفافیت» فکر میکنید، از color-mix() استفاده کنید. وقتی به کانالها فکر میکنید، یعنی «همین رنگ، 0.1 تیرهتر»، از سینتکس رنگ نسبی استفاده کنید. هر دو جای بیشتر توابع رنگ Sass را میگیرند و بدون بیلد دوباره همراه با تغییر تم بهروز میشوند.
| color-mix() | سینتکس رنگ نسبی | darken() در Sass | |
|---|---|---|---|
| محل اجرا | در مرورگر | در مرورگر | هنگام بیلد |
| کار با var() و currentColor | بله | بله | خیر |
| مدل رنگ | فضایی که نام میبرید: srgb، oklab، oklch، hsl و غیره | هر تابع رنگی: oklch()، rgb()، hsl()، lab() | فقط HSL |
| مناسب برای | نسخه روشنتر و تیرهتر، شفافیت، ترکیب دو توکن | تغییر یک کانال: روشنایی، کروما، فام یا آلفا | کدهای قدیمی با رنگهای ثابت |
| اندازه گام | سهمی از فاصله تا رنگ دوم | دقیق، با واحد همان کانال | واحدهای ثابت HSL، ناهموار برای چشم |
| پشتیبانی از | Chrome 111، Safari 16.2، Firefox 113 | Chrome 119، Safari 18، Firefox 128 | همه مرورگرها، خروجی ایستاست |
چرا darken() در Sass گامهای ناهموار میدهد
darken($color, 10%) ده واحد از روشنایی HSL کم میکند. روشنایی HSL همان روشنایی ادراکی نیست، پس تغییری که دیده میشود به فام بستگی دارد. در جدول زیر darken(10%) در کنار oklch(from … calc(l - 0.1) c h) روی چهار رنگ رایج رابط کاربری آمده است، اندازهگیریشده با ΔE OK که در آن حدود 0.02 کوچکترین تفاوتی است که چشم میبیند.
| رنگ | Sass darken(10%) | ΔE OK | OKLCH l − 0.1 | ΔE OK |
|---|---|---|---|---|
| #2563eb | #134cca | 0.078 | #0442c8 | 0.099 |
| #facc15 | #d7ad05 | 0.099 | #d9ab00 | 0.101 |
| #16a34a | #107636 | 0.136 | #00842b | 0.094 |
| #dc2626 | #b21d1d | 0.090 | #b70000 | 0.089 |
همان darken(10%) سبز را 1.7 برابر بیشتر از آبی تغییر میدهد. نسخه OKLCH برای همه فامها بین 0.089 و 0.101 میماند؛ این پراکندگی کم از نگاشت گاموت میآید، چون برخی رنگهای تیرهتر کمی بیرون از sRGB میافتند. خود Sass هم بهجای darken() استفاده از color.adjust() یا color.scale() را توصیه میکند و در CSS خالص به هیچکدام نیازی ندارید.
پشتیبانی مرورگرها و مقدار جایگزین
تابع color-mix() در Chrome و Edge 111، Safari 16.2 و Firefox 113 کار میکند که همه در سال 2023 منتشر شدند. سینتکس رنگ نسبی در Chrome و Edge 119 و Firefox 128 آمد؛ Safari نسخه اولیه آن را در 16.4 و سینتکس فعلی را در Safari 18 عرضه کرد. همه مرورگرهای فعلی از هر دو پشتیبانی میکنند.
ترفند رایج، یعنی گذاشتن یک مقدار ساده در خط قبل، وقتی مقدار جدید شامل var() باشد کار نمیکند. اعلانی که var() دارد همیشه هنگام تجزیه پذیرفته میشود؛ پس مرورگر خط جایگزین شما را کنار میگذارد و اگر نتواند color-mix() را حساب کند، به مقدار ارثبرده یا اولیه برمیگردد. بهجای آن، نسخه مدرن را درون @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);
}
}پرسش و پاسخ
تابع color-mix() در CSS چه کاری انجام میدهد؟
تابع color-mix() دو رنگ را در فضای رنگیای که نام میبرید ترکیب میکند و نتیجه را برمیگرداند؛ مثلاً color-mix(in oklab, var(--brand) 90%, black) برای رنگی 10% تیرهتر از رنگ برند. این تابع در مرورگر اجرا میشود، پس با متغیرهای CSS و currentColor کار میکند.
در color-mix() از کدام فضای رنگی استفاده کنم؟
برای نسخههای روشنتر، تیرهتر و حالت هاور از oklab استفاده کنید، چون گامهای روشنایی یکنواخت میدهد و فام را نگه میدارد. وقتی دو رنگ پراشباع را ترکیب میکنید و نتیجه پراشباع میخواهید از oklch استفاده کنید، و برای ترکیب با transparent از srgb. ترکیب آبی و زرد در srgb خاکستری بیروح #808080 میدهد، در حالی که oklch یک سبزآبی زنده میدهد.
چطور در CSS خالص رنگ را هنگام هاور تیره کنم؟
از background: color-mix(in oklab, var(--brand) 90%, black) یا background: oklch(from var(--brand) calc(l - 0.1) c h) استفاده کنید. برای #2563eb اولی #1f55cc و دومی #0442c8 میدهد و کنتراست متن سفید را از 5.17:1 به ترتیب به 6.50:1 و 8.02:1 میرساند.
چطور رنگ یک متغیر CSS را شفاف کنم؟
بنویسید color-mix(in srgb, var(--brand) 20%, transparent) یا oklch(from var(--brand) l c h / 0.2). هر دو حتی وقتی --brand بهصورت HEX ذخیره شده باشد کار میکنند، خود رنگ را دستنخورده نگه میدارند و فقط کدری آن را کم میکنند.
آیا همه مرورگرها از color-mix() پشتیبانی میکنند؟
بله، همه مرورگرهای فعلی از آن پشتیبانی میکنند: Chrome و Edge از نسخه 111، Safari از 16.2 و Firefox از 113. برای مرورگرهای قدیمیتر، اول یک رنگ ثابت بگذارید و نسخه color-mix() را درون @supports قرار دهید، چون خطی که var() دارد بهتنهایی نمیتواند به مقدار جایگزین برگردد.
تفاوت color-mix() و سینتکس رنگ نسبی چیست؟
تابع color-mix() دو رنگ را با یک درصد ترکیب میکند که برای نسخههای روشنتر و تیرهتر و شفافیت مناسب است. سینتکس رنگ نسبی، مثل oklch(from var(--brand) l c calc(h + 30))، کانالهای جداگانه یک رنگ را بازنویسی میکند که برای گامهای دقیق روشنایی، جابهجایی فام و آلفا مناسب است.