اطمینان از خوانایی نوشتار با رنگ‌های پس‌زمینه پویا

ویژگی‌های وب: contrast-color()

هنگام ساختن مؤلفه‌های واسط کاربر با قابلیت استفاده مجدد (مانند نشان‌ها یا دکمه‌ها) یا پشتیبانی از زمینه‌های پویا، اطمینان از خوانا بودن نوشتار درمقابل رنگ پس‌زمینه غیرقابل‌پیش‌بینی می‌تواند چالش‌برانگیز باشد. تابع 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()، باید یک جایگزین ارائه دهید. اگر رنگ پس‌زمینه مشخص و ثابت است، از رنگ متضاد کدگذاری‌شده استفاده کنید. برای پس‌زمینه‌های پویا که رنگ آن‌ها مشخص نیست، براساس الزامات واسط کاربر و اهداف پشتیبانی مرورگر، راهبردی را از جدول زیر انتخاب کنید.

راهبرد بهترین‌های... ملاحظات
نحو رنگ نسبی محاسبه خودکار و باکیفیت کنتراست فقط با CSS. بالاترین کیفیت بازگشت فقط CSS.
سایه نوشتار افزایش سریع خوانایی در هر پس‌زمینه‌ای. می‌تواند «کثیف» یا «درخشان» به‌نظر برسد؛ ممکن است برای همه طرح‌ها مناسب نباشد.
قلم‌ضربه نوشتار حفظ سیاهی قلم و درعین‌حال اطمینان از کنتراست لبه. درصورت وجود، از paint-order استفاده کنید تا از نازک شدن شکل حروف جلوگیری شود.
رونهاد نیمه‌شفاف اطمینان از وجود حداقل ناحیه کنتراست در پشت نوشتار. ظاهر رنگ پس‌زمینه زیر نوشتار را تغییر می‌دهد، به عنصر نوشتاری جداگانه‌ای نیاز دارد.
فیلترهای SVG کنتراست واکنشی که با تغییر پس‌زمینه به‌روز می‌شود. Requires a separate text element; hacky implementation.

برای مرورگرهایی که از آن پشتیبانی می‌کنند، «سرویس‌های ارتباطی غنی» قوی‌ترین بازگشت خودکار را برای رنگ‌های پویا ارائه می‌دهد.

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

جایگزین‌های «سرویس مقایسه قیمت»

باید: اگر RCS کافی نیست یا پشتیبانی نمی‌شود، گزینه جایگزینی را انتخاب کنید که با محدودیت‌های طراحی شما بیشترین مطابقت را دارد.

گزینه ۱: خط پیرامونی نوشتار

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

گزینه ۲: قلم‌ضربه نوشتار با paint-order برای حفظ شکل حروف

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

گزینه ۳: هم‌پوشانی پس‌زمینه نیمه‌شفاف

.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;
}

گزینه ۴: سایه نوشتار

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

گزینه ۵: فیلترهای SVG

  1. این تعریف فیلتر SVG را بار کنید. اگر لازم است از نسخه‌های Safari تحت‌تأثیر این اشکال WebKit پشتیبانی کنید، باید مستقیماً به سند HTML اضافه شود، که از ارجاع‌های نشانی وب داده و خارجی برای فیلترها جلوگیری می‌کند. درغیراین‌صورت، باید از فایل خارجی بار شود، چه فایل 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);
}