توابع رنگ مدرن؛ فراتر از hex و rgb

تو فصل ۷ که درباره‌ی رنگ‌ها صحبت کردیم، با hex، rgb و hsl آشنا شدیم؛ همون‌هایی که سال‌هاست همه ازشون استفاده می‌کنن. اما CSS این اواخر یه سری تابع رنگ جدید و قدرتمندتر معرفی کرده که مشکلات قدیمی رنگ‌های سنتی رو حل می‌کنن. تو این درس می‌خوایم با این توابع جدید و یه ابزار خیلی کاربردی به اسم color-mix() آشنا بشیم.

مشکل rgb و hsl چیه؟

مشکل اصلی اینه که فضای رنگی rgb و hsl، «درک انسانی» از روشنایی رو در نظر نمی‌گیرن. مثلاً اگه دو رنگ مختلف با همون مقدار lightness تو hsl بسازید (مثلاً یه زرد و یه آبی)، احتمالاً یکیشون به چشم شما خیلی روشن‌تر از اون یکی به نظر می‌رسه، با این‌که از نظر عددی لایتنس یکسانی دارن. این یعنی این فضاهای رنگی، «یکنواخت ادراکی» (perceptually uniform) نیستن.

oklch: فضای رنگی که به چشم انسان نزدیک‌تره

oklch() یکی از مهم‌ترین توابع رنگ جدیده. سه مقدار می‌گیره: Lightness (روشنایی)، Chroma (اشباع رنگ) و Hue (فام):

.box {
  background-color: oklch(70% 0.15 250);
}
پارامترمحدودهتوضیح
Lightness0% تا 100%روشنایی؛ صفر یعنی مشکی کامل
Chromaمعمولاً 0 تا حدود 0.4میزان اشباع رنگ؛ صفر یعنی خاکستری
Hue0 تا 360زاویه‌ی فام، مثل hsl

فرقش با hsl اینه که اگه دو رنگ مختلف تو oklch همون مقدار Lightness رو داشته باشن، واقعاً به چشم هم‌روشنایی به نظر می‌رسن؛ برخلاف hsl که این تضمین رو نمی‌ده. همین ویژگی باعث می‌شه ساختن پالت رنگی (مثلاً چند سایه از یه رنگ، از تیره تا روشن) خیلی قابل‌پیش‌بینی‌تر بشه:

:root {
  --blue-100: oklch(95% 0.02 250);
  --blue-300: oklch(80% 0.08 250);
  --blue-500: oklch(60% 0.15 250);
  --blue-700: oklch(40% 0.15 250);
  --blue-900: oklch(25% 0.1 250);
}

چون فقط عدد Lightness رو تغییر دادیم و Chroma و Hue ثابت موندن، این پنج رنگ واقعاً یه طیف یکنواخت و منطقی از تیره به روشن می‌سازن.

lab و lch: خویشاوندهای oklch

قبل از oklch، دو تابع مشابه دیگه هم بودن: lab() و lch(). این‌ها هم مبتنی بر همون فضای رنگی نزدیک به ادراک انسانی هستن:

.box-1 {
  color: lab(52% 40 59);
}

.box-2 {
  color: lch(52% 72 33);
}

lch دقیقاً مثل oklch از Lightness، Chroma و Hue استفاده می‌کنه، ولی محاسبات پایه‌اش کمی متفاوته و oklch نسخه‌ی بهبودیافته و جدیدتریه؛ به همین دلیل در پروژه‌های جدید معمولاً oklch ترجیح داده می‌شه.

color-mix(): ترکیب دو رنگ با هم

یکی از کاربردی‌ترین توابع جدید، color-mix() هست. این تابع دو رنگ رو با یه نسبت مشخص با هم قاطی می‌کنه، دقیقاً مثل مخلوط کردن رنگ رو بوم نقاشی:

.box {
  background-color: color-mix(in oklch, blue 70%, white);
}

این کد یعنی «۷۰٪ آبی رو با ۳۰٪ سفید (چون مجموع باید ۱۰۰٪ بشه) قاطی کن». پارامتر in oklch مشخص می‌کنه ترکیب تو کدوم فضای رنگی انجام بشه؛ می‌تونید srgb، hsl و چند فضای دیگه رو هم به‌جاش بذارید، ولی نتیجه‌ی بصری بسته به فضای انتخابی کمی فرق می‌کنه.

یه کاربرد خیلی رایج color-mix()، ساختن حالت‌های hover یا disabled بدون نیاز به تعریف یه رنگ جدید دستی:

.btn {
  background: var(--brand-color);
}

.btn:hover {
  background: color-mix(in oklch, var(--brand-color) 85%, black);
}

.btn:disabled {
  background: color-mix(in oklch, var(--brand-color) 40%, white);
}

این‌جوری، اگه بعداً مقدار --brand-color عوض بشه، همه‌ی حالت‌های hover و disabled هم خودکار متناسب باهاش تغییر می‌کنن، بدون این‌که مجبور باشید دستی یه رنگ جدید محاسبه کنید.

چرا این توابع، رنگ‌های وسیع‌تری (Wide Gamut) رو هم پشتیبانی می‌کنن؟

یه فرق مهم دیگه بین این توابع جدید و rgb/hex اینه که فضای رنگی rgb سنتی محدود به چیزیه که بهش sRGB می‌گن، در حالی که oklch و lab می‌تونن رنگ‌هایی رو نمایش بدن که نمایشگرهای مدرن‌تر (مثل نمایشگرهایی با پشتیبانی P3) قادر به نمایششون هستن ولی sRGB اصلاً نمی‌تونه بازشون تولید کنه. یعنی با این توابع جدید، دسترسی به یه طیف رنگی وسیع‌تر و پررنگ‌تر هم باز می‌شه.

نکته مهم: اگه مرورگری oklch یا lab رو پشتیبانی نکنه، کل قانون CSS نادیده گرفته می‌شه، نه این‌که یه رنگ پیش‌فرض جایگزین بشه. برای پشتیبانی از مرورگرهای قدیمی‌تر، معمولاً یه مقدار rgb یا hex رو قبل از مقدار جدید می‌نویسن تا مرورگرهای قدیمی حداقل یه رنگ نزدیک داشته باشن:
.box {
  background-color: #4a7dfc; /* fallback */
  background-color: oklch(60% 0.15 260);
}

پشتیبانی مرورگرها

oklch و color-mix تو مرورگرهای مدرن (Chrome، Firefox، Safari، Edge نسخه‌های اخیر) پشتیبانی می‌شن. با این حال، همیشه قبل از استفاده‌ی جدی تو یه پروژه‌ی production، caniuse رو چک کنید و فراموش نکنید که fallback مناسب بذارید.

اشتباه رایج: فراموش کردن این‌که Chroma تو oklch سقف ثابتی نداره

خیلی‌ها فکر می‌کنن Chroma هم مثل درصد بین ۰ تا ۱۰۰ حرکت می‌کنه، در حالی که این پارامتر یه عدد مطلقه (نه درصد) و بسته به فام و روشنایی، حداکثر مقدار قابل‌نمایشش فرق می‌کنه. عددهای خیلی بزرگ (مثلاً بالای ۰.۴) معمولاً باعث می‌شن مرورگر رنگ رو به نزدیک‌ترین مقدار قابل‌نمایش «کلمپ» کنه، نه این‌که خطا بده.

جمع‌بندی

توابع رنگ مدرن مثل oklch() و lch() برخلاف hsl، یکنواختی ادراکی دارن؛ یعنی تغییر Lightness واقعاً به چشم هم‌روشنایی به نظر می‌رسه، که ساختن پالت‌های رنگی رو خیلی قابل‌پیش‌بینی‌تر می‌کنه. color-mix() هم اجازه می‌ده دو رنگ رو با هر نسبتی ترکیب کنید، بدون محاسبه‌ی دستی؛ کاربرد فوق‌العاده‌ای برای ساختن حالت‌های hover و disabled به‌صورت خودکار داره. فقط حواستون به fallback برای مرورگرهای قدیمی‌تر باشه.