@lpdsgn/astro-themes 0.1.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/README.md +303 -0
- package/dist/client.d.ts +54 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/components/ThemeProvider.astro +223 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/script.d.ts +11 -0
- package/dist/script.js +53 -0
- package/dist/script.js.map +1 -0
- package/dist/toolbar/app.d.ts +21 -0
- package/dist/toolbar/app.js +133 -0
- package/dist/toolbar/app.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# astro-themes
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
Perfect dark mode in Astro with no flash. An Astro integration that mirrors the behavior of [next-themes](https://github.com/pacocoursey/next-themes).
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- ✅ **Perfect dark mode in 2 lines of code**
|
|
11
|
+
- ✅ **No flash on load** (SSR and SSG)
|
|
12
|
+
- ✅ **System preference with `prefers-color-scheme`**
|
|
13
|
+
- ✅ **Themed browser UI with `color-scheme`**
|
|
14
|
+
- ✅ **Sync theme across tabs and windows**
|
|
15
|
+
- ✅ **Force pages to specific themes**
|
|
16
|
+
- ✅ **Class or data attribute selector**
|
|
17
|
+
- ✅ **TypeScript support**
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. Install
|
|
22
|
+
|
|
23
|
+
```sh frame="none"
|
|
24
|
+
pnpm astro add astro-themes
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```sh frame="none"
|
|
28
|
+
npx astro add astro-themes
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```sh frame="none"
|
|
32
|
+
yarn astro add astro-themes
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Add ThemeProvider
|
|
36
|
+
|
|
37
|
+
```astro frame="code" title="src/layouts/Layout.astro"
|
|
38
|
+
---
|
|
39
|
+
import ThemeProvider from "astro-themes/ThemeProvider.astro";
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
<!doctype html>
|
|
43
|
+
<html lang="en">
|
|
44
|
+
<head>
|
|
45
|
+
<ThemeProvider />
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<slot />
|
|
49
|
+
</body>
|
|
50
|
+
</html>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
That's it! Your Astro app now supports dark mode with system preference detection and cross-tab sync.
|
|
54
|
+
|
|
55
|
+
## Styling
|
|
56
|
+
|
|
57
|
+
### CSS Variables
|
|
58
|
+
|
|
59
|
+
By default, `astro-themes` sets `data-theme` on the `<html>` element:
|
|
60
|
+
|
|
61
|
+
```css frame="code" title="src/styles/global.css"
|
|
62
|
+
:root {
|
|
63
|
+
--background: white;
|
|
64
|
+
--foreground: black;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
[data-theme='dark'] {
|
|
68
|
+
--background: black;
|
|
69
|
+
--foreground: white;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### TailwindCSS
|
|
74
|
+
|
|
75
|
+
For Tailwind's class-based dark mode, set `attribute="class"`:
|
|
76
|
+
|
|
77
|
+
```astro
|
|
78
|
+
<ThemeProvider attribute="class" />
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Then configure Tailwind:
|
|
82
|
+
|
|
83
|
+
**Tailwind v4:**
|
|
84
|
+
|
|
85
|
+
```css frame="code" title="src/styles/global.css" ins={3}
|
|
86
|
+
@import 'tailwindcss';
|
|
87
|
+
|
|
88
|
+
@custom-variant dark (&:is(.dark *));
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Tailwind v3:**
|
|
92
|
+
|
|
93
|
+
```js frame="code" title="tailwind.config.js" ins={2}
|
|
94
|
+
module.exports = {
|
|
95
|
+
darkMode: 'selector',
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Now use dark-mode classes:
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<h1 class="text-black dark:text-white">Hello</h1>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Changing the Theme
|
|
106
|
+
|
|
107
|
+
### Using Client Helpers (Recommended)
|
|
108
|
+
|
|
109
|
+
```astro
|
|
110
|
+
<script>
|
|
111
|
+
import { setTheme, toggleTheme, onThemeChange, getResolvedTheme } from 'astro-themes/client';
|
|
112
|
+
|
|
113
|
+
// Toggle between light and dark
|
|
114
|
+
toggleTheme();
|
|
115
|
+
|
|
116
|
+
// Set a specific theme
|
|
117
|
+
setTheme('dark');
|
|
118
|
+
|
|
119
|
+
// Get current resolved theme
|
|
120
|
+
const current = getResolvedTheme(); // 'light' | 'dark'
|
|
121
|
+
|
|
122
|
+
// Listen to theme changes
|
|
123
|
+
const unsubscribe = onThemeChange(({ theme, resolvedTheme }) => {
|
|
124
|
+
console.log('Theme changed:', resolvedTheme);
|
|
125
|
+
});
|
|
126
|
+
</script>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Using the Global Object
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<button id="theme-toggle">Toggle</button>
|
|
133
|
+
|
|
134
|
+
<script>
|
|
135
|
+
document.getElementById('theme-toggle').addEventListener('click', () => {
|
|
136
|
+
const { resolvedTheme, setTheme } = window.__ASTRO_THEMES__;
|
|
137
|
+
setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
|
|
138
|
+
});
|
|
139
|
+
</script>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Configuration
|
|
143
|
+
|
|
144
|
+
### ThemeProvider Props
|
|
145
|
+
|
|
146
|
+
| Prop | Type | Default | Description |
|
|
147
|
+
|------|------|---------|-------------|
|
|
148
|
+
| `storageKey` | `string` | `'theme'` | localStorage key for theme setting |
|
|
149
|
+
| `defaultTheme` | `string` | `'system'` | Default theme (`'light'`, `'dark'`, or `'system'`) |
|
|
150
|
+
| `themes` | `string[]` | `['light', 'dark']` | Available theme names |
|
|
151
|
+
| `attribute` | `string \| string[]` | `'data-theme'` | HTML attribute to set. Use `'class'` for Tailwind |
|
|
152
|
+
| `value` | `object` | - | Map theme names to custom attribute values |
|
|
153
|
+
| `forcedTheme` | `string` | - | Force a specific theme on this page |
|
|
154
|
+
| `enableSystem` | `boolean` | `true` | Enable system preference detection |
|
|
155
|
+
| `enableColorScheme` | `boolean` | `true` | Set `color-scheme` CSS property |
|
|
156
|
+
| `disableTransitionOnChange` | `boolean` | `false` | Disable transitions when switching |
|
|
157
|
+
| `nonce` | `string` | - | CSP nonce for the script tag |
|
|
158
|
+
| `scriptProps` | `object` | - | Additional props for the script tag |
|
|
159
|
+
|
|
160
|
+
### Integration Options
|
|
161
|
+
|
|
162
|
+
For centralized configuration without using `ThemeProvider`, configure the integration directly:
|
|
163
|
+
|
|
164
|
+
```js frame="code" title="astro.config.mjs"
|
|
165
|
+
import { defineConfig } from "astro/config";
|
|
166
|
+
import astroThemes from "astro-themes";
|
|
167
|
+
|
|
168
|
+
export default defineConfig({
|
|
169
|
+
integrations: [
|
|
170
|
+
astroThemes({
|
|
171
|
+
injectScript: true, // Auto-inject theme script
|
|
172
|
+
defaultProps: {
|
|
173
|
+
attribute: "class",
|
|
174
|
+
defaultTheme: "system",
|
|
175
|
+
themes: ["light", "dark"],
|
|
176
|
+
},
|
|
177
|
+
devToolbar: true, // Enable Dev Toolbar App
|
|
178
|
+
}),
|
|
179
|
+
],
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
| Option | Type | Default | Description |
|
|
184
|
+
|--------|------|---------|-------------|
|
|
185
|
+
| `injectScript` | `boolean` | `false` | Auto-inject theme script (no `ThemeProvider` needed) |
|
|
186
|
+
| `defaultProps` | `object` | `{}` | Default theme configuration (same options as **[ThemeProvider Props](#themeprovider-props)**) |
|
|
187
|
+
| `devToolbar` | `boolean` | `true` | Enable the Dev Toolbar App |
|
|
188
|
+
|
|
189
|
+
> **Tip:** Use `injectScript: true` for consistent settings across all pages. You can still use `ThemeProvider` with `forcedTheme` on specific pages.
|
|
190
|
+
|
|
191
|
+
## Examples
|
|
192
|
+
|
|
193
|
+
### Force a Page Theme
|
|
194
|
+
|
|
195
|
+
```astro frame="code" title="src/pages/dark-only.astro"
|
|
196
|
+
---
|
|
197
|
+
import ThemeProvider from "astro-themes/ThemeProvider.astro";
|
|
198
|
+
import Layout from "../layouts/Layout.astro";
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
<Layout>
|
|
202
|
+
<ThemeProvider forcedTheme="dark" />
|
|
203
|
+
<!-- This page is always dark -->
|
|
204
|
+
</Layout>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Multiple Themes
|
|
208
|
+
|
|
209
|
+
```astro
|
|
210
|
+
<ThemeProvider themes={['light', 'dark', 'purple', 'pink']} />
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Custom Attribute Values
|
|
214
|
+
|
|
215
|
+
```astro
|
|
216
|
+
<ThemeProvider
|
|
217
|
+
attribute="data-theme"
|
|
218
|
+
value={{ light: 'light-mode', dark: 'dark-mode' }}
|
|
219
|
+
/>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Disable Transitions on Change
|
|
223
|
+
|
|
224
|
+
```astro
|
|
225
|
+
<ThemeProvider disableTransitionOnChange />
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Cloudflare Rocket Loader
|
|
229
|
+
|
|
230
|
+
```astro
|
|
231
|
+
<ThemeProvider scriptProps={{ 'data-cfasync': 'false' }} />
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## API Reference
|
|
235
|
+
|
|
236
|
+
### Client Helpers
|
|
237
|
+
|
|
238
|
+
Import from `astro-themes/client`:
|
|
239
|
+
|
|
240
|
+
| Function | Description |
|
|
241
|
+
|----------|-------------|
|
|
242
|
+
| `getTheme()` | Get complete theme state object |
|
|
243
|
+
| `setTheme(theme)` | Set theme (string or callback function) |
|
|
244
|
+
| `toggleTheme()` | Toggle between light and dark |
|
|
245
|
+
| `getResolvedTheme()` | Get resolved theme (`'light'` or `'dark'`) |
|
|
246
|
+
| `getSystemTheme()` | Get system preference |
|
|
247
|
+
| `getThemes()` | Get list of available themes |
|
|
248
|
+
| `isForcedTheme()` | Check if current page has forced theme |
|
|
249
|
+
| `onThemeChange(callback)` | Subscribe to changes (returns unsubscribe) |
|
|
250
|
+
|
|
251
|
+
### Theme State Object
|
|
252
|
+
|
|
253
|
+
Available via `window.__ASTRO_THEMES__`:
|
|
254
|
+
|
|
255
|
+
| Property | Type | Description |
|
|
256
|
+
|----------|------|-------------|
|
|
257
|
+
| `theme` | `string` | Current theme name |
|
|
258
|
+
| `resolvedTheme` | `string` | Resolved theme (`'light'` or `'dark'`) |
|
|
259
|
+
| `systemTheme` | `'light' \| 'dark'` | System preference |
|
|
260
|
+
| `forcedTheme` | `string \| undefined` | Forced theme if set |
|
|
261
|
+
| `themes` | `string[]` | Available themes |
|
|
262
|
+
| `setTheme(theme)` | `function` | Update the theme |
|
|
263
|
+
|
|
264
|
+
## Avoiding Hydration Mismatch
|
|
265
|
+
|
|
266
|
+
Since the theme is unknown on the server, use CSS to conditionally show content:
|
|
267
|
+
|
|
268
|
+
```css
|
|
269
|
+
[data-theme='dark'] .light-only { display: none; }
|
|
270
|
+
[data-theme='light'] .dark-only { display: none; }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Or check in client-side scripts:
|
|
274
|
+
|
|
275
|
+
```astro
|
|
276
|
+
<script>
|
|
277
|
+
if (window.__ASTRO_THEMES__) {
|
|
278
|
+
const { resolvedTheme } = window.__ASTRO_THEMES__;
|
|
279
|
+
// Update UI based on theme
|
|
280
|
+
}
|
|
281
|
+
</script>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Contributing
|
|
285
|
+
|
|
286
|
+
This package is structured as a monorepo:
|
|
287
|
+
|
|
288
|
+
- `playground` – Development testing environment
|
|
289
|
+
- `packages/astro-themes` – The integration package
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
pnpm i --frozen-lockfile
|
|
293
|
+
pnpm dev
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## License
|
|
297
|
+
|
|
298
|
+
[MIT Licensed](https://github.com/LPdsgn/astro-themes/blob/main/LICENSE). Made with ❤️
|
|
299
|
+
|
|
300
|
+
## Acknowledgements
|
|
301
|
+
|
|
302
|
+
- Inspired by [next-themes](https://github.com/pacocoursey/next-themes) by Paco Coursey
|
|
303
|
+
- Built with [astro-integration-kit](https://github.com/florian-lefebvre/astro-integration-kit) by Florian Lefebvre
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ThemeState } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Client-side helpers for interacting with the theme
|
|
5
|
+
* These are meant to be used in client-side scripts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
interface Window {
|
|
10
|
+
__ASTRO_THEMES__?: ThemeState;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get the current theme state
|
|
15
|
+
* Returns undefined if ThemeProvider is not mounted
|
|
16
|
+
*/
|
|
17
|
+
declare function getTheme(): ThemeState | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Set the theme
|
|
20
|
+
* @param theme - Theme name or function that receives current theme and returns new theme
|
|
21
|
+
*/
|
|
22
|
+
declare function setTheme(theme: string | ((prevTheme: string) => string)): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get the resolved theme (system theme resolved to actual value)
|
|
25
|
+
*/
|
|
26
|
+
declare function getResolvedTheme(): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Get the system theme preference
|
|
29
|
+
*/
|
|
30
|
+
declare function getSystemTheme(): "dark" | "light" | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Check if theme is forced for the current page
|
|
33
|
+
*/
|
|
34
|
+
declare function isForcedTheme(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Get the list of available themes
|
|
37
|
+
*/
|
|
38
|
+
declare function getThemes(): string[];
|
|
39
|
+
/**
|
|
40
|
+
* Subscribe to theme changes
|
|
41
|
+
* @param callback - Function called when theme changes
|
|
42
|
+
* @returns Cleanup function to unsubscribe
|
|
43
|
+
*/
|
|
44
|
+
declare function onThemeChange(callback: (detail: {
|
|
45
|
+
theme: string;
|
|
46
|
+
resolvedTheme: string;
|
|
47
|
+
}) => void): () => void;
|
|
48
|
+
/**
|
|
49
|
+
* Toggle between light and dark themes
|
|
50
|
+
* If current theme is neither light nor dark, switches to light
|
|
51
|
+
*/
|
|
52
|
+
declare function toggleTheme(): void;
|
|
53
|
+
|
|
54
|
+
export { getResolvedTheme, getSystemTheme, getTheme, getThemes, isForcedTheme, onThemeChange, setTheme, toggleTheme };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function n(){if(!(typeof window>"u"))return window.__ASTRO_THEMES__}function o(e){let t=n();t&&t.setTheme(e)}function i(){return n()?.resolvedTheme}function s(){return n()?.systemTheme}function d(){return!!n()?.forcedTheme}function h(){return n()?.themes??[]}function m(e){if(typeof window>"u")return()=>{};let t=r=>{e(r.detail)};return window.addEventListener("astro-themes:change",t),()=>{window.removeEventListener("astro-themes:change",t)}}function u(){let e=n();if(!e)return;let t=e.resolvedTheme;e.setTheme(t==="light"?"dark":"light")}export{i as getResolvedTheme,s as getSystemTheme,n as getTheme,h as getThemes,d as isForcedTheme,m as onThemeChange,o as setTheme,u as toggleTheme};
|
|
2
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["/**\n * Client-side helpers for interacting with the theme\n * These are meant to be used in client-side scripts\n */\n\nimport type { ThemeState } from \"./types.js\";\n\ndeclare global {\n\tinterface Window {\n\t\t__ASTRO_THEMES__?: ThemeState;\n\t}\n}\n\n/**\n * Get the current theme state\n * Returns undefined if ThemeProvider is not mounted\n */\nexport function getTheme(): ThemeState | undefined {\n\tif (typeof window === \"undefined\") return undefined;\n\treturn window.__ASTRO_THEMES__;\n}\n\n/**\n * Set the theme\n * @param theme - Theme name or function that receives current theme and returns new theme\n */\nexport function setTheme(theme: string | ((prevTheme: string) => string)): void {\n\tconst state = getTheme();\n\tif (state) {\n\t\tstate.setTheme(theme);\n\t}\n}\n\n/**\n * Get the resolved theme (system theme resolved to actual value)\n */\nexport function getResolvedTheme(): string | undefined {\n\treturn getTheme()?.resolvedTheme;\n}\n\n/**\n * Get the system theme preference\n */\nexport function getSystemTheme(): \"dark\" | \"light\" | undefined {\n\treturn getTheme()?.systemTheme;\n}\n\n/**\n * Check if theme is forced for the current page\n */\nexport function isForcedTheme(): boolean {\n\treturn !!getTheme()?.forcedTheme;\n}\n\n/**\n * Get the list of available themes\n */\nexport function getThemes(): string[] {\n\treturn getTheme()?.themes ?? [];\n}\n\n/**\n * Subscribe to theme changes\n * @param callback - Function called when theme changes\n * @returns Cleanup function to unsubscribe\n */\nexport function onThemeChange(\n\tcallback: (detail: { theme: string; resolvedTheme: string }) => void\n): () => void {\n\tif (typeof window === \"undefined\") return () => {};\n\n\tconst handler = (e: CustomEvent<{ theme: string; resolvedTheme: string }>) => {\n\t\tcallback(e.detail);\n\t};\n\n\twindow.addEventListener(\"astro-themes:change\", handler as EventListener);\n\n\treturn () => {\n\t\twindow.removeEventListener(\"astro-themes:change\", handler as EventListener);\n\t};\n}\n\n/**\n * Toggle between light and dark themes\n * If current theme is neither light nor dark, switches to light\n */\nexport function toggleTheme(): void {\n\tconst state = getTheme();\n\tif (!state) return;\n\n\tconst resolved = state.resolvedTheme;\n\tstate.setTheme(resolved === \"light\" ? \"dark\" : \"light\");\n}\n"],"mappings":"AAiBO,SAASA,GAAmC,CAClD,GAAI,SAAO,OAAW,KACtB,OAAO,OAAO,gBACf,CAMO,SAASC,EAASC,EAAuD,CAC/E,IAAMC,EAAQH,EAAS,EACnBG,GACHA,EAAM,SAASD,CAAK,CAEtB,CAKO,SAASE,GAAuC,CACtD,OAAOJ,EAAS,GAAG,aACpB,CAKO,SAASK,GAA+C,CAC9D,OAAOL,EAAS,GAAG,WACpB,CAKO,SAASM,GAAyB,CACxC,MAAO,CAAC,CAACN,EAAS,GAAG,WACtB,CAKO,SAASO,GAAsB,CACrC,OAAOP,EAAS,GAAG,QAAU,CAAC,CAC/B,CAOO,SAASQ,EACfC,EACa,CACb,GAAI,OAAO,OAAW,IAAa,MAAO,IAAM,CAAC,EAEjD,IAAMC,EAAWC,GAA6D,CAC7EF,EAASE,EAAE,MAAM,CAClB,EAEA,cAAO,iBAAiB,sBAAuBD,CAAwB,EAEhE,IAAM,CACZ,OAAO,oBAAoB,sBAAuBA,CAAwB,CAC3E,CACD,CAMO,SAASE,GAAoB,CACnC,IAAMT,EAAQH,EAAS,EACvB,GAAI,CAACG,EAAO,OAEZ,IAAMU,EAAWV,EAAM,cACvBA,EAAM,SAASU,IAAa,QAAU,OAAS,OAAO,CACvD","names":["getTheme","setTheme","theme","state","getResolvedTheme","getSystemTheme","isForcedTheme","getThemes","onThemeChange","callback","handler","e","toggleTheme","resolved"]}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ThemeProviderProps } from "../types.js";
|
|
3
|
+
import { getMinifiedInlineScript } from "../script.js";
|
|
4
|
+
|
|
5
|
+
interface Props extends ThemeProviderProps {}
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
storageKey = "theme",
|
|
9
|
+
defaultTheme: defaultThemeProp,
|
|
10
|
+
forcedTheme,
|
|
11
|
+
enableSystem = true,
|
|
12
|
+
enableColorScheme = true,
|
|
13
|
+
disableTransitionOnChange = false,
|
|
14
|
+
themes = ["light", "dark"],
|
|
15
|
+
attribute = "data-theme",
|
|
16
|
+
value,
|
|
17
|
+
nonce,
|
|
18
|
+
scriptProps = {},
|
|
19
|
+
} = Astro.props;
|
|
20
|
+
|
|
21
|
+
// If enableSystem is false and no defaultTheme is set, default to 'light'
|
|
22
|
+
const defaultTheme = defaultThemeProp ?? (enableSystem ? "system" : "light");
|
|
23
|
+
|
|
24
|
+
// Generate the inline script to prevent flash
|
|
25
|
+
const inlineScript = getMinifiedInlineScript(
|
|
26
|
+
attribute,
|
|
27
|
+
storageKey,
|
|
28
|
+
defaultTheme,
|
|
29
|
+
forcedTheme,
|
|
30
|
+
themes,
|
|
31
|
+
value,
|
|
32
|
+
enableSystem,
|
|
33
|
+
enableColorScheme
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Build script attributes
|
|
37
|
+
const scriptAttrs: Record<string, unknown> = {
|
|
38
|
+
...scriptProps,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (nonce) {
|
|
42
|
+
scriptAttrs.nonce = nonce;
|
|
43
|
+
}
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
{/* Inline script to prevent flash - runs before page renders */}
|
|
47
|
+
<script is:inline set:html={inlineScript} {...scriptAttrs}></script>
|
|
48
|
+
|
|
49
|
+
{/* Client-side script for theme management */}
|
|
50
|
+
<script
|
|
51
|
+
is:inline
|
|
52
|
+
data-astro-themes-config
|
|
53
|
+
data-storage-key={storageKey}
|
|
54
|
+
data-default-theme={defaultTheme}
|
|
55
|
+
data-forced-theme={forcedTheme}
|
|
56
|
+
data-enable-system={enableSystem}
|
|
57
|
+
data-enable-color-scheme={enableColorScheme}
|
|
58
|
+
data-disable-transition-on-change={disableTransitionOnChange}
|
|
59
|
+
data-themes={JSON.stringify(themes)}
|
|
60
|
+
data-attribute={JSON.stringify(attribute)}
|
|
61
|
+
data-value={value ? JSON.stringify(value) : undefined}
|
|
62
|
+
>
|
|
63
|
+
(function() {
|
|
64
|
+
// Get configuration from script element
|
|
65
|
+
var configEl = document.querySelector('script[data-astro-themes-config]');
|
|
66
|
+
if (!configEl) return;
|
|
67
|
+
|
|
68
|
+
var storageKey = configEl.getAttribute('data-storage-key') || 'theme';
|
|
69
|
+
var defaultTheme = configEl.getAttribute('data-default-theme') || 'system';
|
|
70
|
+
var forcedTheme = configEl.getAttribute('data-forced-theme') || undefined;
|
|
71
|
+
var enableSystem = configEl.getAttribute('data-enable-system') === 'true';
|
|
72
|
+
var enableColorScheme = configEl.getAttribute('data-enable-color-scheme') === 'true';
|
|
73
|
+
var disableTransitionOnChange = configEl.getAttribute('data-disable-transition-on-change') === 'true';
|
|
74
|
+
var themes = JSON.parse(configEl.getAttribute('data-themes') || '["light","dark"]');
|
|
75
|
+
var attribute = JSON.parse(configEl.getAttribute('data-attribute') || '"data-theme"');
|
|
76
|
+
var value = configEl.getAttribute('data-value') ? JSON.parse(configEl.getAttribute('data-value')) : undefined;
|
|
77
|
+
|
|
78
|
+
var el = document.documentElement;
|
|
79
|
+
var systemThemes = ['light', 'dark'];
|
|
80
|
+
var MEDIA = '(prefers-color-scheme: dark)';
|
|
81
|
+
|
|
82
|
+
function getSystemTheme() {
|
|
83
|
+
return window.matchMedia(MEDIA).matches ? 'dark' : 'light';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getStoredTheme() {
|
|
87
|
+
try {
|
|
88
|
+
return localStorage.getItem(storageKey) || defaultTheme;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return defaultTheme;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function saveTheme(theme) {
|
|
95
|
+
try {
|
|
96
|
+
localStorage.setItem(storageKey, theme);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
// localStorage not available
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function disableAnimation() {
|
|
103
|
+
var css = document.createElement('style');
|
|
104
|
+
css.appendChild(
|
|
105
|
+
document.createTextNode(
|
|
106
|
+
'*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}'
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
document.head.appendChild(css);
|
|
110
|
+
|
|
111
|
+
return function() {
|
|
112
|
+
// Force restyle
|
|
113
|
+
(function() { return window.getComputedStyle(document.body); })();
|
|
114
|
+
// Wait for next tick before removing
|
|
115
|
+
setTimeout(function() {
|
|
116
|
+
document.head.removeChild(css);
|
|
117
|
+
}, 1);
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function applyTheme(theme) {
|
|
122
|
+
var resolved = theme;
|
|
123
|
+
if (theme === 'system' && enableSystem) {
|
|
124
|
+
resolved = getSystemTheme();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var enable = disableTransitionOnChange ? disableAnimation() : null;
|
|
128
|
+
|
|
129
|
+
var attributes = Array.isArray(attribute) ? attribute : [attribute];
|
|
130
|
+
var name = value && value[resolved] ? value[resolved] : resolved;
|
|
131
|
+
var attrs = !value ? themes : Object.values(value);
|
|
132
|
+
|
|
133
|
+
attributes.forEach(function(attr) {
|
|
134
|
+
if (attr === 'class') {
|
|
135
|
+
el.classList.remove.apply(el.classList, attrs);
|
|
136
|
+
if (name) el.classList.add(name);
|
|
137
|
+
} else if (attr.startsWith('data-')) {
|
|
138
|
+
if (name) {
|
|
139
|
+
el.setAttribute(attr, name);
|
|
140
|
+
} else {
|
|
141
|
+
el.removeAttribute(attr);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (enableColorScheme) {
|
|
147
|
+
var colorScheme = systemThemes.indexOf(resolved) !== -1 ? resolved : null;
|
|
148
|
+
el.style.colorScheme = colorScheme || '';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (enable) enable();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Current state
|
|
155
|
+
var currentTheme = forcedTheme || getStoredTheme();
|
|
156
|
+
var resolvedTheme = currentTheme === 'system' ? getSystemTheme() : currentTheme;
|
|
157
|
+
|
|
158
|
+
// Create the theme state object
|
|
159
|
+
var themeState = {
|
|
160
|
+
get theme() { return forcedTheme || currentTheme; },
|
|
161
|
+
get resolvedTheme() { return forcedTheme || (currentTheme === 'system' ? getSystemTheme() : currentTheme); },
|
|
162
|
+
get systemTheme() { return getSystemTheme(); },
|
|
163
|
+
get forcedTheme() { return forcedTheme; },
|
|
164
|
+
get themes() { return enableSystem ? themes.concat(['system']) : themes; },
|
|
165
|
+
setTheme: function(newTheme) {
|
|
166
|
+
if (forcedTheme) return; // No-op when forced
|
|
167
|
+
|
|
168
|
+
if (typeof newTheme === 'function') {
|
|
169
|
+
newTheme = newTheme(currentTheme);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
currentTheme = newTheme;
|
|
173
|
+
saveTheme(newTheme);
|
|
174
|
+
applyTheme(newTheme);
|
|
175
|
+
|
|
176
|
+
// Dispatch event for listeners
|
|
177
|
+
window.dispatchEvent(new CustomEvent('astro-themes:change', {
|
|
178
|
+
detail: { theme: newTheme, resolvedTheme: newTheme === 'system' ? getSystemTheme() : newTheme }
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Expose to window
|
|
184
|
+
window.__ASTRO_THEMES__ = themeState;
|
|
185
|
+
|
|
186
|
+
// Listen for system theme changes
|
|
187
|
+
if (enableSystem) {
|
|
188
|
+
var media = window.matchMedia(MEDIA);
|
|
189
|
+
var handleMediaChange = function(e) {
|
|
190
|
+
if (currentTheme === 'system') {
|
|
191
|
+
applyTheme('system');
|
|
192
|
+
window.dispatchEvent(new CustomEvent('astro-themes:change', {
|
|
193
|
+
detail: { theme: 'system', resolvedTheme: getSystemTheme() }
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Use modern API if available, fall back to deprecated one
|
|
199
|
+
if (media.addEventListener) {
|
|
200
|
+
media.addEventListener('change', handleMediaChange);
|
|
201
|
+
} else {
|
|
202
|
+
media.addListener(handleMediaChange);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Listen for storage changes (sync across tabs)
|
|
207
|
+
window.addEventListener('storage', function(e) {
|
|
208
|
+
if (e.key !== storageKey) return;
|
|
209
|
+
|
|
210
|
+
if (!e.newValue) {
|
|
211
|
+
themeState.setTheme(defaultTheme);
|
|
212
|
+
} else if (e.newValue !== currentTheme) {
|
|
213
|
+
currentTheme = e.newValue;
|
|
214
|
+
applyTheme(e.newValue);
|
|
215
|
+
window.dispatchEvent(new CustomEvent('astro-themes:change', {
|
|
216
|
+
detail: { theme: e.newValue, resolvedTheme: e.newValue === 'system' ? getSystemTheme() : e.newValue }
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
})();
|
|
221
|
+
</script>
|
|
222
|
+
|
|
223
|
+
<slot />
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as astro from 'astro';
|
|
2
|
+
export { AstroThemesConfig, Attribute, ScriptProps, ThemeProviderProps, ThemeState, ValueObject } from './types.js';
|
|
3
|
+
export { getResolvedTheme, getSystemTheme, getTheme, getThemes, isForcedTheme, onThemeChange, setTheme, toggleTheme } from './client.js';
|
|
4
|
+
|
|
5
|
+
declare const integration: (options?: {
|
|
6
|
+
injectScript?: boolean | undefined;
|
|
7
|
+
defaultProps?: {
|
|
8
|
+
storageKey?: string | undefined;
|
|
9
|
+
defaultTheme?: string | undefined;
|
|
10
|
+
forcedTheme?: string | undefined;
|
|
11
|
+
enableSystem?: boolean | undefined;
|
|
12
|
+
enableColorScheme?: boolean | undefined;
|
|
13
|
+
disableTransitionOnChange?: boolean | undefined;
|
|
14
|
+
value?: Record<string, string> | undefined;
|
|
15
|
+
themes?: string[] | undefined;
|
|
16
|
+
attribute?: string | string[] | undefined;
|
|
17
|
+
} | undefined;
|
|
18
|
+
devToolbar?: boolean | undefined;
|
|
19
|
+
} | undefined) => astro.AstroIntegration & {};
|
|
20
|
+
|
|
21
|
+
export { integration as default, integration };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import{defineIntegration as g}from"astro-integration-kit";import{z as e}from"astro/zod";function c(r,t,n,o,a,s,m,h){return`
|
|
2
|
+
(function() {
|
|
3
|
+
var el = document.documentElement;
|
|
4
|
+
var attribute = ${JSON.stringify(r)};
|
|
5
|
+
var storageKey = ${JSON.stringify(t)};
|
|
6
|
+
var defaultTheme = ${JSON.stringify(n)};
|
|
7
|
+
var forcedTheme = ${JSON.stringify(o)};
|
|
8
|
+
var themes = ${JSON.stringify(a)};
|
|
9
|
+
var value = ${JSON.stringify(s)};
|
|
10
|
+
var enableSystem = ${JSON.stringify(m)};
|
|
11
|
+
var enableColorScheme = ${JSON.stringify(h)};
|
|
12
|
+
var systemThemes = ['light', 'dark'];
|
|
13
|
+
|
|
14
|
+
function updateDOM(theme) {
|
|
15
|
+
var attributes = Array.isArray(attribute) ? attribute : [attribute];
|
|
16
|
+
attributes.forEach(function(attr) {
|
|
17
|
+
var isClass = attr === 'class';
|
|
18
|
+
var classes = isClass && value ? themes.map(function(t) { return value[t] || t; }) : themes;
|
|
19
|
+
if (isClass) {
|
|
20
|
+
el.classList.remove.apply(el.classList, classes);
|
|
21
|
+
el.classList.add(value && value[theme] ? value[theme] : theme);
|
|
22
|
+
} else {
|
|
23
|
+
el.setAttribute(attr, value && value[theme] ? value[theme] : theme);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
setColorScheme(theme);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function setColorScheme(theme) {
|
|
30
|
+
if (enableColorScheme && systemThemes.indexOf(theme) !== -1) {
|
|
31
|
+
el.style.colorScheme = theme;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getSystemTheme() {
|
|
36
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (forcedTheme) {
|
|
40
|
+
updateDOM(forcedTheme);
|
|
41
|
+
} else {
|
|
42
|
+
try {
|
|
43
|
+
var themeName = localStorage.getItem(storageKey) || defaultTheme;
|
|
44
|
+
var isSystem = enableSystem && themeName === 'system';
|
|
45
|
+
var theme = isSystem ? getSystemTheme() : themeName;
|
|
46
|
+
updateDOM(theme);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
updateDOM(defaultTheme === 'system' && enableSystem ? getSystemTheme() : defaultTheme);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})();
|
|
52
|
+
`.trim()}function l(r,t,n,o,a,s,m,h){return c(r,t,n,o,a,s,m,h).replace(/\s+/g," ").replace(/\s*{\s*/g,"{").replace(/\s*}\s*/g,"}").replace(/\s*;\s*/g,";").replace(/\s*,\s*/g,",").replace(/\s*\(\s*/g,"(").replace(/\s*\)\s*/g,")").trim()}var f="theme",u="system",p=["light","dark"],T="data-theme",v=e.object({injectScript:e.boolean().default(!1),defaultProps:e.object({storageKey:e.string().default(f),defaultTheme:e.string().default(u),forcedTheme:e.string().optional(),enableSystem:e.boolean().default(!0),enableColorScheme:e.boolean().default(!0),disableTransitionOnChange:e.boolean().default(!1),themes:e.array(e.string()).default(p),attribute:e.union([e.string(),e.array(e.string())]).default(T),value:e.record(e.string()).optional()}).default({}),devToolbar:e.boolean().default(!0)}).default({}),d=g({name:"astro-themes",optionsSchema:v,setup({options:r}){return{hooks:{"astro:config:setup":({logger:t,injectScript:n,addDevToolbarApp:o,command:a})=>{if(t.info("astro-themes initialized"),r.injectScript){let s=r.defaultProps,m=l(s.attribute,s.storageKey,s.defaultTheme,s.forcedTheme,s.themes,s.value,s.enableSystem,s.enableColorScheme);n("head-inline",m),t.info("Flash-prevention script injected via integration")}r.devToolbar&&a==="dev"&&(o({id:"astro-themes-toolbar",name:"Theme Switcher",icon:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>',entrypoint:"astro-themes/toolbar"}),t.debug("Dev Toolbar App registered"))},"astro:config:done":({injectTypes:t})=>{t({filename:"types.d.ts",content:`declare global {
|
|
53
|
+
interface Window {
|
|
54
|
+
__ASTRO_THEMES__?: {
|
|
55
|
+
theme: string;
|
|
56
|
+
resolvedTheme: string;
|
|
57
|
+
systemTheme: "dark" | "light";
|
|
58
|
+
forcedTheme?: string;
|
|
59
|
+
themes: string[];
|
|
60
|
+
setTheme: (theme: string | ((prevTheme: string) => string)) => void;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export {};`})}}}}});function i(){if(!(typeof window>"u"))return window.__ASTRO_THEMES__}function y(r){let t=i();t&&t.setTheme(r)}function S(){return i()?.resolvedTheme}function b(){return i()?.systemTheme}function E(){return!!i()?.forcedTheme}function w(){return i()?.themes??[]}function x(r){if(typeof window>"u")return()=>{};let t=n=>{r(n.detail)};return window.addEventListener("astro-themes:change",t),()=>{window.removeEventListener("astro-themes:change",t)}}function _(){let r=i();if(!r)return;let t=r.resolvedTheme;r.setTheme(t==="light"?"dark":"light")}var D=d;export{D as default,S as getResolvedTheme,b as getSystemTheme,i as getTheme,w as getThemes,d as integration,E as isForcedTheme,x as onThemeChange,y as setTheme,_ as toggleTheme};
|
|
65
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/integration.ts","../src/script.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["import { defineIntegration } from \"astro-integration-kit\";\nimport { z } from \"astro/zod\";\nimport type { AstroThemesConfig, ThemeProviderProps } from \"./types.js\";\nimport { getMinifiedInlineScript } from \"./script.js\";\n\n// Default configuration values\nconst DEFAULT_STORAGE_KEY = \"theme\";\nconst DEFAULT_THEME = \"system\";\nconst DEFAULT_THEMES = [\"light\", \"dark\"];\nconst DEFAULT_ATTRIBUTE = \"data-theme\";\n\n// Options schema for the integration\nconst optionsSchema = z\n\t.object({\n\t\t/**\n\t\t * Inject the flash-prevention script automatically via integration\n\t\t * When true, you don't need to use ThemeProvider component\n\t\t * @default false\n\t\t */\n\t\tinjectScript: z.boolean().default(false),\n\t\t/**\n\t\t * Default theme configuration used when injectScript is true\n\t\t */\n\t\tdefaultProps: z\n\t\t\t.object({\n\t\t\t\tstorageKey: z.string().default(DEFAULT_STORAGE_KEY),\n\t\t\t\tdefaultTheme: z.string().default(DEFAULT_THEME),\n\t\t\t\tforcedTheme: z.string().optional(),\n\t\t\t\tenableSystem: z.boolean().default(true),\n\t\t\t\tenableColorScheme: z.boolean().default(true),\n\t\t\t\tdisableTransitionOnChange: z.boolean().default(false),\n\t\t\t\tthemes: z.array(z.string()).default(DEFAULT_THEMES),\n\t\t\t\tattribute: z\n\t\t\t\t\t.union([z.string(), z.array(z.string())])\n\t\t\t\t\t.default(DEFAULT_ATTRIBUTE),\n\t\t\t\tvalue: z.record(z.string()).optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\t/**\n\t\t * Enable the Dev Toolbar App for theme switching during development\n\t\t * @default true\n\t\t */\n\t\tdevToolbar: z.boolean().default(true),\n\t})\n\t.default({});\n\nexport const integration = defineIntegration({\n\tname: \"astro-themes\",\n\toptionsSchema,\n\tsetup({ options }) {\n\t\treturn {\n\t\t\thooks: {\n\t\t\t\t\"astro:config:setup\": ({\n\t\t\t\t\tlogger,\n\t\t\t\t\tinjectScript,\n\t\t\t\t\taddDevToolbarApp,\n\t\t\t\t\tcommand,\n\t\t\t\t}) => {\n\t\t\t\t\tlogger.info(\"astro-themes initialized\");\n\n\t\t\t\t\t// Inject flash-prevention script if enabled\n\t\t\t\t\tif (options.injectScript) {\n\t\t\t\t\t\tconst props = options.defaultProps;\n\t\t\t\t\t\tconst script = getMinifiedInlineScript(\n\t\t\t\t\t\t\tprops.attribute,\n\t\t\t\t\t\t\tprops.storageKey,\n\t\t\t\t\t\t\tprops.defaultTheme,\n\t\t\t\t\t\t\tprops.forcedTheme,\n\t\t\t\t\t\t\tprops.themes,\n\t\t\t\t\t\t\tprops.value,\n\t\t\t\t\t\t\tprops.enableSystem,\n\t\t\t\t\t\t\tprops.enableColorScheme\n\t\t\t\t\t\t);\n\t\t\t\t\t\tinjectScript(\"head-inline\", script);\n\t\t\t\t\t\tlogger.info(\"Flash-prevention script injected via integration\");\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add Dev Toolbar App in dev mode\n\t\t\t\t\tif (options.devToolbar && command === \"dev\") {\n\t\t\t\t\t\taddDevToolbarApp({\n\t\t\t\t\t\t\tid: \"astro-themes-toolbar\",\n\t\t\t\t\t\t\tname: \"Theme Switcher\",\n\t\t\t\t\t\t\ticon: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"4\"/><path d=\"M12 2v2\"/><path d=\"M12 20v2\"/><path d=\"m4.93 4.93 1.41 1.41\"/><path d=\"m17.66 17.66 1.41 1.41\"/><path d=\"M2 12h2\"/><path d=\"M20 12h2\"/><path d=\"m6.34 17.66-1.41 1.41\"/><path d=\"m19.07 4.93-1.41 1.41\"/></svg>`,\n\t\t\t\t\t\t\tentrypoint: \"astro-themes/toolbar\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tlogger.debug(\"Dev Toolbar App registered\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"astro:config:done\": ({ injectTypes }) => {\n\t\t\t\t\t// Inject global types for window.__ASTRO_THEMES__\n\t\t\t\t\tinjectTypes({\n\t\t\t\t\t\tfilename: \"types.d.ts\",\n\t\t\t\t\t\tcontent: `declare global {\n interface Window {\n __ASTRO_THEMES__?: {\n theme: string;\n resolvedTheme: string;\n systemTheme: \"dark\" | \"light\";\n forcedTheme?: string;\n themes: string[];\n setTheme: (theme: string | ((prevTheme: string) => string)) => void;\n };\n }\n}\nexport {};`,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t},\n});\n\nexport type { AstroThemesConfig, ThemeProviderProps };\n","/**\n * Inline script that runs before page render to prevent flash\n * This script is injected into the head and executes immediately\n */\nexport function getInlineScript(\n\tattribute: string | string[],\n\tstorageKey: string,\n\tdefaultTheme: string,\n\tforcedTheme: string | undefined,\n\tthemes: string[],\n\tvalue: Record<string, string> | undefined,\n\tenableSystem: boolean,\n\tenableColorScheme: boolean\n): string {\n\t// The script must be minified and self-contained\n\tconst script = `\n(function() {\n var el = document.documentElement;\n var attribute = ${JSON.stringify(attribute)};\n var storageKey = ${JSON.stringify(storageKey)};\n var defaultTheme = ${JSON.stringify(defaultTheme)};\n var forcedTheme = ${JSON.stringify(forcedTheme)};\n var themes = ${JSON.stringify(themes)};\n var value = ${JSON.stringify(value)};\n var enableSystem = ${JSON.stringify(enableSystem)};\n var enableColorScheme = ${JSON.stringify(enableColorScheme)};\n var systemThemes = ['light', 'dark'];\n\n function updateDOM(theme) {\n var attributes = Array.isArray(attribute) ? attribute : [attribute];\n attributes.forEach(function(attr) {\n var isClass = attr === 'class';\n var classes = isClass && value ? themes.map(function(t) { return value[t] || t; }) : themes;\n if (isClass) {\n el.classList.remove.apply(el.classList, classes);\n el.classList.add(value && value[theme] ? value[theme] : theme);\n } else {\n el.setAttribute(attr, value && value[theme] ? value[theme] : theme);\n }\n });\n setColorScheme(theme);\n }\n\n function setColorScheme(theme) {\n if (enableColorScheme && systemThemes.indexOf(theme) !== -1) {\n el.style.colorScheme = theme;\n }\n }\n\n function getSystemTheme() {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n\n if (forcedTheme) {\n updateDOM(forcedTheme);\n } else {\n try {\n var themeName = localStorage.getItem(storageKey) || defaultTheme;\n var isSystem = enableSystem && themeName === 'system';\n var theme = isSystem ? getSystemTheme() : themeName;\n updateDOM(theme);\n } catch (e) {\n updateDOM(defaultTheme === 'system' && enableSystem ? getSystemTheme() : defaultTheme);\n }\n }\n})();\n`.trim();\n\n\treturn script;\n}\n\n/**\n * Returns a minified version of the inline script\n */\nexport function getMinifiedInlineScript(\n\tattribute: string | string[],\n\tstorageKey: string,\n\tdefaultTheme: string,\n\tforcedTheme: string | undefined,\n\tthemes: string[],\n\tvalue: Record<string, string> | undefined,\n\tenableSystem: boolean,\n\tenableColorScheme: boolean\n): string {\n\t// Minified version for production\n\treturn getInlineScript(\n\t\tattribute,\n\t\tstorageKey,\n\t\tdefaultTheme,\n\t\tforcedTheme,\n\t\tthemes,\n\t\tvalue,\n\t\tenableSystem,\n\t\tenableColorScheme\n\t)\n\t\t.replace(/\\s+/g, \" \")\n\t\t.replace(/\\s*{\\s*/g, \"{\")\n\t\t.replace(/\\s*}\\s*/g, \"}\")\n\t\t.replace(/\\s*;\\s*/g, \";\")\n\t\t.replace(/\\s*,\\s*/g, \",\")\n\t\t.replace(/\\s*\\(\\s*/g, \"(\")\n\t\t.replace(/\\s*\\)\\s*/g, \")\")\n\t\t.trim();\n}\n","/**\n * Client-side helpers for interacting with the theme\n * These are meant to be used in client-side scripts\n */\n\nimport type { ThemeState } from \"./types.js\";\n\ndeclare global {\n\tinterface Window {\n\t\t__ASTRO_THEMES__?: ThemeState;\n\t}\n}\n\n/**\n * Get the current theme state\n * Returns undefined if ThemeProvider is not mounted\n */\nexport function getTheme(): ThemeState | undefined {\n\tif (typeof window === \"undefined\") return undefined;\n\treturn window.__ASTRO_THEMES__;\n}\n\n/**\n * Set the theme\n * @param theme - Theme name or function that receives current theme and returns new theme\n */\nexport function setTheme(theme: string | ((prevTheme: string) => string)): void {\n\tconst state = getTheme();\n\tif (state) {\n\t\tstate.setTheme(theme);\n\t}\n}\n\n/**\n * Get the resolved theme (system theme resolved to actual value)\n */\nexport function getResolvedTheme(): string | undefined {\n\treturn getTheme()?.resolvedTheme;\n}\n\n/**\n * Get the system theme preference\n */\nexport function getSystemTheme(): \"dark\" | \"light\" | undefined {\n\treturn getTheme()?.systemTheme;\n}\n\n/**\n * Check if theme is forced for the current page\n */\nexport function isForcedTheme(): boolean {\n\treturn !!getTheme()?.forcedTheme;\n}\n\n/**\n * Get the list of available themes\n */\nexport function getThemes(): string[] {\n\treturn getTheme()?.themes ?? [];\n}\n\n/**\n * Subscribe to theme changes\n * @param callback - Function called when theme changes\n * @returns Cleanup function to unsubscribe\n */\nexport function onThemeChange(\n\tcallback: (detail: { theme: string; resolvedTheme: string }) => void\n): () => void {\n\tif (typeof window === \"undefined\") return () => {};\n\n\tconst handler = (e: CustomEvent<{ theme: string; resolvedTheme: string }>) => {\n\t\tcallback(e.detail);\n\t};\n\n\twindow.addEventListener(\"astro-themes:change\", handler as EventListener);\n\n\treturn () => {\n\t\twindow.removeEventListener(\"astro-themes:change\", handler as EventListener);\n\t};\n}\n\n/**\n * Toggle between light and dark themes\n * If current theme is neither light nor dark, switches to light\n */\nexport function toggleTheme(): void {\n\tconst state = getTheme();\n\tif (!state) return;\n\n\tconst resolved = state.resolvedTheme;\n\tstate.setTheme(resolved === \"light\" ? \"dark\" : \"light\");\n}\n","// Main integration export\nimport { integration } from \"./integration.js\";\n\nexport default integration;\n\n// Re-export integration\nexport { integration };\n\n// Re-export types\nexport type {\n\tAttribute,\n\tValueObject,\n\tScriptProps,\n\tThemeProviderProps,\n\tThemeState,\n\tAstroThemesConfig,\n} from \"./types.js\";\n\n// Re-export client helpers\nexport {\n\tgetTheme,\n\tsetTheme,\n\tgetResolvedTheme,\n\tgetSystemTheme,\n\tisForcedTheme,\n\tgetThemes,\n\tonThemeChange,\n\ttoggleTheme,\n} from \"./client.js\";\n"],"mappings":"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","names":["defineIntegration","z","getInlineScript","attribute","storageKey","defaultTheme","forcedTheme","themes","value","enableSystem","enableColorScheme","getMinifiedInlineScript","DEFAULT_STORAGE_KEY","DEFAULT_THEME","DEFAULT_THEMES","DEFAULT_ATTRIBUTE","optionsSchema","z","integration","defineIntegration","options","logger","injectScript","addDevToolbarApp","command","props","script","getMinifiedInlineScript","injectTypes","getTheme","setTheme","theme","state","getResolvedTheme","getSystemTheme","isForcedTheme","getThemes","onThemeChange","callback","handler","e","toggleTheme","resolved","index_default","integration"]}
|
package/dist/script.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline script that runs before page render to prevent flash
|
|
3
|
+
* This script is injected into the head and executes immediately
|
|
4
|
+
*/
|
|
5
|
+
declare function getInlineScript(attribute: string | string[], storageKey: string, defaultTheme: string, forcedTheme: string | undefined, themes: string[], value: Record<string, string> | undefined, enableSystem: boolean, enableColorScheme: boolean): string;
|
|
6
|
+
/**
|
|
7
|
+
* Returns a minified version of the inline script
|
|
8
|
+
*/
|
|
9
|
+
declare function getMinifiedInlineScript(attribute: string | string[], storageKey: string, defaultTheme: string, forcedTheme: string | undefined, themes: string[], value: Record<string, string> | undefined, enableSystem: boolean, enableColorScheme: boolean): string;
|
|
10
|
+
|
|
11
|
+
export { getInlineScript, getMinifiedInlineScript };
|
package/dist/script.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
function l(e,t,s,r,a,i,n,m){return`
|
|
2
|
+
(function() {
|
|
3
|
+
var el = document.documentElement;
|
|
4
|
+
var attribute = ${JSON.stringify(e)};
|
|
5
|
+
var storageKey = ${JSON.stringify(t)};
|
|
6
|
+
var defaultTheme = ${JSON.stringify(s)};
|
|
7
|
+
var forcedTheme = ${JSON.stringify(r)};
|
|
8
|
+
var themes = ${JSON.stringify(a)};
|
|
9
|
+
var value = ${JSON.stringify(i)};
|
|
10
|
+
var enableSystem = ${JSON.stringify(n)};
|
|
11
|
+
var enableColorScheme = ${JSON.stringify(m)};
|
|
12
|
+
var systemThemes = ['light', 'dark'];
|
|
13
|
+
|
|
14
|
+
function updateDOM(theme) {
|
|
15
|
+
var attributes = Array.isArray(attribute) ? attribute : [attribute];
|
|
16
|
+
attributes.forEach(function(attr) {
|
|
17
|
+
var isClass = attr === 'class';
|
|
18
|
+
var classes = isClass && value ? themes.map(function(t) { return value[t] || t; }) : themes;
|
|
19
|
+
if (isClass) {
|
|
20
|
+
el.classList.remove.apply(el.classList, classes);
|
|
21
|
+
el.classList.add(value && value[theme] ? value[theme] : theme);
|
|
22
|
+
} else {
|
|
23
|
+
el.setAttribute(attr, value && value[theme] ? value[theme] : theme);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
setColorScheme(theme);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function setColorScheme(theme) {
|
|
30
|
+
if (enableColorScheme && systemThemes.indexOf(theme) !== -1) {
|
|
31
|
+
el.style.colorScheme = theme;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getSystemTheme() {
|
|
36
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (forcedTheme) {
|
|
40
|
+
updateDOM(forcedTheme);
|
|
41
|
+
} else {
|
|
42
|
+
try {
|
|
43
|
+
var themeName = localStorage.getItem(storageKey) || defaultTheme;
|
|
44
|
+
var isSystem = enableSystem && themeName === 'system';
|
|
45
|
+
var theme = isSystem ? getSystemTheme() : themeName;
|
|
46
|
+
updateDOM(theme);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
updateDOM(defaultTheme === 'system' && enableSystem ? getSystemTheme() : defaultTheme);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})();
|
|
52
|
+
`.trim()}function o(e,t,s,r,a,i,n,m){return l(e,t,s,r,a,i,n,m).replace(/\s+/g," ").replace(/\s*{\s*/g,"{").replace(/\s*}\s*/g,"}").replace(/\s*;\s*/g,";").replace(/\s*,\s*/g,",").replace(/\s*\(\s*/g,"(").replace(/\s*\)\s*/g,")").trim()}export{l as getInlineScript,o as getMinifiedInlineScript};
|
|
53
|
+
//# sourceMappingURL=script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/script.ts"],"sourcesContent":["/**\n * Inline script that runs before page render to prevent flash\n * This script is injected into the head and executes immediately\n */\nexport function getInlineScript(\n\tattribute: string | string[],\n\tstorageKey: string,\n\tdefaultTheme: string,\n\tforcedTheme: string | undefined,\n\tthemes: string[],\n\tvalue: Record<string, string> | undefined,\n\tenableSystem: boolean,\n\tenableColorScheme: boolean\n): string {\n\t// The script must be minified and self-contained\n\tconst script = `\n(function() {\n var el = document.documentElement;\n var attribute = ${JSON.stringify(attribute)};\n var storageKey = ${JSON.stringify(storageKey)};\n var defaultTheme = ${JSON.stringify(defaultTheme)};\n var forcedTheme = ${JSON.stringify(forcedTheme)};\n var themes = ${JSON.stringify(themes)};\n var value = ${JSON.stringify(value)};\n var enableSystem = ${JSON.stringify(enableSystem)};\n var enableColorScheme = ${JSON.stringify(enableColorScheme)};\n var systemThemes = ['light', 'dark'];\n\n function updateDOM(theme) {\n var attributes = Array.isArray(attribute) ? attribute : [attribute];\n attributes.forEach(function(attr) {\n var isClass = attr === 'class';\n var classes = isClass && value ? themes.map(function(t) { return value[t] || t; }) : themes;\n if (isClass) {\n el.classList.remove.apply(el.classList, classes);\n el.classList.add(value && value[theme] ? value[theme] : theme);\n } else {\n el.setAttribute(attr, value && value[theme] ? value[theme] : theme);\n }\n });\n setColorScheme(theme);\n }\n\n function setColorScheme(theme) {\n if (enableColorScheme && systemThemes.indexOf(theme) !== -1) {\n el.style.colorScheme = theme;\n }\n }\n\n function getSystemTheme() {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n\n if (forcedTheme) {\n updateDOM(forcedTheme);\n } else {\n try {\n var themeName = localStorage.getItem(storageKey) || defaultTheme;\n var isSystem = enableSystem && themeName === 'system';\n var theme = isSystem ? getSystemTheme() : themeName;\n updateDOM(theme);\n } catch (e) {\n updateDOM(defaultTheme === 'system' && enableSystem ? getSystemTheme() : defaultTheme);\n }\n }\n})();\n`.trim();\n\n\treturn script;\n}\n\n/**\n * Returns a minified version of the inline script\n */\nexport function getMinifiedInlineScript(\n\tattribute: string | string[],\n\tstorageKey: string,\n\tdefaultTheme: string,\n\tforcedTheme: string | undefined,\n\tthemes: string[],\n\tvalue: Record<string, string> | undefined,\n\tenableSystem: boolean,\n\tenableColorScheme: boolean\n): string {\n\t// Minified version for production\n\treturn getInlineScript(\n\t\tattribute,\n\t\tstorageKey,\n\t\tdefaultTheme,\n\t\tforcedTheme,\n\t\tthemes,\n\t\tvalue,\n\t\tenableSystem,\n\t\tenableColorScheme\n\t)\n\t\t.replace(/\\s+/g, \" \")\n\t\t.replace(/\\s*{\\s*/g, \"{\")\n\t\t.replace(/\\s*}\\s*/g, \"}\")\n\t\t.replace(/\\s*;\\s*/g, \";\")\n\t\t.replace(/\\s*,\\s*/g, \",\")\n\t\t.replace(/\\s*\\(\\s*/g, \"(\")\n\t\t.replace(/\\s*\\)\\s*/g, \")\")\n\t\t.trim();\n}\n"],"mappings":"AAIO,SAASA,EACfC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACS,CAuDT,MArDe;AAAA;AAAA;AAAA,oBAGI,KAAK,UAAUP,CAAS,CAAC;AAAA,qBACxB,KAAK,UAAUC,CAAU,CAAC;AAAA,uBACxB,KAAK,UAAUC,CAAY,CAAC;AAAA,sBAC7B,KAAK,UAAUC,CAAW,CAAC;AAAA,iBAChC,KAAK,UAAUC,CAAM,CAAC;AAAA,gBACvB,KAAK,UAAUC,CAAK,CAAC;AAAA,uBACd,KAAK,UAAUC,CAAY,CAAC;AAAA,4BACvB,KAAK,UAAUC,CAAiB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyC3D,KAAK,CAGP,CAKO,SAASC,EACfR,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACS,CAET,OAAOR,EACNC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,CACD,EACE,QAAQ,OAAQ,GAAG,EACnB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,YAAa,GAAG,EACxB,QAAQ,YAAa,GAAG,EACxB,KAAK,CACR","names":["getInlineScript","attribute","storageKey","defaultTheme","forcedTheme","themes","value","enableSystem","enableColorScheme","getMinifiedInlineScript"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as astro from 'astro';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Astro Dev Toolbar App for theme switching
|
|
5
|
+
* Uses native Astro Dev Toolbar components for consistent UI
|
|
6
|
+
*/
|
|
7
|
+
declare global {
|
|
8
|
+
interface Window {
|
|
9
|
+
__ASTRO_THEMES__?: {
|
|
10
|
+
theme: string;
|
|
11
|
+
resolvedTheme: string;
|
|
12
|
+
systemTheme: "dark" | "light";
|
|
13
|
+
forcedTheme?: string;
|
|
14
|
+
themes: string[];
|
|
15
|
+
setTheme: (theme: string | ((prevTheme: string) => string)) => void;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
declare const _default: astro.DevToolbarApp;
|
|
20
|
+
|
|
21
|
+
export { _default as default };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import{defineToolbarApp as T}from"astro/toolbar";var C=T({init(v,u){let n=document.createElement("astro-dev-toolbar-window"),c=document.createElement("style");c.textContent=`
|
|
2
|
+
.theme-content {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-flow: row wrap;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.theme-section {
|
|
8
|
+
margin-bottom: 1rem;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.theme-section:last-child {
|
|
12
|
+
margin-bottom: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.theme-label {
|
|
16
|
+
font-size: 0.75rem;
|
|
17
|
+
font-weight: 500;
|
|
18
|
+
color: rgba(255, 255, 255, 0.7);
|
|
19
|
+
margin-bottom: 0.5rem;
|
|
20
|
+
text-transform: uppercase;
|
|
21
|
+
letter-spacing: 0.05em;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.theme-status {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex: 1 1 auto;
|
|
27
|
+
width: 100%;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: 0.75rem;
|
|
30
|
+
padding: 1rem;
|
|
31
|
+
box-shadow: 0px 0px 0px 0px rgba(0, 0, 0, 0.10), 0px 1px 2px 0px rgba(0, 0, 0, 0.10), 0px 4px 4px 0px rgba(0, 0, 0, 0.09), 0px 10px 6px 0px rgba(0, 0, 0, 0.05), 0px 17px 7px 0px rgba(0, 0, 0, 0.01), 0px 26px 7px 0px rgba(0, 0, 0, 0.00);
|
|
32
|
+
border: 1px solid rgba(35, 38, 45, 1);
|
|
33
|
+
border-radius: .5rem;
|
|
34
|
+
margin-bottom: 1rem;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.theme-status-icon {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
width: 24px;
|
|
42
|
+
height: 24px;
|
|
43
|
+
color: white;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.theme-status-icon svg {
|
|
47
|
+
width: 24px;
|
|
48
|
+
height: 24px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.theme-status-text {
|
|
52
|
+
font-size: 0.875rem;
|
|
53
|
+
color: white;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.theme-status-resolved {
|
|
57
|
+
font-size: 0.75rem;
|
|
58
|
+
color: rgba(255, 255, 255, 0.5);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.theme-grid {
|
|
62
|
+
display: flex;
|
|
63
|
+
gap: 0.5rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.theme-grid astro-dev-toolbar-button svg {
|
|
67
|
+
width: 16px;
|
|
68
|
+
height: 16px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.theme-divider {
|
|
72
|
+
width: 1px;
|
|
73
|
+
background: rgba(255, 255, 255, 0.1);
|
|
74
|
+
margin: 0 1rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (max-width: 562px) {
|
|
78
|
+
.theme-divider {
|
|
79
|
+
height: 1px;
|
|
80
|
+
margin: .2rem 0 1rem 0;
|
|
81
|
+
}
|
|
82
|
+
.theme-section, .theme-divider {
|
|
83
|
+
flex: 1 1 auto;
|
|
84
|
+
width: 100%;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.quick-actions {
|
|
89
|
+
display: flex;
|
|
90
|
+
gap: 0.5rem;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.quick-actions astro-dev-toolbar-button {
|
|
94
|
+
flex: 1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.quick-actions astro-dev-toolbar-button svg {
|
|
98
|
+
width: 16px;
|
|
99
|
+
height: 16px;
|
|
100
|
+
}
|
|
101
|
+
`,n.appendChild(c);let x=`
|
|
102
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><!-- Icon from Carbon by IBM - undefined --><path fill="currentColor" d="M16 12.005a4 4 0 1 1-4 4a4.005 4.005 0 0 1 4-4m0-2a6 6 0 1 0 6 6a6 6 0 0 0-6-6M5.394 6.813L6.81 5.399l3.505 3.506L8.9 10.319zM2 15.005h5v2H2zm3.394 10.193L8.9 21.692l1.414 1.414l-3.505 3.506zM15 25.005h2v5h-2zm6.687-1.9l1.414-1.414l3.506 3.506l-1.414 1.414zm3.313-8.1h5v2h-5zm-3.313-6.101l3.506-3.506l1.414 1.414l-3.506 3.506zM15 2.005h2v5h-2z"/></svg>
|
|
103
|
+
`,w=`
|
|
104
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><!-- Icon from Carbon by IBM - undefined --><path fill="currentColor" d="M13.503 5.414a15.076 15.076 0 0 0 11.593 18.194a11.1 11.1 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1 1 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.07 13.07 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3"/></svg>`,h=`
|
|
105
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><!-- Icon from Carbon by IBM - undefined --><path fill="currentColor" d="M10 30H4a2 2 0 0 1-2-2V16a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2M4 16v12h6V16Z"/><path fill="currentColor" d="M28 4H6a2 2 0 0 0-2 2v6h2V6h22v14H14v2h2v4h-2v2h9v-2h-5v-4h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2"/></svg>
|
|
106
|
+
`,f=`
|
|
107
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><!-- Icon from Carbon by IBM - undefined --><circle cx="10" cy="12" r="2" fill="currentColor"/><circle cx="16" cy="9" r="2" fill="currentColor"/><circle cx="22" cy="12" r="2" fill="currentColor"/><circle cx="23" cy="18" r="2" fill="currentColor"/><circle cx="19" cy="23" r="2" fill="currentColor"/><path fill="currentColor" d="M16.54 2A14 14 0 0 0 2 16a4.82 4.82 0 0 0 6.09 4.65l1.12-.31a3 3 0 0 1 3.79 2.9V27a3 3 0 0 0 3 3a14 14 0 0 0 14-14.54A14.05 14.05 0 0 0 16.54 2m8.11 22.31A11.93 11.93 0 0 1 16 28a1 1 0 0 1-1-1v-3.76a5 5 0 0 0-5-5a5 5 0 0 0-1.33.18l-1.12.31A2.82 2.82 0 0 1 4 16A12 12 0 0 1 16.47 4A12.18 12.18 0 0 1 28 15.53a11.9 11.9 0 0 1-3.35 8.79Z"/></svg>
|
|
108
|
+
`,r=document.createElement("div");r.className="theme-content",n.appendChild(r);function a(){return window.__ASTRO_THEMES__||{theme:"system",resolvedTheme:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light",systemTheme:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light",themes:["light","dark","system"],setTheme:e=>{let o=typeof e=="function"?e(localStorage.getItem("theme")||"system"):e;localStorage.setItem("theme",o),window.location.reload()}}}function m(e){switch(e){case"light":return x;case"dark":return w;case"system":return h;default:return f}}function i(){let e=a(),o=e.theme,g=e.resolvedTheme,b=e.themes.includes("system")?e.themes:[...e.themes,"system"];r.innerHTML=`
|
|
109
|
+
<div class="theme-status">
|
|
110
|
+
<span class="theme-status-icon">${m(g)}</span>
|
|
111
|
+
<div>
|
|
112
|
+
<div class="theme-status-text">${o.charAt(0).toUpperCase()+o.slice(1)}</div>
|
|
113
|
+
<div class="theme-status-resolved">Resolved: ${g}</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div class="theme-section">
|
|
118
|
+
<div class="theme-label">Select Theme</div>
|
|
119
|
+
<div class="theme-grid" id="theme-grid"></div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div class="theme-divider"></div>
|
|
123
|
+
|
|
124
|
+
<div class="theme-section">
|
|
125
|
+
<div class="theme-label">Quick Actions</div>
|
|
126
|
+
<div class="theme-grid" id="quick-actions"></div>
|
|
127
|
+
</div>
|
|
128
|
+
`;let p=r.querySelector("#theme-grid");if(p)for(let t of b){let s=document.createElement("astro-dev-toolbar-button");s.innerHTML=`
|
|
129
|
+
<span style="display:inline-flex;align-items:center;gap:0.5rem;">
|
|
130
|
+
${m(t).replace('width="32" height="32"','width="16" height="16"')} ${t.charAt(0).toUpperCase()+t.slice(1)}
|
|
131
|
+
</span>
|
|
132
|
+
`,s.size="medium",s.buttonStyle=t===o?"purple":"gray",s.dataset.theme=t,s.addEventListener("click",()=>{a().setTheme(t),setTimeout(i,50)}),p.appendChild(s)}let d=r.querySelector("#quick-actions");if(d){let t=document.createElement("astro-dev-toolbar-button");t.innerHTML='<span style="display:inline-flex;align-items:center;gap:0.5rem;"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 32 32"><path fill="currentColor" d="M26 18A10 10 0 1 1 16 8h6.182l-3.584 3.585L20 13l6-6l-6-6l-1.402 1.414L22.182 6H16a12 12 0 1 0 12 12Z"/></svg> Toggle</span>',t.size="medium",t.buttonStyle="outline",t.addEventListener("click",()=>{let l=a(),y=l.resolvedTheme==="dark"?"light":"dark";l.setTheme(y),setTimeout(i,50)}),d.appendChild(t);let s=document.createElement("astro-dev-toolbar-button");s.innerHTML=`<span style="display:inline-flex;align-items:center;gap:0.5rem;">${h.replace('width="32" height="32"','width="16" height="16"')} System</span>`,s.size="medium",s.buttonStyle="outline",s.addEventListener("click",()=>{a().setTheme("system"),setTimeout(i,50)}),d.appendChild(s)}}i(),v.appendChild(n),u.onToggled(({state:e})=>{n.style.display=e?"block":"none"}),n.style.display="none",window.addEventListener("astro-themes:change",()=>{i()}),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{i()})}});export{C as default};
|
|
133
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/toolbar/app.ts"],"sourcesContent":["/**\n * Astro Dev Toolbar App for theme switching\n * Uses native Astro Dev Toolbar components for consistent UI\n */\n\nimport { defineToolbarApp } from \"astro/toolbar\";\n\n// Extend Window interface for this file\ndeclare global {\n\tinterface Window {\n\t\t__ASTRO_THEMES__?: {\n\t\t\ttheme: string;\n\t\t\tresolvedTheme: string;\n\t\t\tsystemTheme: \"dark\" | \"light\";\n\t\t\tforcedTheme?: string;\n\t\t\tthemes: string[];\n\t\t\tsetTheme: (theme: string | ((prevTheme: string) => string)) => void;\n\t\t};\n\t}\n}\n\nexport default defineToolbarApp({\n\tinit(canvas, app) {\n\t\t// Create native window component\n\t\tconst toolbarWindow = document.createElement(\n\t\t\t\"astro-dev-toolbar-window\"\n\t\t) as HTMLElement;\n\n\t\t// Add custom styles for our content\n\t\tconst style = document.createElement(\"style\");\n\t\tstyle.textContent = `\n\t\t\t.theme-content {\n\t\t\t\tdisplay: flex;\n \t\t\t\tflex-flow: row wrap;\n\t\t\t}\n\n\t\t\t.theme-section {\n\t\t\t\tmargin-bottom: 1rem;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-section:last-child {\n\t\t\t\tmargin-bottom: 0;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-label {\n\t\t\t\tfont-size: 0.75rem;\n\t\t\t\tfont-weight: 500;\n\t\t\t\tcolor: rgba(255, 255, 255, 0.7);\n\t\t\t\tmargin-bottom: 0.5rem;\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tletter-spacing: 0.05em;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-status {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex: 1 1 auto;\n\t\t\t\twidth: 100%;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: 0.75rem;\n\t\t\t\tpadding: 1rem;\n\t\t\t\tbox-shadow: 0px 0px 0px 0px rgba(0, 0, 0, 0.10), 0px 1px 2px 0px rgba(0, 0, 0, 0.10), 0px 4px 4px 0px rgba(0, 0, 0, 0.09), 0px 10px 6px 0px rgba(0, 0, 0, 0.05), 0px 17px 7px 0px rgba(0, 0, 0, 0.01), 0px 26px 7px 0px rgba(0, 0, 0, 0.00);\n\t\t\t\tborder: 1px solid rgba(35, 38, 45, 1);\n\t\t\t\tborder-radius: .5rem;\n\t\t\t\tmargin-bottom: 1rem;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-status-icon {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\twidth: 24px;\n\t\t\t\theight: 24px;\n\t\t\t\tcolor: white;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-status-icon svg {\n\t\t\t\twidth: 24px;\n\t\t\t\theight: 24px;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-status-text {\n\t\t\t\tfont-size: 0.875rem;\n\t\t\t\tcolor: white;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-status-resolved {\n\t\t\t\tfont-size: 0.75rem;\n\t\t\t\tcolor: rgba(255, 255, 255, 0.5);\n\t\t\t}\n\t\t\t\n\t\t\t.theme-grid {\n\t\t\t\tdisplay: flex;\n\t\t\t\tgap: 0.5rem;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-grid astro-dev-toolbar-button svg {\n\t\t\t\twidth: 16px;\n\t\t\t\theight: 16px;\n\t\t\t}\n\t\t\t\n\t\t\t.theme-divider {\n\t\t\t\twidth: 1px;\n\t\t\t\tbackground: rgba(255, 255, 255, 0.1);\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\t\t\t\n\t\t\t@media (max-width: 562px) {\n\t\t\t\t.theme-divider {\n\t\t\t\t\theight: 1px;\n\t\t\t\t\tmargin: .2rem 0 1rem 0;\n\t\t\t\t}\n\t\t\t\t.theme-section, .theme-divider {\n\t\t\t\t\tflex: 1 1 auto;\n\t\t\t\t\twidth: 100%;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t.quick-actions {\n\t\t\t\tdisplay: flex;\n\t\t\t\tgap: 0.5rem;\n\t\t\t}\n\t\t\t\n\t\t\t.quick-actions astro-dev-toolbar-button {\n\t\t\t\tflex: 1;\n\t\t\t}\n\t\t\t\n\t\t\t.quick-actions astro-dev-toolbar-button svg {\n\t\t\t\twidth: 16px;\n\t\t\t\theight: 16px;\n\t\t\t}\n\t\t`;\n\t\ttoolbarWindow.appendChild(style);\n\n\t\tconst lightModeIcon = `\n\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" viewBox=\"0 0 32 32\"><!-- Icon from Carbon by IBM - undefined --><path fill=\"currentColor\" d=\"M16 12.005a4 4 0 1 1-4 4a4.005 4.005 0 0 1 4-4m0-2a6 6 0 1 0 6 6a6 6 0 0 0-6-6M5.394 6.813L6.81 5.399l3.505 3.506L8.9 10.319zM2 15.005h5v2H2zm3.394 10.193L8.9 21.692l1.414 1.414l-3.505 3.506zM15 25.005h2v5h-2zm6.687-1.9l1.414-1.414l3.506 3.506l-1.414 1.414zm3.313-8.1h5v2h-5zm-3.313-6.101l3.506-3.506l1.414 1.414l-3.506 3.506zM15 2.005h2v5h-2z\"/></svg>\n\t\t`;\n\t\tconst darkModeIcon = `\n\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" viewBox=\"0 0 32 32\"><!-- Icon from Carbon by IBM - undefined --><path fill=\"currentColor\" d=\"M13.503 5.414a15.076 15.076 0 0 0 11.593 18.194a11.1 11.1 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1 1 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.07 13.07 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3\"/></svg>`;\n\t\tconst systemModeIcon = `\n\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" viewBox=\"0 0 32 32\"><!-- Icon from Carbon by IBM - undefined --><path fill=\"currentColor\" d=\"M10 30H4a2 2 0 0 1-2-2V16a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2M4 16v12h6V16Z\"/><path fill=\"currentColor\" d=\"M28 4H6a2 2 0 0 0-2 2v6h2V6h22v14H14v2h2v4h-2v2h9v-2h-5v-4h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2\"/></svg>\n\t\t`;\n\t\tconst defaultModeIcon = `\n\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" viewBox=\"0 0 32 32\"><!-- Icon from Carbon by IBM - undefined --><circle cx=\"10\" cy=\"12\" r=\"2\" fill=\"currentColor\"/><circle cx=\"16\" cy=\"9\" r=\"2\" fill=\"currentColor\"/><circle cx=\"22\" cy=\"12\" r=\"2\" fill=\"currentColor\"/><circle cx=\"23\" cy=\"18\" r=\"2\" fill=\"currentColor\"/><circle cx=\"19\" cy=\"23\" r=\"2\" fill=\"currentColor\"/><path fill=\"currentColor\" d=\"M16.54 2A14 14 0 0 0 2 16a4.82 4.82 0 0 0 6.09 4.65l1.12-.31a3 3 0 0 1 3.79 2.9V27a3 3 0 0 0 3 3a14 14 0 0 0 14-14.54A14.05 14.05 0 0 0 16.54 2m8.11 22.31A11.93 11.93 0 0 1 16 28a1 1 0 0 1-1-1v-3.76a5 5 0 0 0-5-5a5 5 0 0 0-1.33.18l-1.12.31A2.82 2.82 0 0 1 4 16A12 12 0 0 1 16.47 4A12.18 12.18 0 0 1 28 15.53a11.9 11.9 0 0 1-3.35 8.79Z\"/></svg>\n\t\t`;\n\n\t\t// Create content container\n\t\tconst content = document.createElement(\"div\");\n\t\tcontent.className = \"theme-content\";\n\t\ttoolbarWindow.appendChild(content);\n\n\t\tfunction getThemeState() {\n\t\t\treturn (\n\t\t\t\twindow.__ASTRO_THEMES__ || {\n\t\t\t\t\ttheme: \"system\",\n\t\t\t\t\tresolvedTheme: window.matchMedia(\"(prefers-color-scheme: dark)\")\n\t\t\t\t\t\t.matches\n\t\t\t\t\t\t? \"dark\"\n\t\t\t\t\t\t: \"light\",\n\t\t\t\t\tsystemTheme: window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n\t\t\t\t\t\t? (\"dark\" as const)\n\t\t\t\t\t\t: (\"light\" as const),\n\t\t\t\t\tthemes: [\"light\", \"dark\", \"system\"],\n\t\t\t\t\tsetTheme: (theme: string | ((prev: string) => string)) => {\n\t\t\t\t\t\tconst themeValue =\n\t\t\t\t\t\t\ttypeof theme === \"function\"\n\t\t\t\t\t\t\t\t? theme(localStorage.getItem(\"theme\") || \"system\")\n\t\t\t\t\t\t\t\t: theme;\n\t\t\t\t\t\tlocalStorage.setItem(\"theme\", themeValue);\n\t\t\t\t\t\twindow.location.reload();\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\n\t\tfunction getThemeIcon(theme: string): string {\n\t\t\tswitch (theme) {\n\t\t\t\tcase \"light\":\n\t\t\t\t\treturn lightModeIcon;\n\t\t\t\tcase \"dark\":\n\t\t\t\t\treturn darkModeIcon;\n\t\t\t\tcase \"system\":\n\t\t\t\t\treturn systemModeIcon;\n\t\t\t\tdefault:\n\t\t\t\t\treturn defaultModeIcon;\n\t\t\t}\n\t\t}\n\n\t\tfunction render() {\n\t\t\tconst state = getThemeState();\n\t\t\tconst currentTheme = state.theme;\n\t\t\tconst resolvedTheme = state.resolvedTheme;\n\t\t\tconst themes = state.themes.includes(\"system\")\n\t\t\t\t? state.themes\n\t\t\t\t: [...state.themes, \"system\"];\n\n\t\t\tcontent.innerHTML = `\n\t\t\t\t<div class=\"theme-status\">\n\t\t\t\t\t<span class=\"theme-status-icon\">${getThemeIcon(resolvedTheme)}</span>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div class=\"theme-status-text\">${currentTheme.charAt(0).toUpperCase() + currentTheme.slice(1)}</div>\n\t\t\t\t\t\t<div class=\"theme-status-resolved\">Resolved: ${resolvedTheme}</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\n\t\t\t\t<div class=\"theme-section\">\n\t\t\t\t\t<div class=\"theme-label\">Select Theme</div>\n\t\t\t\t\t<div class=\"theme-grid\" id=\"theme-grid\"></div>\n\t\t\t\t</div>\n\t\t\t\t\n\t\t\t\t<div class=\"theme-divider\"></div>\n\t\t\t\t\n\t\t\t\t<div class=\"theme-section\">\n\t\t\t\t\t<div class=\"theme-label\">Quick Actions</div>\n\t\t\t\t\t<div class=\"theme-grid\" id=\"quick-actions\"></div>\n\t\t\t\t</div>\n\t\t\t`;\n\n\t\t\t// Create theme buttons using native components\n\t\t\tconst themeGrid = content.querySelector(\"#theme-grid\");\n\t\t\tif (themeGrid) {\n\t\t\t\tfor (const theme of themes) {\n\t\t\t\t\tconst btn = document.createElement(\n\t\t\t\t\t\t\"astro-dev-toolbar-button\"\n\t\t\t\t\t) as HTMLElement & {\n\t\t\t\t\t\tbuttonStyle: string;\n\t\t\t\t\t\tsize: string;\n\t\t\t\t\t};\n\t\t\t\t\tbtn.innerHTML = `\n\t\t\t\t\t\t<span style=\"display:inline-flex;align-items:center;gap:0.5rem;\">\n\t\t\t\t\t\t\t${getThemeIcon(theme).replace('width=\"32\" height=\"32\"', 'width=\"16\" height=\"16\"')} ${theme.charAt(0).toUpperCase() + theme.slice(1)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t`;\n\t\t\t\t\tbtn.size = \"medium\";\n\t\t\t\t\tbtn.buttonStyle = theme === currentTheme ? \"purple\" : \"gray\";\n\t\t\t\t\tbtn.dataset.theme = theme;\n\t\t\t\t\tbtn.addEventListener(\"click\", () => {\n\t\t\t\t\t\tconst state = getThemeState();\n\t\t\t\t\t\tstate.setTheme(theme);\n\t\t\t\t\t\tsetTimeout(render, 50);\n\t\t\t\t\t});\n\t\t\t\t\tthemeGrid.appendChild(btn);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Create quick action buttons\n\t\t\tconst quickActions = content.querySelector(\"#quick-actions\");\n\t\t\tif (quickActions) {\n\t\t\t\t// Toggle button\n\t\t\t\tconst toggleBtn = document.createElement(\n\t\t\t\t\t\"astro-dev-toolbar-button\"\n\t\t\t\t) as HTMLElement & {\n\t\t\t\t\tbuttonStyle: string;\n\t\t\t\t\tsize: string;\n\t\t\t\t};\n\t\t\t\ttoggleBtn.innerHTML = `<span style=\"display:inline-flex;align-items:center;gap:0.5rem;\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 32 32\"><path fill=\"currentColor\" d=\"M26 18A10 10 0 1 1 16 8h6.182l-3.584 3.585L20 13l6-6l-6-6l-1.402 1.414L22.182 6H16a12 12 0 1 0 12 12Z\"/></svg> Toggle</span>`;\n\t\t\t\ttoggleBtn.size = \"medium\";\n\t\t\t\ttoggleBtn.buttonStyle = \"outline\";\n\t\t\t\ttoggleBtn.addEventListener(\"click\", () => {\n\t\t\t\t\tconst state = getThemeState();\n\t\t\t\t\tconst newTheme = state.resolvedTheme === \"dark\" ? \"light\" : \"dark\";\n\t\t\t\t\tstate.setTheme(newTheme);\n\t\t\t\t\tsetTimeout(render, 50);\n\t\t\t\t});\n\t\t\t\tquickActions.appendChild(toggleBtn);\n\n\t\t\t\t// System button\n\t\t\t\tconst systemBtn = document.createElement(\n\t\t\t\t\t\"astro-dev-toolbar-button\"\n\t\t\t\t) as HTMLElement & {\n\t\t\t\t\tbuttonStyle: string;\n\t\t\t\t\tsize: string;\n\t\t\t\t};\n\t\t\t\tsystemBtn.innerHTML = `<span style=\"display:inline-flex;align-items:center;gap:0.5rem;\">${systemModeIcon.replace('width=\"32\" height=\"32\"', 'width=\"16\" height=\"16\"')} System</span>`;\n\t\t\t\tsystemBtn.size = \"medium\";\n\t\t\t\tsystemBtn.buttonStyle = \"outline\";\n\t\t\t\tsystemBtn.addEventListener(\"click\", () => {\n\t\t\t\t\tconst state = getThemeState();\n\t\t\t\t\tstate.setTheme(\"system\");\n\t\t\t\t\tsetTimeout(render, 50);\n\t\t\t\t});\n\t\t\t\tquickActions.appendChild(systemBtn);\n\t\t\t}\n\t\t}\n\n\t\t// Initial render\n\t\trender();\n\n\t\t// Append window to canvas\n\t\tcanvas.appendChild(toolbarWindow);\n\n\t\t// Handle app toggle visibility\n\t\tapp.onToggled(({ state }) => {\n\t\t\ttoolbarWindow.style.display = state ? \"block\" : \"none\";\n\t\t});\n\n\t\t// Initially hidden until toggled\n\t\ttoolbarWindow.style.display = \"none\";\n\n\t\t// Listen for theme changes\n\t\twindow.addEventListener(\"astro-themes:change\", () => {\n\t\t\trender();\n\t\t});\n\n\t\t// Listen for system theme changes\n\t\twindow\n\t\t\t.matchMedia(\"(prefers-color-scheme: dark)\")\n\t\t\t.addEventListener(\"change\", () => {\n\t\t\t\trender();\n\t\t\t});\n\t},\n});\n"],"mappings":"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","names":["defineToolbarApp","app_default","canvas","app","toolbarWindow","style","lightModeIcon","darkModeIcon","systemModeIcon","defaultModeIcon","content","getThemeState","theme","themeValue","getThemeIcon","render","state","currentTheme","resolvedTheme","themes","themeGrid","btn","quickActions","toggleBtn","newTheme","systemBtn"]}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for astro-themes
|
|
3
|
+
* Mirrors the next-themes API for Astro
|
|
4
|
+
*/
|
|
5
|
+
/** Data attribute pattern for HTML attributes */
|
|
6
|
+
type DataAttribute = `data-${string}`;
|
|
7
|
+
/** Attribute type - can be 'class' or any data-* attribute */
|
|
8
|
+
type Attribute = DataAttribute | "class";
|
|
9
|
+
/** Mapping of theme name to HTML attribute value */
|
|
10
|
+
interface ValueObject {
|
|
11
|
+
[themeName: string]: string;
|
|
12
|
+
}
|
|
13
|
+
/** Props for the inline script element */
|
|
14
|
+
interface ScriptProps {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* ThemeProvider component props
|
|
19
|
+
* All your theme configuration is passed to ThemeProvider
|
|
20
|
+
*/
|
|
21
|
+
interface ThemeProviderProps {
|
|
22
|
+
/**
|
|
23
|
+
* Key used to store theme setting in localStorage
|
|
24
|
+
* @default 'theme'
|
|
25
|
+
*/
|
|
26
|
+
storageKey?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Default theme name. If `enableSystem` is false, the default theme is 'light'
|
|
29
|
+
* @default 'system'
|
|
30
|
+
*/
|
|
31
|
+
defaultTheme?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Forced theme name for the current page (does not modify saved theme settings)
|
|
34
|
+
*/
|
|
35
|
+
forcedTheme?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Whether to switch between `dark` and `light` based on `prefers-color-scheme`
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
enableSystem?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to indicate to browsers which color scheme is used (dark or light)
|
|
43
|
+
* for built-in UI like inputs and buttons
|
|
44
|
+
* @default true
|
|
45
|
+
*/
|
|
46
|
+
enableColorScheme?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Optionally disable all CSS transitions when switching themes
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
disableTransitionOnChange?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* List of theme names
|
|
54
|
+
* @default ['light', 'dark']
|
|
55
|
+
*/
|
|
56
|
+
themes?: string[];
|
|
57
|
+
/**
|
|
58
|
+
* HTML attribute modified based on the active theme
|
|
59
|
+
* Accepts `class` and `data-*` (meaning any data attribute, `data-mode`, `data-color`, etc.)
|
|
60
|
+
* Can also be an array to set multiple attributes
|
|
61
|
+
* @default 'data-theme'
|
|
62
|
+
*/
|
|
63
|
+
attribute?: Attribute | Attribute[];
|
|
64
|
+
/**
|
|
65
|
+
* Optional mapping of theme name to attribute value
|
|
66
|
+
* value is an `object` where key is the theme name and value is the attribute value
|
|
67
|
+
*/
|
|
68
|
+
value?: ValueObject;
|
|
69
|
+
/**
|
|
70
|
+
* Optional nonce passed to the injected `script` tag, used to allow-list the script in your CSP
|
|
71
|
+
*/
|
|
72
|
+
nonce?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Optional props to pass to the injected `script` tag
|
|
75
|
+
*/
|
|
76
|
+
scriptProps?: ScriptProps;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Theme state exposed to client-side JavaScript
|
|
80
|
+
* Available via window.__ASTRO_THEMES__
|
|
81
|
+
*/
|
|
82
|
+
interface ThemeState {
|
|
83
|
+
/** Active theme name */
|
|
84
|
+
theme: string;
|
|
85
|
+
/** If the active theme is "system", this returns whether the system preference resolved to "dark" or "light" */
|
|
86
|
+
resolvedTheme: string;
|
|
87
|
+
/** System theme preference ("dark" or "light") regardless of active theme */
|
|
88
|
+
systemTheme: "dark" | "light";
|
|
89
|
+
/** Forced page theme or undefined */
|
|
90
|
+
forcedTheme?: string;
|
|
91
|
+
/** The list of themes */
|
|
92
|
+
themes: string[];
|
|
93
|
+
/** Update the theme */
|
|
94
|
+
setTheme: (theme: string | ((prevTheme: string) => string)) => void;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Configuration for the astro-themes integration
|
|
98
|
+
*/
|
|
99
|
+
interface AstroThemesConfig {
|
|
100
|
+
/**
|
|
101
|
+
* Inject the flash-prevention script automatically via integration
|
|
102
|
+
* When true, you don't need to use ThemeProvider component for basic setup
|
|
103
|
+
* @default false
|
|
104
|
+
*/
|
|
105
|
+
injectScript?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Default props for the ThemeProvider component or injected script
|
|
108
|
+
*/
|
|
109
|
+
defaultProps?: Partial<ThemeProviderProps>;
|
|
110
|
+
/**
|
|
111
|
+
* Enable the Dev Toolbar App for theme switching during development
|
|
112
|
+
* @default true
|
|
113
|
+
*/
|
|
114
|
+
devToolbar?: boolean;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export type { AstroThemesConfig, Attribute, ScriptProps, ThemeProviderProps, ThemeState, ValueObject };
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lpdsgn/astro-themes",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Perfect dark mode in Astro with no flash. System preference, multiple themes, and sync across tabs.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./component": "./dist/components/ThemeProvider.astro",
|
|
13
|
+
"./ThemeProvider.astro": "./dist/components/ThemeProvider.astro",
|
|
14
|
+
"./client": {
|
|
15
|
+
"types": "./dist/client.d.ts",
|
|
16
|
+
"default": "./dist/client.js"
|
|
17
|
+
},
|
|
18
|
+
"./types": {
|
|
19
|
+
"types": "./dist/types.d.ts",
|
|
20
|
+
"default": "./dist/types.js"
|
|
21
|
+
},
|
|
22
|
+
"./toolbar": {
|
|
23
|
+
"types": "./dist/toolbar/app.d.ts",
|
|
24
|
+
"default": "./dist/toolbar/app.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "tsup --watch",
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"prepublishOnly": "pnpm build"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"astro-integration",
|
|
37
|
+
"astro-component",
|
|
38
|
+
"withastro",
|
|
39
|
+
"astro",
|
|
40
|
+
"dark-mode",
|
|
41
|
+
"themes",
|
|
42
|
+
"theme-provider",
|
|
43
|
+
"next-themes"
|
|
44
|
+
],
|
|
45
|
+
"author": "",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/LPdsgn/astro-themes.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": "https://github.com/LPdsgn/astro-themes/issues",
|
|
51
|
+
"homepage": "https://github.com/LPdsgn/astro-themes#readme",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"astro": "^5.1.3"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"astro-integration-kit": "0.19.1",
|
|
58
|
+
"zod": "^4.3.5"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"tsup": "8.5.1"
|
|
62
|
+
}
|
|
63
|
+
}
|