תחביר צבע יחסי של 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 של color changed הוא שינוי צבע מוחלט, כלומר שינוי שלא משתמש בערך המשתנה אלא מציין ערך חדש לגמרי.

כל הדגמות מופיעות באוסף הזה ב-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);
}

כהה צבע

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

כהה במידה מסוימת

בדוגמה הבאה, הפונקציה .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 הגברת צבע

האפקט הזה דומה להוספת רוויה לצבע, אבל יש כמה הבדלים ביניהם. ראשית, זהו שינוי 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, כדי לראות עד כמה לוחות הצבעים האלה דינמיים. זה כיף!

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

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

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

: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))
  );
}