const themeToggle = document.createElement('template') themeToggle.innerHTML = ` ` class ThemeToggle extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }).append(themeToggle.content.cloneNode(true)) this.isChecked = false this.hasTheme = 'light' this.toggleState = this.toggleState.bind(this) this.fireEvent = this.fireEvent.bind(this) this.handleThemeChange = this.handleThemeChange.bind(this) } static get observedAttributes() { return ['checked']; } daylight() { this.hasTheme = 'light' document.body.dataset.theme = 'light' this.setAttribute('aria-checked', 'false') } nightlight() { this.hasTheme = 'dark' document.body.dataset.theme = 'dark' this.setAttribute('aria-checked', 'true') } toggleState() { this.toggleAttribute('checked') this.fireEvent() } handleKeyDown(e) { if (e.code === 'Space') { this.toggleState() } } handleThemeChange(e) { if (e.detail.theme !== this.hasTheme) { if (e.detail.theme === 'dark') { this.setAttribute('checked', '') } else { this.removeAttribute('checked') } } } fireEvent() { this.dispatchEvent( new CustomEvent('themechange', { bubbles: true, composed: true, detail: { theme: this.hasTheme } }) ) } connectedCallback() { this.setAttribute('role', 'switch') this.setAttribute('aria-label', 'theme toggle') if (localStorage.getItem(`${window.location.hostname}-theme`) === "dark") { this.nightlight(); this.setAttribute('checked', '') } else if (localStorage.getItem(`${window.location.hostname}-theme`) === "light") { this.daylight(); this.removeAttribute('checked') } else { if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) { this.nightlight(); this.setAttribute('checked', '') } else { this.daylight(); this.removeAttribute('checked') } } this.addEventListener("click", this.toggleState); this.addEventListener("keydown", this.handleKeyDown); document.addEventListener('themechange', this.handleThemeChange) } disconnectedCallback() { this.removeEventListener("click", this.toggleState); this.removeEventListener("keydown", this.handleKeyDown); document.removeEventListener('themechange', this.handleThemeChange) } attributeChangedCallback(name, oldVal, newVal) { if (name === 'checked') { if (this.hasAttribute('checked')) { this.nightlight(); localStorage.setItem(`${window.location.hostname}-theme`, "dark"); } else { this.daylight(); localStorage.setItem(`${window.location.hostname}-theme`, "light"); } } } } window.customElements.define('theme-toggle', ThemeToggle);