web/_javascript/modules/theme.js
Cotes Chung 65f960c31a
perf: speed up page rendering and jekyll build process (#2034)
- 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.
2024-11-16 22:49:55 +08:00

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;