ממיר צבעים ובודק ניגודיות

המירו בין HEX, ‏RGB, ‏HSL, ‏OKLCH ושמות צבע של CSS, ובדקו את יחס הניגודיות לפי WCAG בין שני צבעים.

קדמי
  • HEX#3b82f6
  • RGBrgb(59, 130, 246)
  • HSLhsl(217, 91.2%, 59.8%)
  • OKLCHoklch(62.31% 0.188 259.81)
  • שםאין שם CSS מדויק
רקע
  • HEX#ffffff
  • RGBrgb(255, 255, 255)
  • HSLhsl(0, 0%, 100%)
  • OKLCHoklch(100% 0 0)
  • שםwhite
ניגודיות
3.68:1יחס ניגודיות
טקסט גוף
מתחת ל־18pt, או מתחת ל־14pt מודגש
AAAAA
טקסט גדול
‏18pt ומעלה, או 14pt מודגש ומעלה
AAAAA
רכיבי ממשק וגרפיקה
מסגרות, סמלים וחלקים אחרים שאינם טקסט
AA

טקסט גוף בגודל הזה הוא מה שהסף 4.5:1 נכתב עבורו.

לטקסט גדול נדרש רק 3:1

מה הכלי הזה עושה

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

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

חמשת הכתיבים

כל אחד מהם אפשר להדביק, וכולם חוזרים בפלט:

  • הקסדצימלי — שלוש, ארבע, שש או שמונה ספרות. הצורות הקצרות מכפילות כל ספרה, ולכן ‎#f0a פירושו ‎#ff00aa. ארבע ושמונה ספרות נושאות שקיפות במקום האחרון.
  • RGB — שלושת הערוצים כמספרים או כאחוזים. גם rgb(59, 130, 246) וגם rgb(59 130 246 / 50%) מתקבלים; צורת הפסיקים היא מה שהכלי כותב בחזרה, כי היא עדיין מה שרוב הקוד סביבכם משתמש בו.
  • HSL — גוון במעלות, ואחריו רוויה ובהירות באחוזים. הגוונים מתגלגלים, ולכן 480 ו־120- תקינים ומשמעותם 120 ו־240.
  • OKLCH — בהירות, כרומה וגוון במרחב אחיד תפיסתית. את הבהירות אפשר לכתוב 62.8% או 0.628.
  • שמות CSS — כל 148 מהם, כולל rebeccapurple ושני האיותים של כל אפור. שם מוצג בפלט רק כשההתאמה מדויקת.

מוזרות אחת ששווה להכיר: הקסדצימלי, RGB ו־OKLCH כולם שורדים המרה הלוך ושוב בלי שינוי, אבל HSL לא לגמרי. הוא מודפס ברזולוציה שאנשים באמת קוראים — מעלות שלמות, עשירית אחוז — וזה לא מספיק כדי לייצג את כל 16.7 מיליון צבעי sRGB. המירו הקסדצימלי ל־HSL וחזרה וערוץ אחד עלול לזוז בצעד. החלופה הייתה פלט כמו hsl(217.22, 91.19%, 59.8%), שאיש אינו רוצה להדביק. השגיאה נמוכה בהרבה ממה שהעין מבחינה בו.

למה OKLCH קיים

נראה ש־HSL מתאר בהירות, והוא לא. מספר הבהירות שלו הוא תכונה של המספרים ולא של האור. צהוב טהור וכחול טהור הם שניהם 50% בהירות ב־HSL:

#ffff00   hsl 50%   oklch 96.8%   צהוב
#0000ff   hsl 50%   oklch 45.2%   כחול

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

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

כשצבע אינו ניתן להצגה

ב־OKLCH אפשר לתאר יותר צבעים ממה שמסך רגיל מסוגל לייצר. בקשו בהירות 70% בכרומה 0.4 וקיבלתם ירוק שקיים בעיקרון ובשום מקום ב־sRGB. הכלי מצמיד צבע כזה לתחום ומסמן אותו כמחוץ ל־sRGB, במקום להחזיר בשקט משהו אחר.

האזהרה מופיעה רק כשההצמדה באמת משנה את הצבע שמתקבל. ההבחנה הזו חשובה יותר משהיא נשמעת. אדום sRGB יושב בדיוק על גבול ה־gamut, ולכן ערך OKLCH מפורסם עבורו, מעוגל לארבע ספרות, נופל טכנית מעט מחוצה לו — בערך שני חלקים מעשרת אלפים, שנוחתים על אותו בייט ואינם נראים. אזהרה על כך הייתה מאלפת אתכם להתעלם מהאזהרה. צבע מפלטה רחבת־gamut מחטיא בצעדים שלמים: ‏blue-500 של Tailwind v4 מבקש 261 מתוך 255 אפשריים בערוץ הכחול, ועל מסך sRGB הוא באמת אינו הצבע שהתבקש.

ניגודיות ושלושת הסִפִּים

יחס הניגודיות משווה את הבהירות היחסית של שני צבעים. הוא נע מ־1:1 לשני צבעים זהים עד 21:1 לשחור על לבן, ושום דבר אינו יכול לעבור את זה. תקן WCAG 2.1 קובע שלושה סִפִּים, והם אינם ניתנים להחלפה זה בזה:

  • 4.5:1 לטקסט גוף — כל דבר מתחת ל־18pt, או מתחת ל־14pt כשהוא מודגש. זה הסף שאנשים מתכוונים אליו כשהם אומרים שצבע «נכשל בניגודיות».
  • 3:1 לטקסט גדול — 18pt ומעלה, או 14pt מודגש ומעלה. אותיות גדולות נושאות יותר מהמסר בעצמן, ולכן הרף נמוך יותר.
  • 3:1 לרכיבי ממשק וגרפיקה — המסגרת של שדה קלט, החץ ברשימה נפתחת, העמודות בתרשים. נוסף ב־WCAG 2.1 ומוחמץ באופן נרחב, מפני שאנשים בודקים את הטקסט שלהם ולעולם לא את התיבה סביבו.

רמת AAA מעלה את שני הראשונים ל־7:1 ול־4.5:1. שווה לשאוף אליה בטקסט גוף וקשה באמת להגיע אליה בפלטת מותג; רמת AA היא מה שרגולציה וביקורות נגישות מבקשות.

גדלי הנקודה בכללים האלה הם נקודות CSS ולא פיקסלים: ‏18pt הם 24px ו־14pt הם כ־18.7px בהגדרות ברירת המחדל. קל להניח שכותרת בגודל 20px נחשבת טקסט גדול. היא אינה.

צבעים שקופים למחצה

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

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

מה היחס אינו אומר לכם

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

  • עיוורון צבעים. אדום וירוק בעלי בהירות זהה הם ביחס ניגודיות קרוב ל־1:1 ובלתי ניתנים להבחנה עבור כולם — אבל שני צבעים יכולים לעבור את היחס ועדיין להיות הדבר היחיד שמפריד בין שני מצבים, וזה נכשל עבור כל מי שאינו מבחין ביניהם. לעולם אל תישאו משמעות בגוון בלבד.
  • גופנים דקים. הנוסחה אינה יודעת דבר על עובי הקו. משקל דק ביחס 4.5:1 יכול להיות קשה יותר לקריאה ממשקל רגיל ביחס 4:1.
  • הנוסחה עצמה. ידוע שניגודיות WCAG 2 אינה אדיבה לטקסט בהיר על רקע כהה ומדווחת יחסים שמרגישים גבוהים ממה שהם נקראים. ‏APCA, שנוסח עבור WCAG 3, מדגמן זאת טוב יותר — אבל הוא אינו נורמטיבי, אף ביקורת אינה מקבלת אותו עדיין, והצגת שני ציונים סותרים לא הייתה עוזרת לאיש. לכן הוא נדון כאן ואינו מודפס למעלה.

הערת מימוש אחת למי שמשווה תוצאות מול קוד משלו: נוסחת הבהירות משתמשת בסף 0.03928, השונה מעט מ־0.04045 שבהגדרת sRGB הנוכחית. תקן WCAG מצטט את הערך הישן, ולכן זה הערך שבשימוש כאן. התאמה למפרט חשובה יותר מסדר פנימי, וההפרש לעולם אינו הופך «עובר» ל«נכשל».

שאלות נפוצות

למה הצבע ההקסדצימלי שלי משתנה אחרי המרה ל־HSL וחזרה?
מפני ש־HSL מודפס ברזולוציה שאנשים יכולים לקרוא ולהקליד מחדש, ואינה יכולה לנקוב בכל צבע sRGB. זז צעד אחד לכל היותר לערוץ, וזה בלתי נראה. הקסדצימלי, RGB ו־OKLCH כולם הפיכים במדויק, אז השתמשו באחד מהם אם אתם צריכים שלב ביניים ללא אובדן.
מה משמעות «מחוץ ל־sRGB»?
ערך ה־OKLCH שהזנתם נוקב בצבע שהמסך שלכם אינו יכול לייצר, ולכן הוא הוצמד לקרוב ביותר שהוא כן יכול. ערכים שנלקחו מפלטות רחבות־gamut כמו Tailwind v4 עושים זאת דרך קבע. האזהרה מופיעה רק כשההצמדה משנה את הצבע המוצג, כך שערך מעוגל שעדיין נוחת על אותו בייט לא יעורר אותה.
הטקסט שלי עובר AA. האם הוא נגיש?
הוא עובר רף מסוים אחד. היחס אינו אומר דבר על השאלה אם הצבע הוא הדבר היחיד שמבחין בין שני מצבים, על עובי הקו, או על איך הצמד נקרא למי שראייתו לקויה. התייחסו למעבר כאל תחילת הבדיקה ולא כאל סופה.
למה הכותרת שלי בגודל 20px אינה טקסט גדול?
הכלל כתוב בנקודות ולא בפיקסלים: ‏18pt הם 24px, ו־14pt מודגש הם כ־18.7px. כותרת רגילה בגודל 20px נמצאת מתחת לקו ונמדדת מול 4.5:1 כמו כל טקסט גוף אחר.
אפשר לבדוק ניגודיות עבור רקע שקוף למחצה?
לא משני צבעים בלבד — התשובה תלויה במה שנמצא מאחוריו, והעמוד אינו יודע זאת. חשבו את הצבע שאליו הרקע מורכב והזינו אותו. צבע קדמי שקוף למחצה הוא בסדר גמור ומטופל נכון: הוא מורכב מעל הרקע לפני המדידה, בדיוק כפי שהדפדפן מצייר.
האם הפלטה שלי נשלחת לאנשהו?
לא. הניתוח, ההמרה וחישוב הניגודיות רצים כולם בדפדפן; שום דבר שתקלידו לא עוזב את המכשיר שלכם.