ترجیحات کاربر؛ وقتی سیستم‌عامل به شما می‌گه کاربر چی می‌خواد

تو فصل ۲۰ با prefers-reduced-motion آشنا شدیم و تو درس قبل هم به prefers-color-scheme اشاره‌ای کردیم. حالا که تو فصل Accessibility هستیم، وقتشه این دوتا رو با دید کامل‌تری بررسی کنیم، چون هردوشون از یه خانواده‌ی مهم media query هستن: کوئری‌هایی که به‌جای سایز صفحه، ترجیحات شخصی کاربر که تو سیستم‌عامل تنظیم کرده رو می‌پرسن.

فرق اساسی این کوئری‌ها با @media (max-width)

یه @media (max-width: 768px) بر اساس چیزیه که مرورگر «می‌بینه» (سایز پنجره). اما prefers-reduced-motion و prefers-color-scheme بر اساس چیزی هستن که کاربر از قبل، تو تنظیمات سیستم‌عاملش، صراحتاً انتخاب کرده. یعنی این‌ها در واقع یه پیام مستقیم از کاربر به سایت شما هستن: «من این‌جوری راحت‌ترم».

یادآوری prefers-reduced-motion

تو فصل ۲۰ دیدیم این کوئری برای کاربرهایی که به حرکت حساسیت دارن (مثل vestibular disorders) به کار می‌ره:

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

یه نکته‌ی جدید که اینجا اضافه می‌کنیم: به‌جای animation: none یا display: none که کاملاً انیمیشن رو حذف می‌کنن، بعضی وقت‌ها استفاده از یه مدت زمان خیلی کوتاه (مثل 0.01ms) ترجیح داده می‌شه؛ چون اگه کدهای جاوااسکریپتی باشن که منتظر رویداد animationend یا transitionend هستن، با none کامل این رویدادها اصلاً فایر نمی‌شن و ممکنه بخشی از منطق صفحه (مثل حذف یه کلاس بعد از پایان انیمیشن) اجرا نشه. با یه مدت خیلی کوتاه، انیمیشن عملاً نامحسوسه ولی رویدادش هنوز فایر می‌شه.

prefers-color-scheme: تشخیص حالت روشن یا تاریک سیستم‌عامل

این کوئری تشخیص می‌ده کاربر تو سیستم‌عاملش (ویندوز، مک، اندروید، iOS) حالت روشن رو ترجیح می‌ده یا تاریک:

body {
  background: white;
  color: black;
}

@media (prefers-color-scheme: dark) {
  body {
    background: #121212;
    color: #e0e0e0;
  }
}

مقدار سومی هم به اسم no-preference وجود داره (وقتی کاربر هیچ تنظیمی نکرده)، ولی امروزه عملاً بیشتر سیستم‌عامل‌ها یه پیش‌فرض (معمولاً روشن) دارن، پس این مقدار خیلی کم به کار میاد.

ساختن یه سیستم رنگ کامل با custom properties

روش رایج و تمیز، تعریف کردن رنگ‌ها به‌صورت متغیر و override کردنشون داخل media query، به‌جای نوشتن هر ویژگی رنگی جداگانه:

:root {
  --bg: white;
  --text: #1a1a1a;
  --border: #e0e0e0;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #121212;
    --text: #f0f0f0;
    --border: #333;
  }
}

body {
  background: var(--bg);
  color: var(--text);
}

.card {
  border: 1px solid var(--border);
}

این‌جوری، همه‌ی جاهایی که از این متغیرها استفاده می‌کنن، خودکار با حالت تاریک/روشن هماهنگ می‌شن، بدون این‌که مجبور باشید هر selector رو جداگانه داخل media query بازنویسی کنید.

وقتی می‌خواید کاربر بتونه دستی هم انتخاب کنه (نه فقط سیستم‌عامل)

خیلی از سایت‌ها یه دکمه‌ی تغییر تم هم دارن که مستقل از تنظیمات سیستم‌عامله. تو این حالت، معمولاً یه کلاس یا attribute دستی رو با اولویت بالاتر از media query تعریف می‌کنن:

:root {
  --bg: white;
  --text: #1a1a1a;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #121212;
    --text: #f0f0f0;
  }
}

/* انتخاب دستی کاربر، اولویت بالاتر از سیستم‌عامل */
:root[data-theme="dark"] {
  --bg: #121212;
  --text: #f0f0f0;
}

:root[data-theme="light"] {
  --bg: white;
  --text: #1a1a1a;
}

این attribute (data-theme) معمولاً با جاوااسکریپت روی html تنظیم می‌شه و تو localStorage ذخیره می‌شه تا انتخاب کاربر بین بازدیدهای مختلف هم حفظ بشه. نکته‌ی CSS اینجا اینه که چون این selector بعد از media query نوشته شده و specificity بالاتری هم داره (attribute selector)، همیشه بر تنظیم خودکار سیستم‌عامل غلبه می‌کنه.

ترکیب چند ترجیح با هم

می‌شه این کوئری‌ها رو با هم یا با media query های دیگه هم ترکیب کرد:

@media (prefers-color-scheme: dark) and (prefers-reduced-motion: reduce) {
  .hero-animation {
    display: none;
  }
}

سایر prefers-* که کمتر معروفن

چند کوئری دیگه از همین خانواده هم وجود دارن که کمتر استفاده می‌شن ولی ارزش دونستن دارن:

کوئریکاربرد
prefers-contrastکاربر کنتراست بیشتر یا کمتر می‌خواد
prefers-reduced-transparencyکاربر شفافیت کمتر (پس‌زمینه‌های محو) می‌خواد
forced-colorsتشخیص حالت کنتراست بالای اجباری ویندوز
نکته مهم: رعایت این کوئری‌ها یه پیشنهاد اختیاری «فانتزی» نیست؛ این‌ها ترجیحات صریحی هستن که کاربر با زحمت رفته تو تنظیمات سیستم‌عاملش فعالشون کرده. نادیده گرفتنشون یعنی نادیده گرفتن یه درخواست مستقیم از کاربر.

اشتباه رایج: فقط تست کردن با تنظیمات پیش‌فرض سیستم‌عامل

خیلی از توسعه‌دهنده‌ها هیچ‌وقت تنظیمات سیستم‌عامل خودشون رو عوض نمی‌کنن تا ببینن سایتشون تو حالت تاریک یا reduced-motion چطور به نظر می‌رسه. یه تست ساده و سریع: قبل از publish کردن هر پروژه، حداقل یه بار حالت تاریک و reduced-motion سیستم‌عاملتون رو فعال کنید و صفحه رو دوباره ببینید.

جمع‌بندی

prefers-reduced-motion و prefers-color-scheme به شما اجازه می‌دن مستقیماً به ترجیحات صریح کاربر تو سطح سیستم‌عامل واکنش نشون بدید، نه فقط سایز صفحه. استفاده از custom property ها برای رنگ‌ها، override کردن این تنظیمات رو تمیز و قابل‌نگهداری می‌کنه، و اگه سایتتون سوییچ دستی تم هم داره، با attribute selector می‌تونید انتخاب کاربر رو بر تنظیم خودکار سیستم‌عامل اولویت بدید.