@lessonkit/themes 0.9.3 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -34
- package/dist/index.cjs +23 -1
- package/dist/index.js +23 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lessonkit/themes
|
|
2
2
|
|
|
3
|
-
[](https://github.com/eddiethedean/lessonkit/actions/workflows/ci.yml)
|
|
4
|
-
[](https://lessonkit.readthedocs.io/en/latest/)
|
|
5
3
|
[](https://www.npmjs.com/package/@lessonkit/themes)
|
|
4
|
+
[](https://lessonkit.readthedocs.io/en/latest/reference/theming.html)
|
|
6
5
|
[](https://github.com/eddiethedean/lessonkit/blob/main/LICENSE)
|
|
7
6
|
|
|
8
|
-
Design tokens and
|
|
9
|
-
|
|
10
|
-
**Docs:** [Theming reference](https://lessonkit.readthedocs.io/en/latest/reference/theming.html) · [Theming & accessibility guide](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/theming-and-accessibility.html)
|
|
7
|
+
Design tokens, presets, and CSS variable utilities for LessonKit.
|
|
11
8
|
|
|
12
9
|
## Install
|
|
13
10
|
|
|
@@ -15,41 +12,27 @@ Design tokens and theme utilities for LessonKit.
|
|
|
15
12
|
npm install @lessonkit/themes
|
|
16
13
|
```
|
|
17
14
|
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
### Types
|
|
15
|
+
## Usage
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- `PartialLessonkitThemeV1` — partial overrides for merge / `ThemeProvider`
|
|
17
|
+
```typescript
|
|
18
|
+
import { getPresetTheme, mergeThemes, themeToCssVariables } from "@lessonkit/themes";
|
|
25
19
|
|
|
26
|
-
|
|
20
|
+
const theme = mergeThemes(getPresetTheme("light"), { colors: { primary: "#0066cc" } });
|
|
21
|
+
const vars = themeToCssVariables(theme); // { "--lk-color-primary": "#0066cc", ... }
|
|
22
|
+
```
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
- `getPresetTheme(preset)` — `default` | `light` | `dark` | `brand` (full themes for catalog/validation)
|
|
24
|
+
**Presets:** `default`, `light`, `dark`, `brand` via `getPresetTheme()`
|
|
30
25
|
|
|
31
|
-
|
|
26
|
+
**Utilities:** `validateTheme()`, `mergeThemes()`, `themeToCssDeclarationBlock()`, `buildThemeCatalog()`
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
**Assets:** `theme-contract.v1.json`, `theme-catalog.v1.json`, `base.css`
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
- `mergeThemes(base, ...overrides)` — deep merge, last writer wins
|
|
37
|
-
- `themeToCssVariables(theme)` — flat `--lk-*` map (sorted keys)
|
|
38
|
-
- `themeToCssDeclarationBlock(theme)` — `:root { ... }` text
|
|
39
|
-
- `buildThemeCatalog()` — enumerable token metadata
|
|
30
|
+
Pair with `ThemeProvider` from `@lessonkit/react` for runtime theming.
|
|
40
31
|
|
|
41
|
-
|
|
32
|
+
## Docs
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
"imports": {
|
|
46
|
-
"@lessonkit/themes/theme-contract.v1.json": "./theme-contract.v1.json",
|
|
47
|
-
"@lessonkit/themes/theme-catalog.v1.json": "./theme-catalog.v1.json",
|
|
48
|
-
"@lessonkit/themes/base.css": "./base.css"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
```
|
|
34
|
+
[Theming reference](https://lessonkit.readthedocs.io/en/latest/reference/theming.html)
|
|
52
35
|
|
|
53
|
-
##
|
|
36
|
+
## License
|
|
54
37
|
|
|
55
|
-
|
|
38
|
+
Apache-2.0
|
package/dist/index.cjs
CHANGED
|
@@ -104,24 +104,46 @@ function validateColorsExtra(value, issues) {
|
|
|
104
104
|
}
|
|
105
105
|
return extra;
|
|
106
106
|
}
|
|
107
|
+
function rejectUnknownKeys(obj, allowed, path, issues) {
|
|
108
|
+
for (const key of Object.keys(obj)) {
|
|
109
|
+
if (!allowed.includes(key)) {
|
|
110
|
+
issues.push({ path: path ? `${path}.${key}` : key, message: "unknown property" });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
107
114
|
function validateTheme(input) {
|
|
108
115
|
const issues = [];
|
|
109
116
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
110
117
|
return { ok: false, issues: [{ path: "", message: "theme must be an object" }] };
|
|
111
118
|
}
|
|
112
119
|
const raw = input;
|
|
120
|
+
rejectUnknownKeys(raw, ["name", "colors", "spacing", "typography", "radius", "shadows"], "", issues);
|
|
113
121
|
if (!isNonEmptyString(raw.name)) {
|
|
114
122
|
issues.push({ path: "name", message: "required non-empty string" });
|
|
115
123
|
}
|
|
116
124
|
const colorsBase = validateRequiredGroup("colors", raw.colors, COLOR_KEYS, issues);
|
|
117
125
|
let colorsExtra;
|
|
118
126
|
if (raw.colors !== null && typeof raw.colors === "object" && !Array.isArray(raw.colors)) {
|
|
119
|
-
|
|
127
|
+
const colorsObj = raw.colors;
|
|
128
|
+
rejectUnknownKeys(colorsObj, [...COLOR_KEYS, "extra"], "colors", issues);
|
|
129
|
+
colorsExtra = validateColorsExtra(colorsObj.extra, issues);
|
|
120
130
|
}
|
|
121
131
|
const spacing = validateRequiredGroup("spacing", raw.spacing, SPACING_KEYS, issues);
|
|
132
|
+
if (raw.spacing !== null && typeof raw.spacing === "object" && !Array.isArray(raw.spacing)) {
|
|
133
|
+
rejectUnknownKeys(raw.spacing, SPACING_KEYS, "spacing", issues);
|
|
134
|
+
}
|
|
122
135
|
const typography = validateRequiredGroup("typography", raw.typography, TYPOGRAPHY_KEYS, issues);
|
|
136
|
+
if (raw.typography !== null && typeof raw.typography === "object" && !Array.isArray(raw.typography)) {
|
|
137
|
+
rejectUnknownKeys(raw.typography, TYPOGRAPHY_KEYS, "typography", issues);
|
|
138
|
+
}
|
|
123
139
|
const radius = validateRequiredGroup("radius", raw.radius, RADIUS_KEYS, issues);
|
|
140
|
+
if (raw.radius !== null && typeof raw.radius === "object" && !Array.isArray(raw.radius)) {
|
|
141
|
+
rejectUnknownKeys(raw.radius, RADIUS_KEYS, "radius", issues);
|
|
142
|
+
}
|
|
124
143
|
const shadows = validateRequiredGroup("shadows", raw.shadows, SHADOW_KEYS, issues);
|
|
144
|
+
if (raw.shadows !== null && typeof raw.shadows === "object" && !Array.isArray(raw.shadows)) {
|
|
145
|
+
rejectUnknownKeys(raw.shadows, SHADOW_KEYS, "shadows", issues);
|
|
146
|
+
}
|
|
125
147
|
if (issues.length > 0) {
|
|
126
148
|
return { ok: false, issues };
|
|
127
149
|
}
|
package/dist/index.js
CHANGED
|
@@ -56,24 +56,46 @@ function validateColorsExtra(value, issues) {
|
|
|
56
56
|
}
|
|
57
57
|
return extra;
|
|
58
58
|
}
|
|
59
|
+
function rejectUnknownKeys(obj, allowed, path, issues) {
|
|
60
|
+
for (const key of Object.keys(obj)) {
|
|
61
|
+
if (!allowed.includes(key)) {
|
|
62
|
+
issues.push({ path: path ? `${path}.${key}` : key, message: "unknown property" });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
59
66
|
function validateTheme(input) {
|
|
60
67
|
const issues = [];
|
|
61
68
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
62
69
|
return { ok: false, issues: [{ path: "", message: "theme must be an object" }] };
|
|
63
70
|
}
|
|
64
71
|
const raw = input;
|
|
72
|
+
rejectUnknownKeys(raw, ["name", "colors", "spacing", "typography", "radius", "shadows"], "", issues);
|
|
65
73
|
if (!isNonEmptyString(raw.name)) {
|
|
66
74
|
issues.push({ path: "name", message: "required non-empty string" });
|
|
67
75
|
}
|
|
68
76
|
const colorsBase = validateRequiredGroup("colors", raw.colors, COLOR_KEYS, issues);
|
|
69
77
|
let colorsExtra;
|
|
70
78
|
if (raw.colors !== null && typeof raw.colors === "object" && !Array.isArray(raw.colors)) {
|
|
71
|
-
|
|
79
|
+
const colorsObj = raw.colors;
|
|
80
|
+
rejectUnknownKeys(colorsObj, [...COLOR_KEYS, "extra"], "colors", issues);
|
|
81
|
+
colorsExtra = validateColorsExtra(colorsObj.extra, issues);
|
|
72
82
|
}
|
|
73
83
|
const spacing = validateRequiredGroup("spacing", raw.spacing, SPACING_KEYS, issues);
|
|
84
|
+
if (raw.spacing !== null && typeof raw.spacing === "object" && !Array.isArray(raw.spacing)) {
|
|
85
|
+
rejectUnknownKeys(raw.spacing, SPACING_KEYS, "spacing", issues);
|
|
86
|
+
}
|
|
74
87
|
const typography = validateRequiredGroup("typography", raw.typography, TYPOGRAPHY_KEYS, issues);
|
|
88
|
+
if (raw.typography !== null && typeof raw.typography === "object" && !Array.isArray(raw.typography)) {
|
|
89
|
+
rejectUnknownKeys(raw.typography, TYPOGRAPHY_KEYS, "typography", issues);
|
|
90
|
+
}
|
|
75
91
|
const radius = validateRequiredGroup("radius", raw.radius, RADIUS_KEYS, issues);
|
|
92
|
+
if (raw.radius !== null && typeof raw.radius === "object" && !Array.isArray(raw.radius)) {
|
|
93
|
+
rejectUnknownKeys(raw.radius, RADIUS_KEYS, "radius", issues);
|
|
94
|
+
}
|
|
76
95
|
const shadows = validateRequiredGroup("shadows", raw.shadows, SHADOW_KEYS, issues);
|
|
96
|
+
if (raw.shadows !== null && typeof raw.shadows === "object" && !Array.isArray(raw.shadows)) {
|
|
97
|
+
rejectUnknownKeys(raw.shadows, SHADOW_KEYS, "shadows", issues);
|
|
98
|
+
}
|
|
77
99
|
if (issues.length > 0) {
|
|
78
100
|
return { ok: false, issues };
|
|
79
101
|
}
|