@hadoku/themes 0.0.0-stage → 5.12.16

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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSE-lucide +43 -0
  3. package/README.md +433 -2
  4. package/THEME_USAGE_GUIDE.md +308 -0
  5. package/dist/HadokuThemeRoot.d.ts +53 -0
  6. package/dist/HadokuThemeRoot.js +66 -0
  7. package/dist/ThemeIcons.d.ts +31 -0
  8. package/dist/ThemeIcons.js +91 -0
  9. package/dist/effects.d.ts +25 -0
  10. package/dist/effects.js +25 -0
  11. package/dist/icons/Icon.d.ts +36 -0
  12. package/dist/icons/Icon.js +89 -0
  13. package/dist/icons/index.d.ts +61 -0
  14. package/dist/icons/index.js +87 -0
  15. package/dist/icons/registry.generated.d.ts +158 -0
  16. package/dist/icons/registry.generated.js +446 -0
  17. package/dist/index.d.ts +82 -0
  18. package/dist/index.js +169 -0
  19. package/dist/metadata.d.ts +35 -0
  20. package/dist/metadata.js +108 -0
  21. package/dist/platform/index.d.ts +94 -0
  22. package/dist/platform/index.js +149 -0
  23. package/dist/platform/usePlatform.d.ts +29 -0
  24. package/dist/platform/usePlatform.js +65 -0
  25. package/dist/prefsApiBase.d.ts +37 -0
  26. package/dist/prefsApiBase.js +40 -0
  27. package/dist/theme-types.d.ts +34 -0
  28. package/dist/theme-types.js +1 -0
  29. package/dist/themeContext.d.ts +62 -0
  30. package/dist/themeContext.js +70 -0
  31. package/dist/themePrefs.d.ts +29 -0
  32. package/dist/themePrefs.js +48 -0
  33. package/dist/useRefElement.d.ts +19 -0
  34. package/dist/useRefElement.js +52 -0
  35. package/dist/useTheme.d.ts +46 -0
  36. package/dist/useTheme.js +216 -0
  37. package/package.json +83 -4
  38. package/scripts/audit-tokens.mjs +126 -0
  39. package/scripts/check-contrast.mjs +24 -0
  40. package/scripts/check-docs.mjs +53 -0
  41. package/scripts/check-icons.mjs +473 -0
  42. package/scripts/check-usage.mjs +297 -0
  43. package/scripts/codemod-icons.mjs +396 -0
  44. package/scripts/generate-icons.mjs +199 -0
  45. package/scripts/generate-preview.mjs +374 -0
  46. package/scripts/generate-tailwind-colors.mjs +109 -0
  47. package/scripts/lib/color.mjs +184 -0
  48. package/scripts/lib/contract.d.mts +58 -0
  49. package/scripts/lib/contract.mjs +348 -0
  50. package/scripts/lib/derive.mjs +324 -0
  51. package/scripts/lib/icon-registry.mjs +92 -0
  52. package/scripts/lib/parse-themes.mjs +19 -0
  53. package/scripts/lib/tokens.mjs +188 -0
  54. package/scripts/normalize-tokens.mjs +161 -0
  55. package/src/advanced.css +218 -0
  56. package/src/icons/Icon.tsx +151 -0
  57. package/src/icons/emoji-map.json +202 -0
  58. package/src/icons/index.ts +151 -0
  59. package/src/icons/registry.generated.ts +455 -0
  60. package/src/icons/sources.json +150 -0
  61. package/src/icons.css +145 -0
  62. package/src/style.css +1421 -0
  63. package/src/tailwind-colors.css +88 -0
  64. package/src/tailwind-integration.css +49 -0
package/package.json CHANGED
@@ -1,6 +1,85 @@
1
1
  {
2
2
  "name": "@hadoku/themes",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "5.12.16",
4
+ "description": "18 beautiful themes with a symmetric, WCAG-verified token set and a complete Tailwind v4 color mapping - Light, Dark, Strawberry, Ocean, Cyberpunk, Coffee, Lavender, Nature, Pink, Izakaya",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/WolffM/hadoku-task.git",
9
+ "directory": "themes"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "src/style.css",
14
+ "src/advanced.css",
15
+ "src/tailwind-integration.css",
16
+ "src/tailwind-colors.css",
17
+ "src/icons.css",
18
+ "src/icons",
19
+ "scripts",
20
+ "README.md",
21
+ "THEME_USAGE_GUIDE.md",
22
+ "LICENSE-lucide"
23
+ ],
24
+ "exports": {
25
+ "./style.css": "./src/style.css",
26
+ "./advanced.css": "./src/advanced.css",
27
+ "./tailwind-integration.css": "./src/tailwind-integration.css",
28
+ "./tailwind-colors.css": "./src/tailwind-colors.css",
29
+ "./icons.css": "./src/icons.css",
30
+ "./THEME_USAGE_GUIDE.md": "./THEME_USAGE_GUIDE.md",
31
+ "./check-usage": "./scripts/check-usage.mjs",
32
+ "./contract": {
33
+ "types": "./scripts/lib/contract.d.mts",
34
+ "default": "./scripts/lib/contract.mjs"
35
+ },
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "default": "./dist/index.js"
39
+ },
40
+ "./icons": {
41
+ "types": "./dist/icons/index.d.ts",
42
+ "default": "./dist/icons/index.js"
43
+ },
44
+ "./icons/react": {
45
+ "types": "./dist/icons/Icon.d.ts",
46
+ "default": "./dist/icons/Icon.js"
47
+ },
48
+ "./platform": {
49
+ "types": "./dist/platform/index.d.ts",
50
+ "default": "./dist/platform/index.js"
51
+ },
52
+ "./platform/react": {
53
+ "types": "./dist/platform/usePlatform.d.ts",
54
+ "default": "./dist/platform/usePlatform.js"
55
+ },
56
+ "./check-icons": "./scripts/check-icons.mjs"
57
+ },
58
+ "keywords": [
59
+ "themes",
60
+ "css-variables",
61
+ "design-tokens",
62
+ "dark-mode",
63
+ "color-schemes",
64
+ "tailwind-v4",
65
+ "hadoku"
66
+ ],
67
+ "author": "WolffM",
68
+ "license": "MIT",
69
+ "dependencies": {
70
+ "@wolffm/prefs-client": "npm:@hadoku/prefs-client@^1.1.1",
71
+ "zod": "^4.0.0"
72
+ },
73
+ "peerDependencies": {
74
+ "react": "^18.0.0 || ^19.0.0"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "react": {
78
+ "optional": true
79
+ }
80
+ },
81
+ "bin": {
82
+ "hadoku-check-icons": "./scripts/check-icons.mjs",
83
+ "hadoku-codemod-icons": "./scripts/codemod-icons.mjs"
84
+ }
85
+ }
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Reports the current state of the token matrix: which tokens each theme
4
+ * defines, where families are asymmetric, and which WCAG pairs pass.
5
+ *
6
+ * Read-only. Run before and after a normalization to see exactly what moved.
7
+ */
8
+
9
+ import { parseThemes, allTokenNames, THEMES, FAMILIES, isDarkTheme } from './lib/parse-themes.mjs'
10
+ import { contrast, ratio } from './lib/color.mjs'
11
+
12
+ const themes = parseThemes()
13
+ const names = allTokenNames(themes)
14
+
15
+ /* ---- 1. Presence matrix: which themes define which token ---- */
16
+ const missing = []
17
+ for (const name of names) {
18
+ const absent = THEMES.filter(t => !themes[t][name])
19
+ if (absent.length) missing.push({ name, absent })
20
+ }
21
+
22
+ console.log(`# Token audit\n`)
23
+ console.log(`Themes: ${THEMES.length} Distinct --color-* tokens: ${names.length}\n`)
24
+
25
+ console.log(`## Per-theme drift (token defined in some themes but not others)`)
26
+ if (!missing.length) {
27
+ console.log('None — every token is defined in all 18 themes.\n')
28
+ } else {
29
+ for (const { name, absent } of missing) {
30
+ console.log(`- ${name} — missing in ${absent.length}: ${absent.join(', ')}`)
31
+ }
32
+ console.log('')
33
+ }
34
+
35
+ /* ---- 2. Family symmetry ---- */
36
+ const suffixes = new Set()
37
+ for (const name of names) {
38
+ for (const f of FAMILIES) {
39
+ if (name === `--color-${f}`) suffixes.add('')
40
+ else if (name.startsWith(`--color-${f}-`)) suffixes.add(name.slice(`--color-${f}`.length))
41
+ }
42
+ }
43
+ const sortedSuffixes = [...suffixes].sort()
44
+
45
+ console.log(`## Family symmetry (✓ = defined in all 18 themes)`)
46
+ console.log(`| family | ${sortedSuffixes.map(s => `\`${s || 'base'}\``).join(' | ')} | on-<f> |`)
47
+ console.log(`|---|${sortedSuffixes.map(() => ':-:').join('|')}|:-:|`)
48
+ for (const f of FAMILIES) {
49
+ const cells = sortedSuffixes.map(s => {
50
+ const name = `--color-${f}${s}`
51
+ const count = THEMES.filter(t => themes[t][name]).length
52
+ return count === THEMES.length ? '✓' : count === 0 ? '✗' : `${count}/18`
53
+ })
54
+ const onCount = THEMES.filter(t => themes[t][`--color-on-${f}`]).length
55
+ console.log(
56
+ `| ${f} | ${cells.join(' | ')} | ${onCount === THEMES.length ? '✓' : `${onCount}/18`} |`
57
+ )
58
+ }
59
+ console.log('')
60
+
61
+ /* ---- 3. WCAG pairs ---- */
62
+ /* Three contracts from docs/THEME_SYSTEM_RULES.md:
63
+ §1 on-<f> vs <f> — text on a filled button
64
+ §1 on-<f> vs <f>-dark — same button in its hover/active state
65
+ §2 on-<f>-bg vs <f>-bg — badge text on its tint (the pair §2 flags P1) */
66
+ const failures = { filled: [], hover: [], badge: [] }
67
+
68
+ for (const theme of THEMES) {
69
+ const tk = themes[theme]
70
+ // Translucent -bg tints composite over the card surface they sit on.
71
+ const surface =
72
+ tk['--color-bg-card'] || tk['--color-bg'] || (isDarkTheme(theme) ? '#000' : '#fff')
73
+
74
+ for (const f of FAMILIES) {
75
+ const base = tk[`--color-${f}`]
76
+ const on = tk[`--color-on-${f}`]
77
+ const dark = tk[`--color-${f}-dark`]
78
+ const tint = tk[`--color-${f}-bg`]
79
+ const onTint = tk[`--color-on-${f}-bg`]
80
+
81
+ const check = (bucket, fg, bg) => {
82
+ if (!fg || !bg) return
83
+ const r = contrast(fg, bg, surface)
84
+ if (r < 4.5) failures[bucket].push({ theme, f, fg, bg, r: ratio(r) })
85
+ }
86
+
87
+ check('filled', on, base)
88
+ check('hover', on, dark)
89
+ check('badge', onTint, tint)
90
+ }
91
+ }
92
+
93
+ const report = (title, rows, contract) => {
94
+ console.log(`## ${title}`)
95
+ console.log(`_${contract}_\n`)
96
+ if (!rows.length) {
97
+ console.log('All pairs pass.\n')
98
+ return
99
+ }
100
+ console.log(`**${rows.length} failing pairs**\n`)
101
+ console.log('| theme | family | fg | bg | ratio |')
102
+ console.log('|---|---|---|---|--:|')
103
+ for (const r of rows)
104
+ console.log(`| ${r.theme} | ${r.f} | \`${r.fg}\` | \`${r.bg}\` | ${r.r}:1 |`)
105
+ console.log('')
106
+ }
107
+
108
+ report(
109
+ 'Filled-button contrast — `on-<f>` on `<f>`',
110
+ failures.filled,
111
+ 'THEME_SYSTEM_RULES §1: must be ≥ 4.5:1'
112
+ )
113
+ report(
114
+ 'Button hover contrast — `on-<f>` on `<f>-dark`',
115
+ failures.hover,
116
+ 'THEME_SYSTEM_RULES §1 + §6: the hover state carries the same text, so it must clear 4.5:1 too'
117
+ )
118
+ report(
119
+ 'Badge contrast — `on-<f>-bg` on `<f>-bg`',
120
+ failures.badge,
121
+ 'THEME_SYSTEM_RULES §2: must be ≥ 4.5:1 (closes the P1 unvalidated-pair item)'
122
+ )
123
+
124
+ const total = failures.filled.length + failures.hover.length + failures.badge.length
125
+ console.log(`---\n**Total WCAG failures: ${total}**`)
126
+ process.exitCode = 0 // audit is informational; check-contrast.mjs is the gate
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Gate: the token set must be structurally symmetric and every WCAG pair in
4
+ * docs/THEME_SYSTEM_RULES.md must hold, in all 18 themes.
5
+ *
6
+ * audit-tokens.mjs reports; this one fails the build. The rules themselves live
7
+ * in lib/contract.mjs, which the hosted theme editor and the worker's save route
8
+ * run too — so an editor save cannot pass a gate this script would fail.
9
+ */
10
+
11
+ import { parseThemes } from './lib/parse-themes.mjs'
12
+ import { checkThemes, THEMES, WCAG_PAIR_COUNT } from './lib/contract.mjs'
13
+
14
+ const problems = checkThemes(parseThemes())
15
+
16
+ if (problems.length) {
17
+ console.error(`check-contrast: FAILED — ${problems.length} problem(s)\n`)
18
+ for (const p of problems) console.error(` ${p}`)
19
+ process.exit(1)
20
+ }
21
+
22
+ console.log(
23
+ `check-contrast: OK — ${THEMES.length} themes, symmetric token set, ${WCAG_PAIR_COUNT} WCAG pairs pass, surfaces separated.`
24
+ )
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Gate: THEME_USAGE_GUIDE.md must list exactly the tokens that exist.
4
+ *
5
+ * A usage guide that drifts from the stylesheet is worse than none — it is a
6
+ * confident, wrong answer, and it is what agents read first. This fails the
7
+ * build in both directions: a token missing from the guide, or a token named
8
+ * in the guide that no longer exists.
9
+ */
10
+
11
+ import { readFileSync } from 'node:fs'
12
+ import { resolve, dirname } from 'node:path'
13
+ import { fileURLToPath } from 'node:url'
14
+ import { parseThemes, allTokenNames, THEMES } from './lib/parse-themes.mjs'
15
+
16
+ const here = dirname(fileURLToPath(import.meta.url))
17
+ const GUIDE = resolve(here, '../THEME_USAGE_GUIDE.md')
18
+
19
+ const themes = parseThemes()
20
+ const actual = new Set(allTokenNames(themes).filter(n => THEMES.every(t => themes[t][n])))
21
+
22
+ const text = readFileSync(GUIDE, 'utf8')
23
+ // Only count tokens inside the "Complete token list" section's tables — the
24
+ // anti-pattern table deliberately names removed tokens.
25
+ const listStart = text.indexOf('## Complete token list')
26
+ const listEnd = text.indexOf('## Anti-patterns')
27
+ if (listStart < 0 || listEnd < 0) {
28
+ console.error(
29
+ 'check-docs: FAILED — could not locate the token-list section in THEME_USAGE_GUIDE.md'
30
+ )
31
+ process.exit(1)
32
+ }
33
+ const documented = new Set(
34
+ [...text.slice(listStart, listEnd).matchAll(/`(--color-[a-z0-9-]+)`/g)].map(m => m[1])
35
+ )
36
+
37
+ const undocumented = [...actual].filter(t => !documented.has(t)).sort()
38
+ const stale = [...documented].filter(t => !actual.has(t)).sort()
39
+
40
+ if (undocumented.length || stale.length) {
41
+ console.error('check-docs: FAILED — THEME_USAGE_GUIDE.md is out of sync with style.css\n')
42
+ if (undocumented.length) {
43
+ console.error(` Tokens missing from the guide (${undocumented.length}):`)
44
+ for (const t of undocumented) console.error(` ${t}`)
45
+ }
46
+ if (stale.length) {
47
+ console.error(` Tokens in the guide that no longer exist (${stale.length}):`)
48
+ for (const t of stale) console.error(` ${t}`)
49
+ }
50
+ process.exit(1)
51
+ }
52
+
53
+ console.log(`check-docs: OK — THEME_USAGE_GUIDE.md documents all ${actual.size} tokens.`)