تنظیم و تداوم ترجیحات طرح رنگ با CSS و “لمس” جاوا اسکریپت – مجله Smashing

تنظیم و تداوم ترجیحات طرح رنگ با CSS و "لمس" جاوا اسکریپت - مجله Smashing

بسیاری از وب‌سایت‌های مدرن به کاربران این قدرت را می‌دهند که ترجیحات طرح رنگی خاص سایت را تنظیم کنند. یک پیاده‌سازی اساسی با جاوا اسکریپت ساده است: وقتی کاربر یک چک باکس را تغییر می‌دهد یا روی دکمه کلیک می‌کند گوش دهید، یک کلاس (یا ویژگی) را روی آن تغییر دهید. <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)

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

  1. ترجیح رنگ در سطح صفحه “سیستم” و ترجیح سطح سیستم “تاریک” است.
  2. ترجیح رنگ در سطح صفحه “تاریک” است.

اولین مورد، تکرار ما با توجه به اولویت صفحه است 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 در آخرین قطعه استفاده می کنم. Baseline 2023 آن را به عنوان “به تازگی در دسترس در مرورگرهای اصلی” مشخص کرده است که به این معنی است که پشتیبانی خوب است، اما در زمان نگارش، پشتیبانی در مرورگرهای اندرویدی که در مجموعه مرورگر اصلی 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به دلیل عدم پشتیبانی انتقال، من به صراحت ویژگی هایی را که باید در طول تغییرات تم تغییر کنند، استایل داده ام.

قلم (تغییرکننده تم فقط با CSS (نیاز به :has()) (فشار)) (https://codepen.io/smashingmag/pen/YzMVQja) توسط هنری را ببینید.

تغییر موضوع فقط قلم CSS (نیاز به :has()) (forked) توسط هنری را ببینید.

بد نیست! اما اگر کاربر صفحات را رفرش کند یا به صفحه دیگری برود چه اتفاقی می افتد؟ بارگذاری مجدد به طور موثر فرم انتخابی کاربر را پاک می کند و کاربر را مجبور می کند تا دوباره انتخاب را انجام دهد. این ممکن است در برخی زمینه ها قابل قبول باشد، اما احتمالاً برخلاف انتظارات کاربر است. بیایید جاوا اسکریپت را برای بهبود تدریجی به شکل…

ماندگاری

در اینجا پیاده سازی جاوا اسکریپت وانیلی است. این یک نقطه شروع ساده است – توابع و متغیرها کپسوله نشده اند، اما در عوض ویژگی ها در 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);
}

بیایید آن را امتحان کنیم. این نسخه نمایشی را باز کنید (شاید در یک پنجره جدید)، از منو برای تغییر طرح رنگ استفاده کنید، و سپس صفحه را بازخوانی کنید تا ترجیحات خود را ادامه دهید:

قلم (تغییرکننده تم فقط با CSS (نیاز به :has()) با ماندگاری JS (فشاردار)) (https://codepen.io/smashingmag/pen/GRLmEXX) توسط هنری را ببینید.

تغییر موضوع فقط قلم CSS (نیاز به :has()) با ماندگاری JS (فشار گرفته شده) توسط Henry را ببینید.

اگر ترجیح طرح رنگ سیستم شما “روشن” است و طرح رنگ نمایشی را روی “تاریک” تنظیم کرده اید، ممکن است بلافاصله پس از بارگیری مجدد صفحه قبل از شروع سبک های حالت تاریک، سبک های حالت روشن را برای لحظه ای دریافت کنید. این به این دلیل است که CodePen آن را بارگیری می کند. قبل از اسکریپت های نمایشی، جاوا اسکریپت را داشته باشید. این خارج از کنترل من است، اما شما می توانید برای بهبود این پایداری در پروژه های خود مراقبت کنید.

ملاحظات عملکرد پایدار

جایی که کارها ممکن است مشکل ساز شوند، بازگرداندن اولویت کاربر است بلافاصله. مستقیما پس از بارگذاری صفحه اگر ترجیح طرح رنگ در localStorage با ترجیح طرح رنگ در سطح سیستم کاربر متفاوت است، ممکن است کاربر طرح رنگ ترجیحی سیستم را قبل از بازیابی اولویت سطح صفحه ببیند. (کاربرانی که گزینه «سیستم» را انتخاب کرده اند هرگز آن فلاش را دریافت نخواهند کرد؛ همچنین کسانی که تنظیمات سیستم آنها با گزینه انتخابی آنها در کنترل فرم مطابقت دارد.)

اگر پیاده‌سازی شما «فلش تم رنگی نادرست» را نشان می‌دهد، مشکل کجاست؟ به طور کلی، هرچه اسکریپت ها زودتر روی صفحه ظاهر شوند، خطر کمتری دارد. البته “بهترین گزینه” برای شما به پشته خاص شما بستگی دارد.

چه در مورد مرورگرهایی که پشتیبانی نمی کنند :has()?

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

اگر پشتیبانی از طرح رنگ خود را یک پیشرفت تدریجی در نظر می گیرید، می توانید رابط کاربری انتخابی را در مرورگرهایی که پشتیبانی نمی کنند کاملاً پنهان کنید. :has():

@supports not selector(:has(body)) {
  @media (prefers-color-scheme: dark) {
    :root {
      /* dark styles here */
    }
  }

  #color-scheme {
    display: none;
  }
}

در غیر این صورت، نه تنها برای تداوم، بلکه برای عملکرد اصلی باید به راه حل جاوا اسکریپت تکیه کنید. به آن شنونده رویداد سنتی با تغییر یک کلاس یا ویژگی برگردید.

CSS-Tricks “راهنمای کامل برای حالت تاریک” چندین رویکرد جایگزین را که ممکن است هنگام کار روی جنبه های قدیمی چیزها در نظر بگیرید، توضیح می دهد.

سرمقاله Smashing
(gg, yk)

]
منبع: https://smashingmagazine.com/2024/03/setting-persisting-color-scheme-preferences-css-javascript/