ภาพรวมพื้นฐานของวิธีสร้างคอมโพเนนต์สวิตช์ธีมที่ปรับเปลี่ยนได้และเข้าถึงได้
ในโพสต์นี้ ฉันต้องการแชร์แนวคิดเกี่ยวกับวิธีสร้างคอมโพเนนต์สวิตช์ธีมมืดและธีมสว่าง ลองใช้เดโม
หากต้องการดูวิดีโอ โปรดดูโพสต์นี้ใน 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>
นอกจากนี้ พร็อพเพอร์ตี้มาสก์ยังชี้ไปยัง ID ขององค์ประกอบ SVG
ซึ่งคุณจะสร้างในขั้นตอนถัดไป และสุดท้ายคือการกำหนดสีเติมที่ตรงกับ
สีข้อความของหน้าเว็บด้วย
currentColor
แสงอาทิตย์
![]()
จากนั้นเพิ่มเส้นแสงอาทิตย์ที่ด้านล่างวงกลมภายในองค์ประกอบ
กลุ่ม <g>
group
<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 ไม่โหลด ซึ่งทำให้มีสไตล์การป้องกันที่ดีเมื่อเครือข่ายไม่เสถียร
เลย์เอาต์
คอมโพเนนต์สวิตช์ธีมมีพื้นที่ผิวเล็กๆ จึงไม่จำเป็นต้องใช้กริดหรือ 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: 1px) {
        transform: translateX(0);
        cx: 17px;
      }
    }
  }
}
โปรดสังเกตว่าธีมสีเข้มไม่มีการเปลี่ยนสีหรือการเปลี่ยน คอมโพเนนต์ปุ่มหลัก เป็นเจ้าของสี ซึ่งมีการปรับให้เข้ากับบริบทมืดและ สว่างอยู่แล้ว ข้อมูลการเปลี่ยนควรอยู่หลังการเคลื่อนไหวของผู้ใช้ Media Query ของค่ากำหนด
แอนิเมชัน
ปุ่มควรใช้งานได้และมีสถานะ แต่ไม่มีการเปลี่ยนสถานะในตอนนี้ ส่วนต่อไปนี้จะอธิบายวิธีและสิ่ง ที่เปลี่ยน
การแชร์คำค้นหาสื่อและการนำเข้าการเปลี่ยนภาพ
ปลั๊กอิน 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 คุณจะเห็นไทม์ไลน์สำหรับการเปลี่ยนภาพเคลื่อนไหว คุณสามารถตรวจสอบ ระยะเวลาของภาพเคลื่อนไหวทั้งหมด องค์ประกอบ และเวลาการค่อยๆ เปลี่ยน ได้
  
  ดวงจันทร์
ระบบได้ตั้งค่าตำแหน่งแสงจันทร์และแสงมืดไว้แล้ว ให้เพิ่มรูปแบบการเปลี่ยนภายใน
ของ Media Query --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 ส่วนใหญ่ ฉันพยายามใส่ภาพเคลื่อนไหวบางอย่าง เช่น การเปลี่ยนความทึบแสงแบบครอสเฟด สำหรับผู้ใช้ที่ต้องการลดการเคลื่อนไหว แต่คอมโพเนนต์นี้ทำงานได้ดีกว่าเมื่อมีการเปลี่ยนแปลงสถานะทันที
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
การฟังเหตุการณ์เพื่อดูการเปลี่ยนแปลงใน Media Query ดังนี้
window
  .matchMedia('(prefers-color-scheme: dark)')
  .addEventListener('change', ({matches:isDark}) => {
    theme.value = isDark ? 'dark' : 'light'
    setPreference()
  })
บทสรุป
ตอนนี้คุณรู้วิธีที่ฉันใช้แล้ว คุณจะทำอย่างไร 🙂
มาลองใช้แนวทางที่หลากหลายและเรียนรู้วิธีต่างๆ ในการสร้างสรรค์บนเว็บกัน สร้างการสาธิต ทวีตลิงก์มาให้ฉัน แล้วฉันจะเพิ่มลิงก์นั้น ลงในส่วนรีมิกซ์ของชุมชนด้านล่าง