sdtk-design-kit 0.2.1 → 0.3.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 +67 -125
- package/package.json +2 -1
- package/skills/design-prototype/SKILL.md +276 -0
- package/skills/design-prototype/references/craft.md +75 -0
- package/skills/design-prototype/references/designer-charter.md +56 -0
- package/src/commands/handoff.js +220 -220
- package/src/commands/help.js +7 -3
- package/src/commands/init.js +53 -0
- package/src/commands/prototype.js +140 -653
- package/src/commands/review.js +129 -139
- package/src/commands/start.js +22 -5
- package/src/commands/status.js +10 -2
- package/src/commands/system.js +186 -14
- package/src/commands/update.js +11 -0
- package/src/index.js +4 -0
- package/src/lib/anti-slop-lint.js +0 -11
- package/src/lib/component-contract.js +300 -34
- package/src/lib/design-input-contract.js +3 -2
- package/src/lib/design-paths.js +3 -0
- package/src/lib/design-profiles.js +31 -0
- package/src/lib/screen-briefs.js +235 -24
- package/src/lib/update.js +219 -0
- package/src/lib/prototype-briefs.js +0 -125
- package/src/lib/prototype-component-map.js +0 -219
- package/src/lib/prototype-density.js +0 -377
- package/src/lib/prototype-renderer.js +0 -382
package/src/commands/system.js
CHANGED
|
@@ -18,17 +18,18 @@ function cmdSystemHelp() {
|
|
|
18
18
|
console.log(`SDTK-DESIGN System
|
|
19
19
|
|
|
20
20
|
Usage:
|
|
21
|
-
sdtk-design system [--
|
|
21
|
+
sdtk-design system [--project-path <path>] [--force]
|
|
22
22
|
|
|
23
23
|
Examples:
|
|
24
24
|
sdtk-design system
|
|
25
|
-
sdtk-design system --
|
|
25
|
+
sdtk-design system --force
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Reads (token-driven path, from-spec flow):
|
|
28
|
+
docs/design/DESIGN_TOKENS.json (schema sdtk.design.tokens.v2 from start --from-spec)
|
|
29
|
+
docs/design/DESIGN_BRIEF.md
|
|
30
|
+
docs/design/SCREEN_MAP.md
|
|
30
31
|
|
|
31
|
-
Reads:
|
|
32
|
+
Reads (legacy path, start --idea flow):
|
|
32
33
|
docs/design/DESIGN_BRIEF.md
|
|
33
34
|
docs/design/SCREEN_MAP.md
|
|
34
35
|
|
|
@@ -44,6 +45,7 @@ Safety:
|
|
|
44
45
|
return 0;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
// Legacy preset-driven path (used when DESIGN_TOKENS.json is absent — start --idea flow).
|
|
47
49
|
function includesAny(text, terms) {
|
|
48
50
|
const value = text.toLowerCase();
|
|
49
51
|
return terms.some((term) => value.includes(term));
|
|
@@ -53,8 +55,9 @@ function linesForBullets(items) {
|
|
|
53
55
|
return items.map((item) => `- ${item}`);
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
function systemContent(briefContent, screenMapContent, style
|
|
57
|
-
const
|
|
58
|
+
function systemContent(briefContent, screenMapContent, style) {
|
|
59
|
+
const resolvedStyle = style || DEFAULT_STYLE;
|
|
60
|
+
const preset = getStylePreset(resolvedStyle);
|
|
58
61
|
const source = `${briefContent}\n${screenMapContent}`;
|
|
59
62
|
const isCrm = includesAny(source, ["crm", "lead", "follow-up", "pipeline"]);
|
|
60
63
|
const primaryAction = isCrm ? "Add first lead" : "Start first workflow";
|
|
@@ -71,7 +74,7 @@ function systemContent(briefContent, screenMapContent, style = DEFAULT_STYLE) {
|
|
|
71
74
|
"",
|
|
72
75
|
"## Visual Preset",
|
|
73
76
|
"",
|
|
74
|
-
`- Preset: ${resolveStyleName(
|
|
77
|
+
`- Preset: ${resolveStyleName(resolvedStyle)} (${preset.label}).`,
|
|
75
78
|
`- Direction: ${preset.summary}`,
|
|
76
79
|
"- Presets are compact SDTK-DESIGN guidance adapted from reference patterns, not imported runtime code.",
|
|
77
80
|
"",
|
|
@@ -162,6 +165,159 @@ function systemContent(briefContent, screenMapContent, style = DEFAULT_STYLE) {
|
|
|
162
165
|
].join("\n");
|
|
163
166
|
}
|
|
164
167
|
|
|
168
|
+
// Token-driven path (BK-224): 9-section content from DESIGN_TOKENS.json v2.
|
|
169
|
+
function designSystemContentFromTokens({ tokens }) {
|
|
170
|
+
const { brand, palette, typography, section, component, profile } = tokens;
|
|
171
|
+
const state = (palette && palette.state) || {};
|
|
172
|
+
const ramp = (typography && typography.ramp) || {};
|
|
173
|
+
const fontFamily = (typography && typography.fontFamily) || {};
|
|
174
|
+
const radius = (component && component.radius) || {};
|
|
175
|
+
const shadow = (component && component.shadow) || {};
|
|
176
|
+
const borderWidth = (component && component.borderWidth) || {};
|
|
177
|
+
|
|
178
|
+
const stateRow = (stateKey) => {
|
|
179
|
+
const s = state[stateKey];
|
|
180
|
+
return s ? `${s.base} / soft: ${s.soft}` : "N/A";
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const rampRows = Object.entries(ramp).map(
|
|
184
|
+
([level, spec]) => `- ${level}: ${spec.fontSize}px / weight ${spec.fontWeight} / line-height ${spec.lineHeight}`
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
return [
|
|
188
|
+
"# Design System",
|
|
189
|
+
"",
|
|
190
|
+
"## Visual Theme & Atmosphere",
|
|
191
|
+
"",
|
|
192
|
+
`- Profile: ${profile}`,
|
|
193
|
+
`- Brand mood: ${brand.mood}`,
|
|
194
|
+
`- Density: ${brand.density}`,
|
|
195
|
+
`- Surface: ${palette.surface} (primary background)`,
|
|
196
|
+
`- Surface alt: ${palette.surfaceAlt} (content areas, alternating rows)`,
|
|
197
|
+
`- Primary color: ${palette.primary}`,
|
|
198
|
+
`- Accent color: ${palette.accent}`,
|
|
199
|
+
"- Favor clarity, scannability, and obvious next actions over decorative marketing composition.",
|
|
200
|
+
"- No decorative background gradients, glow layers, or unsourced pattern fills.",
|
|
201
|
+
"",
|
|
202
|
+
"## Color Palette & Roles",
|
|
203
|
+
"",
|
|
204
|
+
`- Primary: ${palette.primary} / hover: ${palette.primaryHover} / soft: ${palette.primarySoft}`,
|
|
205
|
+
`- Accent: ${palette.accent} / hover: ${palette.accentHover} / soft: ${palette.accentSoft}`,
|
|
206
|
+
`- Surface: ${palette.surface}`,
|
|
207
|
+
`- Surface alt: ${palette.surfaceAlt}`,
|
|
208
|
+
`- Surface inverse: ${palette.surfaceInverse}`,
|
|
209
|
+
`- Text primary: ${palette.textPrimary}`,
|
|
210
|
+
`- Text secondary: ${palette.textSecondary}`,
|
|
211
|
+
`- Text muted: ${palette.textMuted}`,
|
|
212
|
+
`- Border: ${palette.border}`,
|
|
213
|
+
`- Border strong: ${palette.borderStrong}`,
|
|
214
|
+
`- State success: ${stateRow("success")}`,
|
|
215
|
+
`- State warning: ${stateRow("warning")}`,
|
|
216
|
+
`- State error: ${stateRow("error")}`,
|
|
217
|
+
`- State info: ${stateRow("info")}`,
|
|
218
|
+
"",
|
|
219
|
+
"## Typography Rules",
|
|
220
|
+
"",
|
|
221
|
+
`- Font family display: ${fontFamily.display || "N/A"}`,
|
|
222
|
+
`- Font family body: ${fontFamily.body || "N/A"}`,
|
|
223
|
+
`- Font family mono: ${fontFamily.mono || "N/A"}`,
|
|
224
|
+
...rampRows,
|
|
225
|
+
"- Do not scale font size with viewport width.",
|
|
226
|
+
"- Keep letter spacing at 0 unless explicitly overridden by brand-tokens.",
|
|
227
|
+
"- One screen-level h1 before any section heading.",
|
|
228
|
+
"",
|
|
229
|
+
"## Component Stylings",
|
|
230
|
+
"",
|
|
231
|
+
`- Card radius: ${radius.card}px`,
|
|
232
|
+
`- Control radius: ${radius.control}px`,
|
|
233
|
+
`- Pill radius: ${radius.pill}px`,
|
|
234
|
+
`- Border width default: ${borderWidth.default}px solid ${palette.border}`,
|
|
235
|
+
`- Border width strong: ${borderWidth.strong}px solid ${palette.borderStrong}`,
|
|
236
|
+
"- Primary button: filled palette.primary, white label, 40px minimum height.",
|
|
237
|
+
"- Secondary button: palette.surface background, palette.border outline, palette.textPrimary label.",
|
|
238
|
+
"- Disabled: 45% opacity, no hover elevation.",
|
|
239
|
+
"- Cards: use only for repeated records or framed tool surfaces; no nested cards.",
|
|
240
|
+
"",
|
|
241
|
+
"## Layout Principles",
|
|
242
|
+
"",
|
|
243
|
+
`- Container max width: ${section.containerMaxWidth}px`,
|
|
244
|
+
`- Content max width: ${section.contentMaxWidth}px`,
|
|
245
|
+
`- Section gap: ${section.sectionGap}px`,
|
|
246
|
+
`- Hero min height: ${section.heroMinHeight}px`,
|
|
247
|
+
`- Grid columns: ${section.gridColumns}`,
|
|
248
|
+
`- Density: ${brand.density}`,
|
|
249
|
+
"- Navigation: deterministic route and section order from explicit screen model.",
|
|
250
|
+
"- Keep fixed-format controls stable with explicit min-height and predictable grid tracks.",
|
|
251
|
+
"",
|
|
252
|
+
"## Depth & Elevation",
|
|
253
|
+
"",
|
|
254
|
+
`- Shadow none: ${shadow.none}`,
|
|
255
|
+
`- Shadow sm: ${shadow.sm}`,
|
|
256
|
+
`- Shadow md: ${shadow.md}`,
|
|
257
|
+
`- Shadow lg: ${shadow.lg}`,
|
|
258
|
+
"- Use shadow.sm for cards; shadow.md for modals and dialogs.",
|
|
259
|
+
"- Do not use heavy drop shadows for decorative layering.",
|
|
260
|
+
"",
|
|
261
|
+
"## Do's & Don'ts",
|
|
262
|
+
"",
|
|
263
|
+
"- Do: use palette token paths (e.g. palette.primary, palette.state.success.base) in component references.",
|
|
264
|
+
"- Do: use typography.ramp token paths for heading hierarchy.",
|
|
265
|
+
"- Do: use section token paths for layout rhythm and container widths.",
|
|
266
|
+
"- Don't: embed raw hex values outside of generated CSS variables.",
|
|
267
|
+
"- Don't: extract brand colors or copy from requirement text.",
|
|
268
|
+
"- Don't: add unsupported decorative backgrounds, blurs, or gradient panels.",
|
|
269
|
+
"- Don't: invent copy, headings, or imagery from requirement text.",
|
|
270
|
+
"- Don't: use placeholder or TBD content in renderer output.",
|
|
271
|
+
"",
|
|
272
|
+
"## Responsive Behavior",
|
|
273
|
+
"",
|
|
274
|
+
`- Max layout width: ${section.containerMaxWidth}px; below this, flow to 100%.`,
|
|
275
|
+
`- Grid: ${section.gridColumns} columns desktop; collapse to 1 column mobile.`,
|
|
276
|
+
`- Section gap: ${section.sectionGap}px desktop; reduce proportionally at tablet/mobile.`,
|
|
277
|
+
"- Typography ramp: use ramp values as-is; do not scale with viewport.",
|
|
278
|
+
"- Navigation: collapse to hamburger or bottom nav at mobile breakpoint.",
|
|
279
|
+
"- Touch targets: minimum 44px by 44px for interactive controls.",
|
|
280
|
+
"",
|
|
281
|
+
"## Renderer Prompt Guide",
|
|
282
|
+
"",
|
|
283
|
+
"- Use token key paths in all component references: palette.primary, typography.ramp.h1, section.heroMinHeight, component.radius.card.",
|
|
284
|
+
"- Enforce one screen-level h1 before any section heading.",
|
|
285
|
+
"- Use semantic HTML landmarks: header, main, nav, section, footer.",
|
|
286
|
+
"- Preserve data/state/component markers from the screen brief; do not invent new ones.",
|
|
287
|
+
"- Avoid placeholder panels; emit NEEDS_* markers for unsourced content.",
|
|
288
|
+
`- Token reference: palette.primary=${palette.primary}, typography.ramp.h1=${ramp.h1 ? ramp.h1.fontSize + "px/" + ramp.h1.fontWeight : "N/A"}, section.heroMinHeight=${section.heroMinHeight}px, component.radius.card=${radius.card}px.`,
|
|
289
|
+
"- State tokens: palette.state.success.base for success states, palette.state.error.base for errors.",
|
|
290
|
+
`- Layout: respect section.containerMaxWidth (${section.containerMaxWidth}px) and section.gridColumns (${section.gridColumns}) from this contract.`,
|
|
291
|
+
"",
|
|
292
|
+
].join("\n");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function loadDesignTokensV2ForSystem(projectPath) {
|
|
296
|
+
const paths = describeDesignPaths(projectPath);
|
|
297
|
+
if (!fs.existsSync(paths.designTokensPath)) {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
let raw;
|
|
301
|
+
try {
|
|
302
|
+
raw = JSON.parse(fs.readFileSync(paths.designTokensPath, "utf-8"));
|
|
303
|
+
} catch (_err) {
|
|
304
|
+
throw new ValidationError(
|
|
305
|
+
"docs/design/DESIGN_TOKENS.json is not valid JSON. Re-run sdtk-design start --from-spec to regenerate. No project files were changed."
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (raw.schema === "sdtk.design.tokens.v1") {
|
|
309
|
+
throw new ValidationError(
|
|
310
|
+
"docs/design/DESIGN_TOKENS.json is schema v1. Re-run sdtk-design start --from-spec to upgrade to v2 tokens. No project files were changed."
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
if (raw.schema !== "sdtk.design.tokens.v2") {
|
|
314
|
+
throw new ValidationError(
|
|
315
|
+
"docs/design/DESIGN_TOKENS.json has an unrecognized schema. Re-run sdtk-design start --from-spec to regenerate. No project files were changed."
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
return raw;
|
|
319
|
+
}
|
|
320
|
+
|
|
165
321
|
function runDesignSystem({ projectPath, force = false, style = DEFAULT_STYLE }) {
|
|
166
322
|
const styleName = resolveStyleName(style);
|
|
167
323
|
const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
|
|
@@ -180,16 +336,29 @@ function runDesignSystem({ projectPath, force = false, style = DEFAULT_STYLE })
|
|
|
180
336
|
throw new ValidationError("docs/design/DESIGN_SYSTEM.md already exists. Re-run with --force to replace this managed design system.");
|
|
181
337
|
}
|
|
182
338
|
|
|
183
|
-
|
|
184
|
-
const
|
|
339
|
+
// Try v2 token-driven path; fall back to legacy preset path when tokens are absent.
|
|
340
|
+
const tokens = loadDesignTokensV2ForSystem(resolvedProjectPath);
|
|
341
|
+
|
|
342
|
+
let content;
|
|
343
|
+
let tokenDriven = false;
|
|
344
|
+
if (tokens) {
|
|
345
|
+
content = designSystemContentFromTokens({ tokens });
|
|
346
|
+
tokenDriven = true;
|
|
347
|
+
} else {
|
|
348
|
+
const briefContent = fs.readFileSync(paths.designBriefPath, "utf-8");
|
|
349
|
+
const screenMapContent = fs.readFileSync(paths.screenMapPath, "utf-8");
|
|
350
|
+
content = systemContent(briefContent, screenMapContent, styleName);
|
|
351
|
+
}
|
|
352
|
+
|
|
185
353
|
fs.mkdirSync(path.dirname(paths.designSystemPath), { recursive: true });
|
|
186
|
-
fs.writeFileSync(paths.designSystemPath,
|
|
354
|
+
fs.writeFileSync(paths.designSystemPath, content, "utf-8");
|
|
187
355
|
|
|
188
356
|
return {
|
|
189
357
|
projectPath: resolvedProjectPath,
|
|
190
358
|
relativeDesignSystemPath: "docs/design/DESIGN_SYSTEM.md",
|
|
191
359
|
forced: Boolean(force),
|
|
192
|
-
style: styleName,
|
|
360
|
+
style: tokenDriven ? null : styleName,
|
|
361
|
+
tokenDriven,
|
|
193
362
|
};
|
|
194
363
|
}
|
|
195
364
|
|
|
@@ -204,7 +373,9 @@ function cmdSystem(args) {
|
|
|
204
373
|
});
|
|
205
374
|
|
|
206
375
|
console.log(`[design] Wrote ${result.relativeDesignSystemPath}: ${result.projectPath}`);
|
|
207
|
-
|
|
376
|
+
if (!result.tokenDriven && result.style) {
|
|
377
|
+
console.log(`[design] Style: ${result.style}`);
|
|
378
|
+
}
|
|
208
379
|
console.log(`[design] Overwrite: ${result.forced ? "enabled by --force" : "not needed"}`);
|
|
209
380
|
console.log("[design] No .sdtk/atlas, SDTK-WIKI output, source files, network, or app code was modified.");
|
|
210
381
|
console.log("[design] Next: sdtk-design handoff");
|
|
@@ -216,4 +387,5 @@ module.exports = {
|
|
|
216
387
|
cmdSystemHelp,
|
|
217
388
|
runDesignSystem,
|
|
218
389
|
systemContent,
|
|
390
|
+
designSystemContentFromTokens,
|
|
219
391
|
};
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const { cmdScreens } = require("./commands/screens");
|
|
|
10
10
|
const { cmdStart } = require("./commands/start");
|
|
11
11
|
const { cmdStatus } = require("./commands/status");
|
|
12
12
|
const { cmdSystem } = require("./commands/system");
|
|
13
|
+
const { cmdUpdate } = require("./commands/update");
|
|
13
14
|
const { cmdWireframe } = require("./commands/wireframe");
|
|
14
15
|
const { ValidationError } = require("./lib/errors");
|
|
15
16
|
|
|
@@ -76,6 +77,9 @@ async function run(argv) {
|
|
|
76
77
|
if (command === "status") {
|
|
77
78
|
return cmdStatus(args);
|
|
78
79
|
}
|
|
80
|
+
if (command === "update") {
|
|
81
|
+
return cmdUpdate(args);
|
|
82
|
+
}
|
|
79
83
|
if (DEFERRED_COMMANDS.has(command)) {
|
|
80
84
|
throw new ValidationError(
|
|
81
85
|
`Command "${command}" is planned for ${DEFERRED_COMMANDS.get(command)} and is not implemented yet. Run "sdtk-design --help" for the current Foundation Beta surface.`
|
|
@@ -109,17 +109,6 @@ function lintArtifactHtml(html, options = {}) {
|
|
|
109
109
|
"Pull the metric from a real source or remove the claim.",
|
|
110
110
|
screenId
|
|
111
111
|
);
|
|
112
|
-
addRegexFindings(
|
|
113
|
-
findings,
|
|
114
|
-
clean,
|
|
115
|
-
/class="[^"]*\bdensity-evidence\b[^"]*"/i,
|
|
116
|
-
"P0",
|
|
117
|
-
"density-evidence-residue",
|
|
118
|
-
"Legacy density-evidence filler residue detected.",
|
|
119
|
-
"Remove filler section emitters from the renderer.",
|
|
120
|
-
screenId
|
|
121
|
-
);
|
|
122
|
-
|
|
123
112
|
const counts = new Map();
|
|
124
113
|
for (const text of articleTexts(clean)) counts.set(text, (counts.get(text) || 0) + 1);
|
|
125
114
|
const fullDuplicates = new Set();
|