MediaWiki:Timeless.js
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.
/* All JavaScript here will be loaded for users of the Timeless skin */
/**
* Timeless Light/Dark Mode Toggle
*
* Requires the corresponding CSS in MediaWiki:Timeless.css
*/
(function () {
'use strict';
var STORAGE_KEY = 'timeless-theme';
/**
* Get the user's preferred theme.
* Stored preference takes priority; otherwise use system preference.
*/
function getPreferredTheme() {
var stored = localStorage.getItem(STORAGE_KEY);
if (stored === 'dark' || stored === 'light') {
return stored;
}
return window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
/**
* Apply the selected theme.
*/
function applyTheme(theme) {
document.documentElement.classList.toggle('dark-mode', theme === 'dark');
updateButton(theme);
}
/**
* Update the toggle button text and tooltip.
*/
function updateButton(theme) {
var button = document.getElementById('timeless-theme-toggle');
if (!button) {
return;
}
if (theme === 'dark') {
button.textContent = '☀️ Light mode';
button.setAttribute('title', 'Switch to light mode');
button.setAttribute('aria-label', 'Switch to light mode');
} else {
button.textContent = '🌙 Dark mode';
button.setAttribute('title', 'Switch to dark mode');
button.setAttribute('aria-label', 'Switch to dark mode');
}
}
/**
* Create the toggle button.
*/
function createButton() {
if (document.getElementById('timeless-theme-toggle')) {
return;
}
var button = document.createElement('button');
button.id = 'timeless-theme-toggle';
button.type = 'button';
button.className = 'timeless-theme-toggle';
button.addEventListener('click', function () {
var currentTheme =
document.documentElement.classList.contains('dark-mode')
? 'dark'
: 'light';
var newTheme = currentTheme === 'dark' ? 'light' : 'dark';
localStorage.setItem(STORAGE_KEY, newTheme);
applyTheme(newTheme);
});
/*
* Timeless has a personal tools/navigation area.
* Try several possible locations so this works across Timeless versions.
*/
var locations = [
'#p-personal',
'#personal',
'.personal-tools',
'#mw-panel'
];
var target = null;
for (var i = 0; i < locations.length; i++) {
target = document.querySelector(locations[i]);
if (target) {
break;
}
}
if (!target) {
return;
}
/*
* If we found a navigation container, put the button in a list item
* when possible. Otherwise append it directly.
*/
var list = target.querySelector('ul');
if (list) {
var listItem = document.createElement('li');
listItem.id = 'timeless-theme-toggle-container';
listItem.appendChild(button);
list.appendChild(listItem);
} else {
target.appendChild(button);
}
updateButton(getPreferredTheme());
}
/**
* Apply the theme as early as possible.
*/
applyTheme(getPreferredTheme());
/**
* Wait for MediaWiki/Timeless to finish constructing the interface.
*/
mw.loader.using(['mediawiki.util'], function () {
$(createButton);
});
/**
* If the visitor has not manually selected a theme, automatically
* follow changes to their operating-system color scheme.
*/
if (window.matchMedia) {
var mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
var systemThemeChanged = function (event) {
if (!localStorage.getItem(STORAGE_KEY)) {
applyTheme(event.matches ? 'dark' : 'light');
}
};
if (mediaQuery.addEventListener) {
mediaQuery.addEventListener('change', systemThemeChanged);
} else if (mediaQuery.addListener) {
mediaQuery.addListener(systemThemeChanged);
}
}
})();