@eduardoalvarez/arrecife 0.5.1 → 0.6.0
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 +45 -0
- package/README.md +706 -470
- package/dist/brand/index.cjs +112 -95
- package/dist/brand/index.d.cts +40 -39
- package/dist/brand/index.d.ts +40 -39
- package/dist/brand/index.js +5 -4
- package/dist/catalog-D13txprv.d.cts +78 -0
- package/dist/catalog-D13txprv.d.ts +78 -0
- package/dist/chart/index.cjs +100 -83
- package/dist/chart/index.d.cts +66 -66
- package/dist/chart/index.d.ts +66 -66
- package/dist/chart/index.js +14 -12
- package/dist/chunk-25YNFCIF.js +141 -0
- package/dist/{chunk-YZ2SDOVZ.js → chunk-6O3KWB6P.js} +30 -30
- package/dist/chunk-CKRSQPTX.js +36 -0
- package/dist/{chunk-ZEOQKRQ7.js → chunk-DKCN7BAL.js} +1 -1
- package/dist/chunk-GCRII2KQ.js +86 -0
- package/dist/chunk-JMOOFZ3B.js +42 -0
- package/dist/chunk-O4TAH7YJ.js +276 -0
- package/dist/chunk-ODBFN44D.js +45 -0
- package/dist/{chunk-VPT32GPG.js → chunk-PMN7NR3G.js} +2 -2
- package/dist/chunk-XKYHTOUJ.js +27 -0
- package/dist/form/index.cjs +109 -92
- package/dist/form/index.d.cts +43 -42
- package/dist/form/index.d.ts +43 -42
- package/dist/form/index.js +25 -23
- package/dist/index.cjs +1047 -914
- package/dist/index.d.cts +769 -777
- package/dist/index.d.ts +769 -777
- package/dist/index.js +608 -660
- package/dist/{label-DuTvJGxD.d.ts → label-MgHFKnFy.d.cts} +3 -3
- package/dist/{label-DuTvJGxD.d.cts → label-MgHFKnFy.d.ts} +3 -3
- package/dist/og/index.cjs +130 -130
- package/dist/og/index.d.cts +93 -89
- package/dist/og/index.d.ts +93 -89
- package/dist/og/index.js +106 -106
- package/dist/shiki/index.cjs +28 -30
- package/dist/shiki/index.d.cts +4 -4
- package/dist/shiki/index.d.ts +4 -4
- package/dist/shiki/index.js +12 -12
- package/dist/theme/index.cjs +97 -0
- package/dist/theme/index.d.cts +144 -0
- package/dist/theme/index.d.ts +144 -0
- package/dist/theme/index.js +2 -0
- package/dist/tokens/index.cjs +133 -86
- package/dist/tokens/index.d.cts +246 -161
- package/dist/tokens/index.d.ts +246 -161
- package/dist/tokens/index.js +2 -2
- package/dist/tokens/theme.css +133 -98
- package/dist/variants/index.cjs +192 -0
- package/dist/variants/index.d.cts +192 -0
- package/dist/variants/index.d.ts +192 -0
- package/dist/variants/index.js +3 -0
- package/llms.txt +810 -744
- package/package.json +20 -11
- package/dist/catalogo-Du5ID-Hi.d.cts +0 -77
- package/dist/catalogo-Du5ID-Hi.d.ts +0 -77
- package/dist/chunk-E3OMP2DL.js +0 -36
- package/dist/chunk-KPZNNMV5.js +0 -83
- package/dist/chunk-NHS7ETKJ.js +0 -27
- package/dist/chunk-TSPJOM6K.js +0 -229
- package/dist/chunk-UOWIDFCB.js +0 -81
- package/dist/tema/index.cjs +0 -94
- package/dist/tema/index.d.cts +0 -110
- package/dist/tema/index.d.ts +0 -110
- package/dist/tema/index.js +0 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/theme/index.ts
|
|
4
|
+
var THEME_KEY = "arrecife-theme";
|
|
5
|
+
var THEME_ATTRIBUTE = "data-theme";
|
|
6
|
+
var THEME_EVENT = "arrecife:theme";
|
|
7
|
+
var isTheme = (value) => value === "dark" || value === "light";
|
|
8
|
+
function currentTheme() {
|
|
9
|
+
if (typeof document === "undefined") return "dark";
|
|
10
|
+
const set = document.documentElement.getAttribute(THEME_ATTRIBUTE);
|
|
11
|
+
return isTheme(set) ? set : "dark";
|
|
12
|
+
}
|
|
13
|
+
function storedTheme() {
|
|
14
|
+
if (typeof localStorage === "undefined") return null;
|
|
15
|
+
try {
|
|
16
|
+
const value = localStorage.getItem(THEME_KEY);
|
|
17
|
+
return isTheme(value) ? value : null;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function preferredTheme({ base } = {}) {
|
|
23
|
+
const stored = storedTheme();
|
|
24
|
+
if (stored) return stored;
|
|
25
|
+
if (base) return base;
|
|
26
|
+
if (typeof matchMedia === "undefined") return "dark";
|
|
27
|
+
return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
|
|
28
|
+
}
|
|
29
|
+
function applyTheme(theme, persist = true) {
|
|
30
|
+
if (typeof document === "undefined") return;
|
|
31
|
+
document.documentElement.setAttribute(THEME_ATTRIBUTE, theme);
|
|
32
|
+
document.documentElement.style.colorScheme = theme;
|
|
33
|
+
if (persist) {
|
|
34
|
+
try {
|
|
35
|
+
localStorage.setItem(THEME_KEY, theme);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
dispatchEvent(new CustomEvent(THEME_EVENT, { detail: theme }));
|
|
40
|
+
}
|
|
41
|
+
function toggleTheme() {
|
|
42
|
+
const next = currentTheme() === "dark" ? "light" : "dark";
|
|
43
|
+
applyTheme(next);
|
|
44
|
+
return next;
|
|
45
|
+
}
|
|
46
|
+
function watchTheme(onChange, options = {}) {
|
|
47
|
+
if (typeof window === "undefined") return () => {
|
|
48
|
+
};
|
|
49
|
+
const own = (event) => {
|
|
50
|
+
const detail = event.detail;
|
|
51
|
+
if (isTheme(detail)) onChange(detail);
|
|
52
|
+
};
|
|
53
|
+
const otherTab = (event) => {
|
|
54
|
+
if (event.key !== THEME_KEY) return;
|
|
55
|
+
const theme = isTheme(event.newValue) ? event.newValue : preferredTheme(options);
|
|
56
|
+
applyTheme(theme, false);
|
|
57
|
+
onChange(theme);
|
|
58
|
+
};
|
|
59
|
+
addEventListener(THEME_EVENT, own);
|
|
60
|
+
addEventListener("storage", otherTab);
|
|
61
|
+
return () => {
|
|
62
|
+
removeEventListener(THEME_EVENT, own);
|
|
63
|
+
removeEventListener("storage", otherTab);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function themeScript({ base } = {}) {
|
|
67
|
+
return [
|
|
68
|
+
"(function () {",
|
|
69
|
+
" var KEY = " + JSON.stringify(THEME_KEY) + ";",
|
|
70
|
+
" function resolve() {",
|
|
71
|
+
" try {",
|
|
72
|
+
" var stored = localStorage.getItem(KEY);",
|
|
73
|
+
' if (stored === "dark" || stored === "light") return stored;',
|
|
74
|
+
" } catch (e) {}",
|
|
75
|
+
base ? " return " + JSON.stringify(base) + ";" : ' return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";',
|
|
76
|
+
" }",
|
|
77
|
+
" function apply() {",
|
|
78
|
+
" var theme = resolve();",
|
|
79
|
+
" document.documentElement.setAttribute(" + JSON.stringify(THEME_ATTRIBUTE) + ", theme);",
|
|
80
|
+
" document.documentElement.style.colorScheme = theme;",
|
|
81
|
+
" }",
|
|
82
|
+
" apply();",
|
|
83
|
+
' document.addEventListener("astro:after-swap", apply);',
|
|
84
|
+
"})();"
|
|
85
|
+
].join("\n");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
exports.THEME_ATTRIBUTE = THEME_ATTRIBUTE;
|
|
89
|
+
exports.THEME_EVENT = THEME_EVENT;
|
|
90
|
+
exports.THEME_KEY = THEME_KEY;
|
|
91
|
+
exports.applyTheme = applyTheme;
|
|
92
|
+
exports.currentTheme = currentTheme;
|
|
93
|
+
exports.preferredTheme = preferredTheme;
|
|
94
|
+
exports.storedTheme = storedTheme;
|
|
95
|
+
exports.themeScript = themeScript;
|
|
96
|
+
exports.toggleTheme = toggleTheme;
|
|
97
|
+
exports.watchTheme = watchTheme;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The theme, without React.
|
|
3
|
+
*
|
|
4
|
+
* The library defines the WHOLE theming system — `@custom-variant light`, the
|
|
5
|
+
* `[data-theme]` blocks, the palette for both modes — and until now it exposed
|
|
6
|
+
* nothing that changes it. `eduardoalvarez.dev` and `links` each reimplemented
|
|
7
|
+
* that separately, with their own `localStorage` and their own
|
|
8
|
+
* `astro:after-swap`.
|
|
9
|
+
*
|
|
10
|
+
* This subpackage deliberately does not import React, exactly like `./tokens`,
|
|
11
|
+
* `./og` and `./shiki`: the two projects that need it most are Astro, and one of
|
|
12
|
+
* them ships no framework JavaScript at all. What gets published here is the
|
|
13
|
+
* hard part of the component — avoiding the first-paint flash and surviving
|
|
14
|
+
* navigation — not the button. The button is `ThemeToggle`; it lives at the root
|
|
15
|
+
* and uses this underneath.
|
|
16
|
+
*
|
|
17
|
+
* `check-package-exports.mjs` verifies that `./theme` does not drag React into
|
|
18
|
+
* the published `dist/`, same as the other three portable subpaths.
|
|
19
|
+
*/
|
|
20
|
+
/** The two modes. Dark is primary and is the system default. */
|
|
21
|
+
type Theme = 'dark' | 'light';
|
|
22
|
+
/**
|
|
23
|
+
* The `localStorage` key.
|
|
24
|
+
*
|
|
25
|
+
* It carries the library name because `localStorage` belongs to the origin, not
|
|
26
|
+
* to the project: a bare `theme` collides with whatever else lives on the same
|
|
27
|
+
* domain.
|
|
28
|
+
*/
|
|
29
|
+
declare const THEME_KEY = "arrecife-theme";
|
|
30
|
+
/** The attribute the `[data-theme]` blocks in `theme.css` read. */
|
|
31
|
+
declare const THEME_ATTRIBUTE = "data-theme";
|
|
32
|
+
/**
|
|
33
|
+
* The event emitted when the theme changes.
|
|
34
|
+
*
|
|
35
|
+
* It exists because `storage` only notifies the OTHER tabs: in the tab that made
|
|
36
|
+
* the change nothing fires, and a site with two theme controls — the header and
|
|
37
|
+
* the footer — would leave one of them showing the wrong icon.
|
|
38
|
+
*/
|
|
39
|
+
declare const THEME_EVENT = "arrecife:theme";
|
|
40
|
+
/**
|
|
41
|
+
* The theme currently in place, read from the DOM.
|
|
42
|
+
*
|
|
43
|
+
* From the DOM and not from `localStorage`, deliberately: the head script has
|
|
44
|
+
* already resolved the stored preference against the system one, and resolving
|
|
45
|
+
* it again here is how the two answers drift apart.
|
|
46
|
+
*/
|
|
47
|
+
declare function currentTheme(): Theme;
|
|
48
|
+
/**
|
|
49
|
+
* The stored preference, if any. `null` means «nobody has chosen», which is not
|
|
50
|
+
* the same as «chose dark»: with no choice, the system decides.
|
|
51
|
+
*/
|
|
52
|
+
declare function storedTheme(): Theme | null;
|
|
53
|
+
/**
|
|
54
|
+
* What a site decides when nobody has chosen yet.
|
|
55
|
+
*
|
|
56
|
+
* `base` is not «the fallback», it is «this site IS this mode». Passing it stops
|
|
57
|
+
* the OS from being consulted at all, which is the whole point: the five
|
|
58
|
+
* projects are dark by decision, and with the OS in charge somebody running
|
|
59
|
+
* their machine in light mode saw the blog in light — the opposite of what was
|
|
60
|
+
* agreed.
|
|
61
|
+
*
|
|
62
|
+
* Left out, the OS decides and dark is the fallback. That was the only
|
|
63
|
+
* behaviour until 0.6.0, and it is still the right default for a library: a site
|
|
64
|
+
* that has not decided should follow the reader.
|
|
65
|
+
*/
|
|
66
|
+
type ThemeOptions = {
|
|
67
|
+
/** The mode this site is. Given, `prefers-color-scheme` is not consulted. */
|
|
68
|
+
base?: Theme | undefined;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The theme that applies: whatever was chosen, and with no choice, `base` if the
|
|
72
|
+
* site declared one, else whatever the system asks for. With no
|
|
73
|
+
* `prefers-color-scheme` declared, dark, which is primary.
|
|
74
|
+
*/
|
|
75
|
+
declare function preferredTheme({ base }?: ThemeOptions): Theme;
|
|
76
|
+
/**
|
|
77
|
+
* Sets the theme on `<html>` and persists it.
|
|
78
|
+
*
|
|
79
|
+
* It writes the attribute ALWAYS, including for `dark`. The system default does
|
|
80
|
+
* not need the attribute, but leaving it explicit is what lets a subtree declare
|
|
81
|
+
* the opposite mode — a code block sits on hull in both themes — without
|
|
82
|
+
* inheriting from an unmarked `<html>`.
|
|
83
|
+
*/
|
|
84
|
+
declare function applyTheme(theme: Theme, persist?: boolean): void;
|
|
85
|
+
/** Switches to the opposite one and returns whichever stuck. */
|
|
86
|
+
declare function toggleTheme(): Theme;
|
|
87
|
+
/**
|
|
88
|
+
* Subscribes to theme changes and returns the function that cancels it.
|
|
89
|
+
*
|
|
90
|
+
* It listens to both sources: our own event — the control in this tab — and
|
|
91
|
+
* `storage`, which is a change made in another one. Without the second, two open
|
|
92
|
+
* tabs sit on different themes until one of them is reloaded.
|
|
93
|
+
*/
|
|
94
|
+
declare function watchTheme(onChange: (theme: Theme) => void, options?: ThemeOptions): () => void;
|
|
95
|
+
/**
|
|
96
|
+
* The script that goes INLINE in the `<head>`, before any stylesheet.
|
|
97
|
+
*
|
|
98
|
+
* This is the hard part, and it is the part that got rewritten in every project.
|
|
99
|
+
* Without it the first paint comes out in the default theme and the correct one
|
|
100
|
+
* arrives a frame later: on a dark site that the reader left in light mode, that
|
|
101
|
+
* is a white flash on every load.
|
|
102
|
+
*
|
|
103
|
+
* It has to be a string and it has to be inline. A `<script src>`, even a
|
|
104
|
+
* synchronous one, gets downloaded: the flash comes back. So this is not a
|
|
105
|
+
* module you import, it is text you inject:
|
|
106
|
+
*
|
|
107
|
+
* ```astro
|
|
108
|
+
* ---
|
|
109
|
+
* import { themeScript } from '@eduardoalvarez/arrecife/theme';
|
|
110
|
+
* ---
|
|
111
|
+
* <head>
|
|
112
|
+
* <script is:inline set:html={themeScript} />
|
|
113
|
+
* </head>
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* ```tsx
|
|
117
|
+
* // Next, in the root layout.
|
|
118
|
+
* <head>
|
|
119
|
+
* <script dangerouslySetInnerHTML={{ __html: themeScript }} />
|
|
120
|
+
* </head>
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* It re-attaches on `astro:after-swap` because Astro's view transitions replace
|
|
124
|
+
* the whole `<html>`: without that line the theme is lost on navigation and the
|
|
125
|
+
* flash returns, this time mid-session.
|
|
126
|
+
*
|
|
127
|
+
* `base` is what the five projects were missing. All of them are dark BY
|
|
128
|
+
* DECISION — `eduardoalvarez.dev`'s own script said so out loud: «dark is the
|
|
129
|
+
* brand's PRIMARY mode, so it's the default and doesn't follow the OS setting».
|
|
130
|
+
* With the OS in charge, somebody running their machine in light mode saw the
|
|
131
|
+
* blog in light, which is the opposite of what was agreed, and there was no way
|
|
132
|
+
* to say otherwise: the export was a fixed string with no parameter. So those
|
|
133
|
+
* projects kept their own `public/theme.js` and the library published the hard
|
|
134
|
+
* part for nobody.
|
|
135
|
+
*
|
|
136
|
+
* Called with no options it behaves exactly as it did before: the OS decides and
|
|
137
|
+
* dark is the fallback. That is still the right default for a library — a site
|
|
138
|
+
* that has not decided should follow its reader.
|
|
139
|
+
*
|
|
140
|
+
* <script is:inline set:html={themeScript({ base: 'dark' })} />
|
|
141
|
+
*/
|
|
142
|
+
declare function themeScript({ base }?: ThemeOptions): string;
|
|
143
|
+
|
|
144
|
+
export { THEME_ATTRIBUTE, THEME_EVENT, THEME_KEY, type Theme, type ThemeOptions, applyTheme, currentTheme, preferredTheme, storedTheme, themeScript, toggleTheme, watchTheme };
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The theme, without React.
|
|
3
|
+
*
|
|
4
|
+
* The library defines the WHOLE theming system — `@custom-variant light`, the
|
|
5
|
+
* `[data-theme]` blocks, the palette for both modes — and until now it exposed
|
|
6
|
+
* nothing that changes it. `eduardoalvarez.dev` and `links` each reimplemented
|
|
7
|
+
* that separately, with their own `localStorage` and their own
|
|
8
|
+
* `astro:after-swap`.
|
|
9
|
+
*
|
|
10
|
+
* This subpackage deliberately does not import React, exactly like `./tokens`,
|
|
11
|
+
* `./og` and `./shiki`: the two projects that need it most are Astro, and one of
|
|
12
|
+
* them ships no framework JavaScript at all. What gets published here is the
|
|
13
|
+
* hard part of the component — avoiding the first-paint flash and surviving
|
|
14
|
+
* navigation — not the button. The button is `ThemeToggle`; it lives at the root
|
|
15
|
+
* and uses this underneath.
|
|
16
|
+
*
|
|
17
|
+
* `check-package-exports.mjs` verifies that `./theme` does not drag React into
|
|
18
|
+
* the published `dist/`, same as the other three portable subpaths.
|
|
19
|
+
*/
|
|
20
|
+
/** The two modes. Dark is primary and is the system default. */
|
|
21
|
+
type Theme = 'dark' | 'light';
|
|
22
|
+
/**
|
|
23
|
+
* The `localStorage` key.
|
|
24
|
+
*
|
|
25
|
+
* It carries the library name because `localStorage` belongs to the origin, not
|
|
26
|
+
* to the project: a bare `theme` collides with whatever else lives on the same
|
|
27
|
+
* domain.
|
|
28
|
+
*/
|
|
29
|
+
declare const THEME_KEY = "arrecife-theme";
|
|
30
|
+
/** The attribute the `[data-theme]` blocks in `theme.css` read. */
|
|
31
|
+
declare const THEME_ATTRIBUTE = "data-theme";
|
|
32
|
+
/**
|
|
33
|
+
* The event emitted when the theme changes.
|
|
34
|
+
*
|
|
35
|
+
* It exists because `storage` only notifies the OTHER tabs: in the tab that made
|
|
36
|
+
* the change nothing fires, and a site with two theme controls — the header and
|
|
37
|
+
* the footer — would leave one of them showing the wrong icon.
|
|
38
|
+
*/
|
|
39
|
+
declare const THEME_EVENT = "arrecife:theme";
|
|
40
|
+
/**
|
|
41
|
+
* The theme currently in place, read from the DOM.
|
|
42
|
+
*
|
|
43
|
+
* From the DOM and not from `localStorage`, deliberately: the head script has
|
|
44
|
+
* already resolved the stored preference against the system one, and resolving
|
|
45
|
+
* it again here is how the two answers drift apart.
|
|
46
|
+
*/
|
|
47
|
+
declare function currentTheme(): Theme;
|
|
48
|
+
/**
|
|
49
|
+
* The stored preference, if any. `null` means «nobody has chosen», which is not
|
|
50
|
+
* the same as «chose dark»: with no choice, the system decides.
|
|
51
|
+
*/
|
|
52
|
+
declare function storedTheme(): Theme | null;
|
|
53
|
+
/**
|
|
54
|
+
* What a site decides when nobody has chosen yet.
|
|
55
|
+
*
|
|
56
|
+
* `base` is not «the fallback», it is «this site IS this mode». Passing it stops
|
|
57
|
+
* the OS from being consulted at all, which is the whole point: the five
|
|
58
|
+
* projects are dark by decision, and with the OS in charge somebody running
|
|
59
|
+
* their machine in light mode saw the blog in light — the opposite of what was
|
|
60
|
+
* agreed.
|
|
61
|
+
*
|
|
62
|
+
* Left out, the OS decides and dark is the fallback. That was the only
|
|
63
|
+
* behaviour until 0.6.0, and it is still the right default for a library: a site
|
|
64
|
+
* that has not decided should follow the reader.
|
|
65
|
+
*/
|
|
66
|
+
type ThemeOptions = {
|
|
67
|
+
/** The mode this site is. Given, `prefers-color-scheme` is not consulted. */
|
|
68
|
+
base?: Theme | undefined;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The theme that applies: whatever was chosen, and with no choice, `base` if the
|
|
72
|
+
* site declared one, else whatever the system asks for. With no
|
|
73
|
+
* `prefers-color-scheme` declared, dark, which is primary.
|
|
74
|
+
*/
|
|
75
|
+
declare function preferredTheme({ base }?: ThemeOptions): Theme;
|
|
76
|
+
/**
|
|
77
|
+
* Sets the theme on `<html>` and persists it.
|
|
78
|
+
*
|
|
79
|
+
* It writes the attribute ALWAYS, including for `dark`. The system default does
|
|
80
|
+
* not need the attribute, but leaving it explicit is what lets a subtree declare
|
|
81
|
+
* the opposite mode — a code block sits on hull in both themes — without
|
|
82
|
+
* inheriting from an unmarked `<html>`.
|
|
83
|
+
*/
|
|
84
|
+
declare function applyTheme(theme: Theme, persist?: boolean): void;
|
|
85
|
+
/** Switches to the opposite one and returns whichever stuck. */
|
|
86
|
+
declare function toggleTheme(): Theme;
|
|
87
|
+
/**
|
|
88
|
+
* Subscribes to theme changes and returns the function that cancels it.
|
|
89
|
+
*
|
|
90
|
+
* It listens to both sources: our own event — the control in this tab — and
|
|
91
|
+
* `storage`, which is a change made in another one. Without the second, two open
|
|
92
|
+
* tabs sit on different themes until one of them is reloaded.
|
|
93
|
+
*/
|
|
94
|
+
declare function watchTheme(onChange: (theme: Theme) => void, options?: ThemeOptions): () => void;
|
|
95
|
+
/**
|
|
96
|
+
* The script that goes INLINE in the `<head>`, before any stylesheet.
|
|
97
|
+
*
|
|
98
|
+
* This is the hard part, and it is the part that got rewritten in every project.
|
|
99
|
+
* Without it the first paint comes out in the default theme and the correct one
|
|
100
|
+
* arrives a frame later: on a dark site that the reader left in light mode, that
|
|
101
|
+
* is a white flash on every load.
|
|
102
|
+
*
|
|
103
|
+
* It has to be a string and it has to be inline. A `<script src>`, even a
|
|
104
|
+
* synchronous one, gets downloaded: the flash comes back. So this is not a
|
|
105
|
+
* module you import, it is text you inject:
|
|
106
|
+
*
|
|
107
|
+
* ```astro
|
|
108
|
+
* ---
|
|
109
|
+
* import { themeScript } from '@eduardoalvarez/arrecife/theme';
|
|
110
|
+
* ---
|
|
111
|
+
* <head>
|
|
112
|
+
* <script is:inline set:html={themeScript} />
|
|
113
|
+
* </head>
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* ```tsx
|
|
117
|
+
* // Next, in the root layout.
|
|
118
|
+
* <head>
|
|
119
|
+
* <script dangerouslySetInnerHTML={{ __html: themeScript }} />
|
|
120
|
+
* </head>
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* It re-attaches on `astro:after-swap` because Astro's view transitions replace
|
|
124
|
+
* the whole `<html>`: without that line the theme is lost on navigation and the
|
|
125
|
+
* flash returns, this time mid-session.
|
|
126
|
+
*
|
|
127
|
+
* `base` is what the five projects were missing. All of them are dark BY
|
|
128
|
+
* DECISION — `eduardoalvarez.dev`'s own script said so out loud: «dark is the
|
|
129
|
+
* brand's PRIMARY mode, so it's the default and doesn't follow the OS setting».
|
|
130
|
+
* With the OS in charge, somebody running their machine in light mode saw the
|
|
131
|
+
* blog in light, which is the opposite of what was agreed, and there was no way
|
|
132
|
+
* to say otherwise: the export was a fixed string with no parameter. So those
|
|
133
|
+
* projects kept their own `public/theme.js` and the library published the hard
|
|
134
|
+
* part for nobody.
|
|
135
|
+
*
|
|
136
|
+
* Called with no options it behaves exactly as it did before: the OS decides and
|
|
137
|
+
* dark is the fallback. That is still the right default for a library — a site
|
|
138
|
+
* that has not decided should follow its reader.
|
|
139
|
+
*
|
|
140
|
+
* <script is:inline set:html={themeScript({ base: 'dark' })} />
|
|
141
|
+
*/
|
|
142
|
+
declare function themeScript({ base }?: ThemeOptions): string;
|
|
143
|
+
|
|
144
|
+
export { THEME_ATTRIBUTE, THEME_EVENT, THEME_KEY, type Theme, type ThemeOptions, applyTheme, currentTheme, preferredTheme, storedTheme, themeScript, toggleTheme, watchTheme };
|