@@ -12,6 +12,7 @@ export const COLOR_MODE_CHANGE_EVENT = 'litdev-color-mode-change';
1212
1313interface ThemeState {
1414 mode : ColorMode ;
15+ overridden : boolean ;
1516 mediaQuery : MediaQueryList ;
1617 worker ?: SharedWorker ;
1718 port ?: MessagePort ;
@@ -20,10 +21,12 @@ interface ThemeState {
2021interface ThemeWorkerModeMessage {
2122 type : 'mode' ;
2223 mode : ColorMode ;
24+ overridden : boolean ;
2325}
2426
2527const THEME_STATE_KEY = Symbol . for ( 'lit.dev.theme-state' ) ;
2628const THEME_WORKER_PATH = '/js/global/theme-worker.js' ;
29+ const COLOR_MODE_STORAGE_KEY = 'color-mode' ;
2730
2831const isColorMode = ( value : unknown ) : value is ColorMode =>
2932 value === 'light' || value === 'dark' ;
@@ -43,14 +46,41 @@ const setThemeState = (state: ThemeState) => {
4346 ) [ THEME_STATE_KEY ] = state ;
4447} ;
4548
49+ const getStoredColorMode = ( ) : ColorMode | undefined => {
50+ try {
51+ const mode = sessionStorage . getItem ( COLOR_MODE_STORAGE_KEY ) ;
52+ return isColorMode ( mode ) ? mode : undefined ;
53+ } catch {
54+ return undefined ;
55+ }
56+ } ;
57+
58+ const storeColorMode = ( mode : ColorMode | undefined ) => {
59+ try {
60+ if ( mode ) {
61+ sessionStorage . setItem ( COLOR_MODE_STORAGE_KEY , mode ) ;
62+ } else {
63+ sessionStorage . removeItem ( COLOR_MODE_STORAGE_KEY ) ;
64+ }
65+ } catch {
66+ // Theme switching still works within this document when storage is blocked.
67+ }
68+ } ;
69+
4670/**
4771 * Sets the theme on the page given a color mode.
4872 *
4973 * @param mode The source color to generate the theme.
5074 */
51- function applyColorMode ( state : ThemeState , mode : ColorMode ) {
75+ function applyColorMode (
76+ state : ThemeState ,
77+ mode : ColorMode ,
78+ overridden : boolean
79+ ) {
5280 const modeChanged = state . mode !== mode ;
5381 state . mode = mode ;
82+ state . overridden = overridden ;
83+ storeColorMode ( overridden ? mode : undefined ) ;
5484 document . body . classList . remove ( 'light' , 'dark' , 'auto' ) ;
5585 document . body . classList . add ( mode ) ;
5686 updateMetaColor ( mode ) ;
@@ -118,20 +148,19 @@ export function initializeTheme() {
118148 }
119149
120150 const mediaQuery = window . matchMedia ( '(prefers-color-scheme: dark)' ) ;
151+ const storedMode = getStoredColorMode ( ) ;
121152 const state : ThemeState = {
122- mode : mediaQuery . matches ? 'dark' : 'light' ,
153+ mode : storedMode ?? ( mediaQuery . matches ? 'dark' : 'light' ) ,
154+ overridden : storedMode !== undefined ,
123155 mediaQuery,
124156 } ;
125157 setThemeState ( state ) ;
126- applyColorMode ( state , state . mode ) ;
158+ applyColorMode ( state , state . mode , state . overridden ) ;
127159
128160 mediaQuery . addEventListener ( 'change' , ( { matches} ) => {
129161 const mode = matches ? 'dark' : 'light' ;
130- if ( state . port ) {
131- state . port . postMessage ( { type : 'system-mode' , mode} ) ;
132- } else {
133- applyColorMode ( state , mode ) ;
134- }
162+ applyColorMode ( state , mode , false ) ;
163+ state . port ?. postMessage ( { type : 'system-mode' , mode} ) ;
135164 } ) ;
136165
137166 if ( ! ( 'SharedWorker' in window ) ) {
@@ -160,10 +189,14 @@ export function initializeTheme() {
160189 return ;
161190 }
162191
163- applyColorMode ( state , message . mode ) ;
192+ applyColorMode ( state , message . mode , message . overridden === true ) ;
164193 } ) ;
165194 port . start ( ) ;
166- port . postMessage ( { type : 'connect' , mode : state . mode } ) ;
195+ port . postMessage ( {
196+ type : 'connect' ,
197+ mode : state . mode ,
198+ overridden : state . overridden ,
199+ } ) ;
167200 } catch {
168201 state . worker = undefined ;
169202 state . port = undefined ;
@@ -180,6 +213,6 @@ export function setColorMode(mode: ColorMode) {
180213 return ;
181214 }
182215
183- applyColorMode ( state , mode ) ;
216+ applyColorMode ( state , mode , true ) ;
184217 state . port ?. postMessage ( { type : 'set-mode' , mode} ) ;
185218}
0 commit comments