فرم‌های سازگار با نمانام

ویژگی‌های وب: accent-color

سفارشی‌سازی عناصر استاندارد فرم HTML مانند چارگوش‌های انتخاب و دکمه‌های رادیویی از گذشته دشوار بوده است. توسعه‌دهندگان اغلب با انتخاب بین استفاده از پیش‌فرض‌های مرورگر یا ساختن مؤلفه‌های سفارشی از ابتدا مواجه بودند. ساختن کنترل‌های سفارشی وقت‌گیر است و به‌راحتی می‌تواند به مشکلات دسترس‌پذیری یا حالت‌های مفقود (مانند حالت نامشخص برای چارگوش‌های انتخاب) منجر شود.

ویژگی CSS accent-color روشی ساده برای آوردن رنگ نمانام شما به ورودی‌های فرم HTML داخلی با یک خط CSS، بدون فدا کردن دسترس‌پذیری یا ویژگی‌های مرورگر داخلی ارائه می‌دهد.

نحوه پیاده‌سازی

برای اعمال رنگ نمانام به کنترل‌های فرم:

  1. رنگ نمانامتان را مشخص کنید: رنگی را انتخاب کنید که معرف نمانامتان باشد.
  2. اعمال کردن ویژگی accent-color: accent-color را به عنصر یا عنصر محتوا (مثل body یا فرم خاصی) در CSS خود اضافه کنید.
  3. پشتیبانی از «حالت تاریک» (اختیاری اما توصیه می‌شود): از color-scheme استفاده کنید تا مرورگر متوجه شود سایتتان از حالت تاریک پشتیبانی می‌کند، و درصورت لزوم accent-color را برای کنتراست بهتر تنظیم کنید.

کد نمونه: کنترل‌های فرم سازگار با نمانام

:root {
  --brand-color: #6200ee;
}

/* Apply accent-color to the body or a specific container */
body {
  accent-color: var(--brand-color);
}

/* Optional: Adjust for dark mode if needed */
@media (prefers-color-scheme: dark) {
  :root {
    --brand-color: #bb86fc; /* A lighter shade for dark mode */
  }
}
<form>
  <!-- Checkbox -->
  <label for="subscribe">
    <input type="checkbox" id="subscribe" checked>
    Subscribe to newsletter
  </label>

  <!-- Radio Buttons -->
  <label for="plan-monthly">
    <input type="radio" id="plan-monthly" name="plan" value="monthly">
    Monthly
  </label>
  <label for="plan-yearly">
    <input type="radio" id="plan-yearly" name="plan" value="yearly" checked>
    Yearly
  </label>

  <!-- Range Slider -->
  <label for="volume">Volume:</label>
  <input type="range" id="volume" min="0" max="100" value="70">

  <!-- Progress Bar -->
  <label for="file">Upload Progress:</label>
  <progress id="file" max="100" value="70">70%</progress>
</form>

پیاده‌سازی استراتژیک و روال‌های مطلوب

  • باید از accent-color استفاده کنید تا به‌راحتی کنترل‌های فرم را با نمانامتان مطابقت دهید.
  • به‌طور کورکورانه به مرورگر برای مدیریت کنتراست اعتماد نکنید. درحالی‌که مرورگرها باید به‌طور خودکار رنگ کنتراست واجدشرایط را تعیین کنند، اشکال‌های شناخته‌شده در پیاده‌سازی‌هایی مانند Safari (اشکال WebKit 244233) و Android Chrome (اشکال Chromium 343503163) می‌توانند در معکوس کردن رنگ‌های علامت تیک ناموفق باشند، که منجر به کنترل‌های نامرئی یا سخت‌دیدنی هنگام استفاده از رنگ‌هایی می‌شود که کنتراست کافی با پس‌زمینه ندارند (مثلاً رنگ‌های روشن در حالت روشن، یا رنگ‌های تیره در حالت تاریک).
  • باید accent-color را با color-scheme: light dark ترکیب کنید تا مطمئن شوید کنترل‌های فرم در هر دو زمینه روشن و تیره خوب به‌نظر می‌رسند.
  • از رنگی که خیلی به رنگ پس‌زمینه نزدیک است استفاده نکنید، اگرچه مرورگرها سعی می‌کنند کنتراست را تضمین کنند، بهتر است رنگی با کنتراست پایه خوب ارائه دهید.
  • فرض نکنید که accent-color روی همه عناصر فرم کار می‌کند. درحال‌حاضر، فقط عناصر checkbox، radio، range، و progress را رنگ‌آمیزی می‌کند.

راهبرد جایگزین

برای مرورگرهایی که از accent-color پشتیبانی نمی‌کنند، کنترل‌های فرم به ظاهر پیش‌فرض مرورگر برمی‌گردند. برای اطمینان از سازگاری کامل نمانام و قابلیت اطمینان بالا در همه محیط‌ها، باید استراتژی بازگشت سفارشی را بااستفاده از تکنیک «ورودی پنهان بصری» پیاده‌سازی کنید.

ارتقای پیش‌رو با @supports not

باید از قانون @supports not استفاده کنید تا سبک‌های بازگشتی سفارشی را فقط زمانی اعمال کنید که accent-color پشتیبانی نمی‌شود. این کار تضمین می‌کند که از سادگی accent-color برای مرورگرهای جدید بهره ببرید و درعین‌حال تجربه نمانام‌دار ثابتی را برای مرورگرهای قدیمی‌تر تضمین می‌کند.

۱. ساختار HTML

مطمئن شوید برچسب‌هایتان نوشتار را در <span> قرار می‌دهند تا گزینشگرهای هم‌نیا در CSS مجاز شوند:

<label for="subscribe-fallback">
  <input type="checkbox" id="subscribe-fallback" class="visually-hidden" checked>
  <span>Subscribe to newsletter</span>
</label>

۲. بازگشت «سرویس مقایسه قیمت»

اعمال کردن سبک‌های سفارشی در بلوک @supports not:

/* Fallback for older browsers without accent-color */
@supports not (accent-color: var(--brand-color)) {
  /* Visually hide the native input using the canonical accessible recipe */
  form input[type="checkbox"].visually-hidden {
    position: absolute !important;
    clip-path: inset(50%) !important;
    overflow: hidden !important;
    width: 1px !important;
    height: 1px !important;
    margin: -1px !important;
    padding: 0 !important;
    border: 0 !important;
    white-space: nowrap !important;
  }

  /* Style the wrapper label */
  label {
    position: relative;
    padding-left: 2rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
  }

  /* Custom box for checkbox */
  input[type="checkbox"] + span::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 1.2rem;
    height: 1.2rem;
    border: 2px solid #ccc;
    background: white;
    border-radius: 4px;
    box-sizing: border-box;
    transition: all 0.2s ease;
  }

  /* Ensure custom checkbox shows focus for keyboard users */
  input[type="checkbox"]:focus-visible + span::before {
    outline: 2px solid #000;
    outline-offset: 2px;
  }

  /* Checked State */
  input[type="checkbox"]:checked + span::before {
    background-color: var(--brand-color, #6200ee);
    border-color: var(--brand-color, #6200ee);
  }

  /* Checkmark (Unicode) */
  input[type="checkbox"]:checked + span::after {
    content: "✓";
    position: absolute;
    left: 0.25rem;
    top: 50%;
    transform: translateY(-50%);
    color: white;
    font-weight: bold;
    font-size: 0.9rem;
  }

  /* Fallback for Range Slider */
  input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
  }

  /* Webkit (Chrome, Safari, Edge) */
  input[type="range"]::-webkit-slider-runnable-track {
    width: 100%;
    height: 8px;
    /* Use gradient to show progress for a static value (e.g., 70%) or update with JS */
    background: linear-gradient(to right, var(--brand-color, #6200ee) 70%, #ccc 70%);
    border-radius: 4px;
  }

  input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    height: 16px;
    width: 16px;
    border-radius: 50%;
    background: var(--brand-color, #6200ee);
    cursor: pointer;
    margin-top: -4px; /* Center vertically */
  }

  /* Firefox */
  input[type="range"]::-moz-range-track {
    width: 100%;
    height: 8px;
    background: #ccc;
    border-radius: 4px;
  }

  input[type="range"]::-moz-range-thumb {
    height: 16px;
    width: 16px;
    border-radius: 50%;
    background: var(--brand-color, #6200ee);
    cursor: pointer;
  }

  /* Firefox specific progress bar */
  input[type="range"]::-moz-range-progress {
    background-color: var(--brand-color, #6200ee);
    height: 8px;
    border-radius: 4px;
  }

  /* Fallback for Progress Bar */
  progress {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    border: none;
    background: #ccc;
    height: 8px;
    border-radius: 4px;
  }

  progress::-webkit-progress-bar {
    background-color: #ccc;
    border-radius: 4px;
  }

  progress::-webkit-progress-value {
    background-color: var(--brand-color, #6200ee);
    border-radius: 4px;
  }

  progress::-moz-progress-bar {
    background-color: var(--brand-color, #6200ee);
    border-radius: 4px;
  }
}

پیشرفت دامنه پویا در Webkit Fallback

برای اینکه پر شدن پیشرفت با لغزنده محدوده در مرورگرهای Webkit (بدون accent-color) حرکت کند، می‌توانید از متغیر CSS و مقدار کمی جاوا اسکریپت استفاده کنید.

  1. به‌روزرسانی CSS: از متغیر CSS برای توقف گرادیان استفاده کنید:

    input[type="range"]::-webkit-slider-runnable-track {
      background: linear-gradient(to right, var(--brand-color) var(--progress, 0%), #ccc var(--progress, 0%));
    }
    
  2. افزودن جاوا اسکریپت: متغیر را در رویداد input به‌روز کنید:

if (!CSS.supports('accent-color')) {
  const slider = document.getElementById('volume');
  slider.addEventListener('input', (e) => {
    e.target.style.setProperty('--progress', `${e.target.value}%`);
  });
}