@vxrn/color-scheme 1.13.3 → 1.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/userScheme.ts CHANGED
@@ -19,6 +19,54 @@ type SchemeListener = (setting: SchemeSetting, value: Scheme) => void
19
19
  const listeners = new Set<SchemeListener>()
20
20
  const storageKey = 'vxrn-scheme'
21
21
 
22
+ // force scheme: when set, locks the scheme and ignores user/system preferences
23
+ let _forceScheme: Scheme | null = null
24
+
25
+ function notifyListeners() {
26
+ listeners.forEach((listener) => {
27
+ listener(currentSetting, currentValue)
28
+ })
29
+ }
30
+
31
+ function restoreUnforcedScheme() {
32
+ if (typeof localStorage !== 'undefined') {
33
+ const stored = localStorage.getItem(storageKey) as SchemeSetting | null
34
+ if (stored) {
35
+ currentSetting = stored
36
+ currentValue = stored === 'system' ? resolveValue('system') : stored
37
+ return
38
+ }
39
+ }
40
+
41
+ currentSetting = 'system'
42
+ currentValue = resolveValue('system')
43
+ }
44
+
45
+ /**
46
+ * Lock the color scheme to a fixed value. When set, user preferences and system
47
+ * changes are ignored, and `setUserScheme` becomes a no-op.
48
+ * Pass `null` to clear the force and restore normal behavior.
49
+ */
50
+ export function setForceScheme(scheme: Scheme | null) {
51
+ const wasForced = _forceScheme
52
+
53
+ if (wasForced === scheme) return
54
+
55
+ _forceScheme = scheme
56
+
57
+ if (scheme) {
58
+ currentSetting = scheme
59
+ currentValue = scheme
60
+ } else if (wasForced) {
61
+ restoreUnforcedScheme()
62
+ startWebListener()
63
+ }
64
+ }
65
+
66
+ export function getForceScheme(): Scheme | null {
67
+ return _forceScheme
68
+ }
69
+
22
70
  // eagerly init from localStorage on module load (native only - web uses effect for SSR)
23
71
  function getInitialSetting(): SchemeSetting {
24
72
  if (process.env.TAMAGUI_TARGET === 'native') {
@@ -89,17 +137,17 @@ function resolveValue(setting: SchemeSetting): Scheme {
89
137
 
90
138
  // Only update the resolved value when system theme changes (don't change setting)
91
139
  function updateValueFromSystem() {
140
+ if (_forceScheme) return
92
141
  const value = resolveValue('system')
93
142
  if (value !== currentValue) {
94
143
  currentValue = value
95
144
  // don't call Appearance.setColorScheme when following system - it breaks the listener
96
- listeners.forEach((l) => {
97
- l(currentSetting, currentValue)
98
- })
145
+ notifyListeners()
99
146
  }
100
147
  }
101
148
 
102
149
  function updateScheme(setting: SchemeSetting) {
150
+ if (_forceScheme) return
103
151
  const value = setting === 'system' ? resolveValue('system') : setting
104
152
 
105
153
  if (value !== currentValue || currentSetting !== setting) {
@@ -117,9 +165,7 @@ function updateScheme(setting: SchemeSetting) {
117
165
  }
118
166
  }
119
167
 
120
- listeners.forEach((l) => {
121
- l(currentSetting, currentValue)
122
- })
168
+ notifyListeners()
123
169
  }
124
170
  }
125
171
 
@@ -130,6 +176,7 @@ function updateScheme(setting: SchemeSetting) {
130
176
  * @param setting - 'system', 'light', or 'dark'
131
177
  */
132
178
  export function setUserScheme(setting: SchemeSetting) {
179
+ if (_forceScheme) return
133
180
  if (typeof localStorage !== 'undefined') {
134
181
  localStorage.setItem(storageKey, setting)
135
182
  }
@@ -142,6 +189,7 @@ export function setUserScheme(setting: SchemeSetting) {
142
189
  * @returns Object with setting and resolved value
143
190
  */
144
191
  export function getUserScheme(): { setting: SchemeSetting; value: Scheme } {
192
+ if (_forceScheme) return { setting: _forceScheme, value: _forceScheme }
145
193
  return { setting: currentSetting, value: currentValue }
146
194
  }
147
195
 
@@ -174,31 +222,38 @@ export function onUserSchemeChange(listener: SchemeListener) {
174
222
  */
175
223
  export function useUserScheme(): UserScheme {
176
224
  const [state, setState] = useState(() => getUserScheme())
225
+ const snapshot = getUserScheme()
226
+ const resolvedState =
227
+ state.setting === snapshot.setting && state.value === snapshot.value
228
+ ? state
229
+ : snapshot
177
230
 
178
231
  useIsomorphicLayoutEffect(() => {
179
- // restore from localStorage on mount
180
- if (typeof localStorage !== 'undefined') {
181
- const stored = localStorage.getItem(storageKey) as SchemeSetting | null
182
- if (stored) {
183
- updateScheme(stored)
232
+ if (!_forceScheme) {
233
+ // restore from localStorage on mount
234
+ if (typeof localStorage !== 'undefined') {
235
+ const stored = localStorage.getItem(storageKey) as SchemeSetting | null
236
+ if (stored) {
237
+ updateScheme(stored)
238
+ }
184
239
  }
240
+ startWebListener()
185
241
  }
186
242
 
243
+ // always subscribe so force→unforce transitions propagate via setForceScheme(null)
187
244
  const dispose = onUserSchemeChange((setting, value) => {
188
245
  setState({ setting, value })
189
246
  })
190
247
 
191
- startWebListener()
192
-
193
248
  return dispose
194
249
  }, [])
195
250
 
196
251
  return useMemo(
197
252
  () => ({
198
- setting: state.setting,
199
- value: state.value,
253
+ setting: resolvedState.setting,
254
+ value: resolvedState.value,
200
255
  set: setUserScheme,
201
256
  }),
202
- [state.setting, state.value]
257
+ [resolvedState.setting, resolvedState.value]
203
258
  )
204
259
  }
package/types/index.d.ts CHANGED
@@ -3,11 +3,13 @@ export type { Scheme } from './systemScheme';
3
3
  export type { SchemeSetting, UserScheme } from './userScheme';
4
4
  export { getSystemScheme, useSystemScheme } from './systemScheme';
5
5
  export { getUserScheme, onUserSchemeChange, setUserScheme, useUserScheme, } from './userScheme';
6
- export declare function SchemeProvider({ children, getClassName, defaultScheme, }: {
6
+ export declare function SchemeProvider({ children, getClassName, defaultScheme, forceScheme, }: {
7
7
  children: any;
8
8
  getClassName?: (name: Scheme) => string;
9
9
  /** Force a default scheme when no user preference is stored. Without this, falls back to system preference. */
10
10
  defaultScheme?: Scheme;
11
+ /** Lock the scheme to this value. Ignores user preference, system preference, and localStorage. Prevents hydration flicker. */
12
+ forceScheme?: Scheme;
11
13
  }): import("react/jsx-runtime").JSX.Element;
12
14
  export declare function MetaTheme({ color, darkColor, lightColor, }: {
13
15
  color?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAI5C,YAAY,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAG7D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACjE,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,aAAa,GACd,MAAM,cAAc,CAAA;AAIrB,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,YAAoC,EACpC,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,GAAG,CAAA;IACb,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACvC,+GAA+G;IAC/G,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,2CAsDA;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,SAAS,EACT,UAAU,GACX,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB,2CAyBA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAS5C,YAAY,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAG7D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACjE,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,aAAa,GACd,MAAM,cAAc,CAAA;AAIrB,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,YAAoC,EACpC,aAAa,EACb,WAAW,GACZ,EAAE;IACD,QAAQ,EAAE,GAAG,CAAA;IACb,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACvC,+GAA+G;IAC/G,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,+HAA+H;IAC/H,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,2CAmEA;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,SAAS,EACT,UAAU,GACX,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB,2CA0BA"}
@@ -9,6 +9,13 @@ export type UserScheme = {
9
9
  set: (setting: SchemeSetting) => void;
10
10
  };
11
11
  type SchemeListener = (setting: SchemeSetting, value: Scheme) => void;
12
+ /**
13
+ * Lock the color scheme to a fixed value. When set, user preferences and system
14
+ * changes are ignored, and `setUserScheme` becomes a no-op.
15
+ * Pass `null` to clear the force and restore normal behavior.
16
+ */
17
+ export declare function setForceScheme(scheme: Scheme | null): void;
18
+ export declare function getForceScheme(): Scheme | null;
12
19
  /**
13
20
  * Imperatively set the user's color scheme preference.
14
21
  * Persists to localStorage and updates all listeners.
@@ -1 +1 @@
1
- {"version":3,"file":"userScheme.d.ts","sourceRoot":"","sources":["../src/userScheme.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAE7D,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;AAEvD,MAAM,MAAM,UAAU,GAAG;IACvB,0DAA0D;IAC1D,OAAO,EAAE,aAAa,CAAA;IACtB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,gCAAgC;IAChC,GAAG,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CACtC,CAAA;AAED,KAAK,cAAc,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;AA6GrE;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,QAKnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAEzE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,cAM1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,IAAI,UAAU,CA6B1C"}
1
+ {"version":3,"file":"userScheme.d.ts","sourceRoot":"","sources":["../src/userScheme.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAE7D,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;AAEvD,MAAM,MAAM,UAAU,GAAG;IACvB,0DAA0D;IAC1D,OAAO,EAAE,aAAa,CAAA;IACtB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,gCAAgC;IAChC,GAAG,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CACtC,CAAA;AAED,KAAK,cAAc,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;AA4BrE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,QAcnD;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAwGD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,QAMnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAGzE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,cAM1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAoC1C"}