ภาพรวมพื้นฐานของวิธีสร้างคอมโพเนนต์การเปลี่ยนธีมแบบปรับเปลี่ยนได้และเข้าถึงง่าย
ในโพสต์นี้ ฉันอยากแชร์วิธีสร้างคอมโพเนนต์สวิตช์ธีมสีเข้มและธีมสว่าง ลองใช้เดโม
หากชอบวิดีโอ นี่คือโพสต์นี้เวอร์ชัน YouTube
ภาพรวม
เว็บไซต์อาจมีการตั้งค่าสําหรับการควบคุมรูปแบบสีแทนที่จะพึ่งพาค่ากำหนดของระบบทั้งหมด ซึ่งหมายความว่าผู้ใช้อาจท่องเว็บในโหมดอื่นนอกเหนือจากค่ากำหนดของระบบ เช่น ระบบของผู้ใช้อยู่ในธีมสว่าง แต่ผู้ใช้ต้องการให้เว็บไซต์แสดงในธีมมืด
เมื่อสร้างฟีเจอร์นี้มีข้อควรพิจารณาเกี่ยวกับวิศวกรรมเว็บหลายอย่าง ตัวอย่างเช่น ควรทำให้เบราว์เซอร์ทราบถึงค่ากำหนดโดยเร็วที่สุดเพื่อป้องกันไม่ให้สีของหน้าเว็บกะพริบ และการควบคุมจำเป็นต้องซิงค์กับระบบก่อน จากนั้นจึงอนุญาตข้อยกเว้นที่เก็บไว้ฝั่งไคลเอ็นต์
Markup
ควรใช้ <button>
ในการสลับเพราะจะช่วยให้คุณได้รับประโยชน์จากเหตุการณ์และฟีเจอร์การโต้ตอบของเบราว์เซอร์ เช่น เหตุการณ์การคลิกและการโฟกัส
ปุ่ม
ปุ่มต้องมีคลาสเพื่อการใช้งานจาก CSS และรหัสสำหรับใช้งานจาก JavaScript
นอกจากนี้ เนื่องจากเนื้อหาของปุ่มจะเป็นไอคอน ไม่ใช่ข้อความ ให้เพิ่มแอตทริบิวต์ title [ชื่อ] เพื่อระบุข้อมูลเกี่ยวกับวัตถุประสงค์ของปุ่ม สุดท้าย ให้เพิ่ม [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>
นอกจากนี้ พร็อพเพอร์ตี้มาสก์จะชี้ไปที่รหัสขององค์ประกอบ 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
ระบบจะตั้งค่าเส้นโครงร่างของแต่ละบรรทัด เส้นและรูปร่างวงกลมทำให้เกิดแสงที่สดใส
ดวงจันทร์
เพื่อสร้างภาพลวงตาของการเปลี่ยนระหว่างแสง (ดวงอาทิตย์) กับความมืด (ดวงจันทร์) อย่างราบรื่น ดวงจันทร์เป็นส่วนเสริมของไอคอนดวงอาทิตย์โดยใช้มาสก์ 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 ไม่โหลด ซึ่งทำให้รูปแบบการป้องกันที่ดีต่อความปั่นป่วนของเครือข่าย
เลย์เอาต์
คอมโพเนนต์การเปลี่ยนธีมมีพื้นที่ผิวสัมผัสน้อย คุณจึงไม่จำเป็นต้องใช้ตารางกริดหรือ Flexbox สำหรับเลย์เอาต์ โดยระบบจะใช้การระบุตำแหน่ง 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)
เพื่อเติมสี ส่วนแสงอาทิตย์จะใช้ตัวแปรสำหรับเส้นโครงร่าง
.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: 1) {
transform: translateX(0);
cx: 17;
}
}
}
}
โปรดทราบว่าธีมมืดไม่มีการเปลี่ยนสีหรือการเปลี่ยนแปลง คอมโพเนนต์ปุ่มหลัก เป็นเจ้าของสี ซึ่งสีเหล่านั้นจะปรับในบริบทของแบบมืดและสว่างอยู่แล้ว ข้อมูลการเปลี่ยนควรอยู่หลังคำค้นหาสื่อค่ากำหนดการเคลื่อนไหวของผู้ใช้
แอนิเมชัน
ปุ่มดังกล่าวควรทำงานได้และเก็บสถานะได้ แต่ไม่มีการเปลี่ยนในขั้นตอนนี้ ส่วนต่อไปนี้จะอธิบายถึงการระบุวิธีการและการเปลี่ยนแปลง
การแชร์คำค้นหาสื่อและการนำเข้าการค่อยๆ เปลี่ยน
เพื่อทำให้การเปลี่ยนและภาพเคลื่อนไหวเบื้องหลังค่ากำหนดการเคลื่อนไหวในระบบปฏิบัติการของผู้ใช้เป็นเรื่องง่าย ปลั๊กอิน 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 ใช้งานง่ายและไม่เหมือนใคร ให้นำเข้าส่วนการค่อยๆ เปลี่ยนของ 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 คุณจะเห็นไทม์ไลน์สำหรับการเปลี่ยนภาพเคลื่อนไหว สามารถตรวจสอบระยะเวลาของภาพเคลื่อนไหวทั้งหมด องค์ประกอบ และเวลาการค่อยๆ เปลี่ยนได้
ดวงจันทร์
ตำแหน่งดวงจันทร์สว่างและมืดกำหนดไว้แล้ว โปรดเพิ่มรูปแบบการเปลี่ยนภายในคำค้นหาสื่อ --motionOK
เพื่อเติมชีวิตชีวาและยังเคารพความต้องการของการเคลื่อนไหวของผู้ใช้ด้วย
ช่วงเวลาที่มีความล่าช้าและระยะเวลาเป็นปัจจัยสำคัญในการทำให้การเปลี่ยนนี้ราบรื่น เวลาที่พระอาทิตย์ขึ้นเร็วเกินไป การเปลี่ยนก็ไม่ดูวุ่นวายหรือเล่นสนุก ก็ให้ความรู้สึกวุ่นวาย
.sun-and-moon {
@media (--motionOK) {
& .moon > circle {
transform: translateX(-7px);
transition: transform .25s var(--ease-out-5);
@supports (cx: 1) {
transform: translateX(0);
cx: 17;
transition: cx .25s var(--ease-out-5);
}
}
@nest [data-theme="dark"] & {
& > .moon > circle {
transition-delay: .25s;
transition-duration: .5s;
}
}
}
}
ต้องการการลดการเคลื่อนไหว
ในชาเลนจ์ GUI ส่วนใหญ่ ฉันจะพยายามให้ภาพเคลื่อนไหวบางอย่าง เช่น ภาพครอสเฟดแบบทึบแสง สำหรับผู้ใช้ที่ต้องการลดการเคลื่อนไหว แต่คอมโพเนนต์นี้จะรู้สึกดีขึ้นเมื่อมีการเปลี่ยนแปลงสถานะในทันที
JavaScript
มีงานมากมายสำหรับ JavaScript ในคอมโพเนนต์นี้ ตั้งแต่การจัดการข้อมูล ARIA สำหรับโปรแกรมอ่านหน้าจอไปจนถึงการรับและกำหนดค่าจากพื้นที่เก็บข้อมูลในเครื่อง
ประสบการณ์การโหลดหน้าเว็บ
ต้องไม่แสดงสีกะพริบเมื่อโหลดหน้าเว็บ หากผู้ใช้ที่มีรูปแบบสีเข้มระบุว่าใช้คอมโพเนนต์นี้เป็นแสงที่ต้องการ แล้วโหลดหน้าเว็บอีกครั้ง ในตอนแรกหน้าเว็บจะมืดแล้วจะสว่างเป็นสีสว่าง
การป้องกันปัญหานี้หมายถึงการเรียกใช้การบล็อก JavaScript จำนวนเล็กน้อยโดยตั้งเป้าหมายให้ตั้งค่าแอตทริบิวต์ HTML data-theme
โดยเร็วที่สุด
<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>
ยังไม่ได้รับการแยกวิเคราะห์อย่างสมบูรณ์ แต่เบราว์เซอร์จะมี document.firstElementChild
หรือที่เรียกว่าแท็ก <html>
ฟังก์ชันนี้จะพยายามตั้งค่าทั้ง 2 อย่างให้ซิงค์กัน
แต่เมื่อเรียกใช้ครั้งแรก จะทำได้เพียงตั้งค่าแท็ก HTML เท่านั้น querySelector
จะไม่พบสิ่งใดในตอนแรก และโอเปอเรเตอร์ห่วงโซ่ที่ไม่บังคับทำให้ไม่มีข้อผิดพลาดทางไวยากรณ์เมื่อไม่พบค่าดังกล่าว และจะพยายามเรียกใช้ฟังก์ชัน setAttribute
ถัดไป ระบบจะเรียกฟังก์ชัน reflectPreference()
ทันทีเพื่อให้เอกสาร HTML มีแอตทริบิวต์ data-theme
ที่ตั้งค่าไว้
reflectPreference()
ปุ่มยังต้องการแอตทริบิวต์อยู่ ดังนั้นให้รอเหตุการณ์การโหลดหน้าเว็บ จากนั้นการค้นหา เพิ่ม Listener และตั้งค่าแอตทริบิวต์ต่างๆ ก็ปลอดภัย
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()
})
บทสรุป
ตอนนี้คุณก็รู้แล้วว่าตัวเองทำยังไง คุณจะทำอะไรบ้าง‽ 🙂
มาลองเปลี่ยนแนวทางของเราและเรียนรู้วิธีทั้งหมดเพื่อสร้างเว็บกันเถอะ สร้างเดโม ลิงก์ทวีตฉัน แล้วฉันจะเพิ่มลงในส่วนรีมิกซ์ของชุมชนด้านล่าง