UNPKG

dark-reader-aware-theme-toggle-button

Version:

Dark mode toggle button that detects the Dark Reader extension and steps aside

152 lines (147 loc) 5.2 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { getCurrentTheme: () => getCurrentTheme, getTheme: () => getTheme, initThemeEarly: () => initThemeEarly, isDarkReaderActive: () => isDarkReaderActive, mountThemeToggleButton: () => mountThemeToggleButton, setTheme: () => setTheme, toggleTheme: () => toggleTheme, watchDarkReader: () => watchDarkReader }); module.exports = __toCommonJS(index_exports); // src/is-dark-reader-active.ts function isDarkReaderActive(doc = document) { const html = doc.documentElement; return html.hasAttribute("data-darkreader-scheme") || html.hasAttribute("data-darkreader-mode") || doc.querySelector('meta[name="darkreader"]') != null || doc.getElementById("dark-reader-style") != null; } // src/watch-dark-reader.ts function watchDarkReader(onChange, doc = document) { const html = doc.documentElement; const check = () => onChange(isDarkReaderActive(doc)); check(); const htmlObserver = new MutationObserver(check); htmlObserver.observe(html, { attributes: true, attributeFilter: ["data-darkreader-scheme", "data-darkreader-mode"] }); let headObserver = null; if (doc.head) { headObserver = new MutationObserver(check); headObserver.observe(doc.head, { childList: true, subtree: true }); } return () => { htmlObserver.disconnect(); headObserver?.disconnect(); }; } // src/theme.ts function getStorage(doc) { return doc.defaultView?.localStorage ?? null; } function prefersDark(doc) { return doc.defaultView?.matchMedia("(prefers-color-scheme: dark)").matches ?? false; } function getTheme(options = {}, doc = document) { const { storageKey = "theme", respectSystemPreference = false } = options; const saved = getStorage(doc)?.getItem(storageKey); if (saved === "dark" || saved === "light") return saved; if (respectSystemPreference && prefersDark(doc)) return "dark"; return "light"; } function setTheme(theme, options = {}, doc = document) { const { storageKey = "theme", attribute = "data-theme", darkValue = "dark" } = options; const html = doc.documentElement; if (theme === "dark") { html.setAttribute(attribute, darkValue); } else { html.removeAttribute(attribute); } getStorage(doc)?.setItem(storageKey, theme); } function getCurrentTheme(options = {}, doc = document) { const { attribute = "data-theme", darkValue = "dark" } = options; return doc.documentElement.getAttribute(attribute) === darkValue ? "dark" : "light"; } function toggleTheme(options = {}, doc = document) { const next = getCurrentTheme(options, doc) === "dark" ? "light" : "dark"; setTheme(next, options, doc); return next; } function initThemeEarly(options = {}, doc = document) { const theme = getTheme(options, doc); setTheme(theme, options, doc); return theme; } // src/mount-theme-toggle-button.ts function resolveElement(ref, doc) { return typeof ref === "string" ? doc.querySelector(ref) : ref; } function mountThemeToggleButton(options, doc = document) { const button = resolveElement(options.button, doc); if (!button) { throw new Error("mountThemeToggleButton: button element not found"); } const labelEl = options.label ? resolveElement(options.label, doc) : null; const labels = options.labels ?? { light: "Dark", dark: "Light" }; const hideWhenDR = options.hideWhenDarkReaderActive !== false; const updateLabel = (theme2) => { const text = theme2 === "dark" ? labels.dark : labels.light; if (labelEl) { labelEl.textContent = text; } else { button.textContent = text; } }; const theme = getTheme(options, doc); setTheme(theme, options, doc); updateLabel(theme); options.onThemeChange?.(theme); const onClick = () => { const next = toggleTheme(options, doc); updateLabel(next); options.onThemeChange?.(next); }; button.addEventListener("click", onClick); let unwatchDR; if (hideWhenDR) { unwatchDR = watchDarkReader((active) => { button.style.visibility = active ? "hidden" : ""; }, doc); } return () => { button.removeEventListener("click", onClick); unwatchDR?.(); }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { getCurrentTheme, getTheme, initThemeEarly, isDarkReaderActive, mountThemeToggleButton, setTheme, toggleTheme, watchDarkReader }); //# sourceMappingURL=index.cjs.map