@scalar/use-hooks 0.4.6 → 0.4.7
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @scalar/use-hooks
|
|
2
2
|
|
|
3
|
+
## 0.4.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#9540](https://github.com/scalar/scalar/pull/9540): Fix an SSR hydration mismatch in the color-mode toggle: the system preference is now resolved after mount so the first client render matches the server.
|
|
8
|
+
|
|
3
9
|
## 0.4.6
|
|
4
10
|
|
|
5
11
|
## 0.4.5
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useColorMode.d.ts","sourceRoot":"","sources":["../../src/useColorMode/useColorMode.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useColorMode.d.ts","sourceRoot":"","sources":["../../src/useColorMode/useColorMode.ts"],"names":[],"mappings":"AAmBA,2BAA2B;AAC3B,KAAK,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE5C,+BAA+B;AAC/B,KAAK,aAAa,GAAG,OAAO,GAAG,MAAM,CAAA;AAErC;;GAEG;AACH,wBAAgB,YAAY,CAC1B,IAAI,GAAE;IACJ,oCAAoC;IACpC,gBAAgB,CAAC,EAAE,SAAS,CAAA;IAC5B,8BAA8B;IAC9B,iBAAiB,CAAC,EAAE,SAAS,CAAA;CACzB;IAsGJ,yCAAyC;;IAKzC,+CAA+C;;IAE/C,yDAAyD;;IAEzD,qDAAqD;;IAErD,kDAAkD;0BAhGvB,SAAS;IAkGpC,uCAAuC;mCAzFL,aAAa;EA4FlD"}
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { literal, union, validate } from '@scalar/validation';
|
|
2
2
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
3
3
|
const colorMode = ref('dark');
|
|
4
|
+
/**
|
|
5
|
+
* Reactive snapshot of the system preference, shared across every `useColorMode` instance.
|
|
6
|
+
*
|
|
7
|
+
* It defaults to `'light'` so the first client render matches the server, where
|
|
8
|
+
* `window`/`matchMedia` do not exist. We resolve the real value in `onMounted` to avoid a
|
|
9
|
+
* hydration mismatch in anything bound to the color mode (for example the dark-mode toggle).
|
|
10
|
+
*
|
|
11
|
+
* It lives at module scope (like `colorMode`) so all instances agree on a single value instead
|
|
12
|
+
* of each holding its own copy that resolves at a different mount time.
|
|
13
|
+
*/
|
|
14
|
+
const systemPreference = ref('light');
|
|
4
15
|
const colorModeSchema = union([literal('system'), literal('dark'), literal('light')]);
|
|
5
16
|
/**
|
|
6
17
|
* A composable hook that provides color mode (dark/light) functionality.
|
|
@@ -37,7 +48,7 @@ export function useColorMode(opts = {}) {
|
|
|
37
48
|
}
|
|
38
49
|
/** Writable computed ref for dark/light mode with system preference applied */
|
|
39
50
|
const darkLightMode = computed({
|
|
40
|
-
get: () => (colorMode.value === 'system' ?
|
|
51
|
+
get: () => (colorMode.value === 'system' ? systemPreference.value : colorMode.value),
|
|
41
52
|
set: setColorMode,
|
|
42
53
|
});
|
|
43
54
|
/** Writable computed ref for whether the current color mode is dark */
|
|
@@ -46,11 +57,13 @@ export function useColorMode(opts = {}) {
|
|
|
46
57
|
set: (value) => setColorMode(value ? 'dark' : 'light'),
|
|
47
58
|
});
|
|
48
59
|
/** Applies the appropriate color mode class to the body. */
|
|
49
|
-
function applyColorMode(
|
|
60
|
+
function applyColorMode() {
|
|
50
61
|
if (typeof document === 'undefined' || typeof window === 'undefined') {
|
|
51
62
|
return;
|
|
52
63
|
}
|
|
53
|
-
|
|
64
|
+
// Read from the deferred `systemPreference` ref (not `getSystemModePreference()` directly) so
|
|
65
|
+
// the body class agrees with `darkLightMode`/the toggle before `onMounted` resolves the real value.
|
|
66
|
+
const classMode = overrideColorMode ?? (colorMode.value === 'system' ? systemPreference.value : colorMode.value);
|
|
54
67
|
if (classMode === 'dark') {
|
|
55
68
|
document.body.classList.add('dark-mode');
|
|
56
69
|
document.body.classList.remove('light-mode');
|
|
@@ -66,12 +79,18 @@ export function useColorMode(opts = {}) {
|
|
|
66
79
|
const storedValue = typeof window === 'undefined' ? 'system' : window?.localStorage?.getItem('colorMode');
|
|
67
80
|
const savedColorMode = validate(colorModeSchema, storedValue) ? storedValue : null;
|
|
68
81
|
colorMode.value = overrideColorMode ?? savedColorMode ?? initialColorMode;
|
|
69
|
-
// Watch for
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
// Watch for color mode or system preference changes and update the body class. Watching
|
|
83
|
+
// `systemPreference` means resolving it in `onMounted` (or an OS theme change) re-applies the class.
|
|
84
|
+
watch([colorMode, systemPreference], applyColorMode, { immediate: true });
|
|
85
|
+
const handleChange = () => {
|
|
86
|
+
systemPreference.value = getSystemModePreference();
|
|
87
|
+
};
|
|
72
88
|
const mediaQuery = ref(null);
|
|
73
89
|
// Listen to system preference changes
|
|
74
90
|
onMounted(() => {
|
|
91
|
+
// Now that we are on the client, resolve the real system preference so the
|
|
92
|
+
// color mode updates after hydration instead of mismatching the server.
|
|
93
|
+
systemPreference.value = getSystemModePreference();
|
|
75
94
|
if (typeof window !== 'undefined' && typeof window?.matchMedia === 'function') {
|
|
76
95
|
mediaQuery.value = window.matchMedia('(prefers-color-scheme: dark)');
|
|
77
96
|
mediaQuery.value?.addEventListener('change', handleChange);
|