PageSourceSearch

https://www.responsible.ai/wp-content/themes/rai-theme/assets/src/js/theme-toggle.js

js responsible.ai collected 2026-09-24 09:02:50 UTC 3,883 bytes, 139 lines download raw bytes

1/**
2 * Theme Toggle - Dark/Light Mode Support
3 * RAI Theme - Responsible AI Initiative
4 *
5 * Handles theme switching with:
6 * - localStorage persistence
7 * - System preference detection
8 * - Smooth transitions
9 * - Button icon updates
10 */
11
12(function() {
13    'use strict';
14
15    const STORAGE_KEY = 'rai-theme';
16    const DARK_CLASS = 'dark';
17
18    /**
19     * Get the current theme preference
20     * Priority: localStorage > system preference > light (default)
21     */
22    function getThemePreference() {
23        const stored = localStorage.getItem(STORAGE_KEY);
24        if (stored) {
25            return stored;
26        }
27
28        // Check system preference
29        if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
30            return 'dark';
31        }
32
33        return 'light';
34    }
35
36    /**
37     * Apply the theme to the document
38     */
39    function applyTheme(theme) {
40        const html = document.documentElement;
41
42        if (theme === 'dark') {
43            html.classList.add(DARK_CLASS);
44        } else {
45            html.classList.remove(DARK_CLASS);
46        }
47
48        // Update all toggle buttons
49        updateToggleButtons(theme);
50    }
51
52    /**
53     * Update all theme toggle button icons
54     */
55    function updateToggleButtons(theme) {
56        const buttons = document.querySelectorAll('[data-theme-toggle]');
57
58        buttons.forEach(button => {
59            const moonIcon = button.querySelector('.theme-icon-moon');
60            const sunIcon = button.querySelector('.theme-icon-sun');
61
62            if (moonIcon && sunIcon) {
63                if (theme === 'dark') {
64                    moonIcon.classList.add('hidden');
65                    sunIcon.classList.remove('hidden');
66                } else {
67                    moonIcon.classList.remove('hidden');
68                    sunIcon.classList.add('hidden');
69                }
70            }
71        });
72    }
73
74    /**
75     * Toggle between light and dark themes
76     */
77    function toggleTheme() {
78        const html = document.documentElement;
79        const isDark = html.classList.contains(DARK_CLASS);
80        const newTheme = isDark ? 'light' : 'dark';
81
82        localStorage.setItem(STORAGE_KEY, newTheme);
83        applyTheme(newTheme);
84    }
85
86    /**
87     * Set a specific theme
88     */
89    function setTheme(theme) {
90        localStorage.setItem(STORAGE_KEY, theme);
91        applyTheme(theme);
92    }
93
94    /**
95     * Initialize theme on page load
96     */
97    function init() {
98        // Apply theme immediately to prevent flash
99        const theme = getThemePreference();
100        applyTheme(theme);
101
102        // Listen for system preference changes
103        if (window.matchMedia) {
104            window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
105                // Only auto-switch if user hasn't set a preference
106                if (!localStorage.getItem(STORAGE_KEY)) {
107                    applyTheme(e.matches ? 'dark' : 'light');
108                }
109            });
110        }
111
112        // Set up toggle button click handlers
113        document.addEventListener('click', (e) => {
114            const toggleButton = e.target.closest('[data-theme-toggle]');
115            if (toggleButton) {
116                e.preventDefault();
117                toggleTheme();
118            }
119        });
120    }
121
122    // Apply theme immediately (before DOMContentLoaded) to prevent flash
123    const initialTheme = getThemePreference();
124    document.documentElement.classList.toggle(DARK_CLASS, initialTheme === 'dark');
125
126    // Initialize when DOM is ready
127    if (document.readyState === 'loading') {
128        document.addEventListener('DOMContentLoaded', init);
129    } else {
130        init();
131    }
132
133    // Expose functions globally for external use
134    window.RAITheme = {
135        toggle: toggleTheme,
136        set: setTheme,
137        get: getThemePreference
138    };
139})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.