ตรวจสอบว่าข้อความอ่านง่ายด้วยสีพื้นหลังแบบไดนามิก

ฟีเจอร์บนเว็บ: contrast-color()

เมื่อสร้างคอมโพเนนต์ UI ที่นำมาใช้ซ้ำได้ (เช่น ป้ายหรือปุ่ม) หรือรองรับธีมแบบไดนามิก การตรวจสอบว่าข้อความอ่านได้เมื่อเทียบกับสีพื้นหลังที่คาดเดาไม่ได้อาจเป็นเรื่องที่ท้าทาย ฟังก์ชัน CSS contrast-color() จะเปลี่ยนเป็น black หรือ white เพื่อเพิ่มอัตราส่วนคอนทราสต์ให้สูงสุดเมื่อเทียบกับสีพื้นหลังที่ระบุ

กำหนดสีข้อความที่เหมาะสมที่สุด

เมื่อองค์ประกอบมีสีพื้นหลังที่แทรกแบบไดนามิกหรือเป็นตัวแปร คุณสามารถใช้เบราว์เซอร์เพื่อคำนวณสีข้อความที่ปลอดภัยที่สุดได้

บังคับ: ตั้งค่าพร็อพเพอร์ตี้ color โดยใช้ฟังก์ชัน contrast-color() โดยส่งสีพื้นหลัง (หรือคุณสมบัติที่กำหนดเอง) เป็นอาร์กิวเมนต์

.badge {
  /* Define the background color as a custom property */
  --badge-bg: #e02424;
  background-color: var(--badge-bg);
  /* MANDATORY: Dynamic contrasting color */
  color: contrast-color(var(--badge-bg));
}

ไม่ควร: ใช้ contrast-color() สำหรับ background-color โดยออกแบบมาโดยเฉพาะสำหรับสีพื้นหน้า (เช่น ข้อความหรือเส้นขอบ) เพื่อให้ตัดกับสีพื้นหลังที่กำหนด

ผสานรวมกับการกำหนดธีม

เนื่องจาก contrast-color() ยอมรับค่า <color> ที่ถูกต้องทั้งหมด รวมถึงคุณสมบัติที่กำหนดเองของ CSS จึงมีประสิทธิภาพอย่างยิ่งในระบบการออกแบบ การรวมธีมพื้นหลังเป็นตัวแปรจะช่วยให้มั่นใจได้ว่าเนื้อหาที่อยู่เบื้องหน้าจะอัปเดตคอนทราสต์โดยอัตโนมัติเมื่อใดก็ตามที่ธีมเปลี่ยนไป แม้จะเป็นสีที่ผู้ใช้ระบุไว้ก็ตาม

สิ่งที่ควรทำ: อ้างอิงตัวแปรพื้นหลังที่ใช้ร่วมกันภายใน contrast-color() เพื่อรับประกันการซิงค์ข้อมูลในแอปพลิเคชัน

.theme-card {
  background-color: var(--theme-surface-color);
  color: contrast-color(var(--theme-surface-color));
}

.theme-container[data-theme="light"] {
  --theme-surface-color: #f4f4f4;
}
.theme-container[data-theme="dark"] {
  --theme-surface-color: #1a1a1a;
}
.theme-container[data-theme="custom"] {
  --theme-surface-color: var(--user-custom-theme);
}

ไม่ควร: ฮาร์ดโค้ดสีข้อความแยกต่างหากสำหรับสถานะการโต้ตอบแต่ละสถานะเมื่อพื้นหลังเป็นแบบไดนามิก การดำเนินการดังกล่าวจะทำให้เกิดความไม่ตรงกันซึ่ง contrast-color() มีไว้เพื่อป้องกัน

กลยุทธ์สำรอง

หากต้องการรองรับเบราว์เซอร์ที่ไม่มี contrast-color() คุณต้องระบุทางเลือกสำรอง หากทราบสีพื้นหลังและเป็นสีคงที่ ให้ใช้สีที่ตัดกันซึ่งฮาร์ดโค้ดไว้ สำหรับพื้นหลังแบบไดนามิกที่ไม่ทราบสี ให้เลือกกลยุทธ์จากตารางด้านล่างตามข้อกำหนด UI และเป้าหมายการรองรับเบราว์เซอร์

กลยุทธ์ เหมาะสำหรับ... ข้อควรพิจารณา
ไวยากรณ์สีสัมพัทธ์ การคำนวณคอนทราสต์คุณภาพสูงแบบอัตโนมัติที่ใช้ CSS เท่านั้น วิดีโอสำรองคุณภาพสูงสุดที่ใช้ CSS เท่านั้น
เงาข้อความ เพิ่มความสามารถในการอ่านได้อย่างรวดเร็วบนพื้นหลังทุกประเภท อาจดู "สกปรก" หรือ "เรืองแสง" และอาจไม่เหมาะกับดีไซน์บางประเภท
เส้นขอบข้อความ คงความหนาของแบบอักษรไว้พร้อมกับตรวจสอบว่าขอบมีคอนทราสต์ ใช้ paint-order เพื่อหลีกเลี่ยงการทำให้รูปแบบตัวอักษรบางลง (หากมี)
การวางซ้อนแบบโปร่งแสง ตรวจสอบว่ามีพื้นที่คอนทราสต์ขั้นต่ำอยู่ด้านหลังข้อความ เปลี่ยนรูปลักษณ์ของสีพื้นหลังใต้ข้อความ โดยต้องมีองค์ประกอบของข้อความแยกต่างหาก
ฟิลเตอร์ SVG คอนทราสต์แบบรีแอ็กทีฟที่อัปเดตเมื่อพื้นหลังเปลี่ยนไป ต้องใช้องค์ประกอบของข้อความแยกต่างหาก ซึ่งเป็นการใช้งานที่ซับซ้อน

สำหรับเบราว์เซอร์ที่รองรับ RCS จะเป็นตัวเลือกสำรองอัตโนมัติที่มีประสิทธิภาพที่สุดสำหรับสีแบบไดนามิก

@supports (color: oklch(from red l c h)) {
  .badge {
    /* Highest threshold that passes WCAG. Higher values may be more legible. */
    --threshold: 0.623;
    --l: max(0, sign(var(--threshold) - l));
    color: oklch(from var(--badge-bg) var(--l) 0 h);
  }
}

@supports (color: contrast-color(red)) {
  .badge {
    color: contrast-color(var(--badge-bg));
  }
}

CSS สำรองทางเลือก

สิ่งที่ควรทำ: เลือกทางเลือกสำรองที่ตรงกับข้อจำกัดด้านการออกแบบมากที่สุดหาก RCS ไม่เพียงพอหรือไม่รองรับ

ตัวเลือกที่ 1: เส้นขอบข้อความ

.badge--stroke {
  -webkit-text-stroke: 4px black;
}

ตัวเลือกที่ 2: เส้นขอบข้อความที่มี paint-order เพื่อคงรูปแบบตัวอักษรไว้

.badge--stroke {
  -webkit-text-stroke: 4px black;
  paint-order: stroke fill;
}

ตัวเลือกที่ 3: ภาพซ้อนทับพื้นหลังแบบโปร่งแสง

.badge--overlay {
  position: relative;
  color: #fff;
  overflow: hidden;
}
.badge--overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: rgb(0 0 0 / 0.4);
  z-index: 0;
}
.badge--overlay span {
  position: relative;
  z-index: 1;
}

ตัวเลือกที่ 4: เงาข้อความ

.badge--shadow {
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
}

ตัวเลือกที่ 5: ตัวกรอง SVG

  1. โหลดคำจำกัดความของตัวกรอง SVG นี้ คุณต้องเพิ่มลงในเอกสาร HTML โดยตรงหากต้องการรองรับ Safari เวอร์ชันที่ได้รับผลกระทบจากข้อบกพร่องของ WebKit นี้ ซึ่งจะป้องกันการอ้างอิง URL ภายนอกและ URL ข้อมูลสำหรับตัวกรอง หรือควรโหลดจากไฟล์ภายนอก ไม่ว่าจะเป็นไฟล์ SVG หรือแทรกใน CSS

    <!-- Technique from https://miunau.com/posts/dynamic-text-contrast-in-css/ -->
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0" style="display: none;">
      <defs>
        <filter id="contrast-filter" color-interpolation-filters="sRGB">
          <feColorMatrix type="matrix"
            values="0.2126 0.7152 0.0722 0 0
              0.2126 0.7152 0.0722 0 0
              0.2126 0.7152 0.0722 0 0
              0 0 0 1 0"/>
          <feMorphology operator="dilate" radius="2"/>
          <feComponentTransfer>
            <feFuncR type="linear" slope="-255" intercept="128"/>
            <feFuncG type="linear" slope="-255" intercept="128"/>
            <feFuncB type="linear" slope="-255" intercept="128"/>
          </feComponentTransfer>
          <feComposite operator="in" in2="SourceGraphic"/>
        </filter>
      </defs>
    </svg>
    
  2. ตัดข้อความขึ้นบรรทัดใหม่ใน <span>

    <div class="badge--svg">
      <span>Badge content</span>
    </div>
    
  3. ใช้ตัวกรองกับ <span>

.badge--svg span{
  color: var(--badge-bg);
  filter: url(#contrast-filter);
}