sdtk-design-kit 0.2.1 → 0.3.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/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/help.js +3 -3
- package/src/commands/init.js +53 -0
- package/src/commands/prototype.js +140 -653
- package/src/commands/review.js +515 -525
- package/src/commands/start.js +22 -5
- package/src/commands/status.js +10 -2
- package/src/commands/system.js +186 -14
- 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/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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sdtk-design-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Local-first MVP design planner and reviewer for SDTK workspaces.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sdtk-design": "bin/sdtk-design.js"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"type": "commonjs",
|
|
10
10
|
"files": [
|
|
11
11
|
"bin/",
|
|
12
|
+
"skills/",
|
|
12
13
|
"src/",
|
|
13
14
|
"README.md"
|
|
14
15
|
],
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-prototype
|
|
3
|
+
description: Generate high-fidelity standalone HTML prototypes from an SDTK-DESIGN generation manifest, design system, design tokens, and per-screen briefs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Design Prototype
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks you to generate, regenerate, or polish SDTK-DESIGN prototype screen HTML from `docs/design/prototype/.manifest.json`.
|
|
9
|
+
|
|
10
|
+
## Inputs
|
|
11
|
+
|
|
12
|
+
Read these files before writing any screen HTML:
|
|
13
|
+
|
|
14
|
+
1. `docs/design/prototype/.manifest.json`
|
|
15
|
+
2. This skill's `references/designer-charter.md`
|
|
16
|
+
3. This skill's `references/craft.md`
|
|
17
|
+
4. `docs/design/DESIGN_SYSTEM.md`
|
|
18
|
+
5. `docs/design/DESIGN_TOKENS.json`
|
|
19
|
+
6. Each screen brief named by the manifest entry's `briefPath`
|
|
20
|
+
|
|
21
|
+
The manifest contract is:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"screens": [
|
|
26
|
+
{
|
|
27
|
+
"screenId": "home",
|
|
28
|
+
"title": "Home",
|
|
29
|
+
"role": "home",
|
|
30
|
+
"briefPath": "docs/design/screens/home_DESIGN_BRIEF.md",
|
|
31
|
+
"outPath": "docs/design/prototype/screens/home.html"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Workflow
|
|
38
|
+
|
|
39
|
+
1. Validate that the manifest exists and that every screen entry has `screenId`, `title`, `role`, `briefPath`, and `outPath`.
|
|
40
|
+
2. Read the charter and craft references once for the whole run.
|
|
41
|
+
3. Read `DESIGN_SYSTEM.md` and `DESIGN_TOKENS.json` as the only brand and visual-direction authority.
|
|
42
|
+
4. For each screen, read its `briefPath`, plan the page structure, then write exactly one complete standalone `<!doctype html>` document to its `outPath`.
|
|
43
|
+
5. Keep CSS inline in the file. Do not create external CSS, JavaScript, image, or font files.
|
|
44
|
+
6. Keep each generated HTML file under roughly 1000 lines.
|
|
45
|
+
7. Use real brief-sourced content, data slots, states, actions, and screen purpose. Do not add filler sections to make the page feel dense.
|
|
46
|
+
8. Verify each file after writing: complete doctype, `<html>`, `<head>`, `<body>`, non-empty `<title>`, one `<h1>`, visible state coverage, token usage, no raw debug JSON, no placeholder/filler copy.
|
|
47
|
+
|
|
48
|
+
## NEEDS_* slot rendering convention (BK-227)
|
|
49
|
+
|
|
50
|
+
When emitting NEEDS_* placeholders in screen HTML, distinguish two slot classes by checking the brief's per-section `interactive_data_slots` array (sidecar JSON) or the `interactive=` detail in the brief markdown:
|
|
51
|
+
|
|
52
|
+
**Interactive slots** — markers listed in `interactive_data_slots`. These sit inside repeated card / row / list structures (product cards, order rows, search results, category cards, nav items). Emit each as:
|
|
53
|
+
|
|
54
|
+
<a href="#" data-needs="NEEDS_PRODUCT_NAME">NEEDS_PRODUCT_NAME</a>
|
|
55
|
+
|
|
56
|
+
**Static slots** — every other NEEDS_* marker (brand name in header, footer URLs, hero imagery, single-occurrence content). Emit as:
|
|
57
|
+
|
|
58
|
+
<span data-needs="NEEDS_BRAND_NAME">NEEDS_BRAND_NAME</span>
|
|
59
|
+
|
|
60
|
+
Rules:
|
|
61
|
+
- `href="#"` is the ONLY permitted destination for skeleton anchors. Never invent URLs.
|
|
62
|
+
- The visible marker text and the `data-needs` attribute value are identical across both classes — only the wrapper element changes.
|
|
63
|
+
- If a section mixes interactive and static slots, look up class per marker via set membership on `interactive_data_slots`.
|
|
64
|
+
- Skeleton anchors preserve BK-225 honesty (the `data-needs` attribute marks the gap) and BK-217 no-invented-content (`#` is not a real destination).
|
|
65
|
+
|
|
66
|
+
### Density rule for repeated structures (BK-231)
|
|
67
|
+
|
|
68
|
+
When a section's `interactive_data_slots` is non-empty (BK-227 interactive) OR the section's `components` include a card / row / list-item / nav-item pattern, render **3–4 sample instances** rather than 1. Each instance reuses the same skeleton anchor + marker set from BK-227.
|
|
69
|
+
|
|
70
|
+
Cap at 4 instances per section to keep file size bounded (per-screen HTML must stay under ~1000 lines). Single-occurrence interactive slots (e.g. one cart-summary panel, one add-to-cart button on PDP) are unaffected — apply density only to repeated structures.
|
|
71
|
+
|
|
72
|
+
Composition with BK-229 facts:
|
|
73
|
+
- If `tokens.brand.categories[]` has N entries → render N category instances (use supplied labels per BK-229).
|
|
74
|
+
- For repeated structures NOT bound to factual data (product cards, order rows, search results, generic list items) → render exactly 4 instances, all carrying the same `NEEDS_*` marker set.
|
|
75
|
+
|
|
76
|
+
#### Examples
|
|
77
|
+
|
|
78
|
+
Product cards in a category screen — render 4 instances per BK-231 density rule (section.interactive_data_slots contains NEEDS_PRODUCT_NAME, NEEDS_PRODUCT_IMAGE, NEEDS_SKU, NEEDS_PRICE):
|
|
79
|
+
```
|
|
80
|
+
<div class="product-grid">
|
|
81
|
+
<a href="#" data-needs="NEEDS_PRODUCT_NAME" class="product-card">
|
|
82
|
+
<span data-needs="NEEDS_PRODUCT_IMAGE" class="product-thumb">NEEDS_PRODUCT_IMAGE</span>
|
|
83
|
+
<span class="product-name">NEEDS_PRODUCT_NAME</span>
|
|
84
|
+
<span class="product-sku" data-needs="NEEDS_SKU">NEEDS_SKU</span>
|
|
85
|
+
<span class="product-price" data-needs="NEEDS_PRICE">NEEDS_PRICE</span>
|
|
86
|
+
</a>
|
|
87
|
+
<a href="#" data-needs="NEEDS_PRODUCT_NAME" class="product-card">
|
|
88
|
+
<span data-needs="NEEDS_PRODUCT_IMAGE" class="product-thumb">NEEDS_PRODUCT_IMAGE</span>
|
|
89
|
+
<span class="product-name">NEEDS_PRODUCT_NAME</span>
|
|
90
|
+
<span class="product-sku" data-needs="NEEDS_SKU">NEEDS_SKU</span>
|
|
91
|
+
<span class="product-price" data-needs="NEEDS_PRICE">NEEDS_PRICE</span>
|
|
92
|
+
</a>
|
|
93
|
+
<a href="#" data-needs="NEEDS_PRODUCT_NAME" class="product-card">
|
|
94
|
+
<span data-needs="NEEDS_PRODUCT_IMAGE" class="product-thumb">NEEDS_PRODUCT_IMAGE</span>
|
|
95
|
+
<span class="product-name">NEEDS_PRODUCT_NAME</span>
|
|
96
|
+
<span class="product-sku" data-needs="NEEDS_SKU">NEEDS_SKU</span>
|
|
97
|
+
<span class="product-price" data-needs="NEEDS_PRICE">NEEDS_PRICE</span>
|
|
98
|
+
</a>
|
|
99
|
+
<a href="#" data-needs="NEEDS_PRODUCT_NAME" class="product-card">
|
|
100
|
+
<span data-needs="NEEDS_PRODUCT_IMAGE" class="product-thumb">NEEDS_PRODUCT_IMAGE</span>
|
|
101
|
+
<span class="product-name">NEEDS_PRODUCT_NAME</span>
|
|
102
|
+
<span class="product-sku" data-needs="NEEDS_SKU">NEEDS_SKU</span>
|
|
103
|
+
<span class="product-price" data-needs="NEEDS_PRICE">NEEDS_PRICE</span>
|
|
104
|
+
</a>
|
|
105
|
+
</div>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Brand logo in a header — single occurrence, density rule does NOT apply (section.interactive_data_slots empty — static slot):
|
|
109
|
+
```
|
|
110
|
+
<a href="/" class="logo">
|
|
111
|
+
<span data-needs="NEEDS_BRAND_NAME">NEEDS_BRAND_NAME</span>
|
|
112
|
+
</a>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Order row in order-history — pattern shape shown once; renderer applies BK-231 density to emit 4 such rows (section.interactive_data_slots contains NEEDS_ORDER_ID, NEEDS_DATE, NEEDS_TOTAL):
|
|
116
|
+
```
|
|
117
|
+
<a href="#" data-needs="NEEDS_ORDER_ID" class="order-row">
|
|
118
|
+
<span class="order-id">NEEDS_ORDER_ID</span>
|
|
119
|
+
<span class="order-date" data-needs="NEEDS_DATE">NEEDS_DATE</span>
|
|
120
|
+
<span class="order-total" data-needs="NEEDS_TOTAL">NEEDS_TOTAL</span>
|
|
121
|
+
</a>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Factual content from project-facts (BK-229)
|
|
125
|
+
|
|
126
|
+
Before emitting any NEEDS_* marker, check `tokens.brand` in `DESIGN_TOKENS.json` for a matching factual value. If the field is non-null and non-empty, render the value as visible text and OMIT the `data-needs` attribute and `NEEDS_*` marker. If the field is null or empty, fall back to the BK-227 wrapper emit rule.
|
|
127
|
+
|
|
128
|
+
Marker → token field mapping:
|
|
129
|
+
|
|
130
|
+
| NEEDS_* marker | tokens.brand field |
|
|
131
|
+
|---|---|
|
|
132
|
+
| NEEDS_BRAND_NAME | brand.name |
|
|
133
|
+
| NEEDS_COMPANY_NAME | brand.company |
|
|
134
|
+
| NEEDS_CONTACT_URL | brand.contactUrl |
|
|
135
|
+
| NEEDS_PRIVACY_URL | brand.privacyUrl |
|
|
136
|
+
| NEEDS_TERMS_URL | brand.termsUrl |
|
|
137
|
+
| NEEDS_CATEGORY_LABEL | brand.categories[i].label (keyed by element's `data-key` attribute) |
|
|
138
|
+
|
|
139
|
+
For NEEDS_CATEGORY_LABEL specifically — when a slot is also classified interactive (BK-227), the result combines both rules:
|
|
140
|
+
- BK-227 wraps as `<a href="#" data-needs="NEEDS_CATEGORY_LABEL" data-key="cable">`
|
|
141
|
+
- BK-229 fills label from `brand.categories` if `{ key: "cable", label: "..." }` present
|
|
142
|
+
- Combined output: `<a href="#" data-key="cable">...label...</a>`
|
|
143
|
+
|
|
144
|
+
### Examples (BK-229)
|
|
145
|
+
|
|
146
|
+
Header brand (BK-227 static + BK-229 fact):
|
|
147
|
+
- `tokens.brand.name = "Esteam"`: `<a href="/" class="logo">Esteam</a>`
|
|
148
|
+
- `tokens.brand.name = null`: `<a href="/" class="logo"><span data-needs="NEEDS_BRAND_NAME">NEEDS_BRAND_NAME</span></a>`
|
|
149
|
+
|
|
150
|
+
Footer contact URL (BK-227 static + BK-229 fact):
|
|
151
|
+
- `tokens.brand.contactUrl = "https://example.com/contact"`: `<a href="https://example.com/contact">Contact</a>`
|
|
152
|
+
- null: `<a href="#" data-needs="NEEDS_CONTACT_URL">Contact</a>`
|
|
153
|
+
|
|
154
|
+
Category nav (BK-227 interactive + BK-229 fact, keyed by `data-key`):
|
|
155
|
+
- `tokens.brand.categories[0] = { key: "cable", label: "Cable" }`: `<a href="#" data-key="cable">Cable</a>`
|
|
156
|
+
- absent: `<a href="#" data-needs="NEEDS_CATEGORY_LABEL" data-key="cable">NEEDS_CATEGORY_LABEL</a>`
|
|
157
|
+
|
|
158
|
+
Boundary: do NOT derive `name`, `company`, `*Url`, or `categories` from requirement text, screen names, or any other source. The only source of factual content is the `tokens.brand` block populated by `--project-facts`.
|
|
159
|
+
|
|
160
|
+
## Inter-screen navigation (BK-232)
|
|
161
|
+
|
|
162
|
+
The MVP prototype is a click-through demo across 10 sibling HTML files in `docs/design/prototype/screens/`. When emitting `<a>` elements for top-nav, footer-nav, or sidebar-nav links, convert SPEC-declared route paths to file-relative paths so clicking in browser navigates to the correct sibling file.
|
|
163
|
+
|
|
164
|
+
### Canonical route → file mapping
|
|
165
|
+
|
|
166
|
+
| Route prefix (from brief / SPEC) | Target `href` value |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `/` | `home.html` |
|
|
169
|
+
| `/category` or `/category/*` | `category.html` |
|
|
170
|
+
| `/product` or `/product/*` | `product-detail.html` |
|
|
171
|
+
| `/search` | `search.html` |
|
|
172
|
+
| `/cart` | `cart.html` |
|
|
173
|
+
| `/checkout` or `/checkout/*` | `checkout.html` |
|
|
174
|
+
| `/account` or `/account/info` | `account-info.html` |
|
|
175
|
+
| `/account/orders` (exact) | `order-history.html` |
|
|
176
|
+
| `/account/orders/*` (any order id) | `order-detail.html` |
|
|
177
|
+
| `/configure` or `/configure/*` | `mode-b-configurator.html` |
|
|
178
|
+
|
|
179
|
+
### Fallback rule
|
|
180
|
+
|
|
181
|
+
Routes NOT in the mapping (`/account/quotes`, `/account/addresses`, `/logout`, `/shipping`, `/faq`, `/about`, `/help`, `/admin`, `/api/*`, etc.) → emit `href="#"` (BK-227 skeleton anchor). The placeholder is honest: no rendered file exists for the route.
|
|
182
|
+
|
|
183
|
+
### Factual URL priority (BK-229)
|
|
184
|
+
|
|
185
|
+
When `tokens.brand.contactUrl` / `privacyUrl` / `termsUrl` is non-null AND the link is a footer contact / privacy / terms link, use the supplied URL directly as `href` (overrides the mapping; BK-229 facts always take priority over routing).
|
|
186
|
+
|
|
187
|
+
### Current-page marker
|
|
188
|
+
|
|
189
|
+
When the rendered file IS the current screen (e.g. `order-history.html` rendering the `/account/orders` route), mark the matching nav link with `aria-current="page"`. Keep the `href` value pointing to the same file (self-link is fine).
|
|
190
|
+
|
|
191
|
+
### Example
|
|
192
|
+
|
|
193
|
+
In `home.html` top nav:
|
|
194
|
+
```
|
|
195
|
+
<nav aria-label="Main navigation">
|
|
196
|
+
<a href="category.html">高圧ケーブル</a>
|
|
197
|
+
<a href="category.html">開閉器(PAS)</a>
|
|
198
|
+
<a href="category.html">金具・支持材</a>
|
|
199
|
+
<a href="category.html">碍子・絶縁材</a>
|
|
200
|
+
<a href="search.html">検索</a>
|
|
201
|
+
<a href="mode-b-configurator.html">Mode B</a>
|
|
202
|
+
<a href="account-info.html" class="btn btn-secondary">アカウント</a>
|
|
203
|
+
<a href="cart.html" class="btn btn-primary">カート</a>
|
|
204
|
+
</nav>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
In `order-history.html` sidebar (current page = order-history):
|
|
208
|
+
```
|
|
209
|
+
<nav class="account-nav" aria-label="アカウントナビゲーション">
|
|
210
|
+
<h2>マイアカウント</h2>
|
|
211
|
+
<ul>
|
|
212
|
+
<li><a href="account-info.html">ダッシュボード</a></li>
|
|
213
|
+
<li><a href="order-history.html" aria-current="page">注文履歴</a></li>
|
|
214
|
+
<li><a href="#">見積一覧</a></li>
|
|
215
|
+
<li><a href="account-info.html">アカウント情報</a></li>
|
|
216
|
+
<li><a href="#">配送先一覧</a></li>
|
|
217
|
+
<li><a href="#">ログアウト</a></li>
|
|
218
|
+
</ul>
|
|
219
|
+
</nav>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## CSS scope checklist for nav elements (BK-233)
|
|
223
|
+
|
|
224
|
+
When a screen contains multiple `<nav>` elements (e.g. header nav + sidebar account nav + footer nav), generic `nav ul { ... }` rules cascade into ALL nav contexts and break nested layouts. The renderer MUST scope every nav-targeting CSS rule to its structural context.
|
|
225
|
+
|
|
226
|
+
### Required scoping per nav context
|
|
227
|
+
|
|
228
|
+
1. **Header nav** — use `header nav ul { display: flex; ... }` (NEVER bare `nav ul`).
|
|
229
|
+
2. **Sidebar nav / account nav / side panel nav** — explicit `display: block` (or `flex-direction: column`) on the scoped class selector. Example: `.account-nav ul { display: block; }`.
|
|
230
|
+
3. **Footer nav** — same discipline: `footer nav ul { display: flex; ... }` if horizontal, OR explicit `display: block` if vertical.
|
|
231
|
+
|
|
232
|
+
Rule: if a `<nav>` element appears in more than one structural context within the same screen, scope EVERY nav-targeting rule with a parent selector. Never rely on cascade defaults for nested nav.
|
|
233
|
+
|
|
234
|
+
### Negative example (DON'T)
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
/* Generic nav ul cascades into ALL nav children — breaks sidebar */
|
|
238
|
+
nav ul { list-style: none; display: flex; gap: 1.5rem; }
|
|
239
|
+
.account-nav ul { list-style: none; } /* missing display reset! cascade wins */
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Result: sidebar `<li>` items render in a horizontal row, each becoming a narrow column where Japanese/CJK characters wrap one-per-line.
|
|
243
|
+
|
|
244
|
+
### Positive example (DO)
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
/* Scope header explicitly */
|
|
248
|
+
header nav ul { list-style: none; display: flex; gap: 1.5rem; }
|
|
249
|
+
header nav li { margin: 0; }
|
|
250
|
+
|
|
251
|
+
/* Sidebar explicitly declares display: block */
|
|
252
|
+
.account-nav ul { list-style: none; display: block; margin: 0; padding: 0; }
|
|
253
|
+
.account-nav li { display: block; margin-bottom: 0.25rem; }
|
|
254
|
+
.account-nav a { display: block; padding: 0.5rem 0.75rem; }
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Result: header nav horizontal, sidebar nav proper vertical menu list.
|
|
258
|
+
|
|
259
|
+
### Sanity check during rendering
|
|
260
|
+
|
|
261
|
+
For each screen with multiple `<nav>` elements, verify before output:
|
|
262
|
+
- Does every `nav`-targeting rule have a parent selector (`header nav`, `footer nav`) OR a class scope (`.account-nav`)?
|
|
263
|
+
- Do nested nav class rules explicitly set `display`?
|
|
264
|
+
- Are there any bare `nav ul` or `nav li` rules without a parent / class scope?
|
|
265
|
+
|
|
266
|
+
If any check fails, fix the CSS before emitting the HTML file.
|
|
267
|
+
|
|
268
|
+
## Boundaries
|
|
269
|
+
|
|
270
|
+
- Brand, palette, typography, spacing, mood, and component direction come only from `docs/design/DESIGN_SYSTEM.md` and `docs/design/DESIGN_TOKENS.json`.
|
|
271
|
+
- Do not infer brand direction from requirements, source quotes, screen names, or sample file names.
|
|
272
|
+
- Do not read `docs/ui/**` before generation completes.
|
|
273
|
+
- Do not copy or hash-match sample HTML, PNG, or other reference files.
|
|
274
|
+
- Do not hardcode Esteam screen names, routes, palette, copy, or layouts.
|
|
275
|
+
- Do not call a network service, start a daemon, spawn a subprocess, or require an API key.
|
|
276
|
+
- Do not create `.sdtk/atlas`.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Prototype Craft Rules
|
|
2
|
+
|
|
3
|
+
These rules sit on top of `docs/design/DESIGN_SYSTEM.md` and `docs/design/DESIGN_TOKENS.json`. If there is a conflict, project design tokens and the design system define the brand; these rules define execution quality.
|
|
4
|
+
|
|
5
|
+
## Anti-Slop
|
|
6
|
+
|
|
7
|
+
Must-fix patterns:
|
|
8
|
+
|
|
9
|
+
- Default Tailwind indigo or purple hex values used directly.
|
|
10
|
+
- Two-stop trust gradients on hero sections.
|
|
11
|
+
- Emoji inside headings, buttons, feature icons, or primary navigation.
|
|
12
|
+
- Rounded cards with colored left-border accents.
|
|
13
|
+
- Invented metrics such as "10x faster" or "99.9% uptime".
|
|
14
|
+
- Filler copy such as lorem ipsum, sample content, placeholder text, or numbered product cards.
|
|
15
|
+
- Repeated article/card bodies that only change the heading.
|
|
16
|
+
|
|
17
|
+
Should-fix patterns:
|
|
18
|
+
|
|
19
|
+
- Generic hero, feature, pricing, FAQ, CTA sequence when the screen brief calls for an app surface.
|
|
20
|
+
- Placeholder image CDNs.
|
|
21
|
+
- More than 12 raw hex values outside `:root`.
|
|
22
|
+
- `var(--accent)` or `var(--primary)` used so often that everything competes for attention.
|
|
23
|
+
- Decorative blob or wave SVG backgrounds.
|
|
24
|
+
|
|
25
|
+
## Color
|
|
26
|
+
|
|
27
|
+
- Plan four layers before writing CSS: neutrals, one accent, semantic colors, and rare effects.
|
|
28
|
+
- Use tokens or CSS custom properties. Avoid raw hex outside `:root`.
|
|
29
|
+
- Keep accent use scarce: usually one primary CTA and one secondary signal per screen.
|
|
30
|
+
- Do not introduce a second accent unless `DESIGN_TOKENS.json` declares it.
|
|
31
|
+
- Normal text must meet WCAG AA contrast: 4.5:1 for body, 3:1 for large text and UI components.
|
|
32
|
+
- Avoid pure black and pure white backgrounds; use tokenized near-black/near-white values when available.
|
|
33
|
+
|
|
34
|
+
## Typography
|
|
35
|
+
|
|
36
|
+
- Use the font families and scale from the design system.
|
|
37
|
+
- Cap visible type levels to a small, intentional set.
|
|
38
|
+
- One element should be the dominant entry point in each visual region.
|
|
39
|
+
- Use at least two hierarchy vectors for the dominant element: scale, weight, spacing, tracking, or alignment.
|
|
40
|
+
- Body copy should stay readable, with a comfortable line length around 50-75 characters.
|
|
41
|
+
- Button and label text should be concrete and action-oriented.
|
|
42
|
+
- All-caps labels need positive tracking; display sizes need deliberate tighter tracking.
|
|
43
|
+
|
|
44
|
+
## Layout And Composition
|
|
45
|
+
|
|
46
|
+
- Prefer product-specific structure over generic cards.
|
|
47
|
+
- Use CSS Grid for real layout relationships, not only equal card rows.
|
|
48
|
+
- Vary density by screen purpose: dashboards and tables may be tight; landing and decision areas need more air.
|
|
49
|
+
- Do not place cards inside larger decorative cards.
|
|
50
|
+
- Keep navigation and repeated controls stable across screens.
|
|
51
|
+
- Use one decisive flourish: a proportion, type treatment, state surface, or interaction detail that supports the brief.
|
|
52
|
+
|
|
53
|
+
## State Coverage
|
|
54
|
+
|
|
55
|
+
Every interactive list, table, card, form, search, or workflow surface needs visible handling for:
|
|
56
|
+
|
|
57
|
+
- Loading
|
|
58
|
+
- Empty
|
|
59
|
+
- Error
|
|
60
|
+
- Populated
|
|
61
|
+
- Edge cases such as long text, missing optional fields, large result counts, or disabled actions
|
|
62
|
+
|
|
63
|
+
Empty states need a headline, plain explanation, and action. Error states need what happened, why if knowable, and what the user can do next.
|
|
64
|
+
|
|
65
|
+
## Accessibility Baseline
|
|
66
|
+
|
|
67
|
+
- One `<h1>` per document.
|
|
68
|
+
- Use semantic landmarks: `<header>`, `<nav>`, `<main>`, `<section>`, `<aside>`, `<footer>`.
|
|
69
|
+
- Prefer native controls over ARIA recreations.
|
|
70
|
+
- Inputs have visible labels.
|
|
71
|
+
- Icon-only controls have accessible names.
|
|
72
|
+
- Keyboard focus is visible with `:focus-visible`.
|
|
73
|
+
- Interactive targets should be at least 44px when practical.
|
|
74
|
+
- Do not rely on color alone for status.
|
|
75
|
+
- Respect reduced-motion preferences for animation.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Designer Charter
|
|
2
|
+
|
|
3
|
+
You are an expert designer working in HTML. Your job is to produce thoughtful, well-crafted, engineered prototype screens as standalone HTML documents.
|
|
4
|
+
|
|
5
|
+
## Operating Model
|
|
6
|
+
|
|
7
|
+
Work inside the project filesystem. The manifest tells you which files to write. Write files directly to the manifest `outPath`; do not hand off HTML through chat-only wrappers.
|
|
8
|
+
|
|
9
|
+
## Workflow
|
|
10
|
+
|
|
11
|
+
1. Understand the requested output, fidelity, constraints, screen purpose, audience, and active design system.
|
|
12
|
+
2. Explore provided resources before building. Read the manifest, design system, tokens, craft reference, and the current screen brief.
|
|
13
|
+
3. Plan the visual system before writing: background, type scale, layout pattern, density, interaction states, and one decisive flourish.
|
|
14
|
+
4. Build the HTML file. Show real structure early in the file: landmarks, clear heading, primary action, state surfaces, and content tied to the brief.
|
|
15
|
+
5. Verify the completed file before moving to the next screen.
|
|
16
|
+
|
|
17
|
+
## HTML Output Rules
|
|
18
|
+
|
|
19
|
+
- The HTML must be complete and standalone.
|
|
20
|
+
- Inline all CSS in the document.
|
|
21
|
+
- Use semantic landmarks and native controls first.
|
|
22
|
+
- Keep each file under roughly 1000 lines.
|
|
23
|
+
- Use modern CSS deliberately: Grid, container queries where useful, `color-mix()`, `text-wrap: pretty`, and custom properties.
|
|
24
|
+
|
|
25
|
+
## Content Rules
|
|
26
|
+
|
|
27
|
+
- No filler copy, dummy sections, placeholder text, or invented metrics.
|
|
28
|
+
- Use real content from the screen brief: purpose, data slots, user intent, primary action, component expectations, states, and acceptance criteria.
|
|
29
|
+
- Ask for clarification only when a required product fact is impossible to infer from the manifest, design system, tokens, and brief.
|
|
30
|
+
- Use sentence-case headings by default.
|
|
31
|
+
- Mobile hit targets are at least 44px when practical.
|
|
32
|
+
- Do not surprise-add flows or sections outside the brief.
|
|
33
|
+
|
|
34
|
+
## Anti-AI-Slop Rules
|
|
35
|
+
|
|
36
|
+
Avoid these patterns:
|
|
37
|
+
|
|
38
|
+
- Aggressive gradient backgrounds.
|
|
39
|
+
- Gratuitous emoji.
|
|
40
|
+
- Rounded boxes with a colored left-border accent.
|
|
41
|
+
- SVG-as-illustration when a neutral placeholder or real content block belongs there.
|
|
42
|
+
- Overused fonts used without justification.
|
|
43
|
+
- Generic warm beige, peach, pink, orange, or brown canvas treatments when not brand-led.
|
|
44
|
+
- Default indigo or purple accents unless the design tokens explicitly require them.
|
|
45
|
+
- Decorative blobs, bokeh, or wave backgrounds without a functional role.
|
|
46
|
+
|
|
47
|
+
## Visual Craft
|
|
48
|
+
|
|
49
|
+
Use restraint plus one decisive flourish. The page should feel specific to the product through structure, copy, state design, and proportions, not through decoration.
|
|
50
|
+
|
|
51
|
+
## What Not To Do
|
|
52
|
+
|
|
53
|
+
- Do not recreate copyrighted designs or another company's distinctive UI.
|
|
54
|
+
- Do not mention tool internals in the generated HTML.
|
|
55
|
+
- Do not include raw source JSON or debug panels.
|
|
56
|
+
- Do not use product-specific sample files as a generation source.
|
package/src/commands/help.js
CHANGED
|
@@ -13,7 +13,7 @@ Usage:
|
|
|
13
13
|
sdtk-design screens
|
|
14
14
|
sdtk-design wireframe --screen landing
|
|
15
15
|
sdtk-design system --style minimal-saas
|
|
16
|
-
sdtk-design prototype
|
|
16
|
+
sdtk-design prototype
|
|
17
17
|
sdtk-design review --artifact docs/design/prototype/index.html
|
|
18
18
|
sdtk-design handoff
|
|
19
19
|
sdtk-design status
|
|
@@ -21,7 +21,7 @@ Usage:
|
|
|
21
21
|
Examples:
|
|
22
22
|
sdtk-design start --idea "I want to build a lightweight CRM for solo consultants to track leads." --style premium-dashboard
|
|
23
23
|
sdtk-design start --from-spec . --reference-dir ./docs/design/reference-export --profile b2b-commerce
|
|
24
|
-
sdtk-design prototype
|
|
24
|
+
sdtk-design prototype
|
|
25
25
|
sdtk-design review --artifact docs/design/prototype/index.html
|
|
26
26
|
sdtk-design handoff
|
|
27
27
|
sdtk-design status
|
|
@@ -53,7 +53,7 @@ Command status:
|
|
|
53
53
|
screens Available.
|
|
54
54
|
wireframe Available.
|
|
55
55
|
system Available.
|
|
56
|
-
prototype Available
|
|
56
|
+
prototype Available SPEC-driven prototype manifest launcher.
|
|
57
57
|
handoff Available.
|
|
58
58
|
review Available for local markdown and static HTML artifacts.
|
|
59
59
|
start Available beginner facade.
|
package/src/commands/init.js
CHANGED
|
@@ -17,6 +17,13 @@ const INIT_FLAG_DEFS = {
|
|
|
17
17
|
"no-state": { type: "boolean" },
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
|
|
21
|
+
const DESIGN_PROTOTYPE_SKILL_SOURCE = path.join(PACKAGE_ROOT, "skills", "design-prototype");
|
|
22
|
+
const DESIGN_PROTOTYPE_SKILL_TARGETS = [
|
|
23
|
+
path.join(".claude", "skills", "design-prototype"),
|
|
24
|
+
path.join(".agents", "skills", "design-prototype"),
|
|
25
|
+
];
|
|
26
|
+
|
|
20
27
|
function toPosix(value) {
|
|
21
28
|
return String(value || "").replace(/\\/g, "/");
|
|
22
29
|
}
|
|
@@ -113,6 +120,51 @@ function writeManagedFile(filePath, content, projectPath, force, result) {
|
|
|
113
120
|
result.created.push(toPosix(path.relative(projectPath, filePath)));
|
|
114
121
|
}
|
|
115
122
|
|
|
123
|
+
function listSkillFiles(sourceDir) {
|
|
124
|
+
const results = [];
|
|
125
|
+
const stack = [sourceDir];
|
|
126
|
+
while (stack.length > 0) {
|
|
127
|
+
const current = stack.pop();
|
|
128
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
129
|
+
const absolutePath = path.join(current, entry.name);
|
|
130
|
+
if (entry.isDirectory()) {
|
|
131
|
+
stack.push(absolutePath);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (entry.isFile()) {
|
|
135
|
+
results.push(absolutePath);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return results.sort();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function installDesignPrototypeSkill(projectPath, force, result) {
|
|
143
|
+
if (!fs.existsSync(DESIGN_PROTOTYPE_SKILL_SOURCE) || !fs.statSync(DESIGN_PROTOTYPE_SKILL_SOURCE).isDirectory()) {
|
|
144
|
+
throw new ValidationError(`Bundled design-prototype skill is missing: ${DESIGN_PROTOTYPE_SKILL_SOURCE}. No project files were changed.`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const relativeTargetDir of DESIGN_PROTOTYPE_SKILL_TARGETS) {
|
|
148
|
+
const targetDir = path.join(projectPath, relativeTargetDir);
|
|
149
|
+
assertProjectLocalPath(targetDir, projectPath, "skill directory");
|
|
150
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
151
|
+
result.created.push(toPosix(relativeTargetDir) + "/");
|
|
152
|
+
|
|
153
|
+
for (const sourceFile of listSkillFiles(DESIGN_PROTOTYPE_SKILL_SOURCE)) {
|
|
154
|
+
const relativeFile = path.relative(DESIGN_PROTOTYPE_SKILL_SOURCE, sourceFile);
|
|
155
|
+
const targetFile = path.join(targetDir, relativeFile);
|
|
156
|
+
assertProjectLocalPath(targetFile, projectPath, "skill file");
|
|
157
|
+
if (fs.existsSync(targetFile) && !force) {
|
|
158
|
+
result.skipped.push(toPosix(path.relative(projectPath, targetFile)));
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
fs.mkdirSync(path.dirname(targetFile), { recursive: true });
|
|
162
|
+
fs.copyFileSync(sourceFile, targetFile);
|
|
163
|
+
result.created.push(toPosix(path.relative(projectPath, targetFile)));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
116
168
|
function runDesignInit({ projectPath, force = false, state = true }) {
|
|
117
169
|
const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
|
|
118
170
|
if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
|
|
@@ -131,6 +183,7 @@ function runDesignInit({ projectPath, force = false, state = true }) {
|
|
|
131
183
|
ensureDirectory(paths.wireframesPath, resolvedProjectPath, result);
|
|
132
184
|
ensureDirectory(paths.reviewsPath, resolvedProjectPath, result);
|
|
133
185
|
writeManagedFile(paths.designReadmePath, designReadmeContent(), resolvedProjectPath, force, result);
|
|
186
|
+
installDesignPrototypeSkill(resolvedProjectPath, force, result);
|
|
134
187
|
|
|
135
188
|
if (state) {
|
|
136
189
|
ensureDirectory(paths.designStatePath, resolvedProjectPath, result);
|