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.
/* Any JavaScript here will be loaded for all users on every page load. */
/* =========================================================
WikiPartner — Light / Dark Theme Toggle
========================================================= */
(function () {
'use strict';
var DAY = 'day';
var NIGHT = 'night';
function getCurrentTheme() {
var html = document.documentElement;
if (html.classList.contains('skin-theme-clientpref-night')) {
return NIGHT;
}
if (html.classList.contains('skin-theme-clientpref-os')) {
if (
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
) {
return NIGHT;
}
}
return DAY;
}
function applyTheme(theme) {
var html = document.documentElement;
html.classList.remove(
'skin-theme-clientpref-day',
'skin-theme-clientpref-night',
'skin-theme-clientpref-os'
);
html.classList.add('skin-theme-clientpref-' + theme);
}
function saveTheme(theme) {
var isLoggedIn = !!mw.config.get('wgUserName');
/*
* Logged-out readers:
* use MediaWiki's native client preference cookie.
*/
if (!isLoggedIn && mw.user && mw.user.clientPrefs) {
var saved = mw.user.clientPrefs.set('skin-theme', theme);
if (!saved) {
applyTheme(theme);
}
return;
}
/*
* Logged-in users:
* change immediately, then save as a MediaWiki user preference.
*/
applyTheme(theme);
mw.loader.using('mediawiki.api').then(function () {
return new mw.Api().saveOption('skin-theme', theme);
}).catch(function () {
// Theme still works for the current page if saving fails.
});
}
function moonIcon() {
return '' +
'<svg viewBox="0 0 24 24" aria-hidden="true">' +
'<path d="M20.2 15.2A8.5 8.5 0 0 1 8.8 3.8a8.6 8.6 0 1 0 11.4 11.4Z"></path>' +
'<path class="wp-theme-star" d="M17.5 4.5v3M16 6h3"></path>' +
'</svg>';
}
function sunIcon() {
return '' +
'<svg viewBox="0 0 24 24" aria-hidden="true">' +
'<circle cx="12" cy="12" r="4"></circle>' +
'<path d="M12 2v2M12 20v2M4.93 4.93l1.42 1.42M17.65 17.65l1.42 1.42M2 12h2M20 12h2M4.93 19.07l1.42-1.42M17.65 6.35l1.42-1.42"></path>' +
'</svg>';
}
function updateButton(button) {
var theme = getCurrentTheme();
var isDark = theme === NIGHT;
button.innerHTML =
'<span class="wp-theme-icon">' +
(isDark ? sunIcon() : moonIcon()) +
'</span>';
button.setAttribute(
'aria-label',
isDark ? 'Switch to light mode' : 'Switch to dark mode'
);
button.setAttribute(
'title',
isDark ? 'Light mode' : 'Dark mode'
);
}
function createThemeToggle() {
if (document.getElementById('wikipartner-theme-toggle')) {
return;
}
var loginItem = document.getElementById('pt-login');
var list = loginItem ? loginItem.parentNode : null;
/*
* Fallback for logged-in users / slightly different Vector layouts.
*/
if (!list) {
list = document.querySelector(
'#p-personal .vector-menu-content-list, ' +
'#p-personal ul, ' +
'.vector-user-links .vector-menu-content-list'
);
}
if (!list) {
return;
}
var item = document.createElement('li');
item.className = 'mw-list-item wikipartner-theme-item';
var button = document.createElement('button');
button.id = 'wikipartner-theme-toggle';
button.className = 'wikipartner-theme-toggle';
button.type = 'button';
updateButton(button);
button.addEventListener('click', function () {
var current = getCurrentTheme();
var next = current === NIGHT ? DAY : NIGHT;
saveTheme(next);
/*
* clientPrefs changes the class itself for anonymous users,
* but this guarantees immediate visual response everywhere.
*/
applyTheme(next);
updateButton(button);
});
item.appendChild(button);
/*
* Exact requested position:
* immediately LEFT of "Log in".
*/
if (loginItem) {
list.insertBefore(item, loginItem);
} else {
list.insertBefore(item, list.firstChild);
}
/*
* Keep our icon synced if MediaWiki's own appearance
* controls change the theme.
*/
var observer = new MutationObserver(function () {
updateButton(button);
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createThemeToggle);
} else {
createThemeToggle();
}
})();