UNPKG

@ou-imdt/css

Version:

The IMDT CSS library styles native elements with light, extendable CSS. It is developed for Interactive Media Developers at the Open University.

108 lines (97 loc) 3.42 kB
const makeTemplate = () => { const template = document.createElement("template"); const style = `<style defer></style>`; const markup = `<slot></slot>`; template.innerHTML = style + markup; return template; }; class Component extends HTMLElement { static get tag() { return "imdt-themer"; } static get observedAttributes() { return [ "data-active" ]; } constructor() { super(); this.attachShadow({ mode: "open", delegatesFocus: false }); const template = makeTemplate(); this.shadowRoot.append(template.content.cloneNode(true)); } // lifecycle will see everytime an observed attribute changes attributeChangedCallback(name, oldValue, newValue) {} connectedCallback() { this.addEventListener("click", ({ target }) => { this.applyTheme(target.getAttribute("data-name")); }); this.addEventListener("change", ({ target }) => { this.applyTheme(target.getAttribute("data-name")); }); window .matchMedia("(prefers-color-scheme: dark)") .addEventListener("change", (e) => this.addSystemChangeEvent()); this.addSlotChangeEvents(); } addSystemChangeEvent(event) { const newTheme = event.matches ? "dark" : "light"; this.applyTheme(newTheme); } // handles aria pressed state for active theme // this only works for buttons, will need work for other inputs updateTargets(theme = undefined) { const items = this.querySelectorAll("[data-name]"); items.forEach((i) => i.removeAttribute("aria-pressed")); if (theme) { const target = this.querySelector(`[data-name="${theme}"]`); if (target) target.setAttribute("aria-pressed", "true"); } } getSystemTheme() { const isDark = window.matchMedia("(prefers-color-scheme: dark)"); return isDark ? "dark" : "light"; } // Gets theme saved in local storage getLocalTheme() { return localStorage.getItem("theme"); } // adds theme class to body tag // theme !== systemTheme -> set theme in local storage // theme === systemTheme -> remove theme in local storage setTheme(theme) { const systemTheme = this.getSystemTheme(); const localTheme = this.getLocalTheme(); const finalTheme = theme || localTheme || systemTheme; localStorage.setItem("theme", finalTheme); document.body.setAttribute("data-theme", finalTheme); if (finalTheme === systemTheme) { localStorage.removeItem("theme"); } return finalTheme; } // Method that applies theme, fallback to saved local theme, fallback to system theme applyTheme(theme = "") { const final = this.setTheme(theme); this.updateTargets(final); } // THIS FIRES after connect event when slots are loaded, and then whenever they are updated // custom method to show adding events to slots changing // this only fires when element with 'slot' attrib changes, not it's children // use .assignedNodes() to access a slots children // could possibly use mutation observer to look for child changes addSlotChangeEvents() { const slots = Array.from(this.shadowRoot.querySelectorAll("slot")); slots.map((i) => { i.addEventListener("slotchange", (e) => { console.log("slot"); this.setTheme(); }); return true; }); } } // register component if (!customElements.get(Component.tag)) { customElements.define(Component.tag, Component); }