sdtk-design-kit 0.1.0 → 0.1.2

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.
@@ -8,13 +8,21 @@ const { runDesignSystem } = require("./system");
8
8
  const { runDesignWireframe } = require("./wireframe");
9
9
  const { parseFlags } = require("../lib/args");
10
10
  const { describeDesignPaths, resolveProjectPath } = require("../lib/design-paths");
11
+ const { availableProfileNames } = require("../lib/design-profiles");
12
+ const { buildInputContractState, writeInputContractState } = require("../lib/design-input-contract");
11
13
  const { ValidationError } = require("../lib/errors");
14
+ const { DEFAULT_STYLE, availableStyleNames, resolveStyleName } = require("../lib/style-presets");
12
15
 
13
16
  const START_FLAG_DEFS = {
14
17
  help: { type: "boolean" },
15
18
  idea: { type: "string" },
16
19
  "project-path": { type: "string" },
17
20
  force: { type: "boolean" },
21
+ style: { type: "string" },
22
+ "from-spec": { type: "string" },
23
+ "design-brief": { type: "string" },
24
+ "reference-dir": { type: "string" },
25
+ profile: { type: "string" },
18
26
  };
19
27
 
20
28
  const REQUIRED_WIREFRAME_FILES = ["LANDING.md", "ONBOARDING.md", "DASHBOARD.md"];
@@ -23,25 +31,42 @@ function cmdStartHelp() {
23
31
  console.log(`SDTK-DESIGN Start
24
32
 
25
33
  Usage:
26
- sdtk-design start --idea "<rough MVP idea>" [--project-path <path>] [--force]
34
+ sdtk-design start --idea "<rough MVP idea>" [--style <preset>] [--project-path <path>] [--force]
35
+ sdtk-design start --from-spec <projectPath> [--design-brief <file>] [--reference-dir <dir>] [--profile <name>] [--project-path <path>]
27
36
 
28
37
  Example:
29
38
  sdtk-design start --idea "I want to build a lightweight CRM for solo consultants to track leads."
39
+ sdtk-design start --idea "ClientPulse for consultants" --style premium-dashboard
40
+ sdtk-design start --from-spec . --reference-dir ./docs/ui/claude-design-export --profile b2b-commerce
41
+
42
+ Style presets:
43
+ ${availableStyleNames().join(", ")}
44
+ Default: ${DEFAULT_STYLE}
45
+
46
+ Design profiles (optional in from-spec mode):
47
+ ${availableProfileNames().join(", ")}
30
48
 
31
49
  Runs:
32
- brief -> screens -> wireframe --screen all -> system
50
+ idea mode:
51
+ brief -> screens -> wireframe --screen all -> system
52
+ from-spec mode:
53
+ explicit SPEC/design artifact intake -> contract state write
33
54
 
34
55
  Creates:
35
- docs/design/DESIGN_BRIEF.md
36
- docs/design/SCREEN_MAP.md
37
- docs/design/wireframes/LANDING.md
38
- docs/design/wireframes/ONBOARDING.md
39
- docs/design/wireframes/DASHBOARD.md
40
- docs/design/DESIGN_SYSTEM.md
56
+ idea mode:
57
+ docs/design/DESIGN_BRIEF.md
58
+ docs/design/SCREEN_MAP.md
59
+ docs/design/wireframes/LANDING.md
60
+ docs/design/wireframes/ONBOARDING.md
61
+ docs/design/wireframes/DASHBOARD.md
62
+ docs/design/DESIGN_SYSTEM.md
63
+ from-spec mode:
64
+ .sdtk/design/START_INPUT_STATE.json
41
65
 
42
66
  Safety:
43
67
  Local files only.
44
68
  Existing managed core design outputs are not overwritten unless --force is explicit.
69
+ --from-spec consumes explicit artifacts; no raw requirement semantic parsing.
45
70
  No review, handoff, URL, browser, screenshot, vision, or DOM work.
46
71
  No .sdtk/atlas creation or mutation.
47
72
  No SDTK-WIKI output mutation.
@@ -65,11 +90,12 @@ function coreOutputTargets(paths) {
65
90
  ];
66
91
  }
67
92
 
68
- function runDesignStart({ idea, projectPath, force = false }) {
93
+ function runDesignStart({ idea, projectPath, force = false, style = DEFAULT_STYLE }) {
69
94
  const normalizedIdea = normalizeIdea(idea);
70
95
  if (!normalizedIdea) {
71
96
  throw new ValidationError('Missing required --idea "<rough MVP idea>". No project files were changed.');
72
97
  }
98
+ const styleName = resolveStyleName(style);
73
99
 
74
100
  const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
75
101
  if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
@@ -86,12 +112,35 @@ function runDesignStart({ idea, projectPath, force = false }) {
86
112
  runDesignBrief({ idea: normalizedIdea, projectPath: resolvedProjectPath, force });
87
113
  runDesignScreens({ projectPath: resolvedProjectPath, force });
88
114
  runDesignWireframe({ screen: "all", projectPath: resolvedProjectPath, force });
89
- runDesignSystem({ projectPath: resolvedProjectPath, force });
115
+ runDesignSystem({ projectPath: resolvedProjectPath, force, style: styleName });
90
116
 
91
117
  return {
92
118
  projectPath: resolvedProjectPath,
93
119
  written: coreOutputTargets(paths).map((target) => target.relativePath),
94
120
  forced: Boolean(force),
121
+ style: styleName,
122
+ };
123
+ }
124
+
125
+ function runDesignStartFromSpec({
126
+ fromSpecPath,
127
+ projectPath,
128
+ designBrief,
129
+ referenceDir,
130
+ profile,
131
+ }) {
132
+ const contractState = buildInputContractState({
133
+ fromSpecPath,
134
+ projectPath,
135
+ designBriefPath: designBrief,
136
+ referenceDir,
137
+ profile,
138
+ });
139
+ const statePath = writeInputContractState(contractState.projectPath, contractState);
140
+ return {
141
+ ...contractState,
142
+ statePath,
143
+ stateRelativePath: ".sdtk/design/START_INPUT_STATE.json",
95
144
  };
96
145
  }
97
146
 
@@ -99,17 +148,53 @@ function cmdStart(args) {
99
148
  const { flags } = parseFlags(args || [], START_FLAG_DEFS);
100
149
  if (flags.help) return cmdStartHelp();
101
150
 
151
+ const contractMode = Boolean(flags["from-spec"] || flags["design-brief"] || flags["reference-dir"] || flags.profile);
152
+ if (contractMode) {
153
+ const contractResult = runDesignStartFromSpec({
154
+ fromSpecPath: flags["from-spec"],
155
+ projectPath: flags["project-path"],
156
+ designBrief: flags["design-brief"],
157
+ referenceDir: flags["reference-dir"],
158
+ profile: flags.profile,
159
+ });
160
+ if (contractResult.blockers.length > 0) {
161
+ throw new ValidationError(
162
+ `SDTK-DESIGN input contract blocked: ${contractResult.blockers.join(", ")}. Review ${contractResult.stateRelativePath}.`
163
+ );
164
+ }
165
+ console.log(`[design] Started SDTK-DESIGN package: ${contractResult.projectPath}`);
166
+ console.log(`[design] Mode: from-spec`);
167
+ console.log(`[design] Wrote ${contractResult.stateRelativePath}`);
168
+ console.log(`[design] Screen model: ${contractResult.screenModel.totalScreens} explicit screen(s), readiness=${contractResult.screenModel.readiness}`);
169
+ if (contractResult.referenceDirectory && contractResult.referenceDirectory.provided) {
170
+ console.log(
171
+ `[design] Reference map: ${contractResult.referenceMap.mappedCount}/${contractResult.referenceMap.totalFiles} mapped (${contractResult.referenceMap.unmappedCount} unmapped)`
172
+ );
173
+ }
174
+ console.log(
175
+ `[design] Inputs: ${Object.entries(contractResult.artifacts)
176
+ .filter(([, artifact]) => artifact.found)
177
+ .map(([name, artifact]) => `${name}=${artifact.relativeToSpecRoot}`)
178
+ .join(", ")}`
179
+ );
180
+ console.log("[design] No raw requirement semantic parsing, .sdtk/atlas, SDTK-WIKI output, or network activity was used.");
181
+ console.log("[design] Next: sdtk-design prototype");
182
+ return 0;
183
+ }
184
+
102
185
  const result = runDesignStart({
103
186
  idea: flags.idea,
104
187
  projectPath: flags["project-path"],
105
188
  force: Boolean(flags.force),
189
+ style: flags.style,
106
190
  });
107
191
 
108
192
  console.log(`[design] Started SDTK-DESIGN package: ${result.projectPath}`);
109
193
  console.log(`[design] Wrote core artifacts: ${result.written.join(", ")}`);
194
+ console.log(`[design] Style: ${result.style}`);
110
195
  console.log(`[design] Overwrite: ${result.forced ? "enabled by --force" : "not needed"}`);
111
196
  console.log("[design] No review, handoff, URL, browser, screenshot, vision, network, .sdtk/atlas, or SDTK-WIKI output was used.");
112
- console.log("[design] Next: sdtk-design review --artifact docs/design/wireframes/LANDING.md");
197
+ console.log("[design] Next: sdtk-design prototype");
113
198
  return 0;
114
199
  }
115
200
 
@@ -117,5 +202,6 @@ module.exports = {
117
202
  cmdStart,
118
203
  cmdStartHelp,
119
204
  coreOutputTargets,
205
+ runDesignStartFromSpec,
120
206
  runDesignStart,
121
207
  };
@@ -41,6 +41,7 @@ function artifactPlan(paths) {
41
41
  { label: "Onboarding wireframe", relativePath: "docs/design/wireframes/ONBOARDING.md", filePath: path.join(paths.wireframesPath, "ONBOARDING.md"), phase: "core" },
42
42
  { label: "Dashboard wireframe", relativePath: "docs/design/wireframes/DASHBOARD.md", filePath: path.join(paths.wireframesPath, "DASHBOARD.md"), phase: "core" },
43
43
  { label: "Design system", relativePath: "docs/design/DESIGN_SYSTEM.md", filePath: paths.designSystemPath, phase: "core" },
44
+ { label: "Prototype preview", relativePath: "docs/design/prototype/index.html", filePath: paths.prototypeIndexPath, phase: "prototype" },
44
45
  { label: "Design handoff", relativePath: "docs/design/DESIGN_HANDOFF.md", filePath: paths.designHandoffPath, phase: "handoff" },
45
46
  ];
46
47
  }
@@ -52,6 +53,17 @@ function hasReview(paths) {
52
53
  return fs.readdirSync(paths.reviewsPath).some((name) => /^DESIGN_REVIEW_\d{8}\.md$/.test(name));
53
54
  }
54
55
 
56
+ function readInputContractState(paths) {
57
+ if (!fs.existsSync(paths.designStartInputStatePath) || !fs.statSync(paths.designStartInputStatePath).isFile()) {
58
+ return null;
59
+ }
60
+ try {
61
+ return JSON.parse(fs.readFileSync(paths.designStartInputStatePath, "utf-8"));
62
+ } catch (_err) {
63
+ return null;
64
+ }
65
+ }
66
+
55
67
  function inspectDesignStatus(projectPath) {
56
68
  const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
57
69
  if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
@@ -64,14 +76,24 @@ function inspectDesignStatus(projectPath) {
64
76
  exists: fs.existsSync(artifact.filePath),
65
77
  }));
66
78
  const reviewExists = hasReview(paths);
79
+ const inputContractState = readInputContractState(paths);
67
80
  const coreMissing = artifacts.filter((artifact) => artifact.phase === "core" && !artifact.exists);
81
+ const prototypeMissing = artifacts.find((artifact) => artifact.phase === "prototype" && !artifact.exists);
68
82
  const handoffMissing = artifacts.find((artifact) => artifact.phase === "handoff" && !artifact.exists);
69
83
 
70
84
  let nextCommand = "SDTK-CODE can consume docs/design/DESIGN_HANDOFF.md";
71
- if (coreMissing.length > 0) {
85
+ if (inputContractState && inputContractState.mode === "from-spec") {
86
+ if (Array.isArray(inputContractState.blockers) && inputContractState.blockers.length > 0) {
87
+ nextCommand = "Provide missing explicit SPEC/design artifacts and re-run sdtk-design start --from-spec.";
88
+ } else {
89
+ nextCommand = "sdtk-design prototype";
90
+ }
91
+ } else if (coreMissing.length > 0) {
72
92
  nextCommand = 'sdtk-design start --idea "<idea>"';
93
+ } else if (prototypeMissing) {
94
+ nextCommand = "sdtk-design prototype";
73
95
  } else if (!reviewExists) {
74
- nextCommand = "sdtk-design review --artifact docs/design/wireframes/LANDING.md";
96
+ nextCommand = "sdtk-design review --artifact docs/design/prototype/index.html";
75
97
  } else if (handoffMissing) {
76
98
  nextCommand = "sdtk-design handoff";
77
99
  }
@@ -79,6 +101,7 @@ function inspectDesignStatus(projectPath) {
79
101
  return {
80
102
  projectPath: resolvedProjectPath,
81
103
  artifacts,
104
+ inputContractState,
82
105
  reviewExists,
83
106
  nextCommand,
84
107
  };
@@ -118,6 +141,23 @@ function cmdStatus(args) {
118
141
  }
119
142
  }
120
143
  console.log("");
144
+ console.log("Input contract:");
145
+ if (!status.inputContractState) {
146
+ console.log(" - not found");
147
+ } else {
148
+ console.log(` - mode: ${status.inputContractState.mode}`);
149
+ console.log(` - status: ${status.inputContractState.analysisStatus}`);
150
+ if (status.inputContractState.screenModel && typeof status.inputContractState.screenModel.totalScreens === "number") {
151
+ console.log(` - explicit screens: ${status.inputContractState.screenModel.totalScreens}`);
152
+ console.log(` - readiness: ${status.inputContractState.screenModel.readiness}`);
153
+ }
154
+ if (status.inputContractState.profileSelection) {
155
+ console.log(` - profile: ${status.inputContractState.profileSelection}`);
156
+ }
157
+ const blockers = Array.isArray(status.inputContractState.blockers) ? status.inputContractState.blockers : [];
158
+ console.log(` - blockers: ${blockers.length > 0 ? blockers.join(", ") : "none"}`);
159
+ }
160
+ console.log("");
121
161
  console.log(`Next recommended command: ${status.nextCommand}`);
122
162
  console.log("No .sdtk/atlas, SDTK-WIKI output, network, or app code was modified.");
123
163
  return 0;
@@ -5,21 +5,28 @@ const path = require("path");
5
5
  const { parseFlags } = require("../lib/args");
6
6
  const { describeDesignPaths, resolveProjectPath } = require("../lib/design-paths");
7
7
  const { ValidationError } = require("../lib/errors");
8
+ const { DEFAULT_STYLE, availableStyleNames, getStylePreset, resolveStyleName } = require("../lib/style-presets");
8
9
 
9
10
  const SYSTEM_FLAG_DEFS = {
10
11
  help: { type: "boolean" },
11
12
  "project-path": { type: "string" },
12
13
  force: { type: "boolean" },
14
+ style: { type: "string" },
13
15
  };
14
16
 
15
17
  function cmdSystemHelp() {
16
18
  console.log(`SDTK-DESIGN System
17
19
 
18
20
  Usage:
19
- sdtk-design system [--project-path <path>] [--force]
21
+ sdtk-design system [--style <preset>] [--project-path <path>] [--force]
20
22
 
21
- Example:
23
+ Examples:
22
24
  sdtk-design system
25
+ sdtk-design system --style premium-dashboard
26
+
27
+ Style presets:
28
+ ${availableStyleNames().join(", ")}
29
+ Default: ${DEFAULT_STYLE}
23
30
 
24
31
  Reads:
25
32
  docs/design/DESIGN_BRIEF.md
@@ -42,7 +49,12 @@ function includesAny(text, terms) {
42
49
  return terms.some((term) => value.includes(term));
43
50
  }
44
51
 
45
- function systemContent(briefContent, screenMapContent) {
52
+ function linesForBullets(items) {
53
+ return items.map((item) => `- ${item}`);
54
+ }
55
+
56
+ function systemContent(briefContent, screenMapContent, style = DEFAULT_STYLE) {
57
+ const preset = getStylePreset(style);
46
58
  const source = `${briefContent}\n${screenMapContent}`;
47
59
  const isCrm = includesAny(source, ["crm", "lead", "follow-up", "pipeline"]);
48
60
  const primaryAction = isCrm ? "Add first lead" : "Start first workflow";
@@ -57,26 +69,24 @@ function systemContent(briefContent, screenMapContent) {
57
69
  "- Favor clarity, scannability, and obvious next actions over decorative marketing composition.",
58
70
  `- Primary repeated action: ${primaryAction}.`,
59
71
  "",
72
+ "## Visual Preset",
73
+ "",
74
+ `- Preset: ${resolveStyleName(style)} (${preset.label}).`,
75
+ `- Direction: ${preset.summary}`,
76
+ "- Presets are compact SDTK-DESIGN guidance adapted from reference patterns, not imported runtime code.",
77
+ "",
60
78
  "## Typography",
61
79
  "",
62
- "- Font stack: system UI, Segoe UI, Roboto, Helvetica, Arial, sans-serif.",
63
- "- Page title: 28px, 36px line-height, semibold.",
64
- "- Section heading: 18px, 26px line-height, semibold.",
65
- "- Body text: 15px, 22px line-height, regular.",
66
- "- Label text: 13px, 18px line-height, medium.",
80
+ ...linesForBullets(preset.typography),
67
81
  "- Do not scale font size with viewport width; keep letter spacing at 0.",
68
82
  "",
69
83
  "## Color Tokens",
70
84
  "",
71
- "- `color-bg`: #F7F8FA for the application background.",
72
- "- `color-surface`: #FFFFFF for panels, forms, and lists.",
73
- "- `color-text`: #1F2933 for primary text.",
74
- "- `color-muted`: #667085 for secondary text.",
75
- "- `color-primary`: #2563EB for the main CTA and selected states.",
76
- "- `color-success`: #0F8A5F for confirmed progress.",
77
- "- `color-warning`: #B7791F for attention without blocking.",
78
- "- `color-danger`: #B42318 for destructive or failed states.",
79
- "- `color-border`: #D9DEE7 for quiet dividers and input borders.",
85
+ ...linesForBullets(preset.colors),
86
+ "",
87
+ "## Surface / Background Strategy",
88
+ "",
89
+ `- ${preset.surface}`,
80
90
  "",
81
91
  "## Spacing",
82
92
  "",
@@ -94,6 +104,7 @@ function systemContent(briefContent, screenMapContent) {
94
104
  "- Icon buttons: square 36px control with tooltip when the icon is not self-evident.",
95
105
  "- Disabled state: 45% opacity with no hover elevation.",
96
106
  "- CTA copy should use verbs tied to the workflow, such as Add, Save, Continue, or Review.",
107
+ `- Preset CTA treatment: ${preset.cta}`,
97
108
  "",
98
109
  "## Card Style",
99
110
  "",
@@ -102,6 +113,11 @@ function systemContent(briefContent, screenMapContent) {
102
113
  "- Border: 1px solid `color-border`; avoid heavy shadows.",
103
114
  "- Card content order: status, title, next action, supporting metadata.",
104
115
  "- Avoid nested cards and decorative gradient backgrounds.",
116
+ `- Preset density rule: ${preset.density}`,
117
+ "",
118
+ "## Table / Dashboard Density",
119
+ "",
120
+ `- ${preset.dashboard}`,
105
121
  "",
106
122
  "## Form Style",
107
123
  "",
@@ -111,6 +127,15 @@ function systemContent(briefContent, screenMapContent) {
111
127
  "- Prefer segmented controls or toggles for short option sets.",
112
128
  "- Forms should ask for the minimum needed before the first useful action.",
113
129
  "",
130
+ "## Component Recipe Hints",
131
+ "",
132
+ ...linesForBullets(preset.components),
133
+ "",
134
+ "## Mobile Layout Rules",
135
+ "",
136
+ `- ${preset.mobile}`,
137
+ "- Keep text within parent containers at mobile widths and avoid viewport-scaled type.",
138
+ "",
114
139
  "## Tone",
115
140
  "",
116
141
  `- Use practical language around ${toneObject}; avoid enterprise jargon.`,
@@ -120,6 +145,7 @@ function systemContent(briefContent, screenMapContent) {
120
145
  "",
121
146
  "## Accessibility Baseline",
122
147
  "",
148
+ `- Preset accessibility emphasis: ${preset.accessibility}`,
123
149
  "- Maintain visible keyboard focus for all interactive controls.",
124
150
  "- Use 4.5:1 contrast for body text and 3:1 for large text and essential UI boundaries.",
125
151
  "- Do not rely on color alone for status; pair status color with text or icon labels.",
@@ -136,7 +162,8 @@ function systemContent(briefContent, screenMapContent) {
136
162
  ].join("\n");
137
163
  }
138
164
 
139
- function runDesignSystem({ projectPath, force = false }) {
165
+ function runDesignSystem({ projectPath, force = false, style = DEFAULT_STYLE }) {
166
+ const styleName = resolveStyleName(style);
140
167
  const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
141
168
  if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
142
169
  throw new ValidationError(`--project-path is not a valid directory: ${resolvedProjectPath}. No project files were changed.`);
@@ -156,12 +183,13 @@ function runDesignSystem({ projectPath, force = false }) {
156
183
  const briefContent = fs.readFileSync(paths.designBriefPath, "utf-8");
157
184
  const screenMapContent = fs.readFileSync(paths.screenMapPath, "utf-8");
158
185
  fs.mkdirSync(path.dirname(paths.designSystemPath), { recursive: true });
159
- fs.writeFileSync(paths.designSystemPath, systemContent(briefContent, screenMapContent), "utf-8");
186
+ fs.writeFileSync(paths.designSystemPath, systemContent(briefContent, screenMapContent, styleName), "utf-8");
160
187
 
161
188
  return {
162
189
  projectPath: resolvedProjectPath,
163
190
  relativeDesignSystemPath: "docs/design/DESIGN_SYSTEM.md",
164
191
  forced: Boolean(force),
192
+ style: styleName,
165
193
  };
166
194
  }
167
195
 
@@ -172,9 +200,11 @@ function cmdSystem(args) {
172
200
  const result = runDesignSystem({
173
201
  projectPath: flags["project-path"],
174
202
  force: Boolean(flags.force),
203
+ style: flags.style,
175
204
  });
176
205
 
177
206
  console.log(`[design] Wrote ${result.relativeDesignSystemPath}: ${result.projectPath}`);
207
+ console.log(`[design] Style: ${result.style}`);
178
208
  console.log(`[design] Overwrite: ${result.forced ? "enabled by --force" : "not needed"}`);
179
209
  console.log("[design] No .sdtk/atlas, SDTK-WIKI output, source files, network, or app code was modified.");
180
210
  console.log("[design] Next: sdtk-design handoff");
package/src/index.js CHANGED
@@ -4,6 +4,7 @@ const { cmdBrief } = require("./commands/brief");
4
4
  const { cmdHandoff } = require("./commands/handoff");
5
5
  const { cmdHelp } = require("./commands/help");
6
6
  const { cmdInit } = require("./commands/init");
7
+ const { cmdPrototype } = require("./commands/prototype");
7
8
  const { cmdReview } = require("./commands/review");
8
9
  const { cmdScreens } = require("./commands/screens");
9
10
  const { cmdStart } = require("./commands/start");
@@ -66,6 +67,9 @@ async function run(argv) {
66
67
  if (command === "review") {
67
68
  return cmdReview(args);
68
69
  }
70
+ if (command === "prototype") {
71
+ return cmdPrototype(args);
72
+ }
69
73
  if (command === "start") {
70
74
  return cmdStart(args);
71
75
  }