کلیدهای حالت تاریک

کلید روشن/خاموش حالت تاریک به کاربران اجازه می‌دهد آرایش رنگ‌های سیستم را برای یک سایت لغو کنند. لغو با به‌روزرسانی <meta name="color-scheme"> اعمال می‌شود: light dark یعنی «اولویت سیستم را دنبال کن»، light یا dark طرح صریحی را سنجاق می‌کند. رنگ‌ها (light-dark())، رنگ‌های سیستم، و واسط کاربر بومی همگی از ویژگی CSS color-scheme کلید می‌خورند، بنابراین کلید با همگام نگه داشتن آن ویژگی با <meta> کار می‌کند (بازتاب لغو در CSS را ببینید).

کلید روشن/خاموش دائمی، کنترل تنظیمات، یا اصلاً بدون کلید روشن/خاموش؟

اکثر سایت‌ها به کلید تغییر وضعیت آرایش رنگ‌ها در واسط کاربر سایت (سرصفحه، پیمایش، پاصفحه، و غیره) نیاز ندارند. به‌طور پیش‌فرض از اولویت سیستم پیروی کنید و اگر لغو دستی لازم است، کنترل را در سطح تنظیمات جداگانه‌ای قرار دهید: صفحه تنظیمات، پانل، یا هم‌پوشانی.

سنجاق کردن طرح درمقابل سیستم‌عامل هدفی مشروع اما نادر است و اهداف نادر به شفاف‌سازی تدریجی تعلق دارند، نه به کنترلی که همیشه قابل‌مشاهده است.

کلیدهای روشن/خاموش دائمی بیشتر در سایت‌های توسعه‌دهنده یا سایت‌های مرتبط با توسعه‌دهنده (مثلاً برای طراحان) یافت می‌شوند، جایی که تغییر طرح‌ها کاری بسیار متداول‌تر از کاربر وب معمولی است. فقط به‌دلیل اینکه این الگو در سایت‌های توسعه‌دهنده رایج است، آن را اضافه نکنید: علاقه توسعه‌دهنده به این کنترل نماینده کاربران معمولی نیست.

پیاده‌سازی

ابتدا، همه مراحل را در حالت تاریک دنبال کنید تا از هر دو طرح پشتیبانی شود و اولویت سیستم به‌عنوان پیش‌فرض تنظیم شود.

مدل داده

مدل داده برای کلید روشن/خاموش حالت تاریک معمولاً شامل سه حالت است:

  1. سیستم (بدون مقدار ذخیره‌شده): واسط کاربر از آرایش رنگ‌های سیستم پیروی می‌کند. در حالت ایده‌آل، این باید پیش‌فرض باشد.
  2. روشن (ذخیره‌شده به‌عنوان "light"): واسط کاربر بدون توجه به آرایش رنگ‌های سیستم از زمینه روشن استفاده می‌کند.
  3. تیره (ذخیره‌شده به‌عنوان "dark"): واسط کاربر بدون توجه به آرایش رنگ‌ها در سیستم از زمینه تیره استفاده می‌کند.

این حالت معمولاً در localStorage ذخیره می‌شود و در عنصر <meta name="color-scheme"> در سربرگ سند منعکس می‌شود.

بازتاب دادن لغو در CSS

<meta> فقط یک نکته نمایشی است: هرگونه اعلام color-scheme در CSS نویسنده آن را لغو می‌کند. ازآنجاکه حالت تاریک color-scheme: light dark را روی :root تنظیم می‌کند، به‌روزرسانی <meta> به‌تنهایی بدون تأثیر است — CSS اولویت دارد. <meta> را به دارایی نگاشت کنید تا آبشار از آن پیروی کند:

:root {
  color-scheme: light dark;

  &:has(> head > meta[name="color-scheme"][content="dark"]) {
    color-scheme: dark;
  }
  &:has(> head > meta[name="color-scheme"][content="light"]) {
    color-scheme: light;
  }
}

نباید color-scheme: light یا color-scheme: dark را به‌عنوان پیش‌فرض ریشه کدنویسی کنید؛ تعریف پایه باید light dark باقی بماند. CSS نباید به JS وابسته باشد: اگر JS هرگز اجرا نشود، <meta> باقی می‌ماند light dark و سایت از اولویت سیستم پیروی می‌کند — هیچ‌چیز خراب نمی‌شود.

پایداری و جلوگیری از FOUC

لغو را در localStorage حفظ کنید؛ هنگام بازگشت به پیش‌فرض سیستم، ورودی را بردارید.

برای جلوگیری از چشمک زدن طرح اشتباه، مقدار ذخیره‌شده را در یک اسکریپت درون‌خطی (نه type=module، نه defer) که بلافاصله بعداز عنصر <meta> قرار می‌گیرد اعمال کنید:

<meta name="color-scheme" content="light dark">
<script>
document.querySelector('meta[name="color-scheme"]').content = localStorage.getItem("color-scheme") ?? "light dark";
</script>
  • این اسکریپت درون‌خطی فقط برای جلوگیری از FOUC وجود دارد. آن را تا حد امکان کوچک نگه دارید؛ JS اضافی برای مدیریت جابه‌جایی آرایش رنگ‌ها می‌تواند بعداً بار شود.
  • در موارد نادر، دسترسی به localStorage می‌تواند خطا ایجاد کند (مثلاً داده‌های سایت مسدود شده باشد)، اما چون این اسکریپت کار دیگری انجام نمی‌دهد، تنها عارضه جانبی آن خطای کنسول خواهد بود. اگر این موضوع مهم است، در try .. catch بپیچید. مطمئن شوید که زمینه همچنان روی صفحه اعمال می‌شود، حتی اگر نتوان آن را حفظ کرد.
  • لغو می‌تواند از برگه دیگری تغییر کند: رویداد storage از نام کاربری window را برای همگام‌سازی مدیریت کنید.
  • وقتی اولویت سیستم تغییر می‌کند تا با مقدار ذخیره‌شده مطابقت داشته باشد، از matchMedia() برای برداشتن مقدار ذخیره‌شده استفاده نکنید. سیستم‌عامل بسیاری از کاربران طرح‌واره‌ها را به‌طور خودکار براساس زمان روز تغییر می‌دهد؛ هروقت این دو اتفاق هم‌زمان رخ دهد، برداشتن مقدار ذخیره‌شده باعث می‌شود که اصلاً نتوان طرح‌واره‌ای را سنجاق کرد.

انشعاب برای HTML و مقادیر غیررنگی

رنگ‌ها به کار اضافی نیاز ندارند: وقتی <meta> به color-scheme (بالا) نگاشت شود، light-dark() از لغو پیروی می‌کند. این بخش درباره تطبیق دادن مقادیر دیگر (مثلاً font-weight، منابع رسانه، و غیره) است.

وقتی صفحه به‌سادگی از اولویت سیستم پیروی می‌کند، می‌توان از (prefers-color-scheme: dark) برای انشعاب استفاده کرد، ازجمله در منابع <picture> یا <video>. لغو کردن آرایش رنگ‌ها پیچیدگی دیگری اضافه می‌کند: اولویت سیستم دیگر منبع حقیقت نیست و برای میاناهای برنامه‌سازی کاربردی که فقط پرسمان‌های رسانه را می‌پذیرند، جایگزین مستقیمی وجود ندارد.

برای انشعاب، منحصراً به کلاس‌های اعمال‌شده با JS مانند .dark تکیه نکنید، زیرا اگر JS بار نشود و پیش‌فرض سیستم‌عامل تیره باشد، این کلاس‌ها نادرست خواهند بود.

می‌توانید پرسمان رسانه را با انتخابگر :root:has(> head > meta[name="color-scheme"][content="dark"]) (یا light) ترکیب کنید تا شاخه ایجاد کنید. برای جزئیات بیشتر درباره نحوه پیاده‌سازی این مورد به‌صورت شکیل، ترکیب‌های Selector و @-rule را ببینید.

گزینه‌های خود کنترل نیز شاخه‌های غیررنگی هستند: هر دو حالت را در نشان‌گذاری (نماد به‌علاوه نوشتار کنش پنهان‌شده به‌صورت دیداری، مثلاً «رفتن به زمینه تیره») اضافه کنید و حالت غیرفعال را ازطریق این انتخابگرها پنهان کنید. display: none همچنین گزینه زائد را از AT برمی‌دارد تا کنترل برای به‌روز ماندن هنگام تغییر اولویت سیستم به JS نیاز نداشته باشد.

برای رسانه‌های درون HTML، درحال‌حاضر تنها راه این است که هر دو نسخه را به‌عنوان عناصر جداگانه اضافه کنید و رؤیت‌پذیری را به‌طور مناسب تغییر دهید. پنهان کردن عناصر <source> با display: none کار نمی‌کند.

واسط کاربر

دو یا سه ایالت؟

در همه موارد، واسط کاربر باید از برگرداندن یا تنظیم آرایش رنگ‌ها به سیستم پشتیبانی کند.

ترجیحاً دو حالت را در کلیدهای با فضای محدود نمایش دهید، مانند کلیدهای دائمی در chrome سایت (سرصفحه، پیمایش، پاصفحه، و غیره) که فضا محدود است و تغییر تنظیمات معمولاً با هدف کاربر هم‌راستا نیست.

کنترل آرایش رنگ‌ها در سطح تنظیمات جداگانه (صفحه، پانل، یا هم‌پوشانی) سناریو متفاوتی است و ممکن است هر سه حالت را به‌طور صریح نشان دهد («روشن»، «تیره»، «سیستم»): در آنجا، کاربر ازقبل تصمیمات آگاهانه‌ای درباره رفتار آینده می‌گیرد و فضا برای توضیح گزینه‌ها وجود دارد.

اگر سایت طرح‌های بافت‌آگاه را پیاده‌سازی کند (مثلاً حالت نور کم‌تر وقتی سیستم‌عامل تاریک است)، سه حالت صریح نیز تضمین می‌شود، زیرا «روشن» و «سیستم (درحال‌حاضر روشن)» واقعاً متفاوت هستند.

کلیدهای دوحالته

وقتی از چنین کنترلی استفاده می‌شود، معمولاً برای تنظیم موقت راحتی است («الان خیلی روشن است»)، نه برای اطمینان از اینکه وضعیت فعلی در درازمدت حفظ می‌شود. بنابراین، برای کلیدهای دوحالته، کافی است دو تنظیم از سه تنظیم را نمایش دهید: سیستم (تا بتوان تنظیم را برگرداند) و تنظیم مخالف فعلی سیستم، که به‌عنوان مقدار تحت‌اللفظی فعلی آن ذخیره شده است.

تنها دو حالت باید وجود داشته باشد:

  1. پیش‌فرض سیستم — بدون مقدار ذخیره‌شده، <meta> محتوا light dark. به‌عنوان مقدار حل‌شده فعلی آن نمایش داده می‌شود (برای مثال، نماد خورشید درحالت روشن).
  2. لغو کردن — به‌صورت تحت‌اللفظی به‌عنوان light یا dark ذخیره شده است.

اساساً، این یک کنترل سه‌حالته (light dark، light، dark) است که در آن حالت صریح مطابق با اولویت فعلی سیستم غیرقابل‌دسترس است.

وقتی کاربر تغییر وضعیت می‌دهد:

  1. طرح هدف = مخالف طرحی که درحال‌حاضر ارائه شده است (مقدار ذخیره‌شده درصورت وجود، درغیراین‌صورت اولویت سیستم). قصد کاربر «انتخاب کردن متضاد چیزی است که درحال‌حاضر می‌بینم»، نه «انتخاب کردن معکوس پیش‌فرض سیستم».
  2. اگر هدف با اولویت سیستم فعلی متفاوت است، آن را به‌صورت تحت‌اللفظی ذخیره کنید.
  3. اگر هدف با اولویت فعلی سیستم مطابقت داشته باشد، کاربر درحال واگرد کردن تنظیمات خود است: مقدار ذخیره‌شده را بردارید. آن را ذخیره نکنید — این کار باعث می‌شود طرح به‌طور نامرئی دربرابر تغییرات آتی سیستم سنجاق شود.
  4. محتوای <meta> را روی مقدار ذخیره‌شده تنظیم کنید، یا اگر وجود ندارد، روی light dark تنظیم کنید.

مهم: واگرایی باید فقط در زمان ذخیره‌سازی بررسی شود، هرگز به‌صورت عطف به ماسبق بررسی نشود: مقدار ذخیره‌شده‌ای که اولویت سیستم بعداً برای مطابقت تغییر می‌کند باید حفظ شود. سیستم‌عامل بسیاری از کاربران طرح‌واره‌ها را به‌طور خودکار براساس زمان روز تغییر می‌دهد؛ اگر هر زمان که این دو اتفاق هم‌زمان رخ دهند مقدار ذخیره‌شده برداشته شود، سنجاق کردن طرح‌واره به‌طور کلی غیرممکن می‌شود.

سناریوی نمونه، با شروع از سیستم‌عامل تنظیم‌شده روی روشن:

  1. کاربر کلید را تغییر می‌دهد. «تیره» با اولویت سیستم متفاوت است، بنابراین dark ذخیره می‌شود؛ سایت تیره می‌شود.
  2. تنظیم سیستم‌عامل به تیره تغییر می‌کند. سایت تاریک می‌ماند (مقدار ذخیره‌شده اکنون با اولویت سیستم مطابقت دارد، اما حفظ می‌شود).
  3. تنظیم سیستم‌عامل به روشن برمی‌گردد. سایت تاریک می‌ماند.
  4. کاربر کلید را تغییر می‌دهد. هدف (روشن) با اولویت سیستم مطابقت دارد، بنابراین مقدار ذخیره‌شده برداشته می‌شود و سایت دوباره از سیستم پیروی می‌کند.

کلیدهای سه‌حالته

وقتی فضای بیشتری وجود دارد و کاربران ازقبل در ذهنیت تنظیم اولویت‌های بلندمدت هستند، کنترل سه‌حالته می‌تواند شفافیت بیشتری ارائه دهد. در این حالت، مدل داده به‌طور واضح به سه گزینه واسط کاربر نگاشت می‌شود: سیستم، روشن، و تاریک.

برای کاهش بار شناختی، گزینه «سیستم» باید تنظیمات فعلی سیستم را نیز نمایش دهد، برای مثال ازطریق:

  • نماد، مثلاً نماد خورشید در پایین سمت چپ نماد سیستم
  • نوشتار، مثلاً «سیستم (درحال‌حاضر روشن)»