@kondeio/kdf 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/CHANGELOG.md +33 -0
- package/CONTRIBUTING.md +57 -0
- package/LICENSE +21 -0
- package/README.md +374 -0
- package/SECURITY.md +53 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +58 -0
- package/dist/css-generator.d.ts +17 -0
- package/dist/css-generator.js +59 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +62 -0
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +40 -0
- package/dist/postinstall.d.ts +1 -0
- package/dist/postinstall.js +48 -0
- package/dist/resolver.d.ts +11 -0
- package/dist/resolver.js +184 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.js +1 -0
- package/docs/kdf-doc.md +653 -0
- package/docs/kdf-skill.md +374 -0
- package/example/bootstrap/globals.css +12 -0
- package/example/bootstrap/hero-section.tsx +38 -0
- package/example/bootstrap/kdf/homepage.json +35 -0
- package/example/bootstrap/kdf/shared/button.json +10 -0
- package/example/bootstrap/kdf/shared/card.json +4 -0
- package/example/bootstrap/kdf/shared/color.json +17 -0
- package/example/bootstrap/kdf/shared/layout.json +7 -0
- package/example/bootstrap/kdf/shared/typography.json +8 -0
- package/example/bootstrap/konde-server.css +3 -0
- package/example/bootstrap/konde.css +3 -0
- package/example/preview.ts +428 -0
- package/example/pure-css/hero-section.tsx +40 -0
- package/example/pure-css/kdf/homepage.json +20 -0
- package/example/pure-css/kdf/shared/button.json +10 -0
- package/example/pure-css/kdf/shared/card.json +4 -0
- package/example/pure-css/kdf/shared/color.json +11 -0
- package/example/pure-css/kdf/shared/typography.json +8 -0
- package/example/pure-css/konde-server.css +3 -0
- package/example/pure-css/konde.css +3 -0
- package/example/pure-css/styles.css +34 -0
- package/example/sample-pages/about.json +30 -0
- package/example/sample-pages/dashboard.json +12 -0
- package/example/sample-pages/pricing.json +28 -0
- package/example/shadcn/globals.css +15 -0
- package/example/shadcn/hero-section.tsx +34 -0
- package/example/shadcn/konde-server.css +3 -0
- package/example/shadcn/konde.css +3 -0
- package/example/tailwind/globals.css +5 -0
- package/example/tailwind/hero-section.tsx +34 -0
- package/example/tailwind/konde-server.css +3 -0
- package/example/tailwind/konde.css +3 -0
- package/kdf/homepage.json +25 -0
- package/kdf/shared/button.json +10 -0
- package/kdf/shared/card.json +4 -0
- package/kdf/shared/color.json +17 -0
- package/kdf/shared/layout.json +7 -0
- package/kdf/shared/typography.json +8 -0
- package/package.json +77 -0
package/docs/kdf-doc.md
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
# Konde Design Framework
|
|
2
|
+
|
|
3
|
+
Package: `@kondeio/kdf`
|
|
4
|
+
License: MIT
|
|
5
|
+
|
|
6
|
+
Konde Design Framework, or KDF, is a JSON-based design coordination layer for
|
|
7
|
+
Node/server-side web apps and agent-assisted UI work.
|
|
8
|
+
|
|
9
|
+
KDF does for design consistency what i18n does for translation consistency:
|
|
10
|
+
move repeatable UI decisions out of scattered component files and into a stable
|
|
11
|
+
source of truth that both users and agents can read.
|
|
12
|
+
|
|
13
|
+
## Goal
|
|
14
|
+
|
|
15
|
+
Design control and consistency through JSON as the shared language between users
|
|
16
|
+
and agents.
|
|
17
|
+
|
|
18
|
+
- Users own design direction and approval.
|
|
19
|
+
- Users can edit JSON directly when they want precise control.
|
|
20
|
+
- Agents read KDF JSON and implement UI from it.
|
|
21
|
+
- Code renders the approved design.
|
|
22
|
+
- `data-kdf` maps DOM elements back to exact JSON paths.
|
|
23
|
+
- One source of truth keeps every page, component, and agent session consistent.
|
|
24
|
+
|
|
25
|
+
KDF should reduce chat iteration, not create a new design bureaucracy.
|
|
26
|
+
|
|
27
|
+
## The Problem
|
|
28
|
+
|
|
29
|
+
Without KDF, classes live across many `.tsx` files:
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<section className="mx-auto max-w-6xl px-6 py-20">
|
|
33
|
+
<h1 className="text-5xl font-semibold tracking-tight">
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
That works for a single human editor, but it is fragile for agentic work:
|
|
37
|
+
|
|
38
|
+
- agents improvise colors, spacing, typography, and layout
|
|
39
|
+
- users correct the result through chat
|
|
40
|
+
- every page drifts slightly
|
|
41
|
+
- changes require searching component files
|
|
42
|
+
- repeated sessions lose design intent
|
|
43
|
+
|
|
44
|
+
The result is endless "bigger", "more blue", "move left" iteration.
|
|
45
|
+
|
|
46
|
+
## The Solution
|
|
47
|
+
|
|
48
|
+
KDF makes design explicit:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"hero": {
|
|
53
|
+
"wrapper": "mx-auto max-w-6xl px-6 py-20",
|
|
54
|
+
"title": "text-5xl font-semibold tracking-tight"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
const d = getDesign("homepage");
|
|
61
|
+
|
|
62
|
+
<section data-kdf="hero.wrapper" className={d("hero.wrapper")}>
|
|
63
|
+
<h1 data-kdf="hero.title" className={d("hero.title")}>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The rendered UI still uses normal CSS. The difference is ownership:
|
|
67
|
+
|
|
68
|
+
- JSON defines the design.
|
|
69
|
+
- Code renders the design.
|
|
70
|
+
- Agents implement from JSON instead of guessing.
|
|
71
|
+
- Users adjust JSON instead of describing visual corrections repeatedly.
|
|
72
|
+
|
|
73
|
+
## Why Not Just a Design Library?
|
|
74
|
+
|
|
75
|
+
A design library says:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
This is a Button.
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
KDF says:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
This is homepage.hero.cta-primary, rendered as a Button, styled with this token.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
| Approach | What it says | Mapping |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| Design library | "This is a Button" | Generic component, many possible overrides |
|
|
90
|
+
| KDF | "This is `hero.cta-primary` on `homepage`" | One element maps to one design key |
|
|
91
|
+
|
|
92
|
+
Design libraries provide component primitives. They do not always enforce which
|
|
93
|
+
style belongs to which element on which page. KDF adds that missing mapping.
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
Design library:
|
|
97
|
+
Button component -> generic styles
|
|
98
|
+
-> used in many places
|
|
99
|
+
-> every developer or agent can override differently
|
|
100
|
+
-> no exact map from element to design decision
|
|
101
|
+
|
|
102
|
+
KDF:
|
|
103
|
+
data-kdf="hero.cta-primary" -> d("hero.cta-primary") -> one JSON key
|
|
104
|
+
-> one element, one design token
|
|
105
|
+
-> scanner and agent can find the source
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
KDF is not a replacement for a component library. It can sit above shadcn,
|
|
109
|
+
Bootstrap, Chakra, plain CSS, Tailwind, or a custom design system.
|
|
110
|
+
|
|
111
|
+
## What KDF Does
|
|
112
|
+
|
|
113
|
+
KDF translates repeated design decisions from component files into JSON.
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
i18n:
|
|
117
|
+
hardcoded text in JSX -> language keys in JSON -> t("hero.headline")
|
|
118
|
+
|
|
119
|
+
KDF:
|
|
120
|
+
hardcoded classes in JSX -> design tokens in JSON -> d("hero.title")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
KDF stores:
|
|
124
|
+
|
|
125
|
+
- class names
|
|
126
|
+
- shared style references
|
|
127
|
+
- page section order
|
|
128
|
+
- CSS custom properties
|
|
129
|
+
- optional component identity metadata
|
|
130
|
+
|
|
131
|
+
KDF does not store:
|
|
132
|
+
|
|
133
|
+
- business logic
|
|
134
|
+
- event handlers
|
|
135
|
+
- data fetching
|
|
136
|
+
- permissions
|
|
137
|
+
- accessibility behavior
|
|
138
|
+
- component implementation
|
|
139
|
+
|
|
140
|
+
## Architecture
|
|
141
|
+
|
|
142
|
+
Design tokens live in two places:
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
kdf/
|
|
146
|
+
shared/
|
|
147
|
+
button.json
|
|
148
|
+
card.json
|
|
149
|
+
color.json
|
|
150
|
+
layout.json
|
|
151
|
+
typography.json
|
|
152
|
+
homepage.json
|
|
153
|
+
konde-server.css
|
|
154
|
+
konde.css
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
| File | Role |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| `kdf/shared/*.json` | reusable component and style defaults |
|
|
160
|
+
| `kdf/<page>.json` | page-specific composition and overrides |
|
|
161
|
+
| `kdf/konde-server.css` | critical first-paint CSS overrides |
|
|
162
|
+
| `kdf/konde.css` | non-critical user CSS overrides |
|
|
163
|
+
|
|
164
|
+
Symbols:
|
|
165
|
+
|
|
166
|
+
| Symbol | Meaning | Example |
|
|
167
|
+
| --- | --- | --- |
|
|
168
|
+
| `$` | component identity metadata | `"$": "Button"` |
|
|
169
|
+
| `className` | rendered class string | `"@button.cta shadow-xl"` |
|
|
170
|
+
| `@` | shared style reference | `"@button.cta"` |
|
|
171
|
+
| `$layout` | page section order and visibility | `["hero", "footer"]` |
|
|
172
|
+
| `css` | CSS custom properties | `{ "--kdf-accent": "#4F46E5" }` |
|
|
173
|
+
|
|
174
|
+
`$` is metadata for agents and host tooling. The runtime accessor `d(path)`
|
|
175
|
+
returns the resolved `className`, and `d.css(path)` returns the `css` object.
|
|
176
|
+
|
|
177
|
+
## JSON Token Forms
|
|
178
|
+
|
|
179
|
+
### Class string
|
|
180
|
+
|
|
181
|
+
Use a string when a token only needs classes:
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"hero": {
|
|
186
|
+
"wrapper": "flex flex-col gap-8 lg:flex-row"
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
<section data-kdf="hero.wrapper" className={d("hero.wrapper")} />
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Object token
|
|
196
|
+
|
|
197
|
+
Use an object when the token needs metadata, classes, or CSS custom properties:
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"hero": {
|
|
202
|
+
"title": {
|
|
203
|
+
"$": "h1",
|
|
204
|
+
"className": "@typography.h1"
|
|
205
|
+
},
|
|
206
|
+
"cta-primary": {
|
|
207
|
+
"$": "Button",
|
|
208
|
+
"className": "@button.cta"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### CSS custom properties
|
|
215
|
+
|
|
216
|
+
Use `css` when values should be applied as inline style variables:
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"hero": {
|
|
221
|
+
"title": {
|
|
222
|
+
"className": "text-3xl",
|
|
223
|
+
"css": {
|
|
224
|
+
"--kdf-accent": "#4F46E5"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
<h1
|
|
233
|
+
data-kdf="hero.title"
|
|
234
|
+
className={d("hero.title")}
|
|
235
|
+
style={d.css("hero.title")}
|
|
236
|
+
/>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Shared References
|
|
240
|
+
|
|
241
|
+
Shared references are written with `@`:
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"hero": {
|
|
246
|
+
"cta-primary": "@button.cta",
|
|
247
|
+
"cta-large": "@button.cta text-lg shadow-xl"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
`@button.cta` resolves from:
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
kdf/shared/button.json -> cta
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
References can chain through other references. Resolution stops after a small
|
|
259
|
+
depth limit to avoid loops.
|
|
260
|
+
|
|
261
|
+
The component segment is validated. A ref like `@../../secret.value` is rejected
|
|
262
|
+
instead of escaping the `shared/` directory.
|
|
263
|
+
|
|
264
|
+
## Multi-Level Shared Tokens
|
|
265
|
+
|
|
266
|
+
KDF supports a shared-token cascade for apps with multiple design directories.
|
|
267
|
+
|
|
268
|
+
```text
|
|
269
|
+
designs/
|
|
270
|
+
shared/
|
|
271
|
+
button.json <- fallback for all templates
|
|
272
|
+
typography.json
|
|
273
|
+
lander/
|
|
274
|
+
shared/
|
|
275
|
+
button.json <- lander-only override
|
|
276
|
+
homepage.json
|
|
277
|
+
newlander/
|
|
278
|
+
homepage.json <- uses root shared fallback
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Resolution order for `@button.cta`:
|
|
282
|
+
|
|
283
|
+
1. `<KDF_DIR>/shared/button.json`
|
|
284
|
+
2. `<KDF_DIR>/../shared/button.json`
|
|
285
|
+
3. current page tokens
|
|
286
|
+
|
|
287
|
+
This means a template can override only the parts it needs and inherit the rest.
|
|
288
|
+
|
|
289
|
+
## Multi-Template Design
|
|
290
|
+
|
|
291
|
+
Like i18n can switch languages, KDF can switch design templates.
|
|
292
|
+
|
|
293
|
+
```text
|
|
294
|
+
languages/en/ -> designs/lander/
|
|
295
|
+
languages/id/ -> designs/newlander/
|
|
296
|
+
activate language activate design template
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```text
|
|
300
|
+
designs/
|
|
301
|
+
lander/
|
|
302
|
+
shared/
|
|
303
|
+
homepage.json
|
|
304
|
+
newlander/
|
|
305
|
+
shared/
|
|
306
|
+
homepage.json
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Same app, same components, same code. Point `KDF_DIR` to another design folder:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
export default withKDF({ dir: "./designs/lander" })(nextConfig);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Switching templates should be a deliberate host-app decision. KDF provides the
|
|
316
|
+
folder model and resolver behavior.
|
|
317
|
+
|
|
318
|
+
## Page Layout
|
|
319
|
+
|
|
320
|
+
`$layout` controls section order and visibility:
|
|
321
|
+
|
|
322
|
+
```json
|
|
323
|
+
{
|
|
324
|
+
"$layout": ["hero", "features", "footer"],
|
|
325
|
+
"hero": {},
|
|
326
|
+
"features": {},
|
|
327
|
+
"footer": {}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Rules:
|
|
332
|
+
|
|
333
|
+
- sections listed in `$layout` render
|
|
334
|
+
- sections missing from `$layout` are hidden by the host app
|
|
335
|
+
- section order follows array order
|
|
336
|
+
- section token data can remain even when a section is hidden
|
|
337
|
+
|
|
338
|
+
The application still decides how to render sections. KDF provides the design
|
|
339
|
+
and order data.
|
|
340
|
+
|
|
341
|
+
## Why Nested Structure Matters
|
|
342
|
+
|
|
343
|
+
JSON nesting is functional, not just tidy.
|
|
344
|
+
|
|
345
|
+
1. `$layout` uses top-level section keys for page order and visibility.
|
|
346
|
+
2. Dot paths scope repeated names like `hero.title` and `footer.title`.
|
|
347
|
+
3. `data-kdf` maps DOM nodes directly to JSON paths.
|
|
348
|
+
4. Agents and scanners can locate the exact design source for an element.
|
|
349
|
+
5. One section can be replaced without rewriting unrelated tokens.
|
|
350
|
+
|
|
351
|
+
Nested JSON turns page structure into data.
|
|
352
|
+
|
|
353
|
+
## `data-kdf`
|
|
354
|
+
|
|
355
|
+
Every element using a KDF token must include the matching `data-kdf` path:
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
<h1 data-kdf="hero.title" className={d("hero.title")}>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
This enables:
|
|
362
|
+
|
|
363
|
+
- DOM to JSON path mapping
|
|
364
|
+
- Playwright assertions
|
|
365
|
+
- scanner validation
|
|
366
|
+
- agent review
|
|
367
|
+
- visual tooling that targets the exact design token
|
|
368
|
+
|
|
369
|
+
If an element uses `d("hero.title")` but lacks
|
|
370
|
+
`data-kdf="hero.title"`, treat it as invalid KDF usage.
|
|
371
|
+
|
|
372
|
+
## Konde CSS Override Files
|
|
373
|
+
|
|
374
|
+
KDF creates two CSS files during initialization. Both are user-owned. KDF never
|
|
375
|
+
overwrites them after creation.
|
|
376
|
+
|
|
377
|
+
| File | Intended loading | Purpose |
|
|
378
|
+
| --- | --- | --- |
|
|
379
|
+
| `konde-server.css` | imported by the app for first paint | critical CSS variables and no-FOUC overrides |
|
|
380
|
+
| `konde.css` | loaded after framework/app CSS | non-critical tweaks, experiments, escape hatches |
|
|
381
|
+
|
|
382
|
+
Examples:
|
|
383
|
+
|
|
384
|
+
```css
|
|
385
|
+
/* konde-server.css */
|
|
386
|
+
:root { --kdf-primary: #4F46E5; }
|
|
387
|
+
[data-kdf="hero.slider"] { display: none; }
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```css
|
|
391
|
+
/* konde.css */
|
|
392
|
+
[data-kdf="hero.title"] { letter-spacing: -0.02em; }
|
|
393
|
+
[data-kdf="hero.wrapper"] { gap: 3rem; }
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The Next.js plugin exposes paths through environment values. It does not inject
|
|
397
|
+
CSS automatically. The host app wires imports or links explicitly.
|
|
398
|
+
|
|
399
|
+
## Color Definitions
|
|
400
|
+
|
|
401
|
+
KDF JSON stores class names, not CSS engine internals. Color tokens can point to
|
|
402
|
+
whatever the host styling system understands.
|
|
403
|
+
|
|
404
|
+
Tailwind:
|
|
405
|
+
|
|
406
|
+
```json
|
|
407
|
+
{
|
|
408
|
+
"primary-text": "text-[#4F46E5]",
|
|
409
|
+
"brand-bg": "bg-[var(--kdf-primary)]"
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Bootstrap:
|
|
414
|
+
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"primary-text": "text-primary",
|
|
418
|
+
"cta": "btn btn-primary"
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Plain CSS:
|
|
423
|
+
|
|
424
|
+
```json
|
|
425
|
+
{
|
|
426
|
+
"primary-text": "kdf-text-primary"
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
CSS variables belong in app CSS:
|
|
431
|
+
|
|
432
|
+
```css
|
|
433
|
+
:root { --kdf-primary: #4F46E5; }
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Then JSON can reference them through framework-compatible classes:
|
|
437
|
+
|
|
438
|
+
```json
|
|
439
|
+
{
|
|
440
|
+
"title": "text-[var(--kdf-primary)]"
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## CSS Framework Scanning
|
|
445
|
+
|
|
446
|
+
If a styling framework generates CSS by scanning source files, make it scan KDF
|
|
447
|
+
JSON files too. Otherwise classes stored in JSON may not be generated.
|
|
448
|
+
|
|
449
|
+
Tailwind v4:
|
|
450
|
+
|
|
451
|
+
```css
|
|
452
|
+
@import "tailwindcss";
|
|
453
|
+
@source "../kdf/**/*.json";
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Tailwind v3:
|
|
457
|
+
|
|
458
|
+
```ts
|
|
459
|
+
export default {
|
|
460
|
+
content: [
|
|
461
|
+
"./src/**/*.{ts,tsx}",
|
|
462
|
+
"./kdf/**/*.json"
|
|
463
|
+
]
|
|
464
|
+
};
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Runtime API
|
|
468
|
+
|
|
469
|
+
KDF core runs in Node/server-side JavaScript because it reads JSON from disk.
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
import { getDesign, cn, clearKdfCache } from "@kondeio/kdf";
|
|
473
|
+
|
|
474
|
+
const d = getDesign("homepage");
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Accessor behavior:
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
d("hero.title"); // resolved className string
|
|
481
|
+
d.css("hero.title"); // CSS custom properties object
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Server-only rule:
|
|
485
|
+
|
|
486
|
+
- use `getDesign()` in server-rendered code
|
|
487
|
+
- do not call it directly inside browser-only Client Components
|
|
488
|
+
- resolve classes server-side and pass strings down when needed
|
|
489
|
+
|
|
490
|
+
Cache options:
|
|
491
|
+
|
|
492
|
+
```ts
|
|
493
|
+
const d = getDesign("homepage", {
|
|
494
|
+
cache: "auto",
|
|
495
|
+
maxAgeMs: 250
|
|
496
|
+
});
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
Cache modes:
|
|
500
|
+
|
|
501
|
+
```text
|
|
502
|
+
auto production caches forever; development revalidates by mtime/size
|
|
503
|
+
always cache for the lifetime of the process
|
|
504
|
+
none bypass cache
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Use `clearKdfCache()` for explicit invalidation in custom tooling.
|
|
508
|
+
|
|
509
|
+
## Class Composition
|
|
510
|
+
|
|
511
|
+
KDF includes small class composition helpers:
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
import { cn, cx, composeClasses, dedupeClasses, createClassComposer } from "@kondeio/kdf";
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Use `cn()` when combining KDF tokens with conditional classes:
|
|
518
|
+
|
|
519
|
+
```tsx
|
|
520
|
+
className={cn(d("button.base"), active && d("button.active"), className)}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Helpers:
|
|
524
|
+
|
|
525
|
+
- `cn()` joins conditional class values, drops falsy values, normalizes
|
|
526
|
+
whitespace, and removes exact duplicate classes.
|
|
527
|
+
- `cx()` is an alias of `cn()`.
|
|
528
|
+
- `composeClasses()` is the same default composer with an explicit name.
|
|
529
|
+
- `dedupeClasses()` removes exact duplicates from an existing class string.
|
|
530
|
+
- `createClassComposer({ merge })` creates a composer with an app-defined merge
|
|
531
|
+
step.
|
|
532
|
+
|
|
533
|
+
KDF intentionally does not understand semantic utility conflicts. Apps that
|
|
534
|
+
need Tailwind-specific or project-specific merging can inject their own rules.
|
|
535
|
+
|
|
536
|
+
## UI Library Compatibility
|
|
537
|
+
|
|
538
|
+
KDF is UI-library agnostic because it stores design tokens and metadata, not
|
|
539
|
+
component implementations.
|
|
540
|
+
|
|
541
|
+
shadcn-style metadata:
|
|
542
|
+
|
|
543
|
+
```json
|
|
544
|
+
{
|
|
545
|
+
"cta": {
|
|
546
|
+
"$": "Button",
|
|
547
|
+
"variant": "default",
|
|
548
|
+
"className": "@button.cta"
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Bootstrap:
|
|
554
|
+
|
|
555
|
+
```json
|
|
556
|
+
{
|
|
557
|
+
"cta": {
|
|
558
|
+
"$": "button",
|
|
559
|
+
"className": "btn btn-primary btn-lg"
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
Plain CSS:
|
|
565
|
+
|
|
566
|
+
```json
|
|
567
|
+
{
|
|
568
|
+
"cta": {
|
|
569
|
+
"$": "button",
|
|
570
|
+
"className": "my-btn my-btn--primary"
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
KDF gives the host app stable design data. The host app still owns actual
|
|
576
|
+
component rendering.
|
|
577
|
+
|
|
578
|
+
## Full Page Layout Example
|
|
579
|
+
|
|
580
|
+
KDF can describe a complete page structure:
|
|
581
|
+
|
|
582
|
+
```json
|
|
583
|
+
{
|
|
584
|
+
"$layout": ["hero", "section-a", "footer"],
|
|
585
|
+
"page": {
|
|
586
|
+
"wrapper": "flex min-h-screen"
|
|
587
|
+
},
|
|
588
|
+
"hero": {
|
|
589
|
+
"wrapper": "flex flex-col gap-8 lg:flex-row lg:items-center",
|
|
590
|
+
"image-wrapper": "w-full lg:w-1/2 order-1",
|
|
591
|
+
"content": "w-full lg:w-1/2 order-2",
|
|
592
|
+
"title": "@typography.h1",
|
|
593
|
+
"description": "mt-4 @typography.body",
|
|
594
|
+
"actions": "mt-6 flex gap-3",
|
|
595
|
+
"cta-primary": "@button.base @button.cta @button.md",
|
|
596
|
+
"cta-secondary": "@button.base @button.outline @button.md"
|
|
597
|
+
},
|
|
598
|
+
"section-a": {
|
|
599
|
+
"wrapper": "mt-16 border-t pt-16",
|
|
600
|
+
"title": "@typography.h2",
|
|
601
|
+
"content": "mt-4 @typography.body max-w-2xl"
|
|
602
|
+
},
|
|
603
|
+
"footer": {
|
|
604
|
+
"wrapper": "mt-16 border-t py-8",
|
|
605
|
+
"nav": "flex flex-wrap gap-6 justify-center",
|
|
606
|
+
"nav-item": "text-sm text-gray-500 hover:text-gray-700"
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
Design changes that can happen in JSON:
|
|
612
|
+
|
|
613
|
+
- hide `section-a` by removing it from `$layout`
|
|
614
|
+
- reorder page sections by changing `$layout`
|
|
615
|
+
- swap hero image/content order with classes
|
|
616
|
+
- change CTA style through shared `button.json`
|
|
617
|
+
|
|
618
|
+
## Installation
|
|
619
|
+
|
|
620
|
+
Install the package:
|
|
621
|
+
|
|
622
|
+
```bash
|
|
623
|
+
npm install @kondeio/kdf
|
|
624
|
+
pnpm add @kondeio/kdf
|
|
625
|
+
bun add @kondeio/kdf
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
The install initializer creates starter files only when the target `kdf/`
|
|
629
|
+
folder does not already exist. Existing files are never overwritten.
|
|
630
|
+
|
|
631
|
+
Manual initialization:
|
|
632
|
+
|
|
633
|
+
```bash
|
|
634
|
+
npx kdf init
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Custom design directory:
|
|
638
|
+
|
|
639
|
+
```bash
|
|
640
|
+
KDF_DIR=./designs npx kdf init
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
Next.js config:
|
|
644
|
+
|
|
645
|
+
```ts
|
|
646
|
+
import withKDF from "@kondeio/kdf/plugin";
|
|
647
|
+
|
|
648
|
+
export default withKDF({ dir: "./designs" })(nextConfig);
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
## License
|
|
652
|
+
|
|
653
|
+
KDF uses MIT. The root `LICENSE` file is the source of truth.
|