@nghitrum/dsforge 0.1.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 +226 -0
- package/dist/chunk-RI3XDGKU.js +252 -0
- package/dist/cli/index.js +5341 -0
- package/dist/emitter-ZNRPJ4D6.js +12 -0
- package/dist/html-6SIG34W5.js +2417 -0
- package/dist/index.d.ts +3222 -0
- package/dist/index.js +3324 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# dsforge
|
|
2
|
+
|
|
3
|
+
[](https://github.com/nghitrum/dsforge/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
Your design tokens live in Figma. Your components drift from the spec. Your docs are six sprints out of date. dsforge fixes all three with one config file.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Before / After
|
|
12
|
+
|
|
13
|
+
**Before**
|
|
14
|
+
|
|
15
|
+
- Design tokens transcribed by hand into CSS variables across three repos
|
|
16
|
+
- Component variants documented in a Notion page that nobody updates
|
|
17
|
+
- AI coding tools guessing at constraints because there is no machine-readable spec
|
|
18
|
+
|
|
19
|
+
**After**
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
design-system.config.json → dsforge generate
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
dist-ds/
|
|
27
|
+
├── src/ 9 typed React components
|
|
28
|
+
├── tokens/ CSS custom properties, JS map, Tailwind extension
|
|
29
|
+
├── docs/ MDX documentation per component
|
|
30
|
+
├── metadata/ AI-readable JSON contracts per component
|
|
31
|
+
└── showcase.html visual docs — open directly in the browser, no server
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
One config. One command. Everything consistent, everything regeneratable.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx dsforge@latest init # scaffold config + rules
|
|
42
|
+
npx dsforge@latest generate # generate the full design system
|
|
43
|
+
npx dsforge@latest showcase # open the visual docs in your browser
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Output lands in `dist-ds/`. Regenerate it any time by running `generate` again.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Config
|
|
51
|
+
|
|
52
|
+
`design-system.config.json` is the single source of truth. A minimal example:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"meta": { "name": "Acme DS", "version": "1.0.0" },
|
|
57
|
+
"tokens": {
|
|
58
|
+
"global": {
|
|
59
|
+
"brand-600": "#2563eb",
|
|
60
|
+
"neutral-900": "#0f172a",
|
|
61
|
+
"neutral-0": "#ffffff"
|
|
62
|
+
},
|
|
63
|
+
"semantic": {
|
|
64
|
+
"color-action": "{global.brand-600}",
|
|
65
|
+
"color-text-primary": "{global.neutral-900}",
|
|
66
|
+
"color-bg-default": "{global.neutral-0}"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"typography": {
|
|
70
|
+
"fontFamily": "Inter, system-ui, sans-serif",
|
|
71
|
+
"roles": {
|
|
72
|
+
"body": { "size": 16, "weight": 400, "lineHeight": 1.6 },
|
|
73
|
+
"heading": { "size": 24, "weight": 700, "lineHeight": 1.2 }
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"themes": {
|
|
77
|
+
"light": {},
|
|
78
|
+
"dark": { "color-bg-default": "#0f172a", "color-text-primary": "#f1f5f9" }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Tokens use a three-tier system: **global → semantic → component**. References use `{layer.key}` syntax and are resolved at generate time.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## What you get
|
|
88
|
+
|
|
89
|
+
### 9 React components
|
|
90
|
+
|
|
91
|
+
Button · Input · Card · Badge · Checkbox · Radio · Select · Spinner · Toast
|
|
92
|
+
|
|
93
|
+
Each component is typed, themed with your actual tokens, and ships with a prop table, copyable TSX examples, WCAG accessibility notes, and an AI metadata contract.
|
|
94
|
+
|
|
95
|
+
### CSS tokens
|
|
96
|
+
|
|
97
|
+
`base.css` — global and semantic CSS custom properties
|
|
98
|
+
`light.css` / `dark.css` — theme overrides
|
|
99
|
+
`tokens.js` — JS token map for runtime use
|
|
100
|
+
`tailwind.js` — Tailwind theme extension, ready to drop into `tailwind.config.js`
|
|
101
|
+
|
|
102
|
+
### MDX docs
|
|
103
|
+
|
|
104
|
+
One `.mdx` file per component, generated from your config. Import them into any docs site.
|
|
105
|
+
|
|
106
|
+
### AI metadata contracts
|
|
107
|
+
|
|
108
|
+
Each component emits `dist-ds/metadata/<component>.json`:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"component": "Button",
|
|
113
|
+
"role": "action-trigger",
|
|
114
|
+
"variants": ["primary", "secondary", "danger", "ghost"],
|
|
115
|
+
"destructiveVariants": ["danger"],
|
|
116
|
+
"accessibilityContract": {
|
|
117
|
+
"keyboard": true,
|
|
118
|
+
"focusRing": "required",
|
|
119
|
+
"ariaLabel": "required-for-icon-only"
|
|
120
|
+
},
|
|
121
|
+
"aiGuidance": [
|
|
122
|
+
"Use primary for the single most important action on a surface.",
|
|
123
|
+
"Never place two primary buttons side by side.",
|
|
124
|
+
"Use danger only for irreversible destructive actions."
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
AI coding assistants can read these before generating UI — no more inferring constraints from source code.
|
|
130
|
+
|
|
131
|
+
### Visual showcase
|
|
132
|
+
|
|
133
|
+
`showcase.html` is a self-contained documentation site. No build step, no server — open it directly in the browser.
|
|
134
|
+
|
|
135
|
+
Includes live component previews with all variants and states, themed with your actual tokens. Theme switching (light/dark) is built in when both themes are defined in config.
|
|
136
|
+
|
|
137
|
+
### npm-ready package
|
|
138
|
+
|
|
139
|
+
`dist-ds/` includes `package.json`, `tsconfig.json`, and a barrel `index.ts`. Run `npm publish` from `dist-ds/` to ship the design system as a package.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Validation
|
|
144
|
+
|
|
145
|
+
`dsforge validate` runs nine health checks and produces a scored report:
|
|
146
|
+
|
|
147
|
+
| Check | Max score |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| Token architecture | 15 |
|
|
150
|
+
| Typography | 10 |
|
|
151
|
+
| Spacing | 10 |
|
|
152
|
+
| Radius | 5 |
|
|
153
|
+
| Elevation | 5 |
|
|
154
|
+
| Motion | 5 |
|
|
155
|
+
| Themes | 10 |
|
|
156
|
+
| Token resolution | 14 |
|
|
157
|
+
| Governance rules | 15 |
|
|
158
|
+
|
|
159
|
+
Scores below 70 are flagged as warnings. WCAG contrast is checked automatically for all color token pairs.
|
|
160
|
+
|
|
161
|
+
`design-system.rules.json` encodes your governance constraints:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"governance": {
|
|
166
|
+
"requireSemanticTokens": true,
|
|
167
|
+
"forbidHardcodedColors": true,
|
|
168
|
+
"requireAccessibilityProps": true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Diff
|
|
176
|
+
|
|
177
|
+
`dsforge diff old.config.json new.config.json` classifies every change:
|
|
178
|
+
|
|
179
|
+
- **BREAKING** — tokens removed or renamed that are referenced by components
|
|
180
|
+
- **CHANGED** — existing values modified
|
|
181
|
+
- **ADDED** — new tokens or sections
|
|
182
|
+
|
|
183
|
+
Run this before deploying a config update to understand downstream impact.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Commands
|
|
188
|
+
|
|
189
|
+
| Command | What it does |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `dsforge init` | Scaffold `design-system.config.json` and `design-system.rules.json` |
|
|
192
|
+
| `dsforge generate` | Run the full pipeline — tokens, components, metadata, docs, showcase |
|
|
193
|
+
| `dsforge validate` | Run 9 health checks and score against governance rules |
|
|
194
|
+
| `dsforge diff` | Compare two configs — BREAKING / CHANGED / ADDED |
|
|
195
|
+
| `dsforge showcase` | Open `dist-ds/showcase.html` in your default browser |
|
|
196
|
+
|
|
197
|
+
Run `dsforge` with no arguments for an interactive menu.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Coming next
|
|
202
|
+
|
|
203
|
+
- Additional components: Modal, Table, Tooltip, DatePicker
|
|
204
|
+
- **Pro**: AI-assisted token generation from brand guidelines or a Figma file
|
|
205
|
+
- Figma Variables API sync
|
|
206
|
+
- CI integration — fail builds on governance violations
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Development
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
git clone https://github.com/your-org/dsforge
|
|
214
|
+
cd dsforge
|
|
215
|
+
npm install
|
|
216
|
+
|
|
217
|
+
npm run dev # run the CLI without a build step
|
|
218
|
+
npm run typecheck # type-check without emitting
|
|
219
|
+
npm test # run the test suite
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// src/generators/package/emitter.ts
|
|
2
|
+
function generatePackageJson(config, componentNames) {
|
|
3
|
+
const scope = config.meta.npmScope ?? "@myorg";
|
|
4
|
+
const name = `${scope}/${config.meta.name}`;
|
|
5
|
+
const pkg = {
|
|
6
|
+
name,
|
|
7
|
+
version: config.meta.version,
|
|
8
|
+
description: config.meta.description ?? `${config.meta.name} design system`,
|
|
9
|
+
keywords: ["design-system", "tokens", "react", "typescript", "components"],
|
|
10
|
+
license: "MIT",
|
|
11
|
+
type: "module",
|
|
12
|
+
main: "./dist/index.js",
|
|
13
|
+
types: "./dist/index.d.ts",
|
|
14
|
+
exports: {
|
|
15
|
+
".": {
|
|
16
|
+
import: "./dist/index.js",
|
|
17
|
+
types: "./dist/index.d.ts"
|
|
18
|
+
},
|
|
19
|
+
"./tokens/base.css": "./tokens/base.css",
|
|
20
|
+
...Object.fromEntries(
|
|
21
|
+
Object.keys({}).concat(["light", "dark"]).map((t) => [`./tokens/${t}.css`, `./tokens/${t}.css`])
|
|
22
|
+
),
|
|
23
|
+
"./tokens": "./tokens/tokens.js",
|
|
24
|
+
"./tailwind": "./tokens/tailwind.js",
|
|
25
|
+
"./metadata": "./metadata/index.json",
|
|
26
|
+
...Object.fromEntries(
|
|
27
|
+
componentNames.map((c) => [`./metadata/${c}`, `./metadata/${c}.json`])
|
|
28
|
+
)
|
|
29
|
+
},
|
|
30
|
+
files: ["dist", "tokens", "metadata", "docs", "CHANGELOG.md"],
|
|
31
|
+
scripts: {
|
|
32
|
+
build: "tsc",
|
|
33
|
+
prepublishOnly: "npm run build"
|
|
34
|
+
},
|
|
35
|
+
devDependencies: {
|
|
36
|
+
"@types/react": "^18.0.0",
|
|
37
|
+
"@types/react-dom": "^18.0.0",
|
|
38
|
+
typescript: "^5.0.0"
|
|
39
|
+
},
|
|
40
|
+
peerDependencies: {
|
|
41
|
+
react: ">=17.0.0",
|
|
42
|
+
"react-dom": ">=17.0.0"
|
|
43
|
+
},
|
|
44
|
+
peerDependenciesMeta: {
|
|
45
|
+
react: { optional: false },
|
|
46
|
+
"react-dom": { optional: false }
|
|
47
|
+
},
|
|
48
|
+
engines: {
|
|
49
|
+
node: ">=18.0.0"
|
|
50
|
+
},
|
|
51
|
+
dsforge: {
|
|
52
|
+
generatedBy: "dsforge",
|
|
53
|
+
configVersion: config.meta.version,
|
|
54
|
+
preset: config.meta.preset ?? "comfortable",
|
|
55
|
+
components: componentNames,
|
|
56
|
+
themes: []
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
return JSON.stringify(pkg, null, 2);
|
|
60
|
+
}
|
|
61
|
+
function generateTsConfig() {
|
|
62
|
+
return JSON.stringify(
|
|
63
|
+
{
|
|
64
|
+
compilerOptions: {
|
|
65
|
+
target: "ES2020",
|
|
66
|
+
module: "NodeNext",
|
|
67
|
+
moduleResolution: "NodeNext",
|
|
68
|
+
lib: ["ES2020", "DOM"],
|
|
69
|
+
outDir: "./dist",
|
|
70
|
+
rootDir: "./src",
|
|
71
|
+
declaration: true,
|
|
72
|
+
declarationMap: true,
|
|
73
|
+
sourceMap: true,
|
|
74
|
+
strict: true,
|
|
75
|
+
esModuleInterop: true,
|
|
76
|
+
skipLibCheck: true,
|
|
77
|
+
jsx: "react-jsx"
|
|
78
|
+
},
|
|
79
|
+
include: ["src/**/*"],
|
|
80
|
+
exclude: ["node_modules", "dist"]
|
|
81
|
+
},
|
|
82
|
+
null,
|
|
83
|
+
2
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
function generateReadme(config, componentNames) {
|
|
87
|
+
const scope = config.meta.npmScope ?? "@myorg";
|
|
88
|
+
const pkgName = `${scope}/${config.meta.name}`;
|
|
89
|
+
const themeNames = ["light", "dark"];
|
|
90
|
+
return `# ${config.meta.name}
|
|
91
|
+
|
|
92
|
+
> ${config.meta.description ?? "A design system generated by dsforge."}
|
|
93
|
+
|
|
94
|
+
[}.svg)](https://www.npmjs.com/package/${pkgName})
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
|
|
100
|
+
\`\`\`bash
|
|
101
|
+
npm install ${pkgName}
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
## Quick start
|
|
105
|
+
|
|
106
|
+
\`\`\`tsx
|
|
107
|
+
// 1. Import base tokens (once, in your app root)
|
|
108
|
+
import "${pkgName}/tokens/base.css";
|
|
109
|
+
import "${pkgName}/tokens/light.css";
|
|
110
|
+
|
|
111
|
+
// 2. Wrap your app
|
|
112
|
+
import { ThemeProvider, Button } from "${pkgName}";
|
|
113
|
+
|
|
114
|
+
function App() {
|
|
115
|
+
return (
|
|
116
|
+
<ThemeProvider theme="light">
|
|
117
|
+
<Button variant="primary" aria-label="Get started">
|
|
118
|
+
Get started
|
|
119
|
+
</Button>
|
|
120
|
+
</ThemeProvider>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
## Components
|
|
126
|
+
|
|
127
|
+
${componentNames.map(
|
|
128
|
+
(c) => `- **${c.charAt(0).toUpperCase() + c.slice(1)}** \u2014 see \`metadata/${c}.json\` for contract`
|
|
129
|
+
).join("\n")}
|
|
130
|
+
|
|
131
|
+
## Themes
|
|
132
|
+
|
|
133
|
+
${themeNames.map((t) => `- \`${t}\` \u2014 import \`${pkgName}/tokens/${t}.css\``).join("\n")}
|
|
134
|
+
|
|
135
|
+
### Theme switching
|
|
136
|
+
|
|
137
|
+
\`\`\`tsx
|
|
138
|
+
import { useTheme } from "${pkgName}";
|
|
139
|
+
|
|
140
|
+
function ThemeToggle() {
|
|
141
|
+
const { theme, setTheme } = useTheme();
|
|
142
|
+
return (
|
|
143
|
+
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
|
|
144
|
+
Toggle theme
|
|
145
|
+
</button>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
\`\`\`
|
|
149
|
+
|
|
150
|
+
## Using with Tailwind
|
|
151
|
+
|
|
152
|
+
\`\`\`js
|
|
153
|
+
// tailwind.config.js
|
|
154
|
+
const ds = require("${pkgName}/tailwind");
|
|
155
|
+
module.exports = {
|
|
156
|
+
theme: { extend: ds },
|
|
157
|
+
};
|
|
158
|
+
\`\`\`
|
|
159
|
+
|
|
160
|
+
## Without React (CSS tokens only)
|
|
161
|
+
|
|
162
|
+
\`\`\`html
|
|
163
|
+
<link rel="stylesheet" href="node_modules/${pkgName}/tokens/base.css" />
|
|
164
|
+
<link rel="stylesheet" href="node_modules/${pkgName}/tokens/light.css" />
|
|
165
|
+
|
|
166
|
+
<button class="my-button">Click me</button>
|
|
167
|
+
|
|
168
|
+
<style>
|
|
169
|
+
.my-button {
|
|
170
|
+
background: var(--color-action);
|
|
171
|
+
color: var(--color-text-inverse);
|
|
172
|
+
border-radius: var(--radius-md);
|
|
173
|
+
padding: var(--component-padding-sm) var(--component-padding-md);
|
|
174
|
+
}
|
|
175
|
+
</style>
|
|
176
|
+
\`\`\`
|
|
177
|
+
|
|
178
|
+
## Token customization
|
|
179
|
+
|
|
180
|
+
All design tokens are defined in \`design-system.config.json\`. After editing,
|
|
181
|
+
run \`dsforge generate\` to regenerate the package.
|
|
182
|
+
|
|
183
|
+
\`\`\`json
|
|
184
|
+
// design-system.config.json (excerpt)
|
|
185
|
+
{
|
|
186
|
+
"tokens": {
|
|
187
|
+
"global": {
|
|
188
|
+
"color-brand-500": "#2563eb"
|
|
189
|
+
},
|
|
190
|
+
"semantic": {
|
|
191
|
+
"color-action": "{global.color-brand-500}"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
\`\`\`
|
|
196
|
+
|
|
197
|
+
Token references use \`{tier.name}\` syntax to alias values across tiers:
|
|
198
|
+
global \u2192 semantic \u2192 component. Changing a global value propagates through
|
|
199
|
+
every semantic and component token that references it.
|
|
200
|
+
|
|
201
|
+
## AI tool integration
|
|
202
|
+
|
|
203
|
+
The \`metadata/\` directory contains machine-readable component contracts.
|
|
204
|
+
AI coding assistants (Copilot, Cursor, Claude Code) can read these to
|
|
205
|
+
generate UI that respects your governance rules automatically.
|
|
206
|
+
|
|
207
|
+
\`\`\`json
|
|
208
|
+
// ${pkgName}/metadata/button.json
|
|
209
|
+
{
|
|
210
|
+
"component": "Button",
|
|
211
|
+
"allowedVariants": ["primary", "secondary", "danger", "ghost"],
|
|
212
|
+
"requiredProps": ["aria-label"],
|
|
213
|
+
"accessibilityContract": { "keyboard": true, "focusRing": true }
|
|
214
|
+
}
|
|
215
|
+
\`\`\`
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
Generated by [dsforge](https://github.com/nghitrum/dsforge) v${config.meta.version}.
|
|
220
|
+
`;
|
|
221
|
+
}
|
|
222
|
+
function generateChangelog(config) {
|
|
223
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
224
|
+
return `# Changelog
|
|
225
|
+
|
|
226
|
+
All notable changes to \`${config.meta.name}\` are documented here.
|
|
227
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
228
|
+
|
|
229
|
+
## [${config.meta.version}] \u2014 ${date}
|
|
230
|
+
|
|
231
|
+
### Added
|
|
232
|
+
|
|
233
|
+
- Initial release generated by dsforge
|
|
234
|
+
- Components: see \`metadata/index.json\` for full list
|
|
235
|
+
- Token architecture: global \u2192 semantic \u2192 component (3-tier)
|
|
236
|
+
- Themes: light, dark
|
|
237
|
+
- CSS custom property output for all tokens
|
|
238
|
+
- Tailwind theme extension
|
|
239
|
+
- AI-consumable metadata per component
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
_To add a new entry: run \`dsforge diff --from <prev-version>\` and paste the output here._
|
|
244
|
+
`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export {
|
|
248
|
+
generatePackageJson,
|
|
249
|
+
generateTsConfig,
|
|
250
|
+
generateReadme,
|
|
251
|
+
generateChangelog
|
|
252
|
+
};
|