بسیاری از وبسایتهای مدرن به کاربران این قدرت را میدهند که ترجیحات طرح رنگی خاص سایت را تنظیم کنند. یک پیادهسازی اساسی با جاوا اسکریپت ساده است: وقتی کاربر یک چک باکس را تغییر میدهد یا روی دکمه کلیک میکند گوش دهید، یک کلاس (یا ویژگی) را روی آن تغییر دهید. <body>
عنصر در پاسخ، و استایل ها را برای آن کلاس بنویسید تا طرح را با طرح رنگی متفاوت لغو کند.
CSS جدید :has()
شبه طبقه، که از دسامبر 2023 توسط مرورگرهای اصلی پشتیبانی می شود، درهای زیادی را برای توسعه دهندگان فرانت اند باز می کند. من به ویژه در مورد استفاده از آن برای تغییر رابط کاربری در پاسخ به تعامل کاربر هیجانزده هستم بدون جاوا اسکریپت. در جایی که قبلاً از جاوا اسکریپت برای تغییر کلاس ها یا ویژگی ها (یا تنظیم مستقیم سبک ها) استفاده می کردیم، اکنون می توانیم جفت کنیم. :has()
انتخابگرها با عناصر تعاملی بومی HTML.
پشتیبانی از ترجیح طرح رنگ، مانند “حالت تاریک”، یک مورد استفاده عالی است. می توانیم از a استفاده کنیم <select>
عنصری در هر جایی که طرح های رنگی را بر اساس انتخاب شده تغییر می دهد <option>
– نیازی به جاوا اسکریپت نیست، برای ذخیره انتخاب کاربر صرفه جویی کنید، که در ادامه به آن خواهیم پرداخت.
رعایت تنظیمات سیستم
اول، ما با اتخاذ رویکرد اول “حالت نور” از ترجیحات طرح رنگ کاربر در سراسر سیستم پشتیبانی می کنیم. به عبارت دیگر، ما به طور پیشفرض با یک طرح رنگ روشن شروع میکنیم و برای کاربرانی که آن را ترجیح میدهند، آن را با یک طرح رنگ تیره عوض میکنیم.
این prefers-color-scheme
ویژگی رسانه اولویت سیستم کاربر را تشخیص می دهد. استایلهای «تاریک» را در الف بپیچید prefers-color-scheme: dark
پرس و جو رسانه ای
selector {
/* light styles */
@media (prefers-color-scheme: dark) {
/* dark styles */
}
}
بعد، تنظیم کنید color-scheme
ویژگی برای مطابقت با طرح رنگ ترجیحی. تنظیمات color-scheme: dark
مرورگر را به حالت تاریک داخلی خود تغییر میدهد، که شامل یک پسزمینه پیشفرض سیاه، متن پیشفرض سفید، سبکهای «تاریک» برای اسکرولبارها و سایر عناصری است که هدفگیری آنها با CSS دشوار است و موارد دیگر. من از متغیرهای CSS برای اشاره به پویا بودن مقدار استفاده میکنم – و چون تجربه ابزارهای توسعهدهنده مرورگر را دوست دارم – اما ساده color-scheme: light
و color-scheme: dark
خوب کار خواهد کرد
:root {
/* light styles here */
color-scheme: var(--color-scheme, light);
/* system preference is "dark" */
@media (prefers-color-scheme: dark) {
--color-scheme: dark;
/* any additional dark styles here */
}
}
دادن کنترل به کاربران
حالا برای حمایت فراگیر ترجیح سیستم، به کاربران اجازه می دهد بین طرح های رنگ روشن (پیش فرض) و تیره در سطح صفحه انتخاب کنند.
HTML دارای عناصر بومی برای مدیریت تعاملات کاربر است. استفاده از یکی از آن کنترل ها، به جای مثلاً a <div>
nest، شانس اینکه کاربران فناوری کمکی تجربه خوبی داشته باشند را افزایش می دهد. من از a استفاده خواهم کرد <select>
منو با گزینههای «سیستم»، «روشن» و «تاریک». یک گروه از <input type="radio">
اگر بخواهید گزینهها را بهجای منوی کشویی مستقیماً روی سطح قرار دهید، کار میکند.
<select id="color-scheme">
<option value="system" selected>System</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
قبل از به دست آوردن CSS :has()
، به انتخاب کاربر پاسخ می دهد <option>
جاوا اسکریپت مورد نیاز است، برای مثال، تنظیم شنونده رویداد در <select>
برای روشن کردن یک کلاس یا ویژگی <html>
یا <body>
.
اما حالا که داریم :has()
، اکنون می توانیم این کار را تنها با CSS انجام دهیم! شما در هزینه هر یک از بودجه عملکرد خود در یک اسکریپت حالت تاریک صرفه جویی خواهید کرد، به علاوه این کنترل حتی برای کاربرانی که جاوا اسکریپت را غیرفعال کرده اند نیز کار خواهد کرد. و همه افراد “بدون JS” در پروژه راضی خواهند بود.
چیزی که ما نیاز داریم انتخابگر است که در هنگام صفحه روی صفحه اعمال شود :has()
آ select
منو با یک خاص [value]:checked
. بیایید آن را به CSS ترجمه کنیم:
:root:has(select option[value="dark"]:checked)
ما به طور پیش فرض از یک طرح رنگ روشن استفاده می کنیم، بنابراین کافی است دو سناریو احتمالی طرح رنگ تیره را در نظر بگیریم:
- ترجیح رنگ در سطح صفحه “سیستم” و ترجیح سطح سیستم “تاریک” است.
- ترجیح رنگ در سطح صفحه “تاریک” است.
اولین مورد، تکرار ما با توجه به اولویت صفحه است prefers-color-scheme: dark
مورد. ترجیح در سطح سیستم “تاریک” دیگر برای تضمین سبک های تاریک کافی نیست. ما به یک ترجیح سطح سیستم “تاریک” و “ترجیح سطح سیستم” در اولویت سطح صفحه نیاز داریم. ما بسته بندی می کنیم prefers-color-scheme
پرس و جو رسانه ای سبک های طرح تاریک با :has()
انتخابگر ما فقط نوشتیم:
:root {
/* light styles here */
color-scheme: var(--color-scheme, light);
/* page preference is "system", and system preference is "dark" */
@media (prefers-color-scheme: dark) {
&:has(#color-scheme option[value="system"]:checked) {
--color-scheme: dark;
/* any additional dark styles, again */
}
}
}
توجه کنید که من استفاده می کنم CSS Nesting در آن قطعه آخر خط پایه 2023 آیا آن را به عنوان “به تازگی در دسترس در مرورگرهای اصلی” مشخص کرده است که به این معنی است که پشتیبانی خوب است، اما در زمان نگارش، پشتیبانی در مرورگرهای Android شامل نمی شود مجموعه مرورگر اصلی Baseline است محدود. شما می توانید همان نتیجه را بدون تودرتو به دست آورید.
:root {
/* light styles */
color-scheme: var(--color-scheme, light);
/* page preference is "dark" */
&:has(#color-scheme option[value="dark"]:checked) {
--color-scheme: dark;
/* any additional dark styles */
}
}
برای سناریوی حالت تاریک دوم، تقریباً از همان حالت استفاده خواهیم کرد :has()
انتخابگر همانطور که برای سناریوی اول انجام دادیم، این بار بررسی می کنیم که آیا گزینه “تاریک” – به جای گزینه “سیستم” – انتخاب شده است یا خیر:
:root {
/* light styles */
color-scheme: var(--color-scheme, light);
/* page preference is "dark" */
&:has(#color-scheme option[value="dark"]:checked) {
--color-scheme: dark;
/* any additional dark styles */
}
/* page preference is "system", and system preference is "dark" */
@media (prefers-color-scheme: dark) {
&:has(#color-scheme option[value="system"]:checked) {
--color-scheme: dark;
/* any additional dark styles, again */
}
}
}
اکنون سبک های صفحه به هر دو تغییر در تنظیمات سیستم کاربران پاسخ می دهند و تعامل کاربر با رابط کاربری ترجیحی رنگ صفحه — همه با CSS!
اما رنگ ها تغییر می کنند فورا. اجازه دهید انتقال را هموار کنیم.
احترام به ترجیحات حرکت
تغییرات لحظه ای استایل در برخی موارد ممکن است بی ظرافت باشد و این یکی از آنهاست. بنابراین، اجازه دهید یک انتقال CSS را بر روی آن اعمال کنیم :root
برای “سهولت” تغییر بین طرح های رنگی. (سبک های انتقال در :root
به بقیه صفحه کاهش می یابد، که ممکن است نیاز به افزودن داشته باشد transition: none
یا سایر موارد انتقال نادیده گرفته می شود.)
توجه داشته باشید که CSS color-scheme
دارایی از انتقال پشتیبانی نمی کند.
:root {
transition-duration: 200ms;
transition-property: /* properties changed by your light/dark styles */;
}
همه کاربران افزودن یک انتقال را یک بهبود خوشایند نمی دانند. پرس و جو از prefers-reduced-motion
ویژگی رسانه به ما امکان می دهد تنظیمات برگزیده حرکت کاربر را در نظر بگیریم. اگر مقدار روی reduce
، سپس آن را حذف می کنیم transition-duration
برای از بین بردن حرکت ناخواسته
:root {
transition-duration: 200ms;
transition-property: /* properties changed by your light/dark styles */;
@media screen and (prefers-reduced-motion: reduce) {
transition-duration: none;
}
}
انتقال همچنین می تواند تجربه کاربری ضعیفی را در دستگاه هایی ایجاد کند که تغییرات را به کندی ارائه می دهند، به عنوان مثال، دستگاه هایی با صفحه نمایش جوهر الکترونیکی. ما میتوانیم درخواست رسانه «بدون حرکت» را گسترش دهیم تا آن را با استفاده از آن توضیح دهیم update
ویژگی رسانه ای اگر ارزش آن باشد slow
، سپس آن را حذف می کنیم transition-duration
.
:root {
transition-duration: 200ms;
transition-property: /* properties changed by your light/dark styles */;
@media screen and (prefers-reduced-motion: reduce), (update: slow) {
transition-duration: 0s;
}
}
بیایید آنچه را که تا کنون در نسخه ی نمایشی زیر داریم امتحان کنیم. توجه داشته باشید که برای کار کردن color-scheme
به دلیل عدم پشتیبانی انتقال، من به صراحت ویژگی هایی را که باید در طول تغییرات تم تغییر کنند، استایل داده ام.
بد نیست! اما اگر کاربر صفحات را رفرش کند یا به صفحه دیگری برود چه اتفاقی می افتد؟ بارگذاری مجدد به طور موثر فرم انتخابی کاربر را پاک می کند و کاربر را مجبور می کند تا دوباره انتخاب را انجام دهد. این ممکن است در برخی زمینه ها قابل قبول باشد، اما احتمالاً برخلاف انتظارات کاربر است. بیایید جاوا اسکریپت را برای بهبود تدریجی به شکل…
ماندگاری
در اینجا پیاده سازی جاوا اسکریپت وانیلی است. این یک نقطه شروع ساده است – توابع و متغیرها کپسوله نشده اند، اما در عوض ویژگی ها در window
. شما می خواهید این را به گونه ای تطبیق دهید که با قراردادها، چارچوب، کتابخانه و غیره سایت شما مطابقت داشته باشد.
هنگامی که کاربر طرح رنگ را از روی تغییر می دهد <select>
منو، ما انتخاب شده را ذخیره می کنیم <option>
ارزش در جدید localStorage
مورد نامیده می شود "preferredColorScheme"
. در بارگذاری های بعدی صفحه، بررسی می کنیم localStorage
برای "preferredColorScheme"
مورد اگر وجود داشته باشد، و اگر مقدار آن با یکی از گزینههای کنترل فرم مطابقت داشته باشد، با بهروزرسانی برنامهای انتخاب منو، اولویت کاربر را بازیابی میکنیم.
/*
* If a color scheme preference was previously stored,
* select the corresponding option in the color scheme preference UI
* unless it is already selected.
*/
function restoreColorSchemePreference() {
const colorScheme = localStorage.getItem(colorSchemeStorageItemName);
if (!colorScheme) {
// There is no stored preference to restore
return;
}
const option = colorSchemeSelectorEl.querySelector(`[value=${colorScheme}]`);
if (!option) {
// The stored preference has no corresponding option in the UI.
localStorage.removeItem(colorSchemeStorageItemName);
return;
}
if (option.selected) {
// The stored preference's corresponding menu option is already selected
return;
}
option.selected = true;
}
/*
* Store an event target's value in localStorage under colorSchemeStorageItemName
*/
function storeColorSchemePreference({ target }) {
const colorScheme = target.querySelector(":checked").value;
localStorage.setItem(colorSchemeStorageItemName, colorScheme);
}
// The name under which the user's color scheme preference will be stored.
const colorSchemeStorageItemName = "preferredColorScheme";
// The color scheme preference front-end UI.
const colorSchemeSelectorEl = document.querySelector("#color-scheme");
if (colorSchemeSelectorEl) {
restoreColorSchemePreference();
// When the user changes their color scheme preference via the UI,
// store the new preference.
colorSchemeSelectorEl.addEventListener("input", storeColorSchemePreference);
}
بیایید آن را امتحان کنیم. این نسخه نمایشی را باز کنید (شاید در یک پنجره جدید)، از منو برای تغییر طرح رنگ استفاده کنید، و سپس صفحه را بازخوانی کنید تا ترجیحات خود را ادامه دهید:
اگر ترجیح طرح رنگ سیستم شما “روشن” است و طرح رنگ نمایشی را روی “تاریک” تنظیم کرده اید، ممکن است بلافاصله پس از بارگیری مجدد صفحه قبل از شروع سبک های حالت تاریک، سبک های حالت روشن را برای لحظه ای دریافت کنید. این به این دلیل است که CodePen آن را بارگیری می کند. قبل از اسکریپت های نمایشی، جاوا اسکریپت را داشته باشید. این خارج از کنترل من است، اما شما می توانید برای بهبود این پایداری در پروژه های خود مراقبت کنید.
ملاحظات عملکرد پایدار
جایی که کارها ممکن است مشکل ساز شوند، بازگرداندن اولویت کاربر است بلافاصله. مستقیما پس از بارگذاری صفحه اگر ترجیح طرح رنگ در localStorage
با ترجیح طرح رنگ در سطح سیستم کاربر متفاوت است، ممکن است کاربر طرح رنگ ترجیحی سیستم را قبل از بازیابی اولویت سطح صفحه ببیند. (کاربرانی که گزینه «سیستم» را انتخاب کرده اند هرگز آن فلاش را دریافت نخواهند کرد؛ همچنین کسانی که تنظیمات سیستم آنها با گزینه انتخابی آنها در کنترل فرم مطابقت دارد.)
اگر پیاده سازی شما الف را نشان می دهد “فلش تم رنگی نادرست”، مشکل کجاست؟ به طور کلی، هرچه اسکریپت ها زودتر روی صفحه ظاهر شوند، خطر کمتری دارد. البته “بهترین گزینه” برای شما به پشته خاص شما بستگی دارد.
چه در مورد مرورگرهایی که پشتیبانی نمی کنند :has()
?
همه مرورگرهای اصلی پشتیبانی می کنند :has()
امروز اگر می توانید به سیستم عامل های مدرن متمایل شوید. اما اگر لازم است مرورگرهای قدیمی مانند اینترنت اکسپلورر را در نظر بگیرید، دو جهت وجود دارد که می توانید انتخاب کنید: انتخابگر طرح رنگ را برای آن مرورگرها پنهان یا حذف کنید یا از جاوا اسکریپت بیشتر استفاده کنید.
اگر پشتیبانی از طرح رنگ خود را یک پیشرفت تدریجی در نظر می گیرید، می توانید رابط کاربری انتخابی را در مرورگرهایی که پشتیبانی نمی کنند کاملاً پنهان کنید. :has()
:
@supports not selector(:has(body)) {
@media (prefers-color-scheme: dark) {
:root {
/* dark styles here */
}
}
#color-scheme {
display: none;
}
}
در غیر این صورت، نه تنها برای تداوم، بلکه برای عملکرد اصلی باید به راه حل جاوا اسکریپت تکیه کنید. به آن شنونده رویداد سنتی با تغییر یک کلاس یا ویژگی برگردید.
ترفندهای CSSراهنمای کامل حالت تاریک” چندین رویکرد جایگزین را که ممکن است در هنگام کار روی جنبه های قدیمی چیزها نیز در نظر بگیرید، توضیح می دهد.
(gg, yk)