sdtk-design-kit 0.1.0 → 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 +46 -9
- package/package.json +1 -1
- package/src/commands/handoff.js +83 -8
- package/src/commands/help.js +14 -6
- package/src/commands/prototype.js +352 -0
- package/src/commands/review.js +56 -4
- package/src/commands/start.js +15 -4
- package/src/commands/status.js +5 -1
- package/src/commands/system.js +49 -19
- package/src/index.js +4 -0
- package/src/lib/design-paths.js +6 -0
- package/src/lib/domain-profile.js +108 -0
- package/src/lib/style-presets.js +150 -0
package/src/commands/system.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
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
|
}
|
package/src/lib/design-paths.js
CHANGED
|
@@ -5,6 +5,8 @@ const path = require("path");
|
|
|
5
5
|
const DESIGN_DOCS_RELATIVE = path.join("docs", "design");
|
|
6
6
|
const DESIGN_WIREFRAMES_RELATIVE = path.join("docs", "design", "wireframes");
|
|
7
7
|
const DESIGN_REVIEWS_RELATIVE = path.join("docs", "design", "reviews");
|
|
8
|
+
const DESIGN_PROTOTYPE_RELATIVE = path.join("docs", "design", "prototype");
|
|
9
|
+
const DESIGN_PROTOTYPE_INDEX_RELATIVE = path.join("docs", "design", "prototype", "index.html");
|
|
8
10
|
const DESIGN_README_RELATIVE = path.join("docs", "design", "README.md");
|
|
9
11
|
const DESIGN_BRIEF_RELATIVE = path.join("docs", "design", "DESIGN_BRIEF.md");
|
|
10
12
|
const DESIGN_SCREEN_MAP_RELATIVE = path.join("docs", "design", "SCREEN_MAP.md");
|
|
@@ -44,6 +46,8 @@ function describeDesignPaths(projectPath) {
|
|
|
44
46
|
designDocsPath: path.join(root, DESIGN_DOCS_RELATIVE),
|
|
45
47
|
wireframesPath: path.join(root, DESIGN_WIREFRAMES_RELATIVE),
|
|
46
48
|
reviewsPath: path.join(root, DESIGN_REVIEWS_RELATIVE),
|
|
49
|
+
prototypePath: path.join(root, DESIGN_PROTOTYPE_RELATIVE),
|
|
50
|
+
prototypeIndexPath: path.join(root, DESIGN_PROTOTYPE_INDEX_RELATIVE),
|
|
47
51
|
designReadmePath: path.join(root, DESIGN_README_RELATIVE),
|
|
48
52
|
designBriefPath: path.join(root, DESIGN_BRIEF_RELATIVE),
|
|
49
53
|
screenMapPath: path.join(root, DESIGN_SCREEN_MAP_RELATIVE),
|
|
@@ -59,6 +63,8 @@ module.exports = {
|
|
|
59
63
|
DESIGN_DOCS_RELATIVE,
|
|
60
64
|
DESIGN_HANDOFF_RELATIVE,
|
|
61
65
|
DESIGN_MANIFEST_RELATIVE,
|
|
66
|
+
DESIGN_PROTOTYPE_INDEX_RELATIVE,
|
|
67
|
+
DESIGN_PROTOTYPE_RELATIVE,
|
|
62
68
|
DESIGN_README_RELATIVE,
|
|
63
69
|
DESIGN_REVIEWS_RELATIVE,
|
|
64
70
|
DESIGN_SCREEN_MAP_RELATIVE,
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function normalizeText(value) {
|
|
4
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function extractSourceIdea(briefContent) {
|
|
8
|
+
const match = String(briefContent || "").match(/## Source Idea\s+([\s\S]*?)(?:\n## |\s*$)/i);
|
|
9
|
+
return normalizeText(match ? match[1] : "");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function extractProductName(sourceIdea, crmLike) {
|
|
13
|
+
const clean = normalizeText(sourceIdea);
|
|
14
|
+
const namedBuild = clean.match(/\bbuild\s+([A-Z][A-Za-z0-9-]{2,40})(?:,|\s+for|\s+to|\s+that|\s+-)/);
|
|
15
|
+
if (namedBuild) return namedBuild[1];
|
|
16
|
+
return crmLike ? "Client Workspace" : "MVP Workspace";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function slugify(value) {
|
|
20
|
+
const slug = normalizeText(value)
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
23
|
+
.replace(/^-+|-+$/g, "");
|
|
24
|
+
return slug || "mvp-workspace";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function inferDomainProfile({ briefContent = "", screenMapContent = "", designSystemContent = "", wireframeContents = [] } = {}) {
|
|
28
|
+
const sourceIdea = extractSourceIdea(briefContent);
|
|
29
|
+
const combined = normalizeText([sourceIdea, briefContent, screenMapContent, designSystemContent, ...wireframeContents].join(" "));
|
|
30
|
+
const domainSource = normalizeText([sourceIdea, briefContent].join(" "));
|
|
31
|
+
const lower = domainSource.toLowerCase();
|
|
32
|
+
const crmLike = /\b(crm|lead|leads|pipeline|follow-up|follow up|consultant|client conversation|opportunity)\b/.test(lower);
|
|
33
|
+
const productName = extractProductName(sourceIdea || combined, crmLike);
|
|
34
|
+
const slug = slugify(productName);
|
|
35
|
+
|
|
36
|
+
if (crmLike) {
|
|
37
|
+
return {
|
|
38
|
+
kind: "crm",
|
|
39
|
+
productName,
|
|
40
|
+
slug,
|
|
41
|
+
targetUser: "the primary user",
|
|
42
|
+
promise: "Track leads, notes, follow-ups, and simple pipeline status without enterprise CRM weight.",
|
|
43
|
+
setupLabel: "Workspace",
|
|
44
|
+
setupNameLabel: "Workspace name",
|
|
45
|
+
setupNameValue: `${productName} Consulting`,
|
|
46
|
+
primaryAction: "Create workspace",
|
|
47
|
+
secondaryAction: "Preview dashboard",
|
|
48
|
+
itemSingular: "lead",
|
|
49
|
+
itemPlural: "leads",
|
|
50
|
+
itemLabel: "Lead",
|
|
51
|
+
itemNameLabel: "Name",
|
|
52
|
+
itemNameValue: "Avery Lee",
|
|
53
|
+
itemContextLabel: "Next follow-up",
|
|
54
|
+
itemContextValue: "2026-05-27",
|
|
55
|
+
itemAction: "Add lead",
|
|
56
|
+
saveAction: "Save lead",
|
|
57
|
+
collectionSurface: "lead list",
|
|
58
|
+
dashboardTitle: "Pipeline dashboard with next actions visible.",
|
|
59
|
+
emptyState: "Empty state: when no leads exist, keep the first-create CTA prominent.",
|
|
60
|
+
statusTaxonomy: ["New", "Contacted", "Proposal", "Won", "Lost"],
|
|
61
|
+
statusExample: "Proposal",
|
|
62
|
+
metricLabels: ["Active leads", "Due follow-ups", "Proposal value", "Won this month"],
|
|
63
|
+
metricValues: ["12", "4", "$18k", "3"],
|
|
64
|
+
modelName: "Lead",
|
|
65
|
+
storageKeys: [`${slug}.workspace`, `${slug}.leads`],
|
|
66
|
+
nonGoals: "Enterprise CRM automation, complex analytics, billing, or multi-team administration.",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
kind: "generic",
|
|
72
|
+
productName,
|
|
73
|
+
slug,
|
|
74
|
+
targetUser: "the primary user",
|
|
75
|
+
promise: "Turn the core MVP workflow into a clear launch, setup, and dashboard experience.",
|
|
76
|
+
setupLabel: "Workspace",
|
|
77
|
+
setupNameLabel: "Workspace name",
|
|
78
|
+
setupNameValue: `${productName} Team`,
|
|
79
|
+
primaryAction: "Start setup",
|
|
80
|
+
secondaryAction: "Preview dashboard",
|
|
81
|
+
itemSingular: "work item",
|
|
82
|
+
itemPlural: "work items",
|
|
83
|
+
itemLabel: "Work item",
|
|
84
|
+
itemNameLabel: "Item name",
|
|
85
|
+
itemNameValue: "First workflow",
|
|
86
|
+
itemContextLabel: "Next action",
|
|
87
|
+
itemContextValue: "Review progress",
|
|
88
|
+
itemAction: "Add item",
|
|
89
|
+
saveAction: "Save item",
|
|
90
|
+
collectionSurface: "work queue",
|
|
91
|
+
dashboardTitle: "Dashboard with current work and next actions visible.",
|
|
92
|
+
emptyState: "Empty state: when no work items exist, keep the first-create CTA prominent.",
|
|
93
|
+
statusTaxonomy: ["New", "In Progress", "Review", "Done"],
|
|
94
|
+
statusExample: "In Progress",
|
|
95
|
+
metricLabels: ["Active items", "Due actions", "In review", "Completed"],
|
|
96
|
+
metricValues: ["8", "3", "2", "5"],
|
|
97
|
+
modelName: "WorkItem",
|
|
98
|
+
storageKeys: [`${slug}.workspace`, `${slug}.items`],
|
|
99
|
+
nonGoals: "Enterprise workflow automation, advanced analytics, billing, or multi-role administration.",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
extractSourceIdea,
|
|
105
|
+
inferDomainProfile,
|
|
106
|
+
normalizeText,
|
|
107
|
+
slugify,
|
|
108
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { ValidationError } = require("./errors");
|
|
4
|
+
|
|
5
|
+
const DEFAULT_STYLE = "minimal-saas";
|
|
6
|
+
|
|
7
|
+
const STYLE_PRESETS = {
|
|
8
|
+
"minimal-saas": {
|
|
9
|
+
label: "Minimal SaaS",
|
|
10
|
+
summary: "Clean solo-founder SaaS default with practical hierarchy, quiet surfaces, and low-decoration components.",
|
|
11
|
+
typography: [
|
|
12
|
+
"Use system UI, Segoe UI, Roboto, Helvetica, Arial, sans-serif.",
|
|
13
|
+
"Use a clear 32/24/18/15/13px hierarchy for page, section, card, body, and label text.",
|
|
14
|
+
"Keep letter spacing at 0 except optional uppercase micro-labels at 0.04em.",
|
|
15
|
+
],
|
|
16
|
+
colors: [
|
|
17
|
+
"`color-bg`: #F7F8FA for the application background.",
|
|
18
|
+
"`color-surface`: #FFFFFF for panels, forms, and lists.",
|
|
19
|
+
"`color-text`: #1F2933 for primary text.",
|
|
20
|
+
"`color-muted`: #667085 for secondary text.",
|
|
21
|
+
"`color-primary`: #2563EB for the main CTA and selected states.",
|
|
22
|
+
"`color-success`: #0F8A5F, `color-warning`: #B7791F, `color-danger`: #B42318.",
|
|
23
|
+
"`color-border`: #D9DEE7 for quiet dividers and input borders.",
|
|
24
|
+
],
|
|
25
|
+
surface: "Use white cards on a soft gray page. Prefer borders over shadows and keep radius at 8px or less.",
|
|
26
|
+
density: "Use medium density: 16px form/list gaps, 24px section gaps, and compact repeated cards.",
|
|
27
|
+
cta: "One filled primary CTA per screen; secondary actions should be outlined or text-only.",
|
|
28
|
+
dashboard: "Use 3-4 summary cards, a simple list/table, status pills with text, and an obvious empty state.",
|
|
29
|
+
mobile: "Collapse grids to one column under 760px and keep controls at least 44px tall.",
|
|
30
|
+
accessibility: "Prioritize contrast, visible focus, labeled inputs, and text labels for status.",
|
|
31
|
+
components: [
|
|
32
|
+
"Metric card: label, value, short delta.",
|
|
33
|
+
"Lead card/table row: status, name, next action, note metadata.",
|
|
34
|
+
"Form row: label, input, helper or inline error.",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
"premium-dashboard": {
|
|
38
|
+
label: "Premium Dashboard",
|
|
39
|
+
summary: "Dashboard-first MVP style with stronger density, metrics, cards, tables, and command surfaces.",
|
|
40
|
+
typography: [
|
|
41
|
+
"Use Inter-like system sans with tabular numerals for metrics.",
|
|
42
|
+
"Use a 34/24/18/14/12px hierarchy with heavier labels and compact metadata.",
|
|
43
|
+
"Reserve monospace only for technical IDs, timestamps, or compact metric labels.",
|
|
44
|
+
],
|
|
45
|
+
colors: [
|
|
46
|
+
"`color-bg`: #0B1020 for a deep operational shell or #F5F7FB for light dashboards.",
|
|
47
|
+
"`color-surface`: #FFFFFF for light cards or #111827 for dark panels.",
|
|
48
|
+
"`color-text`: #111827 on light surfaces and #F8FAFC on dark surfaces.",
|
|
49
|
+
"`color-muted`: #64748B for secondary text.",
|
|
50
|
+
"`color-primary`: #0F766E for primary actions and active states.",
|
|
51
|
+
"`color-accent`: #2563EB for one chart or selected metric highlight.",
|
|
52
|
+
"`color-border`: #D8E0EA for light mode or rgba(255,255,255,0.10) for dark panels.",
|
|
53
|
+
],
|
|
54
|
+
surface: "Use a structured dashboard shell with cards, tables, and status surfaces. Shadows can be slightly stronger but must stay functional.",
|
|
55
|
+
density: "Use higher density: 12px row gaps, 14-16px card padding, aligned KPI tracks, and scannable table rows.",
|
|
56
|
+
cta: "Primary CTA should be compact and task-oriented, such as Add lead or Review pipeline.",
|
|
57
|
+
dashboard: "Lead with 4 KPI cards, then a pipeline/list region. Keep status, follow-up, and note snippets visible without opening details.",
|
|
58
|
+
mobile: "Stack metric cards into two columns then one column; avoid horizontal scrolling for core lead data.",
|
|
59
|
+
accessibility: "Status color must always pair with text. Keep focus rings visible on dense controls.",
|
|
60
|
+
components: [
|
|
61
|
+
"KPI card: label, value, trend, comparison.",
|
|
62
|
+
"Pipeline panel: status pill, count, next action.",
|
|
63
|
+
"Command surface: compact primary action plus one secondary filter/control.",
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
"bold-founder": {
|
|
67
|
+
label: "Bold Founder",
|
|
68
|
+
summary: "High-contrast launch style for marketing-heavy MVPs with decisive type and CTA hierarchy.",
|
|
69
|
+
typography: [
|
|
70
|
+
"Use a heavy display face fallback stack: Impact, Arial Black, Inter, system-ui.",
|
|
71
|
+
"Use a 56/36/24/16/13px hierarchy and keep body copy short.",
|
|
72
|
+
"Use uppercase labels sparingly for proof points and section markers.",
|
|
73
|
+
],
|
|
74
|
+
colors: [
|
|
75
|
+
"`color-bg`: #111111 for dark launch sections or #FFF7ED for high-contrast light sections.",
|
|
76
|
+
"`color-surface`: #FFFFFF or #18181B for framed proof blocks.",
|
|
77
|
+
"`color-text`: #FAFAFA on dark and #111827 on light.",
|
|
78
|
+
"`color-muted`: #A1A1AA for secondary text.",
|
|
79
|
+
"`color-primary`: #F97316 for the main CTA.",
|
|
80
|
+
"`color-accent`: #22C55E for one success/proof highlight.",
|
|
81
|
+
"`color-border`: #27272A or #111827 for strong edges.",
|
|
82
|
+
],
|
|
83
|
+
surface: "Use bold framed sections, hard borders, and clear contrast. Avoid decorative clutter.",
|
|
84
|
+
density: "Use lower density in hero areas, then compact proof cards and feature rows.",
|
|
85
|
+
cta: "CTA should be visually dominant, direct, and above the fold.",
|
|
86
|
+
dashboard: "If a dashboard is needed, use bold stat cards and clear status blocks rather than subtle utility chrome.",
|
|
87
|
+
mobile: "Reduce hero type, keep CTA full-width, and stack proof points in a single column.",
|
|
88
|
+
accessibility: "High contrast is required; do not use accent-only meaning.",
|
|
89
|
+
components: [
|
|
90
|
+
"Hero lockup: large headline, short proof line, primary CTA.",
|
|
91
|
+
"Proof card: metric, label, customer-facing implication.",
|
|
92
|
+
"Status badge: strong text label plus color.",
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
"warm-editorial": {
|
|
96
|
+
label: "Warm Editorial",
|
|
97
|
+
summary: "Softer consultant/service-product style with restrained serif-led hierarchy and warm surfaces.",
|
|
98
|
+
typography: [
|
|
99
|
+
"Use Georgia, Charter, or Times New Roman for display; use system sans for UI labels and forms.",
|
|
100
|
+
"Use a 44/30/22/16/13px hierarchy with generous line-height for reading.",
|
|
101
|
+
"Use serif display for promise, stats, and narrative sections; keep forms sans and literal.",
|
|
102
|
+
],
|
|
103
|
+
colors: [
|
|
104
|
+
"`color-bg`: #FAF7F2 warm off-white.",
|
|
105
|
+
"`color-surface`: #FFFFFF for elevated content.",
|
|
106
|
+
"`color-text`: #1C1A17 near-black.",
|
|
107
|
+
"`color-muted`: #8A817A for metadata and secondary copy.",
|
|
108
|
+
"`color-primary`: #C0512F terracotta for primary CTA.",
|
|
109
|
+
"`color-accent`: #2F5B4F forest for tags and section dividers.",
|
|
110
|
+
"`color-border`: rgba(47,91,79,0.16) for restrained structure.",
|
|
111
|
+
],
|
|
112
|
+
surface: "Use warm paper backgrounds, restrained cards, and minimal shadows.",
|
|
113
|
+
density: "Use generous spacing for landing and onboarding, then moderate density for dashboard cards.",
|
|
114
|
+
cta: "Primary CTA uses terracotta fill; secondary actions stay outlined.",
|
|
115
|
+
dashboard: "Use readable cards and notes-first presentation for relationship-led work, not heavy analytics chrome.",
|
|
116
|
+
mobile: "Keep reading width narrow, stack cards, and reduce vertical spacing by about one third.",
|
|
117
|
+
accessibility: "Warm palettes still need contrast checks; avoid low-contrast tan-on-cream labels.",
|
|
118
|
+
components: [
|
|
119
|
+
"Editorial hero: serif headline, concise support copy, terracotta CTA.",
|
|
120
|
+
"Relationship card: lead, latest note, next follow-up.",
|
|
121
|
+
"Tag row: forest-accent labels with readable text.",
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
function availableStyleNames() {
|
|
127
|
+
return Object.keys(STYLE_PRESETS);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function resolveStyleName(style) {
|
|
131
|
+
const normalized = String(style || DEFAULT_STYLE).trim().toLowerCase();
|
|
132
|
+
if (STYLE_PRESETS[normalized]) {
|
|
133
|
+
return normalized;
|
|
134
|
+
}
|
|
135
|
+
throw new ValidationError(
|
|
136
|
+
`Unsupported --style "${style}". Supported styles: ${availableStyleNames().join(", ")}. No project files were changed.`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getStylePreset(style) {
|
|
141
|
+
return STYLE_PRESETS[resolveStyleName(style)];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
DEFAULT_STYLE,
|
|
146
|
+
STYLE_PRESETS,
|
|
147
|
+
availableStyleNames,
|
|
148
|
+
getStylePreset,
|
|
149
|
+
resolveStyleName,
|
|
150
|
+
};
|