MediaWiki:ThemeToggle.js
MediaWiki interface page
More actions
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/*
* From https://utg.miraheze.org/wiki/MediaWiki:ThemeToggle.js
* This script adds a theme selector to the sidebar in the Citizen skin.
*/
(function () {
const THEME_STORAGE_KEY = 'selectedTheme';
// Creates the container for the theme selection menu
function createThemeContainer() {
const container = document.createElement('div');
container.id = 'skin-client-prefs-theme-section';
container.className = 'mw-portlet mw-portlet-js citizen-menu';
container.innerHTML = `
<div class="citizen-menu__heading">Accent color</div>
<div class="citizen-menu__content">
<ul class="citizen-menu__content-list">
<li class="mw-list-item mw-list-item-js">
<div><form id="theme-toggle-form"></form></div>
</li>
</ul>
</div>
`;
return container;
}
// Ensures the theme section is present on the page, creating it if necessary
function ensureThemeSection() {
let container = document.getElementById('skin-client-prefs-theme-section');
if (!container) {
container = createThemeContainer();
const skinThemeEl = document.querySelector('#skin-client-prefs-skin-theme');
if (skinThemeEl && skinThemeEl.parentNode) {
skinThemeEl.parentNode.insertBefore(container, skinThemeEl.nextSibling);
} else {
const observer = new MutationObserver((mutations, obs) => {
const target = document.querySelector('#skin-client-prefs-skin-theme');
if (target && target.parentNode) {
target.parentNode.insertBefore(container, target.nextSibling);
obs.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
(document.querySelector('#citizen-preferences-content') || document.body).appendChild(container);
}
}
return document.getElementById('theme-toggle-form');
}
// Loads a theme's CSS file by creating a <link> element in the head
function loadThemeCss(className) {
// DO NOTHING
}
// Removes CSS files of other themes to prevent conflicts
function removeOldThemeCss(currentClass) {
// DO NOTHING
}
// Applies the selected theme class to the <html> element
function applyTheme(className) {
document.documentElement.classList.forEach(cls => {
if (cls.startsWith('theme-')) {
document.documentElement.classList.remove(cls);
}
});
if (className) {
document.documentElement.classList.add(className);
loadThemeCss(className);
removeOldThemeCss(className);
}
}
/**
* Adds a new theme option to the selector menu.
* @param {string} name - The display name of the theme (e.g., "Default").
* @param {string} backgroundImage - Optional URL for a preview image.
* @param {string} className - The CSS class for the theme (e.g., "theme-Default").
*/
window.AddTheme = function (name, backgroundImage = '', className = '') {
const form = ensureThemeSection();
const themeClass = className || `theme-${name}`;
const id = `theme-${name.toLowerCase().replace(/\s+/g, '-')}`;
if (document.getElementById(id)) return; // Don't add if it already exists
const wrapper = document.createElement('div');
wrapper.className = 'citizen-client-prefs-radio';
wrapper.innerHTML = `
<input name="theme-toggle" id="${id}" type="radio" value="${themeClass}" class="citizen-client-prefs-radio__input">
<span class="citizen-client-prefs-radio__icon"></span>
<label for="${id}" class="citizen-client-prefs-radio__label" data-theme-class="${themeClass}">${name}</label>
`;
const input = wrapper.querySelector('input');
// Event listener to apply theme on change
input.addEventListener('change', function () {
if (this.checked) {
localStorage.setItem(THEME_STORAGE_KEY, themeClass); // Save selection
applyTheme(themeClass);
}
});
form.appendChild(wrapper);
};
// Initializes the available themes
function initThemes() {
//
// === Theme Selection ===
//
// AddTheme(ThemeName, PreviewImageURL, CSSClassName)
// - ThemeName: Name that appears in the menu.
// - PreviewImageURL: Optional image for the button. Leave as '' for none.
// - CSSClassName: Must match the class in your CSS file. Defaults to 'theme-ThemeName'.
//
AddTheme('Desert', '', 'theme-Default'); // Light Brown Mix
AddTheme('Outlands', '', 'theme-Outlands'); // Steel+Rust
AddTheme('Floodlands', '', 'theme-Floodlands'); // Blue+Grey
AddTheme('Bonefields', '', 'theme-Bonefields'); // Off-White+Tan
}
// Add themes to the page
initThemes();
// On page load, apply the saved theme from localStorage
$(document).ready(function(){
let savedClass = localStorage.getItem(THEME_STORAGE_KEY);
if (!savedClass) {
savedClass = 'theme-Default'; // Default theme if none is saved
localStorage.setItem(THEME_STORAGE_KEY, savedClass);
}
applyTheme(savedClass);
const radio = document.querySelector(`input[name="theme-toggle"][value="${savedClass}"]`);
if (radio) {
radio.checked = true;
}
});
})();