No edit summary
Tag: Reverted
No edit summary
Tag: Reverted
Line 3: Line 3:
/**
/**
  * Night Mode Toggle for Minerva Neue
  * Night Mode Toggle for Minerva Neue
  * Adds a sticky toggle button that persists user preference in localStorage
  * Theme is applied early via LocalSettings.php hook to prevent FOUC
* This script just adds the toggle button
  */
  */
(function () {
(function () {
Line 10: Line 11:
     var STORAGE_KEY = 'minerva-night-mode';
     var STORAGE_KEY = 'minerva-night-mode';


     // SVG icons (24x24)
     // SVG icons (20x20)
     var MOON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
     var MOON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
     var SUN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';
     var SUN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';


    /**
    * Get saved theme or default to 'day'
    */
     function getSavedTheme() {
     function getSavedTheme() {
         try {
         try {
Line 25: Line 23:
     }
     }


    /**
    * Save theme preference
    */
     function saveTheme(theme) {
     function saveTheme(theme) {
         try {
         try {
             localStorage.setItem(STORAGE_KEY, theme);
             localStorage.setItem(STORAGE_KEY, theme);
         } catch (e) {
         } catch (e) {}
            // localStorage unavailable
        }
     }
     }


    /**
     function applyTheme(theme) {
    * Apply theme via inline style injection (prevents FOUC)
         var html = document.documentElement;
    * This runs immediately, before DOM is ready
         html.classList.remove('skin-theme-clientpref-day', 'skin-theme-clientpref-night');
    */
         html.classList.add('skin-theme-clientpref-' + theme);
     function applyThemeImmediate(theme) {
        // Remove any existing theme style tag
         var existingStyle = document.getElementById('night-mode-immediate');
        if (existingStyle) {
            existingStyle.parentNode.removeChild(existingStyle);
         }
 
        // Inject style tag into head immediately
        var style = document.createElement('style');
        style.id = 'night-mode-immediate';
       
        if (theme === 'night') {
            // Apply night mode class to html element
            style.textContent = 'html { color-scheme: dark; }';
            document.documentElement.classList.remove('skin-theme-clientpref-day');
            document.documentElement.classList.add('skin-theme-clientpref-night');
         } else {
            style.textContent = 'html { color-scheme: light; }';
            document.documentElement.classList.remove('skin-theme-clientpref-night');
            document.documentElement.classList.add('skin-theme-clientpref-day');
        }
 
        // Insert into head as early as possible
        var head = document.head || document.getElementsByTagName('head')[0];
        if (head) {
            head.insertBefore(style, head.firstChild);
        }
     }
     }


    /**
    * Update button appearance
    */
     function updateButtonState(button, theme) {
     function updateButtonState(button, theme) {
         var icon = button.querySelector('.minerva-icon');
         var icon = button.querySelector('.minerva-icon');
        var label = button.querySelector('.toggle-label');
          
          
         if (theme === 'night') {
         if (theme === 'night') {
             icon.innerHTML = SUN_SVG;
             icon.innerHTML = SUN_SVG;
            if (label) label.textContent = 'Light mode';
             button.setAttribute('title', 'Switch to light mode');
             button.setAttribute('title', 'Switch to light mode');
         } else {
         } else {
             icon.innerHTML = MOON_SVG;
             icon.innerHTML = MOON_SVG;
            if (label) label.textContent = 'Dark mode';
             button.setAttribute('title', 'Switch to dark mode');
             button.setAttribute('title', 'Switch to dark mode');
         }
         }
     }
     }


    /**
    * Create the toggle button element
    */
     function createToggleButton() {
     function createToggleButton() {
         var currentTheme = getSavedTheme();
         var currentTheme = getSavedTheme();
Line 111: Line 68:


         var label = document.createElement('span');
         var label = document.createElement('span');
         label.className = 'toggle-label';
         label.className = 'sr-only';
        // Visually hidden but accessible
         label.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;';
         label.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;';
        label.textContent = theme === 'night' ? 'Light mode' : 'Dark mode';
          
          
         button.appendChild(icon);
         button.appendChild(icon);
Line 119: Line 76:
         li.appendChild(button);
         li.appendChild(button);


        // Set initial state
         updateButtonState(button, currentTheme);
         updateButtonState(button, currentTheme);


        // Handle click
         button.addEventListener('click', function (e) {
         button.addEventListener('click', function (e) {
             e.preventDefault();
             e.preventDefault();
             var newTheme = getSavedTheme() === 'night' ? 'day' : 'night';
             var newTheme = getSavedTheme() === 'night' ? 'day' : 'night';
             saveTheme(newTheme);
             saveTheme(newTheme);
             applyThemeImmediate(newTheme);
             applyTheme(newTheme);
             updateButtonState(button, newTheme);
             updateButtonState(button, newTheme);
         });
         });
Line 134: Line 89:
     }
     }


    /**
    * Insert button into the navigation
    */
     function insertButton() {
     function insertButton() {
         var notificationsList = document.querySelector('.minerva-notifications ul');
         var notificationsList = document.querySelector('.minerva-notifications ul');
          
          
         if (notificationsList) {
         if (notificationsList) {
            var toggleButton = createToggleButton();
             notificationsList.appendChild(createToggleButton());
             notificationsList.appendChild(toggleButton);
         } else {
         } else {
            // Fallback: try inserting into minerva-user-navigation
             var userNav = document.querySelector('.minerva-user-navigation');
             var userNav = document.querySelector('.minerva-user-navigation');
             if (userNav) {
             if (userNav) {
Line 157: Line 107:
     }
     }


     /**
     // Insert button when DOM is ready
    * Initialize
    if (document.readyState === 'loading') {
    */
        document.addEventListener('DOMContentLoaded', insertButton);
    function init() {
    } else {
        // Apply saved theme IMMEDIATELY (this is the key to preventing FOUC)
        insertButton();
        applyThemeImmediate(getSavedTheme());
 
        // Insert button when DOM is ready
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', insertButton);
        } else {
            insertButton();
        }
     }
     }
    // Run immediately
    init();
})();
})();

Revision as of 20:02, 14 January 2026

/* Any JavaScript here will be loaded for all users on every page load. */

/**
 * Night Mode Toggle for Minerva Neue
 * Theme is applied early via LocalSettings.php hook to prevent FOUC
 * This script just adds the toggle button
 */
(function () {
    'use strict';

    var STORAGE_KEY = 'minerva-night-mode';

    // SVG icons (20x20)
    var MOON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
    var SUN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';

    function getSavedTheme() {
        try {
            return localStorage.getItem(STORAGE_KEY) || 'day';
        } catch (e) {
            return 'day';
        }
    }

    function saveTheme(theme) {
        try {
            localStorage.setItem(STORAGE_KEY, theme);
        } catch (e) {}
    }

    function applyTheme(theme) {
        var html = document.documentElement;
        html.classList.remove('skin-theme-clientpref-day', 'skin-theme-clientpref-night');
        html.classList.add('skin-theme-clientpref-' + theme);
    }

    function updateButtonState(button, theme) {
        var icon = button.querySelector('.minerva-icon');
        
        if (theme === 'night') {
            icon.innerHTML = SUN_SVG;
            button.setAttribute('title', 'Switch to light mode');
        } else {
            icon.innerHTML = MOON_SVG;
            button.setAttribute('title', 'Switch to dark mode');
        }
    }

    function createToggleButton() {
        var currentTheme = getSavedTheme();

        var li = document.createElement('li');
        
        var button = document.createElement('a');
        button.setAttribute('role', 'button');
        button.setAttribute('href', '#');
        button.id = 'ca-nightmode';
        button.className = 'cdx-button cdx-button--size-large cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--icon-only cdx-button--weight-quiet';
        button.style.cursor = 'pointer';

        var icon = document.createElement('span');
        icon.className = 'minerva-icon';
        icon.style.display = 'flex';
        icon.style.alignItems = 'center';
        icon.style.justifyContent = 'center';
        icon.style.width = '20px';
        icon.style.height = '20px';

        var label = document.createElement('span');
        label.className = 'sr-only';
        label.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;';
        label.textContent = theme === 'night' ? 'Light mode' : 'Dark mode';
        
        button.appendChild(icon);
        button.appendChild(label);
        li.appendChild(button);

        updateButtonState(button, currentTheme);

        button.addEventListener('click', function (e) {
            e.preventDefault();
            var newTheme = getSavedTheme() === 'night' ? 'day' : 'night';
            saveTheme(newTheme);
            applyTheme(newTheme);
            updateButtonState(button, newTheme);
        });

        return li;
    }

    function insertButton() {
        var notificationsList = document.querySelector('.minerva-notifications ul');
        
        if (notificationsList) {
            notificationsList.appendChild(createToggleButton());
        } else {
            var userNav = document.querySelector('.minerva-user-navigation');
            if (userNav) {
                var container = document.createElement('div');
                container.className = 'minerva-notifications';
                var ul = document.createElement('ul');
                ul.appendChild(createToggleButton());
                container.appendChild(ul);
                userNav.insertBefore(container, userNav.firstChild);
            }
        }
    }

    // Insert button when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', insertButton);
    } else {
        insertButton();
    }
})();
MediaWiki Appliance - Powered by TurnKey Linux