start-vibing 4.3.2 → 4.3.4
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 -2
- package/template/.claude/skills/super-design/SKILL.md +21 -4
- package/template/.claude/skills/super-design/references/audit-methodology.md +63 -7
- package/template/.claude/skills/super-design/scripts/harvest-typeui.sh +131 -0
- package/template/.claude/skills/super-design/scripts/score-typeui.mjs +224 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "start-vibing",
|
|
3
|
-
"version": "4.3.
|
|
4
|
-
"description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. super-design 0.6.
|
|
3
|
+
"version": "4.3.4",
|
|
4
|
+
"description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. super-design 0.6.4: Baymard verbatim rules backfilled — search (12), filter (10), breadcrumbs (6), PDP (18) now enumerated in audit-methodology.md with rule IDs + source URLs (joining the existing cc/14 + addr/8 sets), plus docs/research/baymard-public-rules.md catalogs 88 rules with SHOT+QUOTE+SEL+VAL evidence for detectors. DSC-choice typeui selection + harvest-typeui.sh carry over from 0.6.3.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"start-vibing": "./dist/cli.js"
|
|
@@ -8,7 +8,7 @@ description: >
|
|
|
8
8
|
UX audit (WCAG 2.2 AA, Nielsen heuristics, Baymard, CWV), and synthesized
|
|
9
9
|
overview. Re-audits only what changed since last run. On explicit user request,
|
|
10
10
|
applies surgical fixes with full rollback.
|
|
11
|
-
version: 0.6.
|
|
11
|
+
version: 0.6.4
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# super-design
|
|
@@ -33,17 +33,25 @@ Four-phase pipeline with 6 specialist agents:
|
|
|
33
33
|
low density, weak CTA hierarchy, vibecode smell).
|
|
34
34
|
- **Step 3h mobile-native audit** (21-item Duolingo/Linear/Arc/Cash-App
|
|
35
35
|
checklist) — replaces "responsive-web-on-a-phone" thinking.
|
|
36
|
-
- C16 ≤ 4 →
|
|
36
|
+
- C16 ≤ 4 → **DSC-choice** proposal: sd-synthesis runs
|
|
37
|
+
`scripts/score-typeui.mjs --from-audit <dir>` to derive a 7-axis site
|
|
38
|
+
fingerprint (density/contrast/geometry/color/typography/motion/audience)
|
|
39
|
+
and rank all installed typeui-* skills by fit 0-1. Top-3 are written to
|
|
40
|
+
`typeui-proposal.json` + shown in overview.md with a copy-paste CLI.
|
|
37
41
|
Produces findings.json + design-intelligence.json with SHOT+QUOTE+SEL+VAL.
|
|
38
42
|
3. **Synthesis** (sd-synthesis) — unifies research + audit + design-intelligence
|
|
39
|
-
into overview.md (per-page DIS table + executive summary
|
|
43
|
+
into overview.md (per-page DIS table + executive summary + typeui proposal
|
|
44
|
+
when craft ≤ 4).
|
|
40
45
|
4. **Fix** (sd-fix + two-stage verify) — optional. Applies safe fixes with
|
|
41
46
|
technical gates (types/lint/tests) AND semantic verification ("does this
|
|
42
47
|
fix actually resolve the finding, or just mask it?"). Template families:
|
|
43
48
|
A1-A15 a11y · V1-V8 design · U1-U10 ux · P1-P10 perf · **M1-M15 mobile**
|
|
44
49
|
(cards-in-flex-col → compact list, table-on-mobile → card-per-row,
|
|
45
50
|
centered-modal → bottom-sheet, etc.) · **DSC-1 design-skill advisory**
|
|
46
|
-
(proposes typeui-* direction
|
|
51
|
+
(proposes typeui-* direction). With `--typeui <name>` flag, sd-fix loads
|
|
52
|
+
the chosen typeui skill (tokens: primary, radius, spacing scale,
|
|
53
|
+
typography) and rebrands V1-V8 fixes so every spacing/color/radius target
|
|
54
|
+
snaps to that direction's scale — turning advisory into applied. After each
|
|
47
55
|
successful fix, re-drives Playwright to capture an after-screenshot (full
|
|
48
56
|
page + element crop) and emits `docs/super-design/sessions/<id>/fix-report.md`:
|
|
49
57
|
a self-contained visual diff with before/after images, file diffs,
|
|
@@ -109,6 +117,15 @@ Do NOT paste overview into chat.
|
|
|
109
117
|
`name` entry from `scripts/detect-apps.sh`). Required when `--scope <url>`
|
|
110
118
|
is ambiguous between multiple apps.
|
|
111
119
|
- `--fix` — run sd-fix after audit
|
|
120
|
+
- `--typeui <name>` — combine with `--fix` to apply fixes aligned to a
|
|
121
|
+
chosen typeui direction (e.g. `--fix --typeui application`). Loads tokens
|
|
122
|
+
from `~/.claude/skills/typeui-<name>/SKILL.md` and rebrands V1-V8 targets
|
|
123
|
+
to that scale. Picked from the top-3 proposed in overview.md typeui
|
|
124
|
+
block. Without this flag, DSC-1 stays advisory. Run
|
|
125
|
+
`bash scripts/score-typeui.mjs --list` to see all installed directions.
|
|
126
|
+
- `--harvest-typeui` — run `scripts/harvest-typeui.sh` first to pull
|
|
127
|
+
missing typeui-* and related design skills (typeui.sh registry with
|
|
128
|
+
built-in catalog fallback). Idempotent; add `--refresh` to re-download.
|
|
112
129
|
- `--dry-run` — artifacts without committing state
|
|
113
130
|
- `--ci` — non-interactive, create PR, exit non-zero on blockers
|
|
114
131
|
- `--update-baselines` — Re-hash pages and tokens without re-auditing (use after accepted cosmetic drift). Also accepted by `scripts/visual-regression.sh` to overwrite `.super-design/baselines/*.png` with the current capture.
|
|
@@ -311,13 +311,69 @@ source section cited.
|
|
|
311
311
|
|
|
312
312
|
> Source: §3.4.
|
|
313
313
|
|
|
314
|
-
**`baymard-search-*` — Search (12 rules, §3.6):**
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
314
|
+
**`baymard-search-*` — Search (12 rules, §3.6):**
|
|
315
|
+
1. `baymard-search-01` persist search query in the field after submission
|
|
316
|
+
2. `baymard-search-02` autocomplete list capped at 10 items desktop, 4–8 mobile
|
|
317
|
+
3. `baymard-search-03` style category-scope suggestions distinctly within autocomplete
|
|
318
|
+
4. `baymard-search-04` highlight the predictive (un-typed) portion of autocomplete suggestions
|
|
319
|
+
5. `baymard-search-05` copy active autocomplete suggestion into the field on keyboard navigation
|
|
320
|
+
6. `baymard-search-06` support misspellings in autocomplete (spell-tolerant suggestions)
|
|
321
|
+
7. `baymard-search-07` autodirect exact category-match queries to the category page
|
|
322
|
+
8. `baymard-search-08` support synonym and alternate-term searches
|
|
323
|
+
9. `baymard-search-09` support abbreviation and symbol searches
|
|
324
|
+
10. `baymard-search-10` allow "search within" current category on mobile
|
|
325
|
+
11. `baymard-search-11` support non-product queries (return policy, order status, FAQ)
|
|
326
|
+
12. `baymard-search-12` provide 5 proven no-results recovery paths (spelling fix, broaden, contact, etc.)
|
|
327
|
+
|
|
328
|
+
> Source: §3.6 bullets + https://baymard.com/ecommerce-design-examples/34-autocomplete-suggestions + per-rule URLs in `/docs/research/baymard-public-rules.md` (B-S-01…B-S-12).
|
|
329
|
+
|
|
330
|
+
**`baymard-filter-*` — Filter (10 rules, §3.6):**
|
|
331
|
+
1. `baymard-filter-01` provide the 5 essential filter types (category, price, rating, brand, user-relevant attribute)
|
|
332
|
+
2. `baymard-filter-02` have filters for every attribute displayed in list items
|
|
333
|
+
3. `baymard-filter-03` display applied filters in a visible overview area, individually removable
|
|
334
|
+
4. `baymard-filter-04` use OR logic within a facet, AND logic across facets
|
|
335
|
+
5. `baymard-filter-05` show product counts per filter value and update them dynamically
|
|
336
|
+
6. `baymard-filter-06` collapse long facet lists to top 4–6 values with a "Show more" link
|
|
337
|
+
7. `baymard-filter-07` explain industry-specific or jargon filter labels
|
|
338
|
+
8. `baymard-filter-08` promote important filters above the product grid / above the fold
|
|
339
|
+
9. `baymard-filter-09` provide 4 essential sort options (featured, price asc/desc, rating, newest)
|
|
340
|
+
10. `baymard-filter-10` persist filter state when user returns from a product detail page
|
|
341
|
+
|
|
342
|
+
> Source: §3.6 bullets + https://baymard.com/blog/promoting-product-filters + https://baymard.com/blog/have-filters-for-list-item-info + per-rule URLs in `/docs/research/baymard-public-rules.md` (B-F-01…B-F-10).
|
|
343
|
+
|
|
344
|
+
**`baymard-bread-*` — Breadcrumbs (6 rules, §3.7):**
|
|
345
|
+
1. `baymard-bread-01` provide both hierarchy-based AND history-based breadcrumbs
|
|
346
|
+
2. `baymard-bread-02` include the full category hierarchy in the breadcrumb trail
|
|
347
|
+
3. `baymard-bread-03` suppress homepage and current product page layers from the trail
|
|
348
|
+
4. `baymard-bread-04` make breadcrumbs swipeable on mobile for long paths
|
|
349
|
+
5. `baymard-bread-05` use clear tappability cues on mobile breadcrumbs (size, underline, chevron)
|
|
350
|
+
6. `baymard-bread-06` expose `BreadcrumbList` structured-data markup for SEO and assistive tech
|
|
351
|
+
|
|
352
|
+
> Rules 01–05 from verified public research (B-B-01…B-B-05). Rule 06 enumerated from §3.7 narrative; supersede when Baymard Premium IDs become available.
|
|
353
|
+
> Source: §3.7 + https://baymard.com/blog/ecommerce-breadcrumbs + per-rule URLs in `/docs/research/baymard-public-rules.md`.
|
|
354
|
+
|
|
355
|
+
**`baymard-pdp-*` — PDP / Product Detail (18 rules, §3.8):**
|
|
356
|
+
1. `baymard-pdp-01` use button-style size selectors (swatches), not dropdowns
|
|
357
|
+
2. `baymard-pdp-02` provide at least one in-scale product image (size/context reference)
|
|
358
|
+
3. `baymard-pdp-03` show product on a human model for worn or carried items
|
|
359
|
+
4. `baymard-pdp-04` provide sufficient image resolution and zoom on desktop and mobile
|
|
360
|
+
5. `baymard-pdp-05` support both pinch and double-tap zoom gestures on mobile
|
|
361
|
+
6. `baymard-pdp-06` allow guest users to save / wishlist items without account creation
|
|
362
|
+
7. `baymard-pdp-07` display a return-policy link or summary directly on the product page
|
|
363
|
+
8. `baymard-pdp-08` show the lowest shipping-cost estimate (and ETA) on the product page
|
|
364
|
+
9. `baymard-pdp-09` allow carousel navigation across reviewer-submitted images (UGC)
|
|
365
|
+
10. `baymard-pdp-10` respond publicly to negative reviews to demonstrate accountability
|
|
366
|
+
11. `baymard-pdp-11` display price-per-unit for multi-quantity or weight-priced products
|
|
367
|
+
12. `baymard-pdp-12` synchronize product data (price, stock, images) across variation selections
|
|
368
|
+
13. `baymard-pdp-13` include descriptive text or graphics on key product images (annotations)
|
|
369
|
+
14. `baymard-pdp-14` single dominant Add-to-Cart CTA — not 3–6 competing colorful buttons
|
|
370
|
+
15. `baymard-pdp-15` prefer accordion over horizontal tabs for PDP detail sections
|
|
371
|
+
16. `baymard-pdp-16` show out-of-stock variants as visible-but-disabled (never remove from UI)
|
|
372
|
+
17. `baymard-pdp-17` offer a "notify me when back in stock" flow for OOS variants
|
|
373
|
+
18. `baymard-pdp-18` surface size-guide inline on the PDP — not hidden behind a separate page
|
|
374
|
+
|
|
375
|
+
> Rules 01–13 from verified public research (B-P-01…B-P-13). Rules 14–18 enumerated from §3.8 narrative; supersede when Baymard Premium IDs become available.
|
|
376
|
+
> Source: §3.8 + https://baymard.com/research/product-page + per-rule URLs in `/docs/research/baymard-public-rules.md`.
|
|
321
377
|
|
|
322
378
|
### 3.1 Cart abandonment
|
|
323
379
|
**70.19% average abandonment** across 49 studies 2006–2023, range 55–84.27% (https://baymard.com/lists/cart-abandonment-rate). By device: mobile 77.06%, tablet 66.39%, desktop 70.01%. **Reasons** (excluding 43% "just browsing"): extra costs 48%; forced account creation 24%; slow delivery 19%; distrust with CC 18–19%; too long/complicated 17–18%; couldn't see total up front 16%; errors/crashes 13%; returns policy 12%; declined CC 9%; limited payment methods 7%.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# harvest-typeui.sh — discover + install typeui-* and related design skills.
|
|
3
|
+
#
|
|
4
|
+
# Sources, in order:
|
|
5
|
+
# 1. typeui.sh registry (if reachable) — fetch catalog.json, install missing
|
|
6
|
+
# 2. vercel-labs/agent-skills design skills (composition, web-design-guidelines)
|
|
7
|
+
# 3. anthropics/skills webapp-testing + mcp-builder (already covered, idempotent)
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# harvest-typeui.sh # install everything missing (user-scope ~/.claude/skills/)
|
|
11
|
+
# harvest-typeui.sh --list # print catalog without installing
|
|
12
|
+
# harvest-typeui.sh --refresh # re-download even if already installed
|
|
13
|
+
# harvest-typeui.sh --dest <dir> # install to custom dir (default: ~/.claude/skills)
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
DEST="${HOME}/.claude/skills"
|
|
18
|
+
LIST_ONLY=0
|
|
19
|
+
REFRESH=0
|
|
20
|
+
|
|
21
|
+
while (( $# )); do
|
|
22
|
+
case "$1" in
|
|
23
|
+
--list) LIST_ONLY=1 ;;
|
|
24
|
+
--refresh) REFRESH=1 ;;
|
|
25
|
+
--dest) DEST="$2"; shift ;;
|
|
26
|
+
-h|--help)
|
|
27
|
+
sed -n '2,18p' "$0"
|
|
28
|
+
exit 0 ;;
|
|
29
|
+
*) echo "unknown flag: $1" >&2; exit 2 ;;
|
|
30
|
+
esac
|
|
31
|
+
shift
|
|
32
|
+
done
|
|
33
|
+
|
|
34
|
+
mkdir -p "$DEST"
|
|
35
|
+
|
|
36
|
+
TYPEUI_CATALOG_URL="${TYPEUI_CATALOG_URL:-https://typeui.sh/catalog.json}"
|
|
37
|
+
TMP=$(mktemp)
|
|
38
|
+
trap 'rm -f "$TMP"' EXIT
|
|
39
|
+
|
|
40
|
+
log() { echo "[harvest-typeui] $*"; }
|
|
41
|
+
|
|
42
|
+
fetch_catalog() {
|
|
43
|
+
if command -v curl >/dev/null 2>&1; then
|
|
44
|
+
curl -sSfL --max-time 10 "$TYPEUI_CATALOG_URL" -o "$TMP" 2>/dev/null && return 0
|
|
45
|
+
fi
|
|
46
|
+
if command -v wget >/dev/null 2>&1; then
|
|
47
|
+
wget -q --timeout=10 -O "$TMP" "$TYPEUI_CATALOG_URL" 2>/dev/null && return 0
|
|
48
|
+
fi
|
|
49
|
+
return 1
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Fallback catalog — built-in list of known typeui skills + companions.
|
|
53
|
+
# Format: name|base_url (raw SKILL.md location)
|
|
54
|
+
DEFAULT_CATALOG=$(cat <<'EOF'
|
|
55
|
+
typeui-ant|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-ant/SKILL.md
|
|
56
|
+
typeui-application|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-application/SKILL.md
|
|
57
|
+
typeui-artistic|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-artistic/SKILL.md
|
|
58
|
+
typeui-bento|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-bento/SKILL.md
|
|
59
|
+
typeui-bold|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-bold/SKILL.md
|
|
60
|
+
typeui-clean|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-clean/SKILL.md
|
|
61
|
+
typeui-dashboard|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-dashboard/SKILL.md
|
|
62
|
+
typeui-doodle|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-doodle/SKILL.md
|
|
63
|
+
typeui-dramatic|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-dramatic/SKILL.md
|
|
64
|
+
typeui-enterprise|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-enterprise/SKILL.md
|
|
65
|
+
typeui-neobrutalism|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-neobrutalism/SKILL.md
|
|
66
|
+
typeui-paper|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-paper/SKILL.md
|
|
67
|
+
composition-patterns|https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/composition-patterns/SKILL.md
|
|
68
|
+
web-design-guidelines|https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/web-design-guidelines/SKILL.md
|
|
69
|
+
react-best-practices|https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/react-best-practices/SKILL.md
|
|
70
|
+
webapp-testing|https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md
|
|
71
|
+
mcp-builder|https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md
|
|
72
|
+
EOF
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
CATALOG=""
|
|
76
|
+
if fetch_catalog; then
|
|
77
|
+
# typeui.sh returned JSON — extract {name,url} tuples via node
|
|
78
|
+
if command -v node >/dev/null 2>&1; then
|
|
79
|
+
CATALOG=$(node -e "
|
|
80
|
+
const c = JSON.parse(require('fs').readFileSync('$TMP','utf8'));
|
|
81
|
+
const items = c.skills || c.items || c;
|
|
82
|
+
for (const it of items) {
|
|
83
|
+
const n = it.name || it.slug;
|
|
84
|
+
const u = it.skill_url || it.url || it.raw || it.source;
|
|
85
|
+
if (n && u) console.log(n + '|' + u);
|
|
86
|
+
}
|
|
87
|
+
" 2>/dev/null || echo "")
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
if [[ -z "$CATALOG" ]]; then
|
|
92
|
+
log "registry unreachable or empty — using built-in catalog"
|
|
93
|
+
CATALOG="$DEFAULT_CATALOG"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if (( LIST_ONLY )); then
|
|
97
|
+
echo "$CATALOG"
|
|
98
|
+
exit 0
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
INSTALLED=0
|
|
102
|
+
SKIPPED=0
|
|
103
|
+
FAILED=0
|
|
104
|
+
|
|
105
|
+
while IFS='|' read -r name url; do
|
|
106
|
+
[[ -z "$name" ]] && continue
|
|
107
|
+
target_dir="$DEST/$name"
|
|
108
|
+
target_file="$target_dir/SKILL.md"
|
|
109
|
+
|
|
110
|
+
if [[ -f "$target_file" && $REFRESH -eq 0 ]]; then
|
|
111
|
+
SKIPPED=$((SKIPPED + 1))
|
|
112
|
+
continue
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
mkdir -p "$target_dir"
|
|
116
|
+
if command -v curl >/dev/null 2>&1; then
|
|
117
|
+
if curl -sSfL --max-time 15 "$url" -o "$target_file" 2>/dev/null; then
|
|
118
|
+
log "installed $name"
|
|
119
|
+
INSTALLED=$((INSTALLED + 1))
|
|
120
|
+
else
|
|
121
|
+
log "failed $name ($url)"
|
|
122
|
+
FAILED=$((FAILED + 1))
|
|
123
|
+
rmdir "$target_dir" 2>/dev/null || true
|
|
124
|
+
fi
|
|
125
|
+
else
|
|
126
|
+
log "curl missing — cannot fetch $name"
|
|
127
|
+
FAILED=$((FAILED + 1))
|
|
128
|
+
fi
|
|
129
|
+
done <<< "$CATALOG"
|
|
130
|
+
|
|
131
|
+
log "done: installed=$INSTALLED skipped=$SKIPPED failed=$FAILED dest=$DEST"
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// score-typeui.mjs — rank typeui-* skills by fit against a site fingerprint.
|
|
3
|
+
//
|
|
4
|
+
// Usage:
|
|
5
|
+
// node score-typeui.mjs <fingerprint.json> [--top 3]
|
|
6
|
+
// node score-typeui.mjs --from-audit <audit-dir> # derive fingerprint from sd-audit findings + DIS
|
|
7
|
+
// node score-typeui.mjs --list # list available typeui skills with axes
|
|
8
|
+
//
|
|
9
|
+
// Fingerprint input (JSON):
|
|
10
|
+
// { density: "low|medium|high", contrast: "low|medium|high",
|
|
11
|
+
// geometry: "round|square|mixed", color: "muted|vibrant|bold",
|
|
12
|
+
// typography: "neutral|expressive|display", motion: "static|subtle|dynamic",
|
|
13
|
+
// audience: "enterprise|developer|consumer|creative" }
|
|
14
|
+
//
|
|
15
|
+
// Output (stdout): JSON { ranked: [{ name, fit, reasons, applies_tokens }] }
|
|
16
|
+
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
|
|
21
|
+
const AXES = ['density', 'contrast', 'geometry', 'color', 'typography', 'motion', 'audience'];
|
|
22
|
+
|
|
23
|
+
// Static fingerprint matrix — derived from each typeui SKILL.md style foundations.
|
|
24
|
+
// Keep in sync with skills at ~/.claude/skills/typeui-*/SKILL.md. Expand via harvest-typeui.sh.
|
|
25
|
+
const TYPEUI = {
|
|
26
|
+
ant: {
|
|
27
|
+
density: 'high', contrast: 'medium', geometry: 'square',
|
|
28
|
+
color: 'muted', typography: 'neutral', motion: 'static', audience: 'enterprise',
|
|
29
|
+
tokens: { primary: '#1677ff', radius: 6, spacing: [4, 8, 12, 16, 24, 32] },
|
|
30
|
+
summary: 'Structured, enterprise-focused design. Data-dense tables, forms.',
|
|
31
|
+
},
|
|
32
|
+
application: {
|
|
33
|
+
density: 'medium', contrast: 'high', geometry: 'round',
|
|
34
|
+
color: 'vibrant', typography: 'neutral', motion: 'subtle', audience: 'developer',
|
|
35
|
+
tokens: { primary: '#9333ea', radius: 8, spacing: [4, 8, 12, 16, 24, 32] },
|
|
36
|
+
summary: 'Vercel/GitHub purple aesthetic. Top-bar nav, card layouts.',
|
|
37
|
+
},
|
|
38
|
+
artistic: {
|
|
39
|
+
density: 'low', contrast: 'high', geometry: 'mixed',
|
|
40
|
+
color: 'bold', typography: 'expressive', motion: 'dynamic', audience: 'creative',
|
|
41
|
+
tokens: { primary: '#000000', radius: 0, spacing: [8, 16, 24, 40, 64, 96] },
|
|
42
|
+
summary: 'High-contrast, expressive. Creative typography, bold color.',
|
|
43
|
+
},
|
|
44
|
+
bento: {
|
|
45
|
+
density: 'medium', contrast: 'medium', geometry: 'round',
|
|
46
|
+
color: 'muted', typography: 'neutral', motion: 'subtle', audience: 'consumer',
|
|
47
|
+
tokens: { primary: '#0ea5e9', radius: 16, spacing: [8, 16, 24, 32, 48, 64] },
|
|
48
|
+
summary: 'Modular grid of card-blocks. Clear hierarchy, soft spacing.',
|
|
49
|
+
},
|
|
50
|
+
bold: {
|
|
51
|
+
density: 'low', contrast: 'high', geometry: 'square',
|
|
52
|
+
color: 'bold', typography: 'display', motion: 'dynamic', audience: 'consumer',
|
|
53
|
+
tokens: { primary: '#111111', radius: 4, spacing: [8, 16, 24, 48, 80, 128] },
|
|
54
|
+
summary: 'Strong visual presence. Heavyweight type, high-contrast.',
|
|
55
|
+
},
|
|
56
|
+
clean: {
|
|
57
|
+
density: 'low', contrast: 'medium', geometry: 'round',
|
|
58
|
+
color: 'muted', typography: 'neutral', motion: 'static', audience: 'consumer',
|
|
59
|
+
tokens: { primary: '#18181b', radius: 8, spacing: [8, 16, 24, 32, 48, 72] },
|
|
60
|
+
summary: 'Simplicity-focused. Whitespace, legible type, limited palette.',
|
|
61
|
+
},
|
|
62
|
+
dashboard: {
|
|
63
|
+
density: 'high', contrast: 'high', geometry: 'round',
|
|
64
|
+
color: 'vibrant', typography: 'neutral', motion: 'subtle', audience: 'developer',
|
|
65
|
+
tokens: { primary: '#3b82f6', radius: 6, spacing: [4, 8, 12, 16, 24, 32] },
|
|
66
|
+
summary: 'Dark cloud-platform. Modular grids, glass panels, data hierarchy.',
|
|
67
|
+
},
|
|
68
|
+
doodle: {
|
|
69
|
+
density: 'low', contrast: 'medium', geometry: 'mixed',
|
|
70
|
+
color: 'vibrant', typography: 'expressive', motion: 'dynamic', audience: 'creative',
|
|
71
|
+
tokens: { primary: '#f59e0b', radius: 12, spacing: [8, 16, 24, 40, 64, 96] },
|
|
72
|
+
summary: 'Hand-drawn, sketch-like. Doodles, handwritten fonts.',
|
|
73
|
+
},
|
|
74
|
+
dramatic: {
|
|
75
|
+
density: 'low', contrast: 'high', geometry: 'mixed',
|
|
76
|
+
color: 'bold', typography: 'display', motion: 'dynamic', audience: 'creative',
|
|
77
|
+
tokens: { primary: '#dc2626', radius: 0, spacing: [16, 32, 48, 80, 128, 200] },
|
|
78
|
+
summary: 'Theatrical. Bold layouts, immersive visuals, unconventional.',
|
|
79
|
+
},
|
|
80
|
+
enterprise: {
|
|
81
|
+
density: 'high', contrast: 'high', geometry: 'square',
|
|
82
|
+
color: 'muted', typography: 'neutral', motion: 'static', audience: 'enterprise',
|
|
83
|
+
tokens: { primary: '#0f172a', radius: 4, spacing: [4, 8, 12, 16, 24, 32] },
|
|
84
|
+
summary: 'Clean, high-contrast enterprise. Drag-drop, structured layouts.',
|
|
85
|
+
},
|
|
86
|
+
neobrutalism: {
|
|
87
|
+
density: 'medium', contrast: 'high', geometry: 'square',
|
|
88
|
+
color: 'bold', typography: 'display', motion: 'subtle', audience: 'creative',
|
|
89
|
+
tokens: { primary: '#ffd700', radius: 0, spacing: [8, 16, 24, 32, 48, 64] },
|
|
90
|
+
summary: 'Bold borders, vivid accents, raw high-contrast on warm surfaces.',
|
|
91
|
+
},
|
|
92
|
+
paper: {
|
|
93
|
+
density: 'medium', contrast: 'medium', geometry: 'round',
|
|
94
|
+
color: 'muted', typography: 'expressive', motion: 'static', audience: 'consumer',
|
|
95
|
+
tokens: { primary: '#78350f', radius: 4, spacing: [8, 16, 24, 32, 48, 64] },
|
|
96
|
+
summary: 'Paper-textured, print-inspired. Serif/sans, tactile.',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Axis weights — density/contrast/geometry dominate; audience is secondary.
|
|
101
|
+
const WEIGHTS = {
|
|
102
|
+
density: 0.22, contrast: 0.18, geometry: 0.16, color: 0.14,
|
|
103
|
+
typography: 0.12, motion: 0.10, audience: 0.08,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function matchScore(a, b) {
|
|
107
|
+
if (a === b) return 1.0;
|
|
108
|
+
const pairs = {
|
|
109
|
+
'low|medium': 0.5, 'medium|high': 0.5,
|
|
110
|
+
'muted|vibrant': 0.5, 'vibrant|bold': 0.5,
|
|
111
|
+
'neutral|expressive': 0.5, 'expressive|display': 0.5,
|
|
112
|
+
'static|subtle': 0.5, 'subtle|dynamic': 0.5,
|
|
113
|
+
'round|mixed': 0.6, 'square|mixed': 0.6,
|
|
114
|
+
};
|
|
115
|
+
const key = [a, b].sort().join('|');
|
|
116
|
+
return pairs[key] ?? 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function scoreFor(fp, typeui) {
|
|
120
|
+
let total = 0;
|
|
121
|
+
const reasons = [];
|
|
122
|
+
for (const axis of AXES) {
|
|
123
|
+
const fpv = fp[axis];
|
|
124
|
+
const tuv = typeui[axis];
|
|
125
|
+
if (!fpv || !tuv) continue;
|
|
126
|
+
const s = matchScore(fpv, tuv);
|
|
127
|
+
total += s * WEIGHTS[axis];
|
|
128
|
+
if (s >= 0.5) reasons.push(`${axis}:${tuv}`);
|
|
129
|
+
}
|
|
130
|
+
return { fit: Math.round(total * 100) / 100, reasons };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function rank(fp, topN = 3) {
|
|
134
|
+
const scored = Object.entries(TYPEUI).map(([name, tu]) => ({
|
|
135
|
+
name: `typeui-${name}`,
|
|
136
|
+
...scoreFor(fp, tu),
|
|
137
|
+
applies_tokens: tu.tokens,
|
|
138
|
+
summary: tu.summary,
|
|
139
|
+
}));
|
|
140
|
+
scored.sort((a, b) => b.fit - a.fit);
|
|
141
|
+
return scored.slice(0, topN);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Derive fingerprint from sd-audit findings.json + design-intelligence.json.
|
|
145
|
+
function fingerprintFromAudit(auditDir) {
|
|
146
|
+
const fp = {};
|
|
147
|
+
const diPath = path.join(auditDir, 'design-intelligence.json');
|
|
148
|
+
const fPath = path.join(auditDir, 'findings.json');
|
|
149
|
+
if (!fs.existsSync(diPath)) {
|
|
150
|
+
throw new Error(`design-intelligence.json not found in ${auditDir}`);
|
|
151
|
+
}
|
|
152
|
+
const di = JSON.parse(fs.readFileSync(diPath, 'utf8'));
|
|
153
|
+
const findings = fs.existsSync(fPath) ? JSON.parse(fs.readFileSync(fPath, 'utf8')) : [];
|
|
154
|
+
|
|
155
|
+
const scores = di.per_page?.[0]?.categories || di.categories || {};
|
|
156
|
+
const s = (k) => scores[k]?.score ?? scores[k] ?? 5;
|
|
157
|
+
|
|
158
|
+
// Density: C1 density + C11 information scent
|
|
159
|
+
const densityScore = (s('C1') + s('C11')) / 2;
|
|
160
|
+
fp.density = densityScore >= 7 ? 'high' : densityScore >= 4 ? 'medium' : 'low';
|
|
161
|
+
|
|
162
|
+
// Contrast: C6 contrast (inverse — low craft = low contrast detected)
|
|
163
|
+
fp.contrast = s('C6') >= 7 ? 'high' : s('C6') >= 4 ? 'medium' : 'low';
|
|
164
|
+
|
|
165
|
+
// Geometry: C14 border-radius variance (heuristic)
|
|
166
|
+
const radiusVar = di.summary?.radius_variance ?? 0;
|
|
167
|
+
fp.geometry = radiusVar > 8 ? 'mixed' : radiusVar > 2 ? 'round' : 'square';
|
|
168
|
+
|
|
169
|
+
// Color: C5 palette saturation
|
|
170
|
+
const satScore = di.summary?.color_saturation ?? 0.5;
|
|
171
|
+
fp.color = satScore > 0.7 ? 'bold' : satScore > 0.4 ? 'vibrant' : 'muted';
|
|
172
|
+
|
|
173
|
+
// Typography: C7 type scale variance
|
|
174
|
+
const typeVariance = di.summary?.type_variance ?? 0.5;
|
|
175
|
+
fp.typography = typeVariance > 0.7 ? 'display' : typeVariance > 0.4 ? 'expressive' : 'neutral';
|
|
176
|
+
|
|
177
|
+
// Motion: C13 motion/transitions
|
|
178
|
+
fp.motion = s('C13') >= 7 ? 'dynamic' : s('C13') >= 4 ? 'subtle' : 'static';
|
|
179
|
+
|
|
180
|
+
// Audience: inferred from findings categories — best-effort
|
|
181
|
+
const mobileCount = findings.filter((f) => f.id?.startsWith('M')).length;
|
|
182
|
+
const dataCount = findings.filter((f) => /table|dense|data/i.test(f.quote || '')).length;
|
|
183
|
+
fp.audience = dataCount > mobileCount ? 'enterprise' : 'consumer';
|
|
184
|
+
|
|
185
|
+
return fp;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function listSkills() {
|
|
189
|
+
return Object.entries(TYPEUI).map(([name, tu]) => ({
|
|
190
|
+
name: `typeui-${name}`,
|
|
191
|
+
axes: AXES.reduce((o, a) => ({ ...o, [a]: tu[a] }), {}),
|
|
192
|
+
summary: tu.summary,
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// CLI
|
|
197
|
+
const args = process.argv.slice(2);
|
|
198
|
+
if (args.includes('--list')) {
|
|
199
|
+
console.log(JSON.stringify(listSkills(), null, 2));
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let fp;
|
|
204
|
+
let topN = 3;
|
|
205
|
+
const topIdx = args.indexOf('--top');
|
|
206
|
+
if (topIdx >= 0) topN = parseInt(args[topIdx + 1], 10) || 3;
|
|
207
|
+
|
|
208
|
+
const fromAuditIdx = args.indexOf('--from-audit');
|
|
209
|
+
if (fromAuditIdx >= 0) {
|
|
210
|
+
const dir = args[fromAuditIdx + 1];
|
|
211
|
+
if (!dir) {
|
|
212
|
+
console.error('--from-audit requires a directory path');
|
|
213
|
+
process.exit(2);
|
|
214
|
+
}
|
|
215
|
+
fp = fingerprintFromAudit(dir);
|
|
216
|
+
} else if (args[0] && fs.existsSync(args[0])) {
|
|
217
|
+
fp = JSON.parse(fs.readFileSync(args[0], 'utf8'));
|
|
218
|
+
} else {
|
|
219
|
+
console.error('Usage: score-typeui.mjs <fingerprint.json> | --from-audit <dir> | --list');
|
|
220
|
+
process.exit(2);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const ranked = rank(fp, topN);
|
|
224
|
+
console.log(JSON.stringify({ fingerprint: fp, ranked }, null, 2));
|