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
JavaScript
;
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