65f960c31a
- Ensure inline scripts execute after the DOM has fully loaded. - Use Rollup to bundle the theme-mode and Mermaid scripts, reducing the number of Jekyll include snippets.
135 lines
2.8 KiB
JavaScript
135 lines
2.8 KiB
JavaScript
/**
|
|
* Theme management class
|
|
*
|
|
* To reduce flickering during page load, this script should be loaded synchronously.
|
|
*/
|
|
class Theme {
|
|
static #modeKey = 'mode';
|
|
static #modeAttr = 'data-mode';
|
|
static #darkMedia = window.matchMedia('(prefers-color-scheme: dark)');
|
|
static switchable = !document.documentElement.hasAttribute(this.#modeAttr);
|
|
|
|
static get DARK() {
|
|
return 'dark';
|
|
}
|
|
|
|
static get LIGHT() {
|
|
return 'light';
|
|
}
|
|
|
|
/**
|
|
* @returns {string} Theme mode identifier
|
|
*/
|
|
static get ID() {
|
|
return 'theme-mode';
|
|
}
|
|
|
|
/**
|
|
* Gets the current visual state of the theme.
|
|
*
|
|
* @returns {string} The current visual state, either the mode if it exists,
|
|
* or the system dark mode state ('dark' or 'light').
|
|
*/
|
|
static get visualState() {
|
|
if (this.#hasMode) {
|
|
return this.#mode;
|
|
} else {
|
|
return this.#sysDark ? this.DARK : this.LIGHT;
|
|
}
|
|
}
|
|
|
|
static get #mode() {
|
|
return sessionStorage.getItem(this.#modeKey);
|
|
}
|
|
|
|
static get #isDarkMode() {
|
|
return this.#mode === this.DARK;
|
|
}
|
|
|
|
static get #hasMode() {
|
|
return this.#mode !== null;
|
|
}
|
|
|
|
static get #sysDark() {
|
|
return this.#darkMedia.matches;
|
|
}
|
|
|
|
/**
|
|
* Maps theme modes to provided values
|
|
* @param {string} light Value for light mode
|
|
* @param {string} dark Value for dark mode
|
|
* @returns {Object} Mapped values
|
|
*/
|
|
static getThemeMapper(light, dark) {
|
|
return {
|
|
[this.LIGHT]: light,
|
|
[this.DARK]: dark
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Initializes the theme based on system preferences or stored mode
|
|
*/
|
|
static init() {
|
|
if (!this.switchable) {
|
|
return;
|
|
}
|
|
|
|
this.#darkMedia.addEventListener('change', () => {
|
|
const lastMode = this.#mode;
|
|
this.#clearMode();
|
|
|
|
if (lastMode !== this.visualState) {
|
|
this.#notify();
|
|
}
|
|
});
|
|
|
|
if (!this.#hasMode) {
|
|
return;
|
|
}
|
|
|
|
if (this.#isDarkMode) {
|
|
this.#setDark();
|
|
} else {
|
|
this.#setLight();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flips the current theme mode
|
|
*/
|
|
static flip() {
|
|
if (this.#hasMode) {
|
|
this.#clearMode();
|
|
} else {
|
|
this.#sysDark ? this.#setLight() : this.#setDark();
|
|
}
|
|
this.#notify();
|
|
}
|
|
|
|
static #setDark() {
|
|
document.documentElement.setAttribute(this.#modeAttr, this.DARK);
|
|
sessionStorage.setItem(this.#modeKey, this.DARK);
|
|
}
|
|
|
|
static #setLight() {
|
|
document.documentElement.setAttribute(this.#modeAttr, this.LIGHT);
|
|
sessionStorage.setItem(this.#modeKey, this.LIGHT);
|
|
}
|
|
|
|
static #clearMode() {
|
|
document.documentElement.removeAttribute(this.#modeAttr);
|
|
sessionStorage.removeItem(this.#modeKey);
|
|
}
|
|
|
|
/**
|
|
* Notifies other plugins that the theme mode has changed
|
|
*/
|
|
static #notify() {
|
|
window.postMessage({ id: this.ID }, '*');
|
|
}
|
|
}
|
|
|
|
Theme.init();
|
|
|
|
export default Theme;
|