תחביר צבע יחסי של CSS

יצירת צבעים חדשים על סמך הערכים והערוצים של צבע אחר.

Adam Argyle
Adam Argyle

ב-Chrome 119 יש תכונת צבע חזקה מאוד מ-CSS Color Level 5. תחביר צבעים יחסי יוצר דרך חלקה לטיפול בצבעים ב-CSS, ומאפשר לכותבים ולמעצבים:

לפני תחביר הצבע היחסי, כדי לשנות את השקיפות של צבע, צריך ליצור מאפיינים מותאמים אישית לערוצים של צבע, בדרך כלל HSL, ולחבר אותם לצבע סופי ולצבע סופי של וריאנט. המשמעות היא ניהול של הרבה פריטים צבעוניים, שיכולים להפוך במהירות לעול.

:root {
  --brand-hue: 300deg;
  --brand-saturation: 75%;
  --brand-lightness: 50%;

  --brand-hsl:
    var(--brand-hue)
    var(--brand-saturation)
    var(--brand-lightness);

  --brand-color: hsl(var(--brand-hsl));

  /* all this work just so I can set the opacity to 50% in a variant */
  --brand-color-variant: hsl(var(--brand-hsl) / 50%);
}

אחרי התחביר של צבע יחסי, אפשר ליצור צבע מותג בכל מרחב צבעים או תחביר שרוצים, וליצור וריאנט של חצי שקיפיות עם הרבה פחות קוד. בנוסף, קל יותר להבין את הכוונה של הסגנונות והמערכת.

:root {
  --brand-color: hsl(300deg 75% 50%);
  --brand-color-variant: hsl(from var(--brand-color) h s l / 50%);
}

הפוסט הזה יעזור לכם ללמוד את התחביר ולהציג מניפולציות נפוצות של צבעים.

אם אתם מעדיפים סרטון, כמעט כל המאמר הבא מוסבר בסרטון הזה בנושא ממשק משתמש.

סקירה כללית על התחביר

מטרת התחביר של צבע יחסי היא לאפשר להסיק צבע מצבע אחר. הצבע הבסיסי נקרא צבע המקור, והוא הצבע שמופיע אחרי מילת המפתח החדשה from. הדפדפן ימיר את הצבע המקורי ויפרק אותו לחלקים, ויציע את החלקים האלה כמשתנים לשימוש בהגדרת הצבע החדשה.

מוצגת תרשים של תחביר rgb(from green r g b / alpha), עם חץ שיוצא מהחלק העליון של 'ירוק' ומתעקל לתחילת הפונקציה rgb. החץ הזה מתפצל ל-4 חצים שמצביעים על המשתנה הרלוונטי שלהם. 4 החצים הם אדום, ירוק, כחול ואלפא. הערך של האדום והכחול הוא 0, הערך של הירוק הוא 128 והערך של האלפא הוא 100%.

בתרשים הקודם מוצג הצבע המקורי green שמומר למרחב הצבע של הצבע החדש, הופך למספרים נפרדים שמיוצגים כמשתנים r,‏ g,‏ b ו-alpha, שמשמשים ישירות כערכים של הצבע החדש rgb().

התמונה הזו מציגה את הפירוט, התהליך והמשתנים, אבל גם היא לא משנה את הצבע. המשתנים מוחזרים לצבע ללא שינוי, ולכן הצבע עדיין ירוק.

מילת המפתח from

החלק הראשון של התחביר שצריך ללמוד הוא החלק from <color> בנוסף לציון צבע. הוא מופיע ממש לפני שמציינים את הערכים. לפניכם דוגמה לקוד שבו כל מה שנוסף הוא from green, ממש לפני שמציינים את הערכים של rgb().

.syntax-introduction_same-colors {
  color: green;
  color: rgb(0 128 0);
  color: rgb(from green r g b);    /* result = rgb(0 128 0) */
}

כשמגדירים את מפתח המילים from כפרמטר הראשון בסימון פונקציונלי, הוא הופך את הגדרת הצבע לצבע יחסי. אחרי מילת המפתח from, המערכת של CSS מצפה לצבע, צבע שיהיה השראה לצבע הבא.

המרת צבעים

במילים פשוטות יותר, הוא ממיר את הירוק לערוצי r,‏ g ו-b לשימוש בצבע חדש.

rgb(from green r g b)           /* r=0 g=128 b=0 */
rgb(from rgb(0 128 0) r g b);   /* r=0 g=128 b=0 */

צבעים ממאפיינים מותאמים אישית

קריאת rgb from green ברורה מאוד וקלה להבנה. לכן, מאפיינים מותאמים אישית וסינטקס של צבע יחסי הם שילוב מצוין, כי אפשר להסיר את המסתורין מהצבע from. בדרך כלל גם לא צריך לדעת מהו פורמט הצבע של הצבע המותאם אישית של הנכס, כי אתם יוצרים צבע חדש בפורמט שבחרתם.

rgb(from rgb(255 105 180) r g b) /* ????? */
rgb(from var(--hotpink) r g b)   /* clear */

עבודה במרחב הצבעים המועדף

אפשר לבחור את מרחב הצבעים לפי הסימון הפונקציונלי של הצבעים.

rgb(from hsl(120 100% 25%) r g b)     /*  r=0   g=128  b=0    */
hsl(from hsl(120 100% 25%) h s l)     /*  h=120 s=100% l=25%  */
hwb(from hsl(120 100% 25%) h w b)     /*  h=120 w=0%   b=50%  */
lch(from hsl(120 100% 25%) l c h)     /*  l=46  c=68   h=134  */

לשפת הסינופסיס של הצבע היחסי יש את שלב ההמרה הזה. הצבע אחרי from מומר למרחב הצבע כפי שצוין בתחילת הצבע היחסי. אין צורך שהקלט והפלט יהיו זהים, וזה מאוד מרגיע.

היכולת לבחור מרחב צבעים היא גם כלי שימושי, כי הבחירה במרחב צבעים מתמקדת בדרך כלל בסוג החלפת הצבעים ולא בהעדפה. ההעדפה היא בתוצאות, ולא בפורמט הצבע או בסוגי הערוצים. הנושא הזה יתבהר הרבה יותר בקטעים שבהם נסביר על תרחישים לדוגמה, כי מרחבי צבעים שונים מתאימים למשימות שונות.

שילוב, התאמה, השמטה וחזרה על המשתנים

דבר מוזר אבל מרגש לגבי התחביר הזה: אין צורך להחזיר את המשתנים לסדר, ואפשר לחזור עליהם.

rgb(from green g g g)    /* rgb(128 128 128) */
rgb(from green b r g)    /* rgb(0 0 128) */
rgb(from green 0 0 g)    /* rgb(0 0 128) */

אטימות כמשתנה

התחביר מספק גם את השקיפות כמשתנה בשם alpha. הוא אופציונלי, ומופיע אחרי / בסימון הצבע הפונקציונלי.

rgb(from #00800080 r g b / alpha)             /* alpha=50% */
rgb(from rgba(0,128,0,.5) r g b / alpha)      /* alpha=50% */
rgb(from rgb(0 128 0 / 50%) r g b / alpha)    /* alpha=50% */

משתמשים ב-calc()‎ או בפונקציות CSS אחרות במשתנים

עד עכשיו יצרנו את הצבע ירוק שוב ושוב. ללמוד את התחביר ולהכיר את השלבים של ההמרה והפירוק למבנה. עכשיו הגיע הזמן לשנות את המשתנים ולשנות את הפלט כך שלא יהיה זהה לקלט.

green                              /*  h=120 s=100% l=25%  */
hsl(from green calc(h * 2) s l)    /*  h=240 s=100% l=25%  */

עכשיו הוא בצבע כחול כהה! הטון הוכפל, והטון 120 הפך ל-240, מה ששינה את הצבע לחלוטין. כך סובבנו את הטון לאורך גלגל הצבעים. זהו טריק נחמד שקל מאוד לבצע באמצעות מרחבי צבעים צילינדריים כמו HSL,‏ HWB,‏ LCH ו-OKLCH.

כדי לראות באופן חזותי את הערכים של הערוצים, וכך לחשב את החישובים בצורה מדויקת בלי לנחש או לזכור את המפרט, כדאי לנסות את הכלי הזה לערכים של ערוצים בסינטקס של צבע יחסי. הקוד הזה חושף את הערך של כל ערוץ על סמך התחביר שציינתם, וכך מאפשר לכם לדעת בדיוק אילו ערכים זמינים לכם.

בדיקת התמיכה בדפדפנים

@supports (color: rgb(from white r g b)) {
  /* safe to use relative color syntax */
}

תרחישים לדוגמה והדגמות

בדוגמאות ובתרחישי השימוש הבאים יש תחבירים חלופיים רבים להשגת תוצאות דומות או זהות. הווריאציות נובעות ממרחב הצבעים והערוצים שהם מציעים.

בנוסף, בדוגמאות רבות יוצגו התאמות צבע עם הביטויים by ו-to. שינוי הצבע by הוא שינוי צבע יחסי, שינוי שמשתמש בערך של המשתנה ומבצע התאמה על סמך הערך הנוכחי שלו. to הוא שינוי צבע מוחלט, כלומר שינוי שלא משתמש בערך המשתנה אלא מציין ערך חדש לגמרי.

כל הדגמות מופיעות באוסף הזה ב-Codepen.

הוספת בהירות לצבע

מרחב הצבעים OKLCH, ‏ OKLAB,‏ XYZ או sRGB מספקים את התוצאות הצפויות ביותר כשמבהירים צבעים.

הוספת בהירות

בדוגמה הבאה, הפונקציה .lighten-by-25 מקבלת את הצבע blue וממירה אותו ל-OKLCH. לאחר מכן היא מדליקה את הכחול על ידי הגדלת הערוץ l (בהירות) באמצעות הכפלת הערך הנוכחי ב-1.25. כך, הבהירות הכחולה מתקרבת ללבן ב-25%.

.lighten-by-25 {
  background: oklch(from blue calc(l * 1.25) c h);
}

הוספת בהירות לערך ספציפי

בדוגמה הבאה .lighten-to-75 לא נעשה שימוש בערוץ l כדי להבהיר את blue, אלא הערך מוחלף לחלוטין ב-75%.

.lighten-to-75 {
  background: oklch(from blue 75% c h);
}

הכהיית צבע

אותם מרחבי צבעים שמתאימים להבהרת צבע מתאימים גם להכהיית צבע.

הכהיית התמונה ב-amount

בדוגמה הבאה, הפונקציה .darken-by-25 ממירת את הצבע הכחול ל-OKLCH, ואז מכהימה את הכחול על ידי הפחתת הערך של הערוץ l (בהירות) ב-25%, על ידי הכפלת הערך ב-.75. כך הצבע הכחול יתקרב לשחור ב-25%.

.darken-by-25 {
  background: oklch(from blue calc(l * .75) c h);
}

הכהיית התמונה לערך מסוים

בדוגמה הבאה .darken-to-25 לא נעשה שימוש בערוץ l כדי להכהות את blue, אלא הערך מוחלף לחלוטין ב-25%.

.darken-to-25 {
  background: oklch(from blue 25% c h);
}

איך מרוויחים צבע

רוויה לפי סכום

בדוגמה הבאה .saturate-by-50 נעשה שימוש ב-s מ-hsl() כדי להגדיל את הצבע העז של orchid ב-50% יחסי.

.saturate-by-50 {
  background: hsl(from orchid h calc(s * 1.5) l);
}

רוויה לסכום ספציפי

בדוגמה הבאה .saturate-to-100 לא נעשה שימוש בערוץ s מ-hsl(), אלא מצוין ערך הרוויה הרצוי. בדוגמה הזו, רמת הרוויה מועלית ל-100%.

.saturate-to-100 {
  background: hsl(from orchid h 100% l);
}

הפחתת הרוויה של צבע

הפחתת הרוויה ב-amount

בדוגמה הבאה .desaturate-by-half משתמש ב-s מ-hsl() כדי להפחית את הרוויה של indigo בחצי.

.desaturate-by-half {
  background: hsl(from indigo h calc(s / 2) l);
}

הפחתת הרוויה לערך ספציפי

במקום לבצע דילול צבע לפי כמות מסוימת, אפשר לבצע דילול צבע לפי ערך רצוי ספציפי. בדוגמה הבאה .desaturate-to-25 יוצר צבע חדש על סמך indigo, אבל מגדיר את הרוויה ל-25%.

.desaturate-to-25 {
  background: hsl(from indigo h 25% l);
}

הגברת הצבע באמצעות כרומה

האפקט הזה דומה להוספת רוויה לצבע, אבל יש כמה הבדלים ביניהם. ראשית, זהו שינוי chroma ולא שינוי saturation, כי במרחבי הצבעים שאפשר לשפר לטווח דינמי גבוה לא נעשה שימוש ברוויה. מרחבי הצבעים שכוללים את chroma יכולים לתמוך בטווח דינמי גבוה, ומאפשרים ליוצרים לשפר את עוצמת הצבעים הרבה יותר ממה שאפשר לעשות באמצעות רוויה.

.increase-chroma {
  background: oklch(from orange l calc(c + .1) h);
}

שינוי השקיפות של צבע

יצירת וריאנט שקוף למחצה של צבע היא אחת מההתאמות הנפוצות ביותר של צבעים שמתבצעות במערכות עיצוב. אם לא ראיתם את הדוגמה במבוא למאמר, כדאי לעיין בה. היא מתארת בצורה טובה מאוד את מרחב הבעיה.

שינוי השקיפות לפי סכום מסוים

.decrease-opacity-by-25 {
  background: rgb(from lime r g b / calc(alpha / 2));
}

שינוי השקיפות לערך ספציפי

.decrease-opacity-to-25 {
  background: rgb(from lime r g b / 25%);
}

היפוך צבע

היפוך צבעים הוא פונקציה נפוצה של התאמת צבעים שקיימת בספריות צבעים. אחת מהדרכים לעשות זאת היא להמיר צבע ל-RGB ואז לחסר את הערך של כל ערוץ מ-1.

.invert-each-rgb-channel {
  background: rgb(from yellow calc(255 - r) calc(255 - g) calc(255 - b));
}

צבעים משלימים

אם המטרה שלכם לא הייתה להפוך את הצבע, אלא להשלים אותו, סביר להניח שסיבוב הטון הוא מה שאתם מחפשים. בוחרים מרחב צבעים שבו הגוון מוצג כזווית, ואז משתמשים ב-calc() כדי לסובב את הגוון בזווית הרצויה. כדי למצוא את הצבע המשלים של צבע מסוים, מסובבים אותו בחצי סיבוב. במקרה הזה, אפשר להוסיף לערוץ h את הערך 180 או לחסר ממנו את הערך 180 כדי להגיע לתוצאה הרצויה.

.complementary-color {
  background: hsl(from blue calc(h + 180) s l);
}

ניגודיות של צבע

כדי להגיע ליחסי ניגודיות צבעים נגישים, מומלץ להשתמש ב-L&midast;‏ (Lstar). לשם כך נעשה שימוש בערוץ הבהירות (L) (כמעט) אחיד בחוויית הצפייה מ-LCH ומ-OKLCH, ב-calc(). בהתאם לבחירתכם לטירגוט של ניגודיות נמוכה, בינונית או גבוהה, הערך של דלתא L&middot; הוא בערך 40, 50 או 60.

השיטה הזו פועלת היטב בכל גוון ב-LCH או ב-OKLCH.

ניגודיות לצבע כהה יותר

בכיתה .well-contrasting-darker-color מוצגת פונקציית L* עם דלתא של 60. מכיוון שהצבע המקורי הוא צבע כהה (ערך נמוך של בהירות), 60% (‎.6) מתווספים לערוץ הבהירות. הטכניקה הזו משמשת כדי למצוא צבע טקסט כהה עם ניגודיות טובה, באותו גוון, על רקע בהיר.

.well-contrasting-darker-color {
  background: darkred;
  color: oklch(from darkred calc(l + .60) c h);
}

ניגודיות לצבע בהיר יותר

גם בכיתה .well-contrasting-lighter-color מוצג L* עם דלתא של 60%. מכיוון שהצבע המקורי הוא צבע בהיר (ערך גבוה של בהירות), מוצגת 0.60 פחות בערוץ הבהירות.

.well-contrasting-lighter-color {
  background: lightpink;
  color: oklch(from lightpink calc(l - .60) c h);
}

לוחות צבעים

תחביר צבעים יחסי מתאים מאוד ליצירת לוחות צבעים. הוא שימושי במיוחד ובעל יכולות חזקות במיוחד בגלל מספר מרחב הצבעים הזמינים. בכל הדוגמאות הבאות נעשה שימוש ב-OKLCH כי ערוץ הבהירות הוא אמין וניתן לסובב את ערוץ הגוון ללא תופעות לוואי. בדוגמה האחרונה מוצג שילוב של התאמות של בהירות וכיוון גוון כדי לקבל תוצאה מעניינת יותר.

פותחים את קוד המקור לדוגמה שלהן ומנסים לשנות את הערך של --base-color כדי לראות עד כמה הפלטה הזו דינמית. זה כיף!

אם אתם מעדיפים סרטונים, יש לי סרטון ב-YouTube עם מידע מפורט על יצירת לוחות צבעים ב-CSS באמצעות OKLCH.

לוחות צבעים מונוכרומטיים

לוח צבעים מונוכרומטי הוא לוח צבעים שכל הצבעים בו הם באותו גוון, אבל יש בו וריאציות של בהירות וחושך. הצבע האמצעי הוא צבע המקור של לוח הצבעים, ושני צבעים בהירים ושני צבעים כהים יותר ממוקמים משני צידיו.

:root {
  --base-color: deeppink;

  --color-0: oklch(from var(--base-color) calc(l + .20) c h); /* lightest */
  --color-1: oklch(from var(--base-color) calc(l + .10) c h);
  --color-2: var(--base-color);
  --color-3: oklch(from var(--base-color) calc(l - .10) c h);
  --color-4: oklch(from var(--base-color) calc(l - .20) c h); /* darkest */
}
אפשר לנסות כמה פלטות צבעים שנוצרו באמצעות תחביר צבעים יחסי ו-OKLCH

Open Props היא ספרייה של משתני CSS בחינם, שמציעה לוחות צבעים שנוצרו באמצעות האסטרטגיה הזו ומאפשרת להשתמש בהם בקלות באמצעות ייבוא. בנוסף, כולם מבוססים על צבע שאפשר להתאים אישית. פשוט נותנים לו צבע והוא יוצר לוח צבעים.

פלטות אנלוגיות

מכיוון שקל מאוד לבצע רוטציה של הגוון באמצעות OKLCH ו-HSL, קל מאוד ליצור לוח צבעים אנלוגי. תוכלו לסובב את הטון עד שתקבלו את התוצאה הרצויה, לשנות את צבע הבסיס ולראות איך הדפדפן יוצר לוחות צבעים חדשים.

:root {
  --base-color: blue;

  --primary:   var(--base-color);
  --secondary: oklch(from var(--base-color) l c calc(h - 45));
  --tertiary:  oklch(from var(--base-color) l c calc(h + 45));
}

פלטות צבעים משולש

בדומה לצבעים משלימים, לוחות צבעים טריאדיים הם רוטציות של גוונים מנוגדים אך הרמוניים, שמבוססות על צבע בסיס. בעוד שצבע משלים נמצא בצד הנגדי של צבע מסוים, כמו קו ישר שמצויר באמצע גלגל הצבעים, לוחות צבעים טריאדיים הם כמו משולש של קווים, שמוצאים 2 צבעים שמסתובבים באותה מידה סביב צבע בסיס. כדי לעשות זאת, מסובבים את הגוון 120deg.

זוהי פישוט קל של תורת הצבעים, אבל זה מספיק כדי להתחיל להשתמש בפלטה טריאדית מורכבת יותר, אם זה מעניין אתכם.

:root {
  --base-color: yellow;
  --triad-1: oklch(from var(--base-color) l c calc(h - 120));
  --triad-2: oklch(from var(--base-color) l c calc(h + 120));
}

פלטות מרובעניות

לוחות צבעים מרובעניים מורכבים מארבעה צבעים שמחולקים באופן שווה סביב גלגל הצבעים, וכך יוצרים לוח צבעים ללא ערך דומיננטי ברור. אפשר גם לחשוב על זה בתור שני זוגות של צבעים משלימים. אם משתמשים בה בצורה חכמה, היא יכולה להיות משמעותית מאוד.

זוהי פישוט קל של תורת הצבעים, אבל זה מספיק כדי להתחיל להשתמש בפלטה הטטראדית המורכבת יותר, אם זה מעניין אתכם.

:root {
  --base-color: lime;

  --color-1: var(--base-color);
  --color-2: oklch(from var(--base-color) l c calc(h + 90));
  --color-3: oklch(from var(--base-color) l c calc(h + 180));
  --color-4: oklch(from var(--base-color) l c calc(h + 270));
}

מונוכרומטי עם תנועה קלה של הגוון

מומחים רבים בתחום הצבעים משתמשים בטריק הזה. הבעיה היא שסולם צבעים מונוכרומטי יכול להיות משעמם למדי. הפתרון הוא להוסיף לכל צבע חדש שינויי גוון קלים או משמעותיים כשמשנים את הבהירות.

בדוגמה הבאה, הבהירות של כל ריבוע צבע פוחתת ב-10%, וגם הטון מסתובב ב-10 מעלות. התוצאה היא לוח צבעים ורוד כהה עד אינדיגו שנראה מעורבב בצורה חלקה כמו שיוצרים בגרדינט.

:root {
  --base-color: deeppink;

  --color-1: var(--base-color);
  --color-2: oklch(from var(--base-color) calc(l - .10) c calc(h - 10));
  --color-3: oklch(from var(--base-color) calc(l - .20) c calc(h - 20));
  --color-4: oklch(from var(--base-color) calc(l - .30) c calc(h - 30));
  --color-5: oklch(from var(--base-color) calc(l - .40) c calc(h - 40));
}
כדאי לנסות את לידרבורד הזה שנוצר באמצעות OKLCH וסיבוב גוון

בממשק של לוח הבקרה הבא נעשה שימוש באסטרטגיית רוטציית הגוונים הזו. כל פריט ברשימה עוקב אחרי המדד שלו במסמך כמשתנה שנקרא --i. לאחר מכן, האינדקס הזה משמש לכוונון הצבע, הבהירות והגוון. ההתאמה היא רק של 5% או 5deg, הרבה יותר עדינה מהדוגמה שלמעלה עם deeppink, ולכן צריך עין חדה כדי להבחין בסיבה לכך שאפשר להציג את לוח הבקרה הזה בכל גוון בצורה כל כך אלגנטית.

כדאי לשנות את הגוון בפס ההזזה שמתחת ללוח הדירוג, ולראות איך התחביר היחסי של הצבעים יוצר רגעים יפהפיים של צבעים.

li {
  --_bg: oklch(
    /* decrease lightness as list grows */
    calc(75% - (var(--i) * 5%))

    /* decrease chroma as list grows */
    calc(.2 - (var(--i) * .01))

    /* lightly rotate the hue as the list grows */
    calc(var(--hue) - (var(--i) + 5))
  );
}