@r2digisolutions/components 0.14.0 → 0.14.1
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/README.md +14 -11
- package/dist/utils/theme.svelte.d.ts +2 -4
- package/dist/utils/theme.svelte.js +63 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,24 +97,27 @@ Hay dos ejes independientes en `<html>`:
|
|
|
97
97
|
- **slate** — gris frío (hue 265, el look actual).
|
|
98
98
|
- **neutral** — croma 0, sin castaño azul. Misma familia que Tailwind `neutral` (`#171717`, `#fafafa`).
|
|
99
99
|
|
|
100
|
-
Para evitar FOUC, fíjalo en `app.html`
|
|
100
|
+
Para evitar FOUC, fíjalo en `app.html` con **cookie** (no `localStorage`; en SSR no existe `setItem`):
|
|
101
101
|
|
|
102
102
|
```html
|
|
103
103
|
<html lang="es" data-theme="neutral">
|
|
104
104
|
<head>
|
|
105
105
|
<script>
|
|
106
106
|
(function () {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
(!('theme' in localStorage) &&
|
|
113
|
-
!localStorage.getItem('r2-theme') &&
|
|
114
|
-
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
115
|
-
) {
|
|
116
|
-
document.documentElement.classList.add('dark');
|
|
107
|
+
function getCookie(name) {
|
|
108
|
+
var m = document.cookie.match(
|
|
109
|
+
new RegExp('(?:^|; )' + name.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1') + '=([^;]*)')
|
|
110
|
+
);
|
|
111
|
+
return m ? decodeURIComponent(m[1]) : null;
|
|
117
112
|
}
|
|
113
|
+
var palette = getCookie('r2-theme-palette') || 'neutral';
|
|
114
|
+
document.documentElement.setAttribute('data-theme', palette);
|
|
115
|
+
var theme = getCookie('r2-theme');
|
|
116
|
+
var dark =
|
|
117
|
+
theme === 'dark' ||
|
|
118
|
+
((!theme || theme === 'system') &&
|
|
119
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
120
|
+
document.documentElement.classList.toggle('dark', dark);
|
|
118
121
|
})();
|
|
119
122
|
</script>
|
|
120
123
|
</head>
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
* Mode: `class="dark"` on `<html>`
|
|
5
5
|
* Palette: `data-theme="slate" | "neutral"` on `<html>`
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* - Detects system preference via prefers-color-scheme
|
|
9
|
-
* - Reactive via Svelte 5 runes ($state)
|
|
7
|
+
* Persists to cookies (readable by the FOUC script in `app.html` and by SSR).
|
|
10
8
|
*/
|
|
11
9
|
export type Theme = 'light' | 'dark' | 'system';
|
|
12
10
|
export type ThemePalette = 'slate' | 'neutral';
|
|
@@ -23,7 +21,7 @@ declare class ThemeStore {
|
|
|
23
21
|
* `slate` = cool (hue 265). `neutral` = chroma 0, no blue cast.
|
|
24
22
|
*
|
|
25
23
|
* Pass `{ persist: false }` for a page-level override that does not
|
|
26
|
-
*
|
|
24
|
+
* write the cookie.
|
|
27
25
|
*/
|
|
28
26
|
setPalette(palette: ThemePalette, opts?: {
|
|
29
27
|
persist?: boolean;
|
|
@@ -4,32 +4,67 @@
|
|
|
4
4
|
* Mode: `class="dark"` on `<html>`
|
|
5
5
|
* Palette: `data-theme="slate" | "neutral"` on `<html>`
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* - Detects system preference via prefers-color-scheme
|
|
9
|
-
* - Reactive via Svelte 5 runes ($state)
|
|
7
|
+
* Persists to cookies (readable by the FOUC script in `app.html` and by SSR).
|
|
10
8
|
*/
|
|
11
|
-
const
|
|
12
|
-
const
|
|
9
|
+
const THEME_COOKIE = 'r2-theme';
|
|
10
|
+
const PALETTE_COOKIE = 'r2-theme-palette';
|
|
11
|
+
const LEGACY_THEME_KEY = 'r2-theme';
|
|
12
|
+
const LEGACY_PALETTE_KEY = 'r2-theme-palette';
|
|
13
13
|
const DEFAULT_PALETTE = 'slate';
|
|
14
|
+
const COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
14
15
|
function getSystemTheme() {
|
|
15
16
|
if (typeof window === 'undefined')
|
|
16
17
|
return 'light';
|
|
17
18
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
18
19
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
return 'system';
|
|
22
|
-
return localStorage.getItem(STORAGE_KEY) ?? 'system';
|
|
20
|
+
function isTheme(value) {
|
|
21
|
+
return value === 'light' || value === 'dark' || value === 'system';
|
|
23
22
|
}
|
|
24
23
|
function isPalette(value) {
|
|
25
24
|
return value === 'slate' || value === 'neutral';
|
|
26
25
|
}
|
|
26
|
+
function readCookie(name) {
|
|
27
|
+
if (typeof document === 'undefined')
|
|
28
|
+
return null;
|
|
29
|
+
const escaped = name.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1');
|
|
30
|
+
const match = document.cookie.match(new RegExp(`(?:^|; )${escaped}=([^;]*)`));
|
|
31
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
32
|
+
}
|
|
33
|
+
function writeCookie(name, value) {
|
|
34
|
+
if (typeof document === 'undefined')
|
|
35
|
+
return;
|
|
36
|
+
document.cookie = `${name}=${encodeURIComponent(value)};path=/;max-age=${COOKIE_MAX_AGE};SameSite=Lax`;
|
|
37
|
+
}
|
|
38
|
+
function readLegacyStorage(key) {
|
|
39
|
+
if (typeof localStorage?.getItem !== 'function')
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
return localStorage.getItem(key);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function getStoredTheme() {
|
|
49
|
+
const fromCookie = readCookie(THEME_COOKIE);
|
|
50
|
+
if (isTheme(fromCookie))
|
|
51
|
+
return fromCookie;
|
|
52
|
+
const legacy = readLegacyStorage(LEGACY_THEME_KEY);
|
|
53
|
+
if (isTheme(legacy)) {
|
|
54
|
+
writeCookie(THEME_COOKIE, legacy);
|
|
55
|
+
return legacy;
|
|
56
|
+
}
|
|
57
|
+
return 'system';
|
|
58
|
+
}
|
|
27
59
|
function getStoredPalette() {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
60
|
+
const fromCookie = readCookie(PALETTE_COOKIE);
|
|
61
|
+
if (isPalette(fromCookie))
|
|
62
|
+
return fromCookie;
|
|
63
|
+
const legacy = readLegacyStorage(LEGACY_PALETTE_KEY);
|
|
64
|
+
if (isPalette(legacy)) {
|
|
65
|
+
writeCookie(PALETTE_COOKIE, legacy);
|
|
66
|
+
return legacy;
|
|
67
|
+
}
|
|
33
68
|
if (typeof document !== 'undefined') {
|
|
34
69
|
const attr = document.documentElement.getAttribute('data-theme');
|
|
35
70
|
if (isPalette(attr))
|
|
@@ -53,17 +88,16 @@ class ThemeStore {
|
|
|
53
88
|
#palette = $state(DEFAULT_PALETTE);
|
|
54
89
|
#resolved = $derived(this.#theme === 'system' ? getSystemTheme() : this.#theme);
|
|
55
90
|
constructor() {
|
|
56
|
-
if (typeof window
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
91
|
+
if (typeof window === 'undefined')
|
|
92
|
+
return;
|
|
93
|
+
this.#theme = getStoredTheme();
|
|
94
|
+
this.#palette = getStoredPalette();
|
|
95
|
+
applyMode(this.#theme);
|
|
96
|
+
applyPalette(this.#palette);
|
|
97
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
98
|
+
if (this.#theme === 'system')
|
|
99
|
+
applyMode('system');
|
|
100
|
+
});
|
|
67
101
|
}
|
|
68
102
|
get theme() {
|
|
69
103
|
return this.#theme;
|
|
@@ -79,9 +113,7 @@ class ThemeStore {
|
|
|
79
113
|
}
|
|
80
114
|
set(theme) {
|
|
81
115
|
this.#theme = theme;
|
|
82
|
-
|
|
83
|
-
localStorage.setItem(STORAGE_KEY, theme);
|
|
84
|
-
}
|
|
116
|
+
writeCookie(THEME_COOKIE, theme);
|
|
85
117
|
applyMode(theme);
|
|
86
118
|
}
|
|
87
119
|
/**
|
|
@@ -89,13 +121,12 @@ class ThemeStore {
|
|
|
89
121
|
* `slate` = cool (hue 265). `neutral` = chroma 0, no blue cast.
|
|
90
122
|
*
|
|
91
123
|
* Pass `{ persist: false }` for a page-level override that does not
|
|
92
|
-
*
|
|
124
|
+
* write the cookie.
|
|
93
125
|
*/
|
|
94
126
|
setPalette(palette, opts) {
|
|
95
127
|
this.#palette = palette;
|
|
96
|
-
if (opts?.persist !== false
|
|
97
|
-
|
|
98
|
-
}
|
|
128
|
+
if (opts?.persist !== false)
|
|
129
|
+
writeCookie(PALETTE_COOKIE, palette);
|
|
99
130
|
applyPalette(palette);
|
|
100
131
|
}
|
|
101
132
|
toggle() {
|