1/** 2 * Inside The Rink â light/dark toggle. 3 * 4 * The anti-flash half of this lives in an inline <head> script (functions.php), 5 * which must run before first paint. This file only handles interaction. 6 * 7 * Storage is localStorage, not a cookie or user meta, because the HTML is 8 * edge-cached â a server-rendered choice would be baked into the cached page 9 * and served to every visitor. 10 * 11 * Three states, matching the app's ThemeMode in src/context/theme-context.tsx: 12 * no attribute -> follow the OS (default) 13 * data-itr-theme=light -> forced light 14 * data-itr-theme=dark -> forced dark 15 * 16 * The button toggles between light and dark. Clearing site data returns the 17 * visitor to following the OS. 18 */ 19( function () { 20 'use strict'; 21 22 var STORAGE_KEY = 'itr-theme'; 23 var root = document.documentElement; 24 25 function osPrefersDark() { 26 return window.matchMedia && window.matchMedia( '(prefers-color-scheme: dark)' ).matches; 27 } 28 29 /** What the visitor is actually looking at right now. */ 30 function currentTheme() { 31 var forced = root.getAttribute( 'data-itr-theme' ); 32 33 if ( 'light' === forced || 'dark' === forced ) { 34 return forced; 35 } 36 37 return osPrefersDark() ? 'dark' : 'light'; 38 } 39 40 function store( mode ) { 41 try { 42 window.localStorage.setItem( STORAGE_KEY, mode ); 43 } catch ( e ) { 44 // Private mode or storage disabled â the toggle still works for this 45 // page view, it just won't persist. 46 } 47 } 48 49 function apply( mode ) { 50 root.setAttribute( 'data-itr-theme', mode ); 51 store( mode ); 52 syncButtons( mode ); 53 } 54 55 function syncButtons( mode ) { 56 var isDark = 'dark' === mode; 57 var label = isDark ? 'Switch to light mode' : 'Switch to dark mode'; 58 59 document.querySelectorAll( '.itr-theme-toggle' ).forEach( function ( btn ) { 60 btn.setAttribute( 'aria-label', label ); 61 btn.setAttribute( 'title', label ); 62 btn.setAttribute( 'aria-pressed', isDark ? 'true' : 'false' ); 63 } ); 64 } 65 66 function init() { 67 syncButtons( currentTheme() ); 68 69 document.addEventListener( 'click', function ( event ) { 70 var btn = event.target.closest( '.itr-theme-toggle' ); 71 72 if ( ! btn ) { 73 return; 74 } 75 76 event.preventDefault(); 77 apply( 'dark' === currentTheme() ? 'light' : 'dark' ); 78 } ); 79 80 // Keep following the OS until the visitor makes an explicit choice. 81 if ( window.matchMedia ) { 82 var mq = window.matchMedia( '(prefers-color-scheme: dark)' ); 83 var onChange = function () { 84 if ( ! root.hasAttribute( 'data-itr-theme' ) ) { 85 syncButtons( currentTheme() ); 86 } 87 }; 88 89 if ( mq.addEventListener ) { 90 mq.addEventListener( 'change', onChange ); 91 } else if ( mq.addListener ) { 92 mq.addListener( onChange ); 93 } 94 } 95 } 96 97 if ( 'loading' === document.readyState ) { 98 document.addEventListener( 'DOMContentLoaded', init ); 99 } else { 100 init(); 101 } 102}() );
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.