یک نمای کلی از نحوه ساخت یک جزء سوئیچ تم تطبیقی و در دسترس.
در این پست میخواهم فکری در مورد روشی برای ساخت یک جزء سوئیچ تم تیره و روشن به اشتراک بگذارم. نسخه ی نمایشی را امتحان کنید .
اگر ویدیو را ترجیح می دهید، در اینجا یک نسخه YouTube از این پست وجود دارد:
نمای کلی
یک وب سایت ممکن است تنظیماتی را برای کنترل طرح رنگ به جای تکیه کامل بر اولویت سیستم ارائه دهد. این بدان معنی است که کاربران ممکن است در حالتی غیر از تنظیمات برگزیده سیستم خود مرور کنند. به عنوان مثال، سیستم کاربر در یک تم روشن است، اما کاربر ترجیح می دهد وب سایت در تم تاریک نمایش داده شود.
هنگام ساخت این ویژگی چندین ملاحظات مهندسی وب وجود دارد. به عنوان مثال، مرورگر باید در اسرع وقت از این اولویت آگاه شود تا از فلش رنگ صفحه جلوگیری شود، و کنترل باید ابتدا با سیستم همگام شود و سپس استثناهای ذخیره شده در سمت کلاینت را مجاز کند.
نشانه گذاری
یک <button>
باید برای جابجایی استفاده شود، زیرا پس از آن از رویدادها و ویژگیهای تعاملی ارائهشده توسط مرورگر، مانند رویدادهای کلیک و قابلیت تمرکز بهره میبرید.
دکمه
این دکمه به یک کلاس برای استفاده از CSS و یک شناسه برای استفاده از جاوا اسکریپت نیاز دارد. علاوه بر این، از آنجایی که محتوای دکمه به جای متن یک نماد است، یک ویژگی عنوان اضافه کنید تا اطلاعاتی در مورد هدف دکمه ارائه دهید. در آخر، یک [aria-label]
برای نگه داشتن وضعیت دکمه نماد اضافه کنید، تا صفحهخوانها بتوانند وضعیت تم را با افرادی که دارای اختلال بینایی هستند به اشتراک بگذارند.
<button
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label="auto"
>
…
</button>
aria-label
و aria-live
مودب
برای اینکه به خوانندگان صفحه نمایش نشان دهید که تغییرات در aria-label
باید اعلام شود، aria-live="polite"
را به دکمه اضافه کنید.
<button
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label="auto"
aria-live="polite"
>
…
</button>
این افزوده نشانهگذاری به صفحهخوانها سیگنال میدهد که به جای aria-live="assertive"
مودبانه به کاربر بگویند چه چیزی تغییر کرده است. در مورد این دکمه، بسته به اینکه aria-label
به چه چیزی تبدیل شده است، "روشن" یا "تاریک" را اعلام می کند.
نماد گرافیک برداری مقیاس پذیر (SVG).
SVG راهی برای ایجاد اشکال با کیفیت بالا و مقیاس پذیر با حداقل نشانه گذاری فراهم می کند. تعامل با دکمه می تواند حالت های بصری جدیدی را برای بردارها ایجاد کند و SVG را برای آیکون ها عالی کند.
نشانه گذاری SVG زیر در داخل <button>
قرار می گیرد:
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
…
</svg>
aria-hidden
به عنصر SVG اضافه شده است، بنابراین خوانندگان صفحه میدانند که آن را نادیده بگیرند زیرا به عنوان نمایشی علامتگذاری شده است. این کار برای تزئینات بصری، مانند نماد داخل یک دکمه، عالی است. علاوه بر ویژگی viewBox
مورد نیاز در عنصر، ارتفاع و عرض را به دلایل مشابهی اضافه کنید که تصاویر باید اندازه های درون خطی داشته باشند .
خورشید
گرافیک خورشید از یک دایره و خطوط تشکیل شده است که SVG به راحتی دارای اشکال است. <circle>
با تنظیم ویژگیهای cx
و cy
روی 12، که نیمی از اندازه دید (24) است، در مرکز قرار میگیرد و سپس شعاع ( r
) 6
داده میشود که اندازه را تعیین میکند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
</svg>
علاوه بر این، ویژگی mask به شناسه عنصر SVG اشاره میکند که در مرحله بعد ایجاد میکنید، و در نهایت رنگ پر شدهای به آن داده میشود که با رنگ متن صفحه با currentColor
مطابقت داشته باشد.
نور خورشید می درخشد
سپس، خطوط پرتو خورشید درست در زیر دایره، در داخل یک گروه عنصر گروه <g>
اضافه می شوند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
<g class="sun-beams" stroke="currentColor">
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</g>
</svg>
این بار به جای اینکه مقدار fill currentColor
باشد، stroke هر خط تنظیم می شود. خطوط به علاوه اشکال دایره ای با پرتوهای خورشیدی زیبا ایجاد می کنند.
ماه
برای ایجاد توهم انتقال یکپارچه بین نور (خورشید) و تاریکی (ماه)، ماه با استفاده از یک ماسک SVG، نماد خورشید را تقویت می کند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
<g class="sun-beams" stroke="currentColor">
…
</g>
<mask class="moon" id="moon-mask">
<rect x="0" y="0" width="100%" height="100%" fill="white" />
<circle cx="24" cy="10" r="6" fill="black" />
</mask>
</svg>
ماسکهای دارای SVG قدرتمند هستند و به رنگهای سفید و سیاه اجازه میدهند بخشهایی از گرافیک دیگر را حذف یا شامل شوند. نماد خورشید توسط یک شکل <circle>
ماه با یک ماسک SVG، به سادگی با حرکت دادن یک شکل دایره ای به داخل و خارج از یک ناحیه ماسک، گرفته می شود.
چه اتفاقی می افتد اگر CSS بارگیری نشود؟
می تواند خوب باشد که SVG خود را آزمایش کنید، گویی CSS بارگیری نشده است تا مطمئن شوید که نتیجه فوق العاده بزرگ نیست یا باعث ایجاد مشکلاتی در چیدمان نمی شود. ویژگی های ارتفاع و عرض درونی در SVG به علاوه استفاده از currentColor
قوانین سبک حداقلی را برای مرورگر ارائه می دهد تا در صورت بار نشدن CSS از آن استفاده کند. این باعث می شود که سبک های دفاعی خوبی در برابر آشفتگی شبکه ایجاد شود.
طرح بندی
جزء سوئیچ تم دارای سطح کمی است، بنابراین برای چیدمان نیازی به شبکه یا فلکس باکس ندارید. در عوض، از موقعیتیابی SVG و تبدیلهای CSS استفاده میشود.
سبک ها
سبکهای .theme-toggle
عنصر <button>
محفظه ای برای شکل ها و سبک های نماد است. این زمینه والد رنگ ها و اندازه های تطبیقی را برای انتقال به SVG نگه می دارد.
اولین کار این است که دکمه را دایره ای کنید و سبک های دکمه پیش فرض را حذف کنید:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
}
بعد ، برخی از سبک های تعامل را اضافه کنید. برای کاربران ماوس یک سبک مکان نما اضافه کنید. افزودن touch-action: manipulation
برای تجربه لمسی با واکنش سریع . حذف هایلایت نیمه شفاف iOS روی دکمه ها اعمال می شود. در آخر، به حالت فوکوس کمی فضای تنفسی از لبه عنصر بدهید:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
outline-offset: 5px;
}
SVG داخل دکمه نیز به برخی سبک ها نیاز دارد. SVG باید متناسب با اندازه دکمه باشد و برای نرمی بصری، انتهای خط را گرد کند:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
outline-offset: 5px;
& > svg {
inline-size: 100%;
block-size: 100%;
stroke-linecap: round;
}
}
اندازه تطبیقی با پرس و جو رسانه hover
اندازه دکمه آیکون کمی کوچک در 2rem
است، که برای کاربران ماوس خوب است، اما می تواند برای یک اشاره گر درشت مانند یک انگشت مبارزه کند. با استفاده از جستجوی رسانه شناور برای تعیین افزایش اندازه، دکمه را مطابق با بسیاری از دستورالعملهای اندازه لمسی قرار دهید.
.theme-toggle {
--size: 2rem;
…
@media (hover: none) {
--size: 48px;
}
}
سبک های SVG خورشید و ماه
دکمه جنبه های تعاملی جزء سوئیچ تم را نگه می دارد در حالی که SVG در داخل جنبه های بصری و متحرک را نگه می دارد. اینجاست که می توان نماد را زیبا کرد و زنده کرد.
تم سبک
برای اینکه انیمیشنها مقیاس و چرخش از مرکز اشکال SVG اتفاق بیفتند، transform-origin: center center
. رنگ های تطبیقی ارائه شده توسط دکمه در اینجا توسط اشکال استفاده می شود. ماه و خورشید از دکمه ارائه شده var(--icon-fill)
و var(--icon-fill-hover)
برای پر شدن خود استفاده می کنند، در حالی که پرتوهای خورشید از متغیرها برای stroke استفاده می کنند.
.sun-and-moon {
& > :is(.moon, .sun, .sun-beams) {
transform-origin: center center;
}
& > :is(.moon, .sun) {
fill: var(--icon-fill);
@nest .theme-toggle:is(:hover, :focus-visible) > & {
fill: var(--icon-fill-hover);
}
}
& > .sun-beams {
stroke: var(--icon-fill);
stroke-width: 2px;
@nest .theme-toggle:is(:hover, :focus-visible) & {
stroke: var(--icon-fill-hover);
}
}
}
تم تاریک
سبک های ماه باید پرتوهای خورشید را حذف کنند، دایره خورشید را افزایش دهند و ماسک دایره را حرکت دهند.
.sun-and-moon {
@nest [data-theme="dark"] & {
& > .sun {
transform: scale(1.75);
}
& > .sun-beams {
opacity: 0;
}
& > .moon > circle {
transform: translateX(-7px);
@supports (cx: 1px) {
transform: translateX(0);
cx: 17px;
}
}
}
}
توجه داشته باشید که تم تیره هیچ تغییر یا انتقال رنگی ندارد. مولفه دکمه والد مالک رنگ ها است، جایی که آنها در حال حاضر در یک زمینه تاریک و روشن سازگار هستند. اطلاعات انتقال باید پشت درخواست رسانه ترجیحی حرکت کاربر باشد.
انیمیشن
دکمه باید عملکردی و حالت دار باشد اما در این مرحله بدون انتقال باشد. بخش های زیر همه در مورد تعریف چگونگی و چه انتقال است.
به اشتراک گذاری درخواست های رسانه ای و واردات تسهیلات
برای سهولت قرار دادن انتقال ها و انیمیشن ها در پشت تنظیمات برگزیده حرکت سیستم عامل کاربر، افزونه PostCSS Custom Media استفاده از مشخصات CSS پیش نویس شده را برای نحو متغیرهای درخواست رسانه امکان پذیر می کند:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
/* usage example */
@media (--motionOK) {
.sun {
transition: transform .5s var(--ease-elastic-3);
}
}
برای سادهسازیهای CSS منحصربهفرد و آسان برای استفاده، بخش easings Open Props را وارد کنید:
@import "https://unpkg.com/open-props/easings.min.css";
/* usage example */
.sun {
transition: transform .5s var(--ease-elastic-3);
}
خورشید
انتقال خورشید نسبت به ماه بازیگوشتر خواهد بود و این اثر را با کاهشهای فنری به دست میآورد. پرتوهای خورشید باید در حین چرخش مقدار کمی تابیده شوند و مرکز خورشید در حین بزرگ شدن باید مقدار کمی تابیده شود.
سبکهای پیشفرض (موضوع روشن) انتقالها را تعریف میکنند و سبکهای تم تیره، سفارشیسازیهایی را برای انتقال به روشن تعریف میکنند:
.sun-and-moon {
@media (--motionOK) {
& > .sun {
transition: transform .5s var(--ease-elastic-3);
}
& > .sun-beams {
transition:
transform .5s var(--ease-elastic-4),
opacity .5s var(--ease-3)
;
}
@nest [data-theme="dark"] & {
& > .sun {
transform: scale(1.75);
transition-timing-function: var(--ease-3);
transition-duration: .25s;
}
& > .sun-beams {
transform: rotateZ(-25deg);
transition-duration: .15s;
}
}
}
}
در پانل انیمیشن در Chrome DevTools، میتوانید یک جدول زمانی برای انتقال انیمیشن پیدا کنید. مدت زمان کل انیمیشن، عناصر و زمان بندی کاهش قابل بررسی است.
ماه
موقعیتهای روشن و تاریک ماه قبلاً تنظیم شدهاند، سبکهای انتقالی را در جستوجوی رسانه --motionOK
اضافه کنید تا ضمن احترام به اولویتهای حرکتی کاربر، آن را زنده کنید.
زمان بندی با تاخیر و مدت زمان در تمیز کردن این انتقال بسیار مهم است. برای مثال، اگر خورشید خیلی زود گرفته شود، انتقال به نظر هماهنگ یا بازیگوش نیست، احساس آشفتگی می کند.
.sun-and-moon {
@media (--motionOK) {
& .moon > circle {
transform: translateX(-7px);
transition: transform .25s var(--ease-out-5);
@supports (cx: 1px) {
transform: translateX(0);
cx: 17px;
transition: cx .25s var(--ease-out-5);
}
}
@nest [data-theme="dark"] & {
& > .moon > circle {
transition-delay: .25s;
transition-duration: .5s;
}
}
}
}
کاهش حرکت را ترجیح می دهد
در بیشتر چالشهای رابط کاربری گرافیکی، من سعی میکنم برخی از انیمیشنها را برای کاربرانی که حرکت کاهشیافته را ترجیح میدهند، نگه دارم. با این حال، این مؤلفه با تغییرات فوری حالت بهتر احساس می شود.
جاوا اسکریپت
کارهای زیادی برای جاوا اسکریپت در این مؤلفه وجود دارد، از مدیریت اطلاعات ARIA برای صفحهخوانها تا دریافت و تنظیم مقادیر از حافظه محلی .
تجربه بارگذاری صفحه
مهم این بود که در بارگذاری صفحه هیچ چشمک زن رخ ندهد. اگر کاربری با طرح رنگ تیره نشان دهد که نور را با این مؤلفه ترجیح می دهد، سپس صفحه را مجدداً بارگیری می کند، ابتدا صفحه تاریک می شود و سپس به نور چشمک می زند. جلوگیری از این امر به معنای اجرای مقدار کمی از مسدود کردن جاوا اسکریپت با هدف تنظیم هرچه زودتر data-theme
ویژگی HTML بود.
<script src="./theme-toggle.js"></script>
برای رسیدن به این هدف، ابتدا یک تگ <script>
ساده در سند <head>
قبل از هر نشانه گذاری CSS یا <body>
بارگذاری می شود. هنگامی که مرورگر با یک اسکریپت بدون علامت مانند این روبرو می شود، کد را اجرا می کند و آن را قبل از بقیه HTML اجرا می کند. با استفاده کم از این لحظه مسدود کردن، می توان قبل از اینکه CSS اصلی صفحه را نقاشی کند، ویژگی HTML را تنظیم کرد، بنابراین از فلاش یا رنگ ها جلوگیری کرد.
جاوا اسکریپت ابتدا ترجیحات کاربر را در فضای ذخیره سازی محلی بررسی می کند و اگر چیزی در فضای ذخیره سازی یافت نشد، ترجیحات سیستم را بررسی می کند:
const storageKey = 'theme-preference'
const getColorPreference = () => {
if (localStorage.getItem(storageKey))
return localStorage.getItem(storageKey)
else
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
}
تابعی برای تنظیم اولویت کاربر در حافظه محلی در ادامه تجزیه می شود:
const setPreference = () => {
localStorage.setItem(storageKey, theme.value)
reflectPreference()
}
به دنبال آن یک تابع برای تغییر سند با تنظیمات برگزیده.
const reflectPreference = () => {
document.firstElementChild
.setAttribute('data-theme', theme.value)
document
.querySelector('#theme-toggle')
?.setAttribute('aria-label', theme.value)
}
نکته مهمی که در این مرحله باید به آن توجه کرد، وضعیت تجزیه اسناد HTML است. مرورگر هنوز از دکمه "#theme-toggle" اطلاعی ندارد، زیرا تگ <head>
به طور کامل تجزیه نشده است. با این حال، مرورگر دارای یک document.firstElementChild
، با نام تگ <html>
است. این تابع سعی می کند هر دو را برای همگام نگه داشتن آنها تنظیم کند، اما در اولین اجرا فقط می تواند تگ HTML را تنظیم کند. querySelector
در ابتدا چیزی را پیدا نمیکند و اپراتور اختیاری زنجیرهای تضمین میکند که در صورت یافت نشدن و تلاش برای فراخوانی تابع setAttribute، خطای نحوی وجود نداشته باشد.
سپس، تابع reflectPreference()
بلافاصله فراخوانی میشود تا سند HTML مجموعه ویژگیهای data-theme
خود را داشته باشد:
reflectPreference()
دکمه همچنان به ویژگی نیاز دارد، بنابراین منتظر رویداد بارگیری صفحه باشید، سپس پرس و جو، افزودن شنوندگان و تنظیم ویژگیها در آن امن خواهد بود:
window.onload = () => {
// set on load so screen readers can get the latest value on the button
reflectPreference()
// now this script can find and listen for clicks on the control
document
.querySelector('#theme-toggle')
.addEventListener('click', onClick)
}
تجربه جابجایی
هنگامی که دکمه کلیک می شود، موضوع باید در حافظه جاوا اسکریپت و در سند تعویض شود. ارزش موضوع فعلی باید بررسی شود و درباره وضعیت جدید آن تصمیم گیری شود. پس از تنظیم حالت جدید، آن را ذخیره کرده و سند را به روز کنید:
const onClick = () => {
theme.value = theme.value === 'light'
? 'dark'
: 'light'
setPreference()
}
همگام سازی با سیستم
منحصر به فرد این سوئیچ تم، همگام سازی با اولویت سیستم در حین تغییر است. اگر کاربر در حالی که صفحه و این مؤلفه قابل مشاهده است، تنظیمات برگزیده سیستم خود را تغییر دهد، سوئیچ تم برای مطابقت با اولویت کاربر جدید تغییر میکند، گویی که کاربر همزمان با سوئیچ موضوع، با سوئیچ تم تعامل داشته است.
این کار را با جاوا اسکریپت و رویداد matchMedia
که به تغییرات یک درخواست رسانه گوش می دهد، برسید:
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', ({matches:isDark}) => {
theme.value = isDark ? 'dark' : 'light'
setPreference()
})
نتیجه گیری
حالا که می دانید من چگونه این کار را انجام دادم، چگونه این کار را انجام می دهید‽🙂
بیایید رویکردهایمان را متنوع کنیم و همه راههای ساخت در وب را بیاموزیم. یک نسخه نمایشی ایجاد کنید، پیوندها را برای من توییت کنید ، و من آن را به بخش ریمیکس های انجمن در زیر اضافه می کنم!
ریمیکس های انجمن
- @NathanG در Codepen با Vue
- @ShadowShahriar در کدپن
- @tomayac به عنوان یک عنصر سفارشی
- @bramus با جاوا اسکریپت وانیلی
- @JoshWComeau با واکنش
یک نمای کلی از نحوه ساخت یک جزء سوئیچ تم تطبیقی و در دسترس.
در این پست میخواهم فکری در مورد روشی برای ساخت یک جزء سوئیچ تم تیره و روشن به اشتراک بگذارم. نسخه ی نمایشی را امتحان کنید .
اگر ویدیو را ترجیح می دهید، در اینجا یک نسخه YouTube از این پست وجود دارد:
نمای کلی
یک وب سایت ممکن است تنظیماتی را برای کنترل طرح رنگ به جای تکیه کامل بر اولویت سیستم ارائه دهد. این بدان معنی است که کاربران ممکن است در حالتی غیر از تنظیمات برگزیده سیستم خود مرور کنند. به عنوان مثال، سیستم کاربر در یک تم روشن است، اما کاربر ترجیح می دهد وب سایت در تم تاریک نمایش داده شود.
هنگام ساخت این ویژگی چندین ملاحظات مهندسی وب وجود دارد. به عنوان مثال، مرورگر باید در اسرع وقت از این اولویت آگاه شود تا از فلش رنگ صفحه جلوگیری شود، و کنترل باید ابتدا با سیستم همگام شود و سپس استثناهای ذخیره شده در سمت کلاینت را مجاز کند.
نشانه گذاری
یک <button>
باید برای جابجایی استفاده شود، زیرا پس از آن از رویدادها و ویژگیهای تعاملی ارائهشده توسط مرورگر، مانند رویدادهای کلیک و قابلیت تمرکز بهره میبرید.
دکمه
این دکمه به یک کلاس برای استفاده از CSS و یک شناسه برای استفاده از جاوا اسکریپت نیاز دارد. علاوه بر این، از آنجایی که محتوای دکمه به جای متن یک نماد است، یک ویژگی عنوان اضافه کنید تا اطلاعاتی در مورد هدف دکمه ارائه دهید. در آخر، یک [aria-label]
برای نگه داشتن وضعیت دکمه نماد اضافه کنید، تا صفحهخوانها بتوانند وضعیت تم را با افرادی که دارای اختلال بینایی هستند به اشتراک بگذارند.
<button
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label="auto"
>
…
</button>
aria-label
و aria-live
مودب
برای اینکه به خوانندگان صفحه نمایش نشان دهید که تغییرات در aria-label
باید اعلام شود، aria-live="polite"
را به دکمه اضافه کنید.
<button
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label="auto"
aria-live="polite"
>
…
</button>
این افزوده نشانهگذاری به صفحهخوانها سیگنال میدهد که به جای aria-live="assertive"
مودبانه به کاربر بگویند چه چیزی تغییر کرده است. در مورد این دکمه، بسته به اینکه aria-label
به چه چیزی تبدیل شده است، "روشن" یا "تاریک" را اعلام می کند.
نماد گرافیک برداری مقیاس پذیر (SVG).
SVG راهی برای ایجاد اشکال با کیفیت بالا و مقیاس پذیر با حداقل نشانه گذاری فراهم می کند. تعامل با دکمه می تواند حالت های بصری جدیدی را برای بردارها ایجاد کند و SVG را برای آیکون ها عالی کند.
نشانه گذاری SVG زیر در داخل <button>
قرار می گیرد:
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
…
</svg>
aria-hidden
به عنصر SVG اضافه شده است، بنابراین خوانندگان صفحه میدانند که آن را نادیده بگیرند زیرا به عنوان نمایشی علامتگذاری شده است. این کار برای تزئینات بصری، مانند نماد داخل یک دکمه، عالی است. علاوه بر ویژگی viewBox
مورد نیاز در عنصر، ارتفاع و عرض را به دلایل مشابهی اضافه کنید که تصاویر باید اندازه های درون خطی داشته باشند .
خورشید
گرافیک خورشید از یک دایره و خطوط تشکیل شده است که SVG به راحتی دارای اشکال است. <circle>
با تنظیم ویژگیهای cx
و cy
روی 12، که نیمی از اندازه دید (24) است، در مرکز قرار میگیرد و سپس شعاع ( r
) 6
داده میشود که اندازه را تعیین میکند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
</svg>
علاوه بر این، ویژگی mask به شناسه عنصر SVG اشاره میکند که در مرحله بعد ایجاد میکنید، و در نهایت رنگ پر شدهای به آن داده میشود که با رنگ متن صفحه با currentColor
مطابقت داشته باشد.
نور خورشید می درخشد
سپس، خطوط پرتو خورشید درست در زیر دایره، در داخل یک گروه عنصر گروه <g>
اضافه می شوند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
<g class="sun-beams" stroke="currentColor">
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</g>
</svg>
این بار به جای اینکه مقدار fill currentColor
باشد، stroke هر خط تنظیم می شود. خطوط به علاوه اشکال دایره ای با پرتوهای خورشیدی زیبا ایجاد می کنند.
ماه
برای ایجاد توهم انتقال یکپارچه بین نور (خورشید) و تاریکی (ماه)، ماه با استفاده از یک ماسک SVG، نماد خورشید را تقویت می کند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
<g class="sun-beams" stroke="currentColor">
…
</g>
<mask class="moon" id="moon-mask">
<rect x="0" y="0" width="100%" height="100%" fill="white" />
<circle cx="24" cy="10" r="6" fill="black" />
</mask>
</svg>
ماسکهای دارای SVG قدرتمند هستند و به رنگهای سفید و سیاه اجازه میدهند بخشهایی از گرافیک دیگر را حذف یا شامل شوند. نماد خورشید توسط یک شکل <circle>
ماه با یک ماسک SVG، به سادگی با حرکت دادن یک شکل دایره ای به داخل و خارج از یک ناحیه ماسک، گرفته می شود.
اگر CSS بارگیری نشود چه اتفاقی می افتد؟
می تواند خوب باشد که SVG خود را آزمایش کنید، گویی CSS بارگیری نشده است تا مطمئن شوید که نتیجه فوق العاده بزرگ نیست یا باعث ایجاد مشکلاتی در چیدمان نمی شود. ویژگی های ارتفاع و عرض درونی در SVG به علاوه استفاده از currentColor
قوانین سبک حداقلی را برای مرورگر ارائه می دهد تا در صورت بار نشدن CSS از آن استفاده کند. این باعث می شود که سبک های دفاعی خوبی در برابر آشفتگی شبکه ایجاد شود.
طرح بندی
جزء سوئیچ تم دارای سطح کمی است، بنابراین برای چیدمان نیازی به شبکه یا فلکس باکس ندارید. در عوض، از موقعیتیابی SVG و تبدیلهای CSS استفاده میشود.
سبک ها
سبکهای .theme-toggle
عنصر <button>
محفظه ای برای شکل ها و سبک های نماد است. این زمینه والد رنگ ها و اندازه های تطبیقی را برای انتقال به SVG نگه می دارد.
اولین کار این است که دکمه را دایره ای کنید و سبک های دکمه پیش فرض را حذف کنید:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
}
بعد ، برخی از سبک های تعامل را اضافه کنید. برای کاربران ماوس یک سبک مکان نما اضافه کنید. افزودن touch-action: manipulation
برای تجربه لمسی با واکنش سریع . حذف هایلایت نیمه شفاف iOS روی دکمه ها اعمال می شود. در آخر، به حالت فوکوس کمی فضای تنفسی از لبه عنصر بدهید:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
outline-offset: 5px;
}
SVG داخل دکمه نیز به برخی سبک ها نیاز دارد. SVG باید متناسب با اندازه دکمه باشد و برای نرمی بصری، انتهای خط را گرد کند:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
outline-offset: 5px;
& > svg {
inline-size: 100%;
block-size: 100%;
stroke-linecap: round;
}
}
اندازه تطبیقی با جستجوی رسانه hover
اندازه دکمه آیکون کمی کوچک در 2rem
است، که برای کاربران ماوس خوب است، اما می تواند برای یک اشاره گر درشت مانند یک انگشت مبارزه کند. با استفاده از جستجوی رسانه شناور برای تعیین افزایش اندازه، دکمه را مطابق با بسیاری از دستورالعملهای اندازه لمسی قرار دهید.
.theme-toggle {
--size: 2rem;
…
@media (hover: none) {
--size: 48px;
}
}
سبک های SVG خورشید و ماه
دکمه جنبه های تعاملی جزء سوئیچ تم را نگه می دارد در حالی که SVG در داخل جنبه های بصری و متحرک را نگه می دارد. اینجاست که می توان نماد را زیبا کرد و زنده کرد.
تم سبک
برای اینکه انیمیشنها مقیاس و چرخش از مرکز اشکال SVG اتفاق بیفتند، transform-origin: center center
. رنگ های تطبیقی ارائه شده توسط دکمه در اینجا توسط اشکال استفاده می شود. ماه و خورشید از دکمه ارائه شده var(--icon-fill)
و var(--icon-fill-hover)
برای پر شدن خود استفاده می کنند، در حالی که پرتوهای خورشید از متغیرها برای stroke استفاده می کنند.
.sun-and-moon {
& > :is(.moon, .sun, .sun-beams) {
transform-origin: center center;
}
& > :is(.moon, .sun) {
fill: var(--icon-fill);
@nest .theme-toggle:is(:hover, :focus-visible) > & {
fill: var(--icon-fill-hover);
}
}
& > .sun-beams {
stroke: var(--icon-fill);
stroke-width: 2px;
@nest .theme-toggle:is(:hover, :focus-visible) & {
stroke: var(--icon-fill-hover);
}
}
}
تم تاریک
سبک های ماه باید پرتوهای خورشید را حذف کنند، دایره خورشید را افزایش دهند و ماسک دایره را حرکت دهند.
.sun-and-moon {
@nest [data-theme="dark"] & {
& > .sun {
transform: scale(1.75);
}
& > .sun-beams {
opacity: 0;
}
& > .moon > circle {
transform: translateX(-7px);
@supports (cx: 1px) {
transform: translateX(0);
cx: 17px;
}
}
}
}
توجه داشته باشید که تم تیره هیچ تغییر یا انتقال رنگی ندارد. مولفه دکمه والد مالک رنگ ها است، جایی که آنها در حال حاضر در یک زمینه تاریک و روشن سازگار هستند. اطلاعات انتقال باید پشت درخواست رسانه ترجیحی حرکت کاربر باشد.
انیمیشن
دکمه باید عملکردی و حالت دار باشد اما در این مرحله بدون انتقال باشد. بخش های زیر همه در مورد تعریف چگونگی و چه انتقال است.
به اشتراک گذاری درخواست های رسانه ای و واردات تسهیلات
برای سهولت قرار دادن انتقال ها و انیمیشن ها در پشت تنظیمات برگزیده حرکت سیستم عامل کاربر، افزونه PostCSS Custom Media استفاده از مشخصات CSS پیش نویس شده را برای نحو متغیرهای درخواست رسانه امکان پذیر می کند:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
/* usage example */
@media (--motionOK) {
.sun {
transition: transform .5s var(--ease-elastic-3);
}
}
برای سادهسازیهای CSS منحصربهفرد و آسان برای استفاده، بخش easings Open Props را وارد کنید:
@import "https://unpkg.com/open-props/easings.min.css";
/* usage example */
.sun {
transition: transform .5s var(--ease-elastic-3);
}
خورشید
انتقال خورشید نسبت به ماه بازیگوشتر خواهد بود و این اثر را با کاهشهای فنری به دست میآورد. پرتوهای خورشید باید در حین چرخش مقدار کمی تابیده شوند و مرکز خورشید در حین بزرگ شدن باید مقدار کمی تابیده شود.
سبکهای پیشفرض (موضوع روشن) انتقالها را تعریف میکنند و سبکهای تم تیره، سفارشیسازیهایی را برای انتقال به روشن تعریف میکنند:
.sun-and-moon {
@media (--motionOK) {
& > .sun {
transition: transform .5s var(--ease-elastic-3);
}
& > .sun-beams {
transition:
transform .5s var(--ease-elastic-4),
opacity .5s var(--ease-3)
;
}
@nest [data-theme="dark"] & {
& > .sun {
transform: scale(1.75);
transition-timing-function: var(--ease-3);
transition-duration: .25s;
}
& > .sun-beams {
transform: rotateZ(-25deg);
transition-duration: .15s;
}
}
}
}
در پانل انیمیشن در Chrome DevTools، میتوانید یک جدول زمانی برای انتقال انیمیشن پیدا کنید. مدت زمان کل انیمیشن، عناصر و زمان بندی کاهش قابل بررسی است.
ماه
موقعیتهای روشن و تاریک ماه قبلاً تنظیم شدهاند، سبکهای انتقالی را در جستوجوی رسانه --motionOK
اضافه کنید تا ضمن احترام به اولویتهای حرکتی کاربر، آن را زنده کنید.
زمان بندی با تاخیر و مدت زمان در تمیز کردن این انتقال بسیار مهم است. برای مثال، اگر خورشید خیلی زود گرفته شود، انتقال به نظر هماهنگ یا بازیگوش نیست، احساس آشفتگی می کند.
.sun-and-moon {
@media (--motionOK) {
& .moon > circle {
transform: translateX(-7px);
transition: transform .25s var(--ease-out-5);
@supports (cx: 1px) {
transform: translateX(0);
cx: 17px;
transition: cx .25s var(--ease-out-5);
}
}
@nest [data-theme="dark"] & {
& > .moon > circle {
transition-delay: .25s;
transition-duration: .5s;
}
}
}
}
کاهش حرکت را ترجیح می دهد
در بیشتر چالشهای رابط کاربری گرافیکی، من سعی میکنم برخی از انیمیشنها را برای کاربرانی که حرکت کاهشیافته را ترجیح میدهند، نگه دارم. با این حال، این مؤلفه با تغییرات فوری حالت بهتر احساس می شود.
جاوا اسکریپت
کارهای زیادی برای جاوا اسکریپت در این مؤلفه وجود دارد، از مدیریت اطلاعات ARIA برای صفحهخوانها تا دریافت و تنظیم مقادیر از حافظه محلی .
تجربه بارگذاری صفحه
مهم این بود که در بارگذاری صفحه هیچ چشمک زن رخ ندهد. اگر کاربری با طرح رنگ تیره نشان دهد که نور را با این مؤلفه ترجیح می دهد، سپس صفحه را مجدداً بارگیری می کند، ابتدا صفحه تاریک می شود و سپس به نور چشمک می زند. جلوگیری از این امر به معنای اجرای مقدار کمی از مسدود کردن جاوا اسکریپت با هدف تنظیم هرچه زودتر data-theme
ویژگی HTML بود.
<script src="./theme-toggle.js"></script>
برای رسیدن به این هدف، ابتدا یک تگ <script>
ساده در سند <head>
قبل از هر نشانه گذاری CSS یا <body>
بارگذاری می شود. هنگامی که مرورگر با یک اسکریپت بدون علامت مانند این روبرو می شود، کد را اجرا می کند و آن را قبل از بقیه HTML اجرا می کند. با استفاده کم از این لحظه مسدود کردن، می توان قبل از اینکه CSS اصلی صفحه را نقاشی کند، ویژگی HTML را تنظیم کرد، بنابراین از فلاش یا رنگ ها جلوگیری کرد.
جاوا اسکریپت ابتدا ترجیحات کاربر را در فضای ذخیره سازی محلی بررسی می کند و اگر چیزی در فضای ذخیره سازی یافت نشد، ترجیحات سیستم را بررسی می کند:
const storageKey = 'theme-preference'
const getColorPreference = () => {
if (localStorage.getItem(storageKey))
return localStorage.getItem(storageKey)
else
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
}
تابعی برای تنظیم اولویت کاربر در حافظه محلی در ادامه تجزیه می شود:
const setPreference = () => {
localStorage.setItem(storageKey, theme.value)
reflectPreference()
}
به دنبال آن یک تابع برای تغییر سند با تنظیمات برگزیده.
const reflectPreference = () => {
document.firstElementChild
.setAttribute('data-theme', theme.value)
document
.querySelector('#theme-toggle')
?.setAttribute('aria-label', theme.value)
}
نکته مهمی که در این مرحله باید به آن توجه کرد، وضعیت تجزیه اسناد HTML است. مرورگر هنوز از دکمه "#theme-toggle" اطلاعی ندارد، زیرا تگ <head>
به طور کامل تجزیه نشده است. با این حال، مرورگر دارای یک document.firstElementChild
، با نام تگ <html>
است. این تابع سعی می کند هر دو را برای همگام نگه داشتن آنها تنظیم کند، اما در اولین اجرا فقط می تواند تگ HTML را تنظیم کند. querySelector
در ابتدا چیزی را پیدا نمیکند و اپراتور اختیاری زنجیرهای تضمین میکند که در صورت یافت نشدن و تلاش برای فراخوانی تابع setAttribute، خطای نحوی وجود نداشته باشد.
سپس، تابع reflectPreference()
بلافاصله فراخوانی میشود تا سند HTML مجموعه ویژگیهای data-theme
خود را داشته باشد:
reflectPreference()
دکمه همچنان به ویژگی نیاز دارد، بنابراین منتظر رویداد بارگیری صفحه باشید، سپس پرس و جو، افزودن شنوندگان و تنظیم ویژگیها در آن امن خواهد بود:
window.onload = () => {
// set on load so screen readers can get the latest value on the button
reflectPreference()
// now this script can find and listen for clicks on the control
document
.querySelector('#theme-toggle')
.addEventListener('click', onClick)
}
تجربه جابجایی
هنگامی که دکمه کلیک می شود، موضوع باید در حافظه جاوا اسکریپت و در سند تعویض شود. ارزش موضوع فعلی باید بررسی شود و درباره وضعیت جدید آن تصمیم گیری شود. پس از تنظیم حالت جدید، آن را ذخیره کرده و سند را به روز کنید:
const onClick = () => {
theme.value = theme.value === 'light'
? 'dark'
: 'light'
setPreference()
}
همگام سازی با سیستم
منحصر به فرد این سوئیچ تم، همگام سازی با اولویت سیستم در حین تغییر است. اگر کاربر در حالی که صفحه و این مؤلفه قابل مشاهده است، تنظیمات برگزیده سیستم خود را تغییر دهد، سوئیچ تم برای مطابقت با اولویت کاربر جدید تغییر میکند، گویی که کاربر همزمان با سوئیچ موضوع، با سوئیچ تم تعامل داشته است.
این کار را با جاوا اسکریپت و رویداد matchMedia
که به تغییرات یک درخواست رسانه گوش می دهد، برسید:
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', ({matches:isDark}) => {
theme.value = isDark ? 'dark' : 'light'
setPreference()
})
نتیجه گیری
حالا که می دانید من چگونه این کار را انجام دادم، چگونه این کار را انجام می دهید‽🙂
بیایید رویکردهایمان را متنوع کنیم و همه راههای ساخت در وب را بیاموزیم. یک نسخه نمایشی ایجاد کنید، پیوندها را برای من توییت کنید ، و من آن را به بخش ریمیکس های انجمن در زیر اضافه می کنم!
ریمیکس های انجمن
- @NathanG در Codepen با Vue
- @ShadowShahriar در کدپن
- @tomayac به عنوان یک عنصر سفارشی
- @bramus با جاوا اسکریپت وانیلی
- @JoshWComeau با واکنش
یک نمای کلی از نحوه ساخت یک جزء سوئیچ تم تطبیقی و در دسترس.
در این پست میخواهم فکری در مورد روشی برای ساخت یک جزء سوئیچ تم تیره و روشن به اشتراک بگذارم. نسخه ی نمایشی را امتحان کنید .
اگر ویدیو را ترجیح می دهید، در اینجا یک نسخه YouTube از این پست وجود دارد:
نمای کلی
یک وب سایت ممکن است تنظیماتی را برای کنترل طرح رنگ به جای تکیه کامل بر اولویت سیستم ارائه دهد. این بدان معنی است که کاربران ممکن است در حالتی غیر از تنظیمات برگزیده سیستم خود مرور کنند. به عنوان مثال، سیستم کاربر در یک تم روشن است، اما کاربر ترجیح می دهد وب سایت در تم تاریک نمایش داده شود.
هنگام ساخت این ویژگی چندین ملاحظات مهندسی وب وجود دارد. به عنوان مثال، مرورگر باید در اسرع وقت از این اولویت آگاه شود تا از فلش رنگ صفحه جلوگیری شود، و کنترل باید ابتدا با سیستم همگام شود و سپس استثناهای ذخیره شده در سمت کلاینت را مجاز کند.
نشانه گذاری
یک <button>
باید برای جابجایی استفاده شود، زیرا پس از آن از رویدادها و ویژگیهای تعاملی ارائهشده توسط مرورگر، مانند رویدادهای کلیک و قابلیت تمرکز بهره میبرید.
دکمه
این دکمه به یک کلاس برای استفاده از CSS و یک شناسه برای استفاده از جاوا اسکریپت نیاز دارد. علاوه بر این، از آنجایی که محتوای دکمه به جای متن یک نماد است، یک ویژگی عنوان اضافه کنید تا اطلاعاتی در مورد هدف دکمه ارائه دهید. در آخر، یک [aria-label]
برای نگه داشتن وضعیت دکمه نماد اضافه کنید، تا صفحهخوانها بتوانند وضعیت تم را با افرادی که دارای اختلال بینایی هستند به اشتراک بگذارند.
<button
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label="auto"
>
…
</button>
aria-label
و aria-live
مودب
برای اینکه به خوانندگان صفحه نمایش نشان دهید که تغییرات در aria-label
باید اعلام شود، aria-live="polite"
را به دکمه اضافه کنید.
<button
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label="auto"
aria-live="polite"
>
…
</button>
این افزوده نشانهگذاری به صفحهخوانها سیگنال میدهد که به جای aria-live="assertive"
مودبانه به کاربر بگویند چه چیزی تغییر کرده است. در مورد این دکمه، بسته به اینکه aria-label
به چه چیزی تبدیل شده است، "روشن" یا "تاریک" را اعلام می کند.
نماد گرافیک بردار مقیاس پذیر (SVG)
SVG راهی برای ایجاد اشکال با کیفیت بالا و مقیاس پذیر با حداقل نشانه گذاری فراهم می کند. تعامل با دکمه می تواند حالت های بصری جدیدی را برای بردارها ایجاد کند و SVG را برای آیکون ها عالی کند.
نشانه گذاری SVG زیر در داخل <button>
قرار می گیرد:
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
…
</svg>
aria-hidden
به عنصر SVG اضافه شده است، بنابراین خوانندگان صفحه میدانند که آن را نادیده بگیرند زیرا به عنوان نمایشی علامتگذاری شده است. این کار برای تزئینات بصری، مانند نماد داخل یک دکمه، عالی است. علاوه بر ویژگی viewBox
مورد نیاز در عنصر، ارتفاع و عرض را به دلایل مشابهی اضافه کنید که تصاویر باید اندازه های درون خطی داشته باشند .
خورشید
گرافیک خورشید از یک دایره و خطوط تشکیل شده است که SVG به راحتی دارای اشکال است. <circle>
با تنظیم ویژگیهای cx
و cy
روی 12، که نیمی از اندازه دید (24) است، در مرکز قرار میگیرد و سپس شعاع ( r
) 6
داده میشود که اندازه را تعیین میکند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
</svg>
علاوه بر این، ویژگی mask به شناسه عنصر SVG اشاره میکند که در مرحله بعد ایجاد میکنید، و در نهایت رنگ پر شدهای به آن داده میشود که با رنگ متن صفحه با currentColor
مطابقت داشته باشد.
پرتوهای خورشید
سپس، خطوط پرتو خورشید درست در زیر دایره، در داخل یک گروه عنصر گروه <g>
اضافه می شوند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
<g class="sun-beams" stroke="currentColor">
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</g>
</svg>
این بار به جای اینکه مقدار fill currentColor
باشد، stroke هر خط تنظیم می شود. خطوط به علاوه اشکال دایره ای با پرتوهای خورشیدی زیبا ایجاد می کنند.
ماه
برای ایجاد توهم انتقال یکپارچه بین نور (خورشید) و تاریکی (ماه)، ماه با استفاده از یک ماسک SVG، نماد خورشید را تقویت می کند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
<g class="sun-beams" stroke="currentColor">
…
</g>
<mask class="moon" id="moon-mask">
<rect x="0" y="0" width="100%" height="100%" fill="white" />
<circle cx="24" cy="10" r="6" fill="black" />
</mask>
</svg>
ماسکهای دارای SVG قدرتمند هستند و به رنگهای سفید و سیاه اجازه میدهند بخشهایی از گرافیک دیگر را حذف یا شامل شوند. نماد خورشید توسط یک شکل <circle>
ماه با یک ماسک SVG، به سادگی با حرکت دادن یک شکل دایره ای به داخل و خارج از یک ناحیه ماسک، گرفته می شود.
اگر CSS بارگیری نشود چه اتفاقی می افتد؟
می تواند خوب باشد که SVG خود را آزمایش کنید، گویی CSS بارگیری نشده است تا مطمئن شوید که نتیجه فوق العاده بزرگ نیست یا باعث ایجاد مشکلاتی در چیدمان نمی شود. ویژگی های ارتفاع و عرض درونی در SVG به علاوه استفاده از currentColor
قوانین سبک حداقلی را برای مرورگر ارائه می دهد تا در صورت بار نشدن CSS از آن استفاده کند. این باعث می شود که سبک های دفاعی خوبی در برابر آشفتگی شبکه ایجاد شود.
طرح بندی
جزء سوئیچ تم دارای سطح کمی است، بنابراین برای چیدمان نیازی به شبکه یا فلکس باکس ندارید. در عوض، از موقعیتیابی SVG و تبدیلهای CSS استفاده میشود.
سبک ها
سبکهای .theme-toggle
عنصر <button>
محفظه ای برای شکل ها و سبک های نماد است. این زمینه والد رنگ ها و اندازه های تطبیقی را برای انتقال به SVG نگه می دارد.
اولین کار این است که دکمه را دایره ای کنید و سبک های دکمه پیش فرض را حذف کنید:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
}
در مرحله بعد، چند سبک تعامل اضافه کنید. یک سبک مکان نما برای کاربران ماوس اضافه کنید. افزودن touch-action: manipulation
برای تجربه لمسی با واکنش سریع . حذف هایلایت نیمه شفاف iOS روی دکمه ها اعمال می شود. در آخر، به حالت فوکوس کمی فضای تنفسی از لبه عنصر بدهید:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
outline-offset: 5px;
}
SVG داخل دکمه نیز به برخی سبک ها نیاز دارد. SVG باید متناسب با اندازه دکمه باشد و برای نرمی بصری، انتهای خط را گرد کند:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
outline-offset: 5px;
& > svg {
inline-size: 100%;
block-size: 100%;
stroke-linecap: round;
}
}
اندازه تطبیقی با جستجوی رسانه hover
اندازه دکمه آیکون کمی کوچک در 2rem
است، که برای کاربران ماوس خوب است، اما می تواند برای یک اشاره گر درشت مانند یک انگشت مبارزه کند. با استفاده از جستجوی رسانه شناور برای تعیین افزایش اندازه، دکمه را مطابق با بسیاری از دستورالعملهای اندازه لمسی قرار دهید.
.theme-toggle {
--size: 2rem;
…
@media (hover: none) {
--size: 48px;
}
}
سبک های SVG خورشید و ماه
دکمه جنبه های تعاملی جزء سوئیچ تم را نگه می دارد در حالی که SVG در داخل جنبه های بصری و متحرک را نگه می دارد. اینجاست که می توان نماد را زیبا کرد و زنده کرد.
تم سبک
برای اینکه انیمیشنها مقیاس و چرخش از مرکز اشکال SVG اتفاق بیفتند، transform-origin: center center
. رنگ های تطبیقی ارائه شده توسط دکمه در اینجا توسط اشکال استفاده می شود. ماه و خورشید از دکمه ارائه شده var(--icon-fill)
و var(--icon-fill-hover)
برای پر شدن خود استفاده می کنند، در حالی که پرتوهای خورشید از متغیرها برای stroke استفاده می کنند.
.sun-and-moon {
& > :is(.moon, .sun, .sun-beams) {
transform-origin: center center;
}
& > :is(.moon, .sun) {
fill: var(--icon-fill);
@nest .theme-toggle:is(:hover, :focus-visible) > & {
fill: var(--icon-fill-hover);
}
}
& > .sun-beams {
stroke: var(--icon-fill);
stroke-width: 2px;
@nest .theme-toggle:is(:hover, :focus-visible) & {
stroke: var(--icon-fill-hover);
}
}
}
تم تاریک
The moon styles need to remove the sunbeams, scale up the sun circle and move the circle mask.
.sun-and-moon {
@nest [data-theme="dark"] & {
& > .sun {
transform: scale(1.75);
}
& > .sun-beams {
opacity: 0;
}
& > .moon > circle {
transform: translateX(-7px);
@supports (cx: 1px) {
transform: translateX(0);
cx: 17px;
}
}
}
}
Notice the dark theme has no color changes or transitions. The parent button component owns the colors, where they're already adaptive within a dark and light context. The transition information should be behind a user's motion preference media query.
انیمیشن
The button should be functional and stateful but without transitions at this point. The following sections are all about defining how and what transitions.
Sharing media queries and importing easings
برای آسان کردن انتقال و انیمیشن ها در پشت تنظیمات حرکت سیستم عامل کاربر ، افزونه Postcss Plugin Custom Media استفاده از مشخصات CSS پیش نویس را برای متغیرهای پرس و جو رسانه ای امکان پذیر می کند:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
/* usage example */
@media (--motionOK) {
.sun {
transition: transform .5s var(--ease-elastic-3);
}
}
For unique and easy to use CSS easings, import the easings portion of Open Props :
@import "https://unpkg.com/open-props/easings.min.css";
/* usage example */
.sun {
transition: transform .5s var(--ease-elastic-3);
}
خورشید
انتقال خورشید از ماه بازیگوش تر خواهد بود و به این اثر با تساوی های تند و تیز می رسد. آفتابگردان ها باید مقدار کمی را هنگام چرخش گزاف گویی کنند و مرکز خورشید باید مقدار کمی را در مقیاس آن گزاف گویی کند.
سبک های پیش فرض (تم نور) انتقال را تعریف می کند و سبک های تم تاریک سفارشی سازی را برای انتقال به نور تعریف می کنند:
.sun-and-moon {
@media (--motionOK) {
& > .sun {
transition: transform .5s var(--ease-elastic-3);
}
& > .sun-beams {
transition:
transform .5s var(--ease-elastic-4),
opacity .5s var(--ease-3)
;
}
@nest [data-theme="dark"] & {
& > .sun {
transform: scale(1.75);
transition-timing-function: var(--ease-3);
transition-duration: .25s;
}
& > .sun-beams {
transform: rotateZ(-25deg);
transition-duration: .15s;
}
}
}
}
In the Animation panel in Chrome DevTools, you can find a timeline for animation transitions. مدت زمان انیمیشن کل ، عناصر و زمان بندی کاهش می یابد.
ماه
موقعیت های نور و تاریک ماه در حال حاضر تنظیم شده است ، سبک های انتقال را در داخل پرس و جو رسانه ای --motionOK
اضافه کنید تا ضمن احترام به ترجیحات حرکتی کاربر ، آن را به زندگی بیاورد.
The timing with delay and duration are critical in making this transition clean. If the sun is eclipsed too early, for example, the transition doesn't feel orchestrated or playful, it feels chaotic.
.sun-and-moon {
@media (--motionOK) {
& .moon > circle {
transform: translateX(-7px);
transition: transform .25s var(--ease-out-5);
@supports (cx: 1px) {
transform: translateX(0);
cx: 17px;
transition: cx .25s var(--ease-out-5);
}
}
@nest [data-theme="dark"] & {
& > .moon > circle {
transition-delay: .25s;
transition-duration: .5s;
}
}
}
}
کاهش حرکت را ترجیح می دهد
In most GUI Challenges I try to keep some animation, like opacity cross fades, for users who prefer reduced motion. This component felt better with instant state changes however.
جاوا اسکریپت
کار زیادی برای JavaScript در این مؤلفه وجود دارد ، از مدیریت اطلاعات ARIA برای خوانندگان صفحه تا دریافت و تنظیم مقادیر از ذخیره محلی .
The page load experience
It was important that no color flashing occurs on page load. اگر یک کاربر با یک طرح رنگ تیره نشان می دهد که آنها نور را با این مؤلفه ترجیح می دهند ، سپس صفحه را بارگیری مجدد می کنند ، در ابتدا صفحه تاریک خواهد بود و به نور می چرخد. Preventing this meant running a small amount of blocking JavaScript with the goal to set the HTML attribute data-theme
as early as possible.
<script src="./theme-toggle.js"></script>
برای دستیابی به این هدف ، یک برچسب ساده <script>
در سند <head>
ابتدا بارگذاری می شود ، قبل از هرگونه نشانه CSS یا <body>
. هنگامی که مرورگر با یک اسکریپت بدون علامت مانند این روبرو می شود ، کد را اجرا می کند و قبل از بقیه HTML آن را اجرا می کند. با استفاده از این لحظه مسدود کننده ، می توان ویژگی HTML را قبل از اینکه CSS اصلی صفحه را نقاشی کند ، تنظیم کنید ، بنابراین از فلاش یا رنگ جلوگیری می کند.
JavaScript ابتدا برای ترجیح کاربر در ذخیره سازی محلی و بازگرداندن آن بررسی می کند تا ترجیح سیستم را در صورتی که در ذخیره سازی پیدا نشده است ، بررسی کند:
const storageKey = 'theme-preference'
const getColorPreference = () => {
if (localStorage.getItem(storageKey))
return localStorage.getItem(storageKey)
else
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
}
تابعی برای تنظیم اولویت کاربر در ذخیره سازی محلی در مرحله بعدی تجزیه می شود:
const setPreference = () => {
localStorage.setItem(storageKey, theme.value)
reflectPreference()
}
به دنبال یک تابع برای اصلاح سند با تنظیمات برگزیده.
const reflectPreference = () => {
document.firstElementChild
.setAttribute('data-theme', theme.value)
document
.querySelector('#theme-toggle')
?.setAttribute('aria-label', theme.value)
}
نکته مهمی که در این مرحله باید به آن توجه داشته باشید وضعیت تجزیه و تحلیل سند HTML است. مرورگر هنوز در مورد دکمه "#theme-toggle" نمی داند ، زیرا برچسب <head>
کاملاً تجزیه نشده است. با <html>
حال ، مرورگر دارای یک document.firstElementChild
است. این تابع تلاش می کند هر دو را برای همگام سازی آنها تنظیم کند ، اما در اولین اجرا فقط قادر به تنظیم برچسب HTML خواهد بود. querySelector
در ابتدا چیزی پیدا نمی کند و اپراتور زنجیره ای اختیاری در هنگام یافتن خطای نحوی را تضمین نمی کند و سعی می شود عملکرد SetAttribute استناد شود.
در مرحله بعد ، این عملکرد reflectPreference()
بلافاصله خوانده می شود تا سند HTML دارای مجموعه ویژگی data-theme
خود باشد:
reflectPreference()
این دکمه هنوز به ویژگی نیاز دارد ، بنابراین منتظر رویداد بارگذاری صفحه باشید ، پس از آن پرس و جو ، اضافه کردن شنوندگان و تنظیم ویژگی ها در امان خواهد بود:
window.onload = () => {
// set on load so screen readers can get the latest value on the button
reflectPreference()
// now this script can find and listen for clicks on the control
document
.querySelector('#theme-toggle')
.addEventListener('click', onClick)
}
تجربه جابجایی
هنگامی که دکمه کلیک شد ، موضوع باید در حافظه JavaScript و در سند تعویض شود. The current theme value will need to be inspected and a decision made about its new state. Once the new state is set, save it and update the document:
const onClick = () => {
theme.value = theme.value === 'light'
? 'dark'
: 'light'
setPreference()
}
Synchronizing with the system
Unique to this theme switch is synchronization with the system preference as it changes. اگر کاربر در حالی که یک صفحه و این مؤلفه قابل مشاهده است ، ترجیح سیستم خود را تغییر دهد ، سوئیچ تم تغییر می دهد تا با اولویت کاربر جدید مطابقت داشته باشد ، گویی کاربر همزمان با سوئیچ تم در همان زمان سوئیچ سیستم تعامل داشته است.
Achieve this with JavaScript and a matchMedia
event listening for changes to a media query:
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', ({matches:isDark}) => {
theme.value = isDark ? 'dark' : 'light'
setPreference()
})
نتیجه گیری
حالا که می دانید من چگونه این کار را کردم ، چگونه می توانید
بیایید رویکردهای خود را متنوع کنیم و تمام راه های ساخت در وب را بیاموزیم. یک نسخه ی نمایشی ایجاد کنید ، به من پیوندهایی توییت کنید ، و من آن را به بخش Remixes Community در زیر اضافه می کنم!
ریمیکس های جامعه
- nathang on codepen با vue
- Shadowshahriar در Codepen
- tomayac به عنوان یک عنصر سفارشی
- Bramus با جاوا اسکریپت وانیلی
- joshwcomeau با React
مروری اساسی در مورد نحوه ساخت یک مؤلفه سوئیچ تم سازگار و در دسترس.
در این پست می خواهم تفکر را به روشی برای ساخت یک مؤلفه سوئیچ تم تاریک و سبک به اشتراک بگذارم. نسخه ی نمایشی را امتحان کنید .
اگر ویدیو را ترجیح می دهید ، در اینجا یک نسخه YouTube از این پست وجود دارد:
نمای کلی
A website may provide settings for controlling the color scheme instead of relying entirely on the system preference. This means that users may browse in a mode other than their system preferences. For example, a user's system is in a light theme, but the user prefers the website to display in the dark theme.
There are several web engineering considerations when building this feature. به عنوان مثال ، مرورگر باید در اسرع وقت از اولویت آگاه شود تا از چشمک زدن به رنگ صفحه جلوگیری شود و کنترل باید ابتدا با سیستم همگام شود و سپس به استثنائات ذخیره شده در سمت مشتری اجازه دهد.
نشانه گذاری
از <button>
باید برای ضامن استفاده شود ، زیرا شما از وقایع و ویژگی های تعامل با مرورگر مانند رویدادهای کلیک و تمرکز استفاده می کنید.
دکمه
این دکمه برای استفاده از CSS و شناسه برای استفاده از JavaScript به یک کلاس نیاز دارد. علاوه بر این ، از آنجا که محتوای دکمه به جای متن یک نماد است ، یک ویژگی عنوان را برای ارائه اطلاعات در مورد هدف دکمه اضافه کنید. آخر اینکه ، برای نگه داشتن وضعیت دکمه نماد ، یک [aria-label]
اضافه کنید ، بنابراین خوانندگان صفحه نمایش می توانند وضعیت موضوع را برای افرادی که از نظر بینایی هستند به اشتراک بگذارند.
<button
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label="auto"
>
…
</button>
aria-label
and aria-live
polite
To indicate to screen readers that changes to aria-label
should be announced, add aria-live="polite"
to the button.
<button
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label="auto"
aria-live="polite"
>
…
</button>
این نشانه گذاری علاوه بر این ، به جای aria-live="assertive"
، خوانندگان را به نمایش می گذارد ، به کاربر بگویید چه چیزی تغییر کرده است. در مورد این دکمه ، بسته به آنچه که aria-label
شده است ، "نور" یا "تاریک" را اعلام می کند.
The scalable vector graphic (SVG) icon
SVG راهی برای ایجاد اشکال با کیفیت بالا و مقیاس پذیر با حداقل نشانه گذاری فراهم می کند. تعامل با دکمه می تواند حالت های بصری جدیدی را برای بردارها ایجاد کند و SVG را برای نمادها عالی کند.
The following SVG markup goes inside the <button>
:
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
…
</svg>
aria-hidden
has been added to the SVG element so screen readers know to ignore it as it's marked presentational. This is great to do for visual decorations, like the icon inside a button. In addition to the required viewBox
attribute on the element, add height and width for similar reasons that images should get inline sizes .
خورشید
گرافیک خورشید از یک دایره و خطوطی تشکیل شده است که SVG به راحتی شکل دارد. <circle>
با تنظیم خواص cx
و cy
به 12 ، که نیمی از اندازه Viewport (24) است ، متمرکز شده است ، و سپس شعاع ( r
) 6
را که اندازه آن را تعیین می کند ، داده می شود.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
</svg>
علاوه بر این ، ویژگی ماسک به شناسه یک عنصر SVG اشاره می کند ، که شما در مرحله بعدی ایجاد خواهید کرد ، و در نهایت با رنگ پر از رنگ صفحه با currentColor
با رنگ صفحه مطابقت دارد.
پرتوهای خورشید
در مرحله بعد ، خطوط آفتابگردان دقیقاً زیر دایره ، در داخل یک گروه گروه <g>
اضافه می شوند.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
<g class="sun-beams" stroke="currentColor">
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</g>
</svg>
این بار ، به جای مقدار پر شدن currentColor
، سکته مغزی هر خط تنظیم شده است. خطوط به علاوه شکل های دایره یک خورشید زیبا با تیرها ایجاد می کنند.
ماه
برای ایجاد توهم یک انتقال یکپارچه بین نور (خورشید) و تاریک (ماه) ، ماه با استفاده از ماسک SVG ، افزایش نماد خورشید است.
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
<g class="sun-beams" stroke="currentColor">
…
</g>
<mask class="moon" id="moon-mask">
<rect x="0" y="0" width="100%" height="100%" fill="white" />
<circle cx="24" cy="10" r="6" fill="black" />
</mask>
</svg>
Masks with SVG are powerful, allowing the colors white and black to either remove or include parts of another graphic. نماد خورشید با یک ماسک SVG ، به سادگی با حرکت دادن یک شکل دایره در داخل و خارج از یک منطقه ماسک ، توسط یک ماه <circle>
ماه گرفتگی می شود.
چه اتفاقی می افتد اگر CSS بارگیری نشود؟
خوب است که SVG خود را آزمایش کنید که گویی CSS برای اطمینان از نتیجه فوق العاده بزرگ یا باعث ایجاد مشکلات طرح نشده است. ویژگی های ارتفاع درون خطی و عرض در SVG به علاوه استفاده از currentColor
قوانین حداقل سبک را برای مرورگر می دهد تا در صورت بارگیری CSS از مرورگر استفاده کند. این باعث می شود سبک های دفاعی خوب در برابر آشفتگی شبکه باشد.
طرح بندی
مؤلفه سوئیچ تم دارایی سطح کمی دارد ، بنابراین برای چیدمان به شبکه یا Flexbox احتیاج ندارید. Instead, SVG positioning and CSS transforms are used.
سبک ها
.theme-toggle
styles
The <button>
element is the container for the icon shapes and styles. این زمینه والدین رنگ ها و اندازه های تطبیقی را برای انتقال به SVG نگه می دارد.
The first task is to make the button a circle and remove the default button styles:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
}
Next, add some interaction styles. Add a cursor style for mouse users. Add touch-action: manipulation
for a fast reacting touch experience . Remove the semi-transparent highlight iOS applies to buttons. Last, give the focus state outline some breathing room from the edge of the element:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
outline-offset: 5px;
}
The SVG inside of the button needs some styles as well. SVG باید متناسب با اندازه دکمه باشد و برای نرمی بصری ، خط را به پایان برساند:
.theme-toggle {
--size: 2rem;
background: none;
border: none;
padding: 0;
inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
outline-offset: 5px;
& > svg {
inline-size: 100%;
block-size: 100%;
stroke-linecap: round;
}
}
اندازه تطبیقی با پرس و جو رسانه hover
اندازه دکمه نماد در 2rem
کمی کوچک است ، که برای کاربران ماوس خوب است اما می تواند یک مبارزه برای یک نشانگر درشت مانند انگشت باشد. با استفاده از یک پرس و جو رسانه Hover برای مشخص کردن افزایش اندازه ، دکمه را با بسیاری از دستورالعمل های اندازه لمسی روبرو کنید.
.theme-toggle {
--size: 2rem;
…
@media (hover: none) {
--size: 48px;
}
}
سبک های Sun and Moon SVG
این دکمه جنبه های تعاملی مؤلفه سوئیچ تم را در خود جای داده است در حالی که SVG در داخل جنبه های بصری و متحرک را در خود نگه می دارد. این جایی است که می توان نماد را زیبا کرد و زنده کرد.
تم سبک
برای اینکه انیمیشن های مقیاس و چرخش از مرکز اشکال SVG اتفاق بیفتد ، transform-origin: center center
. رنگ های تطبیقی ارائه شده توسط دکمه در اینجا توسط اشکال استفاده می شود. ماه و خورشید از دکمه ارائه شده var(--icon-fill)
و var(--icon-fill-hover)
برای پر کردن آنها استفاده می کنند ، در حالی که آفتابگردان ها از متغیرهای سکته مغزی استفاده می کنند.
.sun-and-moon {
& > :is(.moon, .sun, .sun-beams) {
transform-origin: center center;
}
& > :is(.moon, .sun) {
fill: var(--icon-fill);
@nest .theme-toggle:is(:hover, :focus-visible) > & {
fill: var(--icon-fill-hover);
}
}
& > .sun-beams {
stroke: var(--icon-fill);
stroke-width: 2px;
@nest .theme-toggle:is(:hover, :focus-visible) & {
stroke: var(--icon-fill-hover);
}
}
}
تم تاریک
سبک های ماه باید آفتابگردان ها را از بین ببرد ، دایره آفتاب را اندازه گیری کند و ماسک دایره را جابجا کند.
.sun-and-moon {
@nest [data-theme="dark"] & {
& > .sun {
transform: scale(1.75);
}
& > .sun-beams {
opacity: 0;
}
& > .moon > circle {
transform: translateX(-7px);
@supports (cx: 1px) {
transform: translateX(0);
cx: 17px;
}
}
}
}
توجه کنید که موضوع تاریک هیچ تغییر رنگی یا انتقال ندارد. مؤلفه دکمه والدین دارای رنگ ها است ، جایی که آنها در یک زمینه تاریک و سبک سازگار هستند. اطلاعات انتقال باید در پشت پرس و جو رسانه اولویت حرکت کاربر باشد.
انیمیشن
دکمه باید کاربردی و حالت دار اما بدون انتقال در این مرحله باشد. بخش های زیر همه چیز در مورد تعیین چگونگی و چه انتقال است.
به اشتراک گذاری نمایش داده های رسانه ای و واردات
برای آسان کردن انتقال و انیمیشن ها در پشت تنظیمات حرکت سیستم عامل کاربر ، افزونه Postcss Plugin Custom Media استفاده از مشخصات CSS پیش نویس را برای متغیرهای پرس و جو رسانه ای امکان پذیر می کند:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
/* usage example */
@media (--motionOK) {
.sun {
transition: transform .5s var(--ease-elastic-3);
}
}
برای استفاده منحصر به فرد و آسان برای استفاده از CSS ، بخش Easings از غرفه های باز را وارد کنید:
@import "https://unpkg.com/open-props/easings.min.css";
/* usage example */
.sun {
transition: transform .5s var(--ease-elastic-3);
}
خورشید
انتقال خورشید از ماه بازیگوش تر خواهد بود و به این اثر با تساوی های تند و تیز می رسد. آفتابگردان ها باید مقدار کمی را هنگام چرخش گزاف گویی کنند و مرکز خورشید باید مقدار کمی را در مقیاس آن گزاف گویی کند.
سبک های پیش فرض (تم نور) انتقال را تعریف می کند و سبک های تم تاریک سفارشی سازی را برای انتقال به نور تعریف می کنند:
.sun-and-moon {
@media (--motionOK) {
& > .sun {
transition: transform .5s var(--ease-elastic-3);
}
& > .sun-beams {
transition:
transform .5s var(--ease-elastic-4),
opacity .5s var(--ease-3)
;
}
@nest [data-theme="dark"] & {
& > .sun {
transform: scale(1.75);
transition-timing-function: var(--ease-3);
transition-duration: .25s;
}
& > .sun-beams {
transform: rotateZ(-25deg);
transition-duration: .15s;
}
}
}
}
در پانل انیمیشن در Chrome Devtools ، می توانید یک جدول زمانی برای انتقال انیمیشن پیدا کنید. مدت زمان انیمیشن کل ، عناصر و زمان بندی کاهش می یابد.
ماه
موقعیت های نور و تاریک ماه در حال حاضر تنظیم شده است ، سبک های انتقال را در داخل پرس و جو رسانه ای --motionOK
اضافه کنید تا ضمن احترام به ترجیحات حرکتی کاربر ، آن را به زندگی بیاورد.
زمان با تأخیر و مدت زمان تمیز کردن این انتقال بسیار مهم است. اگر خورشید خیلی زود گرفتار شود ، به عنوان مثال ، انتقال احساس ارکستر یا بازیگوش نمی کند ، احساس هرج و مرج می کند.
.sun-and-moon {
@media (--motionOK) {
& .moon > circle {
transform: translateX(-7px);
transition: transform .25s var(--ease-out-5);
@supports (cx: 1px) {
transform: translateX(0);
cx: 17px;
transition: cx .25s var(--ease-out-5);
}
}
@nest [data-theme="dark"] & {
& > .moon > circle {
transition-delay: .25s;
transition-duration: .5s;
}
}
}
}
کاهش حرکت را ترجیح می دهد
در اکثر چالش های GUI سعی می کنم برخی از انیمیشن ها ، مانند محو شدن Opacity Cross را برای کاربرانی که حرکت را کاهش می دهند ، حفظ کنم. با این حال ، این مؤلفه با تغییرات حالت فوری احساس بهتری داشت.
جاوا اسکریپت
کار زیادی برای JavaScript در این مؤلفه وجود دارد ، از مدیریت اطلاعات ARIA برای خوانندگان صفحه تا دریافت و تنظیم مقادیر از ذخیره محلی .
تجربه بار صفحه
این مهم بود که هیچ چشمک زن در بار صفحه رخ ندهد. اگر یک کاربر با یک طرح رنگ تیره نشان می دهد که آنها نور را با این مؤلفه ترجیح می دهند ، سپس صفحه را بارگیری مجدد می کنند ، در ابتدا صفحه تاریک خواهد بود و به نور می چرخد. جلوگیری از این امر به معنای اجرای مقدار کمی از مسدود کردن جاوا اسکریپت با هدف تنظیم تنظیم data-theme
HTML در سریعترین زمان ممکن است.
<script src="./theme-toggle.js"></script>
برای دستیابی به این هدف ، یک برچسب ساده <script>
در سند <head>
ابتدا بارگذاری می شود ، قبل از هرگونه نشانه CSS یا <body>
. هنگامی که مرورگر با یک اسکریپت بدون علامت مانند این روبرو می شود ، کد را اجرا می کند و قبل از بقیه HTML آن را اجرا می کند. با استفاده از این لحظه مسدود کننده ، می توان ویژگی HTML را قبل از اینکه CSS اصلی صفحه را نقاشی کند ، تنظیم کنید ، بنابراین از فلاش یا رنگ جلوگیری می کند.
JavaScript ابتدا برای ترجیح کاربر در ذخیره سازی محلی و بازگرداندن آن بررسی می کند تا ترجیح سیستم را در صورتی که در ذخیره سازی پیدا نشده است ، بررسی کند:
const storageKey = 'theme-preference'
const getColorPreference = () => {
if (localStorage.getItem(storageKey))
return localStorage.getItem(storageKey)
else
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
}
تابعی برای تنظیم اولویت کاربر در ذخیره سازی محلی در مرحله بعدی تجزیه می شود:
const setPreference = () => {
localStorage.setItem(storageKey, theme.value)
reflectPreference()
}
به دنبال یک تابع برای اصلاح سند با تنظیمات برگزیده.
const reflectPreference = () => {
document.firstElementChild
.setAttribute('data-theme', theme.value)
document
.querySelector('#theme-toggle')
?.setAttribute('aria-label', theme.value)
}
نکته مهمی که در این مرحله باید به آن توجه داشته باشید وضعیت تجزیه و تحلیل سند HTML است. مرورگر هنوز در مورد دکمه "#theme-toggle" نمی داند ، زیرا برچسب <head>
کاملاً تجزیه نشده است. با <html>
حال ، مرورگر دارای یک document.firstElementChild
است. این تابع تلاش می کند هر دو را برای همگام سازی آنها تنظیم کند ، اما در اولین اجرا فقط قادر به تنظیم برچسب HTML خواهد بود. querySelector
در ابتدا چیزی پیدا نمی کند و اپراتور زنجیره ای اختیاری در هنگام یافتن خطای نحوی را تضمین نمی کند و سعی می شود عملکرد SetAttribute استناد شود.
در مرحله بعد ، این عملکرد reflectPreference()
بلافاصله خوانده می شود تا سند HTML دارای مجموعه ویژگی data-theme
خود باشد:
reflectPreference()
این دکمه هنوز به ویژگی نیاز دارد ، بنابراین منتظر رویداد بارگذاری صفحه باشید ، پس از آن پرس و جو ، اضافه کردن شنوندگان و تنظیم ویژگی ها در امان خواهد بود:
window.onload = () => {
// set on load so screen readers can get the latest value on the button
reflectPreference()
// now this script can find and listen for clicks on the control
document
.querySelector('#theme-toggle')
.addEventListener('click', onClick)
}
تجربه جابجایی
هنگامی که دکمه کلیک شد ، موضوع باید در حافظه JavaScript و در سند تعویض شود. ارزش موضوع فعلی باید مورد بازرسی قرار گیرد و تصمیمی درباره وضعیت جدید آن گرفته شود. پس از تنظیم حالت جدید ، آن را ذخیره کرده و سند را به روز کنید:
const onClick = () => {
theme.value = theme.value === 'light'
? 'dark'
: 'light'
setPreference()
}
همگام سازی با سیستم
منحصر به فرد برای این سوئیچ تم ، هماهنگ سازی با ترجیح سیستم با تغییر است. اگر کاربر در حالی که یک صفحه و این مؤلفه قابل مشاهده است ، ترجیح سیستم خود را تغییر دهد ، سوئیچ تم تغییر می دهد تا با اولویت کاربر جدید مطابقت داشته باشد ، گویی کاربر همزمان با سوئیچ تم در همان زمان سوئیچ سیستم تعامل داشته است.
با JavaScript و یک رویداد matchMedia
به این هدف برسید و به دنبال تغییر در یک پرس و جو رسانه ای باشید:
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', ({matches:isDark}) => {
theme.value = isDark ? 'dark' : 'light'
setPreference()
})
نتیجه گیری
حالا که می دانید من چگونه این کار را کردم ، چگونه می توانید
بیایید رویکردهای خود را متنوع کنیم و تمام راه های ساخت در وب را بیاموزیم. یک نسخه ی نمایشی ایجاد کنید ، به من پیوندهایی توییت کنید ، و من آن را به بخش Remixes Community در زیر اضافه می کنم!
ریمیکس های جامعه
- nathang on codepen با vue
- Shadowshahriar در Codepen
- tomayac به عنوان یک عنصر سفارشی
- Bramus با جاوا اسکریپت وانیلی
- joshwcomeau با React