@hadoku/themes 0.0.0-stage → 5.12.15
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/LICENSE +21 -0
- package/LICENSE-lucide +43 -0
- package/README.md +433 -2
- package/THEME_USAGE_GUIDE.md +308 -0
- package/dist/HadokuThemeRoot.d.ts +53 -0
- package/dist/HadokuThemeRoot.js +66 -0
- package/dist/ThemeIcons.d.ts +31 -0
- package/dist/ThemeIcons.js +91 -0
- package/dist/effects.d.ts +25 -0
- package/dist/effects.js +25 -0
- package/dist/icons/Icon.d.ts +36 -0
- package/dist/icons/Icon.js +89 -0
- package/dist/icons/index.d.ts +61 -0
- package/dist/icons/index.js +87 -0
- package/dist/icons/registry.generated.d.ts +158 -0
- package/dist/icons/registry.generated.js +446 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +169 -0
- package/dist/metadata.d.ts +35 -0
- package/dist/metadata.js +108 -0
- package/dist/platform/index.d.ts +94 -0
- package/dist/platform/index.js +149 -0
- package/dist/platform/usePlatform.d.ts +29 -0
- package/dist/platform/usePlatform.js +65 -0
- package/dist/prefsApiBase.d.ts +37 -0
- package/dist/prefsApiBase.js +40 -0
- package/dist/theme-types.d.ts +34 -0
- package/dist/theme-types.js +1 -0
- package/dist/themeContext.d.ts +62 -0
- package/dist/themeContext.js +70 -0
- package/dist/themePrefs.d.ts +29 -0
- package/dist/themePrefs.js +48 -0
- package/dist/useRefElement.d.ts +19 -0
- package/dist/useRefElement.js +52 -0
- package/dist/useTheme.d.ts +46 -0
- package/dist/useTheme.js +216 -0
- package/package.json +83 -4
- package/scripts/audit-tokens.mjs +126 -0
- package/scripts/check-contrast.mjs +24 -0
- package/scripts/check-docs.mjs +53 -0
- package/scripts/check-icons.mjs +473 -0
- package/scripts/check-usage.mjs +297 -0
- package/scripts/codemod-icons.mjs +396 -0
- package/scripts/generate-icons.mjs +199 -0
- package/scripts/generate-preview.mjs +374 -0
- package/scripts/generate-tailwind-colors.mjs +109 -0
- package/scripts/lib/color.mjs +184 -0
- package/scripts/lib/contract.d.mts +58 -0
- package/scripts/lib/contract.mjs +348 -0
- package/scripts/lib/derive.mjs +324 -0
- package/scripts/lib/icon-registry.mjs +92 -0
- package/scripts/lib/parse-themes.mjs +19 -0
- package/scripts/lib/tokens.mjs +188 -0
- package/scripts/normalize-tokens.mjs +161 -0
- package/src/advanced.css +218 -0
- package/src/icons/Icon.tsx +151 -0
- package/src/icons/emoji-map.json +202 -0
- package/src/icons/index.ts +151 -0
- package/src/icons/registry.generated.ts +455 -0
- package/src/icons/sources.json +150 -0
- package/src/icons.css +145 -0
- package/src/style.css +1421 -0
- package/src/tailwind-colors.css +88 -0
- package/src/tailwind-integration.css +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 WolffM
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/LICENSE-lucide
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
The following Lucide icons are derived from the Feather project:
|
|
20
|
+
|
|
21
|
+
airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
|
|
22
|
+
|
|
23
|
+
The MIT License (MIT) (for the icons listed above)
|
|
24
|
+
|
|
25
|
+
Copyright (c) 2013-present Cole Bemis
|
|
26
|
+
|
|
27
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
28
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
29
|
+
in the Software without restriction, including without limitation the rights
|
|
30
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
32
|
+
furnished to do so, subject to the following conditions:
|
|
33
|
+
|
|
34
|
+
The above copyright notice and this permission notice shall be included in all
|
|
35
|
+
copies or substantial portions of the Software.
|
|
36
|
+
|
|
37
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
38
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
39
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
40
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
41
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
42
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
43
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,434 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @wolffm/themes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Beautiful color themes using CSS custom properties. Framework-agnostic and ready to use in any web project.
|
|
4
|
+
|
|
5
|
+
## Themes
|
|
6
|
+
|
|
7
|
+
9 theme families (18 themes), each with light and dark variants:
|
|
8
|
+
|
|
9
|
+
- **Light** (default) / **Dark** - Clean blue and white / Sophisticated midnight with purple accents
|
|
10
|
+
- **Strawberry** Light/Dark - Sweet pink tones
|
|
11
|
+
- **Ocean** Light/Dark - Deep sea blues and tropical coral
|
|
12
|
+
- **Cyberpunk** Light/Dark - Neon dystopia with electric colors
|
|
13
|
+
- **Coffee** Light/Dark - Rich espresso and cream tones
|
|
14
|
+
- **Lavender** Light/Dark - Soft purple elegance
|
|
15
|
+
- **Nature** Light/Dark - Lush leaf greens inspired by forests and earth
|
|
16
|
+
- **Pink** Light/Dark - Bubblegum pink and neon glows
|
|
17
|
+
- **Izakaya** Light/Dark - Japanese izakaya-inspired warm tones (Kitsune contest winner)
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @wolffm/themes
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Import CSS
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import '@wolffm/themes/style.css'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Use Theme Utilities
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { setTheme, loadTheme, saveTheme, THEMES } from '@wolffm/themes'
|
|
37
|
+
|
|
38
|
+
// Set theme (applies immediately)
|
|
39
|
+
setTheme('dark')
|
|
40
|
+
setTheme('cyberpunk-dark')
|
|
41
|
+
|
|
42
|
+
// Save to sessionStorage and apply
|
|
43
|
+
saveTheme('lavender-light')
|
|
44
|
+
|
|
45
|
+
// Load saved theme on app start
|
|
46
|
+
const theme = loadTheme()
|
|
47
|
+
|
|
48
|
+
// Get all available themes
|
|
49
|
+
console.log(THEMES) // ['light', 'dark', 'strawberry-light', ...]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Apply to HTML
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<!-- Set globally on html element -->
|
|
56
|
+
<html data-theme="dark">
|
|
57
|
+
<!-- Or on a specific container -->
|
|
58
|
+
<div data-theme="strawberry-light">
|
|
59
|
+
<!-- Your content -->
|
|
60
|
+
</div>
|
|
61
|
+
</html>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## CSS Variables
|
|
65
|
+
|
|
66
|
+
Each theme defines 47 variables (41 color + 6 shadow). Color variables use the
|
|
67
|
+
`--color-*` namespace — which is also Tailwind v4's — so they map straight to
|
|
68
|
+
utilities. Everything else is namespaced `--hdk-*` to avoid colliding with
|
|
69
|
+
Tailwind internals.
|
|
70
|
+
|
|
71
|
+
> **Which token do I use for X?** → [`THEME_USAGE_GUIDE.md`](./THEME_USAGE_GUIDE.md).
|
|
72
|
+
> That is the doc to read (and to point agents at) before writing any styles.
|
|
73
|
+
|
|
74
|
+
### Colors — 5 semantic families × 6 tokens
|
|
75
|
+
|
|
76
|
+
Every family carries an identical set. The rectangle is the point: the
|
|
77
|
+
symmetric guess is always correct.
|
|
78
|
+
|
|
79
|
+
| | fill | gradient/pressed | tint | hover overlay | text on fill | text on tint |
|
|
80
|
+
| --------- | ----------------- | ---------------- | ----- | ------------- | -------------------- | ----------------------- |
|
|
81
|
+
| `primary` | `--color-primary` | `-dark` | `-bg` | `-hover` | `--color-on-primary` | `--color-on-primary-bg` |
|
|
82
|
+
| `success` | `--color-success` | `-dark` | `-bg` | `-hover` | `--color-on-success` | `--color-on-success-bg` |
|
|
83
|
+
| `warning` | `--color-warning` | `-dark` | `-bg` | `-hover` | `--color-on-warning` | `--color-on-warning-bg` |
|
|
84
|
+
| `danger` | `--color-danger` | `-dark` | `-bg` | `-hover` | `--color-on-danger` | `--color-on-danger-bg` |
|
|
85
|
+
| `neutral` | `--color-neutral` | `-dark` | `-bg` | `-hover` | `--color-on-neutral` | `--color-on-neutral-bg` |
|
|
86
|
+
|
|
87
|
+
### Colors — structural
|
|
88
|
+
|
|
89
|
+
- `--color-text` (+ `-secondary`, `-tertiary`, `-muted`)
|
|
90
|
+
- `--color-border` (+ `-light`)
|
|
91
|
+
- `--color-bg` (+ `-card`, `-alt`, `-hover`, `-overlay`)
|
|
92
|
+
|
|
93
|
+
Every `on-*` pair is verified ≥4.5:1 against its surface in all 18 themes by
|
|
94
|
+
`scripts/check-contrast.mjs`.
|
|
95
|
+
|
|
96
|
+
### Typography
|
|
97
|
+
|
|
98
|
+
- `--font-family`
|
|
99
|
+
- `--hdk-text-*` (xs, sm, md, base, lg) - font sizes
|
|
100
|
+
- `--font-weight-*` (normal, semibold, bold)
|
|
101
|
+
- `--line-height-*` (normal, relaxed)
|
|
102
|
+
|
|
103
|
+
### Spacing
|
|
104
|
+
|
|
105
|
+
- `--hdk-space-*` (xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl)
|
|
106
|
+
|
|
107
|
+
### Layout
|
|
108
|
+
|
|
109
|
+
- `--hdk-radius` (+ sm, lg variants) - border radius
|
|
110
|
+
- `--hdk-shadow-*` (sm, md, lg, focus, focus-sm, focus-alt)
|
|
111
|
+
|
|
112
|
+
### Transitions
|
|
113
|
+
|
|
114
|
+
- `--transition-fast`
|
|
115
|
+
- `--transition-smooth`
|
|
116
|
+
|
|
117
|
+
### Usage Example
|
|
118
|
+
|
|
119
|
+
```css
|
|
120
|
+
.my-button {
|
|
121
|
+
background: var(--color-primary);
|
|
122
|
+
color: var(--color-on-primary);
|
|
123
|
+
padding: var(--hdk-space-md) var(--hdk-space-xl);
|
|
124
|
+
border-radius: var(--hdk-radius);
|
|
125
|
+
font-size: var(--hdk-text-base);
|
|
126
|
+
box-shadow: var(--hdk-shadow-sm);
|
|
127
|
+
transition: var(--transition-fast);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.my-button:hover {
|
|
131
|
+
background: var(--color-primary-dark);
|
|
132
|
+
box-shadow: var(--hdk-shadow-md);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Tailwind v4 Compatibility
|
|
137
|
+
|
|
138
|
+
This package uses namespaced CSS variables (`--hdk-*`) to avoid collisions with Tailwind v4's internal variables.
|
|
139
|
+
|
|
140
|
+
### Why Namespacing?
|
|
141
|
+
|
|
142
|
+
Tailwind v4 uses CSS custom properties internally:
|
|
143
|
+
|
|
144
|
+
- `--spacing-*` powers `max-w-md`, `p-4`, `gap-*`, etc.
|
|
145
|
+
- `--radius-*` powers `rounded-md`, `rounded-lg`, etc.
|
|
146
|
+
- `--font-size-*` powers `text-sm`, `text-base`, etc.
|
|
147
|
+
- `--shadow-*` powers `shadow-sm`, `shadow-md`, etc.
|
|
148
|
+
|
|
149
|
+
Without namespacing, a theme variable like `--spacing-md: 8px` would break `max-w-md` (changing it from 28rem to 8px).
|
|
150
|
+
|
|
151
|
+
### Variable Mapping
|
|
152
|
+
|
|
153
|
+
| Old Name (v1.x) | New Name (v2.x) |
|
|
154
|
+
| ----------------- | ----------------- |
|
|
155
|
+
| `--spacing-md` | `--hdk-space-md` |
|
|
156
|
+
| `--font-size-sm` | `--hdk-text-sm` |
|
|
157
|
+
| `--border-radius` | `--hdk-radius` |
|
|
158
|
+
| `--shadow-sm` | `--hdk-shadow-sm` |
|
|
159
|
+
| `--shadow-modal` | `--hdk-shadow-lg` |
|
|
160
|
+
|
|
161
|
+
### Tailwind Integration
|
|
162
|
+
|
|
163
|
+
Three imports, no hand-written `@theme` block:
|
|
164
|
+
|
|
165
|
+
```css
|
|
166
|
+
@import '@wolffm/themes/style.css'; /* 1. tokens — MUST be unlayered */
|
|
167
|
+
@import 'tailwindcss'; /* 2. Tailwind */
|
|
168
|
+
@import '@wolffm/themes/tailwind-colors.css'; /* 3. all 41 colours */
|
|
169
|
+
@import '@wolffm/themes/tailwind-integration.css'; /* 4. radius/shadow/type (optional) */
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
You now get `bg-primary`, `text-on-primary`, `bg-success-bg`,
|
|
173
|
+
`text-on-danger-bg`, `border-border`, `bg-bg-card` — every token, as a utility.
|
|
174
|
+
|
|
175
|
+
**Never hand-roll a `@theme` colour block.** A local copy is exactly the drift
|
|
176
|
+
this package exists to prevent; `tailwind-colors.css` is generated from
|
|
177
|
+
`style.css` and CI fails if the two disagree.
|
|
178
|
+
|
|
179
|
+
**Step 1 must stay unlayered.** Tailwind emits each `@theme` entry into
|
|
180
|
+
`@layer theme` as `:root { --x: var(--x) }`, which is self-referential and
|
|
181
|
+
invalid on its own. It resolves only because `style.css` declares the same
|
|
182
|
+
properties outside any cascade layer, and unlayered wins. Import it with
|
|
183
|
+
`layer(...)` and all 41 colours silently become `transparent`.
|
|
184
|
+
|
|
185
|
+
`tailwind-integration.css` (step 4) is separate and optional — it maps
|
|
186
|
+
`--hdk-*` radius/shadow/type onto `rounded-*`, `shadow-*`, `text-*`.
|
|
187
|
+
|
|
188
|
+
### Migration from v1.x
|
|
189
|
+
|
|
190
|
+
Search and replace in your CSS:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
var(--spacing- → var(--hdk-space-
|
|
194
|
+
var(--font-size- → var(--hdk-text-
|
|
195
|
+
var(--border-radius → var(--hdk-radius
|
|
196
|
+
var(--shadow- → var(--hdk-shadow-
|
|
197
|
+
var(--shadow-modal) → var(--hdk-shadow-lg)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## React Integration
|
|
201
|
+
|
|
202
|
+
### Using the `useTheme` Hook (Recommended)
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { useTheme, THEME_FAMILIES, THEME_ICON_MAP } from '@wolffm/themes'
|
|
206
|
+
import { ConnectedThemePicker } from '@wolffm/task-ui-components'
|
|
207
|
+
import '@wolffm/themes/style.css'
|
|
208
|
+
import '@wolffm/task-ui-components/theme-picker.css'
|
|
209
|
+
|
|
210
|
+
function App() {
|
|
211
|
+
const { theme, setTheme } = useTheme()
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<ConnectedThemePicker
|
|
215
|
+
themeFamilies={THEME_FAMILIES}
|
|
216
|
+
currentTheme={theme}
|
|
217
|
+
onThemeChange={setTheme}
|
|
218
|
+
getThemeIcon={theme => {
|
|
219
|
+
const Icon = THEME_ICON_MAP[theme]
|
|
220
|
+
return Icon ? <Icon /> : null
|
|
221
|
+
}}
|
|
222
|
+
/>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Manual React Integration
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
import { useEffect, useState } from 'react'
|
|
231
|
+
import { loadTheme, setTheme, THEMES, type Theme } from '@wolffm/themes'
|
|
232
|
+
import '@wolffm/themes/style.css'
|
|
233
|
+
|
|
234
|
+
function App() {
|
|
235
|
+
const [theme, setThemeState] = useState<Theme>('light')
|
|
236
|
+
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
const savedTheme = loadTheme()
|
|
239
|
+
setThemeState(savedTheme)
|
|
240
|
+
}, [])
|
|
241
|
+
|
|
242
|
+
const handleThemeChange = (newTheme: Theme) => {
|
|
243
|
+
setTheme(newTheme)
|
|
244
|
+
setThemeState(newTheme)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<select value={theme} onChange={e => handleThemeChange(e.target.value as Theme)}>
|
|
249
|
+
{THEMES.map(t => (
|
|
250
|
+
<option key={t} value={t}>
|
|
251
|
+
{t}
|
|
252
|
+
</option>
|
|
253
|
+
))}
|
|
254
|
+
</select>
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Framework Examples
|
|
260
|
+
|
|
261
|
+
### Vue
|
|
262
|
+
|
|
263
|
+
```vue
|
|
264
|
+
<script setup lang="ts">
|
|
265
|
+
import { ref, onMounted } from 'vue'
|
|
266
|
+
import { loadTheme, setTheme, THEMES, type Theme } from '@wolffm/themes'
|
|
267
|
+
import '@wolffm/themes/style.css'
|
|
268
|
+
|
|
269
|
+
const theme = ref<Theme>('light')
|
|
270
|
+
|
|
271
|
+
onMounted(() => {
|
|
272
|
+
theme.value = loadTheme()
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
const handleThemeChange = (newTheme: Theme) => {
|
|
276
|
+
setTheme(newTheme)
|
|
277
|
+
theme.value = newTheme
|
|
278
|
+
}
|
|
279
|
+
</script>
|
|
280
|
+
|
|
281
|
+
<template>
|
|
282
|
+
<select v-model="theme" @change="handleThemeChange(theme)">
|
|
283
|
+
<option v-for="t in THEMES" :key="t" :value="t">{{ t }}</option>
|
|
284
|
+
</select>
|
|
285
|
+
</template>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Vanilla JS
|
|
289
|
+
|
|
290
|
+
```javascript
|
|
291
|
+
import { loadTheme, setTheme, THEMES } from '@wolffm/themes'
|
|
292
|
+
import '@wolffm/themes/style.css'
|
|
293
|
+
|
|
294
|
+
// Load saved theme on page load
|
|
295
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
296
|
+
loadTheme()
|
|
297
|
+
|
|
298
|
+
// Theme switcher
|
|
299
|
+
const select = document.getElementById('theme-select')
|
|
300
|
+
THEMES.forEach(theme => {
|
|
301
|
+
const option = document.createElement('option')
|
|
302
|
+
option.value = theme
|
|
303
|
+
option.textContent = theme
|
|
304
|
+
select.appendChild(option)
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
select.addEventListener('change', e => {
|
|
308
|
+
setTheme(e.target.value)
|
|
309
|
+
})
|
|
310
|
+
})
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Theme Editor
|
|
314
|
+
|
|
315
|
+
Hosted at **https://hadoku.me/task/editor** (service tier and up). Pick a theme,
|
|
316
|
+
drag sliders, and the app's real components recolour live; **Save** commits the
|
|
317
|
+
change to `main` under a new version, which publishes.
|
|
318
|
+
|
|
319
|
+
- **Per token** — brightness / saturation / hue / opacity sliders (OKLCH, so a
|
|
320
|
+
brightness step looks even across hues) plus a typed value
|
|
321
|
+
- **Per group** — Surfaces, Text and each semantic family can be shifted at once
|
|
322
|
+
- **Contract** — the same checks `lint:css` runs (`scripts/lib/contract.mjs`,
|
|
323
|
+
exported as `@wolffm/themes/contract`), live; a failure blocks Save, and the
|
|
324
|
+
save route re-checks before committing
|
|
325
|
+
- **Comfort** — advisory meters for what AA cannot see: page glare, card-vs-page
|
|
326
|
+
separation, border visibility, text harshness, and whether `success` / `warning`
|
|
327
|
+
/ `danger` still read as their meaning
|
|
328
|
+
|
|
329
|
+
The editor ships as `@wolffm/task/editor` (src/editor/ in the repo root); its
|
|
330
|
+
save route is `worker/src/routes/theme-editor.ts`. Locally, `pnpm run dev:api`
|
|
331
|
+
plus `vite` serve it at `/editor.html` against an in-memory GitHub — a local
|
|
332
|
+
save never touches the real repo.
|
|
333
|
+
|
|
334
|
+
## API Reference
|
|
335
|
+
|
|
336
|
+
### React Utilities (New in v1.1.3+)
|
|
337
|
+
|
|
338
|
+
#### `useTheme(): { theme: Theme, setTheme: (theme: Theme) => void }`
|
|
339
|
+
|
|
340
|
+
React hook for stateful theme management. Automatically saves to sessionStorage and syncs across tabs.
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
const { theme, setTheme } = useTheme()
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### `THEME_FAMILIES: ThemeFamily[]`
|
|
347
|
+
|
|
348
|
+
Pre-configured array of all 9 theme families with icons and labels. Use with `ThemePicker` or `ConnectedThemePicker` components from `@wolffm/task-ui-components`.
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
import { THEME_FAMILIES } from '@wolffm/themes'
|
|
352
|
+
// Each family includes: lightTheme, darkTheme, lightLabel, darkLabel, lightIcon, darkIcon
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### `THEME_ICON_MAP: Record<Theme, IconComponent>`
|
|
356
|
+
|
|
357
|
+
Map of theme names to their corresponding icon components.
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
import { THEME_ICON_MAP } from '@wolffm/themes'
|
|
361
|
+
const Icon = THEME_ICON_MAP['cyberpunk-dark']
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Core Functions
|
|
365
|
+
|
|
366
|
+
#### `setTheme(theme: Theme): void`
|
|
367
|
+
|
|
368
|
+
Apply a theme immediately. Sets `data-theme` attribute on `<html>` element.
|
|
369
|
+
|
|
370
|
+
#### `getTheme(): Theme`
|
|
371
|
+
|
|
372
|
+
Get the currently active theme.
|
|
373
|
+
|
|
374
|
+
#### `saveTheme(theme: Theme): void`
|
|
375
|
+
|
|
376
|
+
Save theme to sessionStorage and apply it.
|
|
377
|
+
|
|
378
|
+
#### `loadTheme(): Theme`
|
|
379
|
+
|
|
380
|
+
Load saved theme from sessionStorage, apply it, and return the theme name. Respects browser's `prefers-color-scheme` preference if no saved theme. Returns 'light' if no preference.
|
|
381
|
+
|
|
382
|
+
#### `initTheme(): Theme`
|
|
383
|
+
|
|
384
|
+
Convenience function. Same as `loadTheme()`.
|
|
385
|
+
|
|
386
|
+
#### `clearTheme(): void`
|
|
387
|
+
|
|
388
|
+
Remove saved theme and reset to 'light'.
|
|
389
|
+
|
|
390
|
+
### Types
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
type Theme =
|
|
394
|
+
| 'light'
|
|
395
|
+
| 'dark'
|
|
396
|
+
| 'strawberry-light'
|
|
397
|
+
| 'strawberry-dark'
|
|
398
|
+
| 'ocean-light'
|
|
399
|
+
| 'ocean-dark'
|
|
400
|
+
| 'cyberpunk-light'
|
|
401
|
+
| 'cyberpunk-dark'
|
|
402
|
+
| 'coffee-light'
|
|
403
|
+
| 'coffee-dark'
|
|
404
|
+
| 'lavender-light'
|
|
405
|
+
| 'lavender-dark'
|
|
406
|
+
| 'nature-light'
|
|
407
|
+
| 'nature-dark'
|
|
408
|
+
| 'pink-light'
|
|
409
|
+
| 'pink-dark'
|
|
410
|
+
| 'izakaya-light'
|
|
411
|
+
| 'izakaya-dark'
|
|
412
|
+
|
|
413
|
+
const THEMES: readonly Theme[]
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Browser Support
|
|
417
|
+
|
|
418
|
+
Works in all modern browsers that support:
|
|
419
|
+
|
|
420
|
+
- CSS Custom Properties (CSS Variables)
|
|
421
|
+
- ES2022 modules
|
|
422
|
+
- sessionStorage
|
|
423
|
+
|
|
424
|
+
## License
|
|
425
|
+
|
|
426
|
+
MIT
|
|
427
|
+
|
|
428
|
+
## Author
|
|
429
|
+
|
|
430
|
+
WolffM - [GitHub](https://github.com/WolffM)
|
|
431
|
+
|
|
432
|
+
## Related Packages
|
|
433
|
+
|
|
434
|
+
- [@wolffm/task](https://github.com/WolffM/hadoku-task) - Task management micro-frontend using these themes
|