@bacnh85/pi-ux 0.4.6 → 0.5.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/CHANGELOG.md +111 -0
- package/README.md +15 -21
- package/extensions/index.js +16 -3
- package/hooks/ux-audit.js +2 -1
- package/package.json +2 -2
- package/skills/ux-capture/SKILL.md +86 -2
- package/skills/ux-design/SKILL.md +149 -52
- package/skills/ux-presets/SKILL.md +134 -7
- package/skills/ux-routing/SKILL.md +45 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,116 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 (2026-09-16)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Step 1.5 — Derive a direction** in the ux-design skill: the positive
|
|
8
|
+
counterpart to the guardrail. From the brief's subject material, commit —
|
|
9
|
+
before any markup — to mood adjectives, a one-line visual concept, a type
|
|
10
|
+
voice, a color mood (hex), and exactly one signature element. Test for a
|
|
11
|
+
real direction: two different designers following it must produce visibly
|
|
12
|
+
different pages. Replaces the passive generic-default check.
|
|
13
|
+
- **Direction playbook** (injected, ux-design): the positive layer the skill
|
|
14
|
+
previously lacked. Typography-voice pairing table (editorial → Newsreader +
|
|
15
|
+
Source Sans 3, ledger → Spline Sans pair, technical → IBM Plex pair, warm
|
|
16
|
+
consumer → DM Sans, …) with off-table alternatives; color-mood construction
|
|
17
|
+
(temperature first, every neutral tinted, committed accent posture, band
|
|
18
|
+
rhythm); composition anatomy (hierarchy by scale/weight/color, hero formula,
|
|
19
|
+
rows-over-card-grids, shaped whitespace, depth via composition); the
|
|
20
|
+
signature element how-to; default-vs-directed contrast snippets.
|
|
21
|
+
- **Step 4 render-inspect is REQUIRED in strict mode** for multimodal models,
|
|
22
|
+
with a concrete LOOK checklist (squint test, dead zones, monotony, timidity
|
|
23
|
+
— "cover the logo, could this page belong to anyone?", type sizes, mood
|
|
24
|
+
visibility) instead of the previous "fix what regex gates can't see".
|
|
25
|
+
- **Style-direction starters** in ux-presets (Option B+): S1 Editorial print
|
|
26
|
+
(Newsreader + Source Sans 3, paper/ink/oxblood), S2 Ledger (Spline Sans
|
|
27
|
+
pair, desk/rule/tabular-nums, stamp badges), S3 Warm consumer (DM Sans,
|
|
28
|
+
linen/basil, category spines, freshness badges). All color pairs verified
|
|
29
|
+
against the shipped APCA implementation (Lc ≥75 body, ≥60 small-bold
|
|
30
|
+
badges). B1/B2 remain as neutral fallbacks.
|
|
31
|
+
- **`ux-routing` skill** — the model-routing table moved out of the injected
|
|
32
|
+
body into a consult-when-delegating reference skill (offsets the added
|
|
33
|
+
injection weight; routing prose was irrelevant to most non-delegating
|
|
34
|
+
turns).
|
|
35
|
+
- **`bench/` design-quality harness**: three fixed briefs (landing page,
|
|
36
|
+
dashboard, mobile screen), `run.sh` (headless pi run with pinned model +
|
|
37
|
+
screenshot capture at desktop/mobile widths), and a fixed six-axis rubric
|
|
38
|
+
(hierarchy, composition, typography, color/mood, copy, feel). Results are
|
|
39
|
+
gitignored; see bench/README section in the main README.
|
|
40
|
+
- **ux-capture: layout probe + viewport-truth section.** Headless Chrome
|
|
41
|
+
clamps window width to 500px, so a `--window-size=390` capture renders at
|
|
42
|
+
500 and crops the PNG — right-edge "cuts" that aren't in your CSS. The
|
|
43
|
+
skill now documents the clamp and prescribes a 390×844 iframe-wrapper probe
|
|
44
|
+
(an iframe is a true 390px CSS viewport) that measures
|
|
45
|
+
`scrollWidth`/viewport honestly on the delivered file. Capturing mobile via
|
|
46
|
+
the wrapper is also required: never widen a viewport to make a problem
|
|
47
|
+
invisible.
|
|
48
|
+
- **ux-capture: capture with entrance animations disabled**
|
|
49
|
+
(`--force-prefers-reduced-motion`). Staggered page-load reveals with
|
|
50
|
+
`opacity:0` backwards-fill screenshot as blank sections otherwise — the
|
|
51
|
+
forced query doubles as a reduced-motion audit (every section must remain
|
|
52
|
+
fully readable with animations off).
|
|
53
|
+
- **ux-design LOOK checklist** is now pinned to the brief's target viewport,
|
|
54
|
+
with an explicit dead-zone check for app screens (content must not leave a
|
|
55
|
+
large empty region below the last element at the target height).
|
|
56
|
+
- **Polish pass (critic-driven):** a glm-5.3-flash calibration review against
|
|
57
|
+
a reference-grade page named five half-point gaps; each became a rule in
|
|
58
|
+
the Direction playbook — display scale floor (h1 ≥ clamp(2.75rem, 7vw,
|
|
59
|
+
6.75rem), 3–4× body), signature elements at composition scale (≥25vw,
|
|
60
|
+
edge-bleeding, reduced contrast), a three-surface color budget (header
|
|
61
|
+
inherits the base), repeating rows as fixed column grids with a shared
|
|
62
|
+
terminal axis, and a 2–3-mark "punctuation kit" (accent H1 terminal,
|
|
63
|
+
framed pull quotes, stamps, texture glyphs — placeholder monograms banned).
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- Step 0 rung 4 reframed: a preset is a **floor, not an identity** — after
|
|
68
|
+
dropping one in, the direction step must still bend display face, neutrals,
|
|
69
|
+
and accent until the page could not be mistaken for the stock preset.
|
|
70
|
+
- Step 5 gate table now lists exactly what `ux_audit` implements (contrast /
|
|
71
|
+
tokens / states+motion / slop tells); the unimplemented rows (token
|
|
72
|
+
coverage, shadow recipes, component hygiene) are documented as model-side
|
|
73
|
+
checks instead of implying mechanical enforcement.
|
|
74
|
+
- ux_audit tool description fixed: "block handoff until this fails to pass"
|
|
75
|
+
→ "handoff is blocked until this passes".
|
|
76
|
+
|
|
77
|
+
### Why
|
|
78
|
+
|
|
79
|
+
Field benchmarking (`bench/`, glm-5.3-flash, three briefs, fixed rubric —
|
|
80
|
+
hierarchy / composition / typography / color-mood / copy / feel, 1–5 each)
|
|
81
|
+
showed the 0.4.x skill produced correct-but-forgettable pages: ~70% of the
|
|
82
|
+
injected guidance was prohibitions, so the model played safe (uniform
|
|
83
|
+
sections, no signature, cream-clay/broadsheet cliché drift on landing pages).
|
|
84
|
+
Discipline without direction is half the method; 0.5.0 ships the other half.
|
|
85
|
+
|
|
86
|
+
**Measured:** baseline 0.4.7 = 60.5/90 → 0.5.0 = 79.5/90 (+19); after the
|
|
87
|
+
critic-driven polish pass, **landing = 29.5 and 29 on two consecutive runs
|
|
88
|
+
(goal ≥29)**, run total 84/90, and three new case types (portfolio, pricing,
|
|
89
|
+
settings-form) scored 28–28.5 on first try — all 15 scored pages pass
|
|
90
|
+
`ux_audit`, and a non-UI task with pi-ux active shows zero derailment.
|
|
91
|
+
Winning directions each run were distinct and subject-grounded (alpine
|
|
92
|
+
expedition log with route-card signature; white/ink/vermilion with an
|
|
93
|
+
elevation-profile card; forest-green badge-stamp identity; ink-ledger
|
|
94
|
+
dashboard with OVERDUE stamps; grocer's stock-card mobile screen; printed
|
|
95
|
+
monograph portfolio). Per-run evidence in `bench/results/*/SCORES.md`
|
|
96
|
+
(gitignored; scores reproduced here).
|
|
97
|
+
|
|
98
|
+
## 0.4.7 (2026-09-12)
|
|
99
|
+
|
|
100
|
+
### Fixed
|
|
101
|
+
|
|
102
|
+
- `ux_audit` states hints now match the actual failure. With interactive
|
|
103
|
+
selectors present but focus/disabled rules failing (the fragment case 0.4.6
|
|
104
|
+
promised to catch), the result appends "states rules may live in another
|
|
105
|
+
file — pass the COMPLETE stylesheet" (previously: no hint at all). With zero
|
|
106
|
+
interactive selectors the only possible failure is reduced-motion, so the
|
|
107
|
+
hint names that fix and only conditionally suggests the fragment case
|
|
108
|
+
instead of assertively mislabeling a complete stylesheet.
|
|
109
|
+
(`scanStates` reports `hasInteractive`; `formatAuditResult` is now exported
|
|
110
|
+
and covered by a test.)
|
|
111
|
+
- README Usage: bare `/ux` resets to the configured default mode (it never
|
|
112
|
+
toggled).
|
|
113
|
+
|
|
3
114
|
## 0.4.6 (2026-09-06)
|
|
4
115
|
|
|
5
116
|
### Added
|
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# pi-ux
|
|
2
2
|
|
|
3
|
-
Anti-slop UI/UX design discipline for the [Pi coding agent](https://pi.dev). Anchors a lintable **DESIGN.md**, runs **deterministic** slop-audit gates (APCA contrast + tokens + states + slop tells), and works **with text-only models** (DeepSeek-v4, GLM-5.2, Kimi K3) — `agy`/Gemini/Claude is optional polish, never the review gate.
|
|
3
|
+
Anti-slop UI/UX design discipline for the [Pi coding agent](https://pi.dev). Anchors a lintable **DESIGN.md**, **derives a design direction** (mood, type voice, color mood, signature element) from the subject, runs **deterministic** slop-audit gates (APCA contrast + tokens + states + slop tells), and works **with text-only models** (DeepSeek-v4, GLM-5.2, Kimi K3) — `agy`/Gemini/Claude is optional polish, never the review gate.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
7
|
-
AI-generated UI converges on
|
|
7
|
+
AI-generated UI fails in two directions. Without discipline it converges on slop — purple/indigo glow, shadow-heavy cards, missing focus/disabled/error states. Without direction it converges on the *correct but forgettable* default — Inter, a blue accent, white cards, timid sizes — which passes every lint and still has no feel, because under vague direction models reach for high-frequency statistical patterns either way. The fix is both halves: **own the system** in a DESIGN.md, **derive a direction** from the subject, then gate deterministically.
|
|
8
8
|
|
|
9
9
|
**Text-only models now lead frontend** (Kimi K3, an open MIT model, is #1 on the Arena.ai Frontend Code Arena, ahead of Claude Fable 5). Inside a fully-specified system they produce non-slop UI — which means the review gate can be mechanical, not a vision-LLM call.
|
|
10
10
|
|
|
@@ -19,7 +19,7 @@ Pi auto-discovers the extension and skill.
|
|
|
19
19
|
## Usage
|
|
20
20
|
|
|
21
21
|
```
|
|
22
|
-
/ux #
|
|
22
|
+
/ux # reset to configured default mode
|
|
23
23
|
/ux strict # guardrail + enforce ux_audit gate before handoff (default)
|
|
24
24
|
/ux lite # guardrail only (ideation, exploration)
|
|
25
25
|
/ux off # disable
|
|
@@ -45,14 +45,15 @@ The injected skill enforces **Constraint-First Design Generation**:
|
|
|
45
45
|
```bash
|
|
46
46
|
npx @google/design.md lint DESIGN.md
|
|
47
47
|
```
|
|
48
|
-
If absent, reuse a preset (`ux-presets` skill: shadcn/Material/Radix) or scaffold one once. pi-ux orchestrates `@google/design.md` via shell-out — **not** a runtime dependency.
|
|
48
|
+
If absent, reuse a preset or style-direction starter (`ux-presets` skill: shadcn/Material/Radix; S1 Editorial print / S2 Ledger / S3 Warm consumer) or scaffold one once. Presets are floors, not identities. pi-ux orchestrates `@google/design.md` via shell-out — **not** a runtime dependency.
|
|
49
49
|
1. **5-field brief per screen** — user job, inventory, token constraints, required states, one reference.
|
|
50
|
+
1.5. **Derive a direction** — from the subject's material, commit to mood adjectives, a one-line visual concept, a type voice (pairing table), a color mood (hex), and one signature element — before any markup. The test: two different designers following the direction must produce visibly different pages.
|
|
50
51
|
2. **Generate fast, converge early** — text-only models inside the locked system; 2-loop convergence trigger.
|
|
51
52
|
3. **Normalise** the draft back into tokens/elevation/spacing.
|
|
52
|
-
4. **Render & Inspect** (multimodal models) — reference-first capture, then screenshot your own build and LOOK. Default: local headless-Chrome capture read back inline (offline, no daemon); alternative: `web_screenshot` (pi-web 0.6.2+, PNG returned inline) at a daemon-reachable address
|
|
53
|
+
4. **Render & Inspect** (multimodal models; required in strict mode) — reference-first capture, then screenshot your own build and LOOK against a concrete checklist (squint test, dead zones, monotony, timidity, type, mood). Default: local headless-Chrome capture read back inline (offline, no daemon); alternative: `web_screenshot` (pi-web 0.6.2+, PNG returned inline) at a daemon-reachable address. Judge at viewer resolution (1×–3×); never chase sub-visible precision. Skip when text-only — the deterministic gates are the whole loop.
|
|
53
54
|
5. **Slop-audit gate** — run `ux_audit` (measurable): APCA contrast, token coverage, state coverage, slop tells, tracked-eyebrow/near-black taste tells, reduced-motion coverage.
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
The **Direction playbook** ships in the skill as the positive layer: a typography-voice pairing table (editorial → Newsreader + Source Sans 3, ledger → Spline Sans pair, …), color-mood construction (temperature, tinted neutrals, committed accent posture, band rhythm), composition anatomy (hero formula, rows-over-card-grids, shaped whitespace), the signature element, and default-vs-directed contrast snippets. Taste rules add the named cliché clusters (the cream/terracotta "Claude look", acid-on-black, broadsheet kit, SaaS-card kit, template chrome), typography-as-personality, one-orchestrated-motion, and design-writing rules.
|
|
56
57
|
|
|
57
58
|
## The `ux_audit` tool
|
|
58
59
|
|
|
@@ -75,25 +76,18 @@ Returns pass/fail per gate + a formatted report. In `strict` mode this is the ga
|
|
|
75
76
|
|
|
76
77
|
## Model routing (deterministic-first)
|
|
77
78
|
|
|
78
|
-
The
|
|
79
|
-
|
|
80
|
-
| Step | Best tool/model |
|
|
81
|
-
|------|-----------------|
|
|
82
|
-
| Define system (DESIGN.md) | Reuse a preset OR `agy_execute mode=plan pro-high` (Gemini) **once** |
|
|
83
|
-
| Lint system | `npx @google/design.md lint DESIGN.md` (shell-out) |
|
|
84
|
-
| Per-screen brief | **GLM-5.2** (1M ctx) |
|
|
85
|
-
| Generate variants | **DeepSeek-v4**, **GLM-5.2**, or **Kimi K3** (text-only, inside constraints) |
|
|
86
|
-
| Normalise into system | **DeepSeek-v4** or **GLM-5.2** |
|
|
87
|
-
| Slop audit | `ux_audit` tool (deterministic) + DESIGN.md lint |
|
|
88
|
-
| Optional polish (never a gate) | `agy_execute mode=accept-edits sonnet` (Claude) |
|
|
89
|
-
|
|
90
|
-
**The inversion rule:** the cheaper/weaker the model, the MORE you must externalise constraints. **The deterministic-first principle:** don't spend vision-model quota on what `ux_audit` computes for free.
|
|
79
|
+
The full who-does-what table lives in the **`ux-routing`** skill (not injected — consulted when delegating). The gate is mechanical, not a vision-LLM call. **The inversion rule:** the cheaper/weaker the model, the MORE you must externalise constraints. **The deterministic-first principle:** don't spend vision-model quota on what `ux_audit` computes for free.
|
|
91
80
|
|
|
92
81
|
## Skills
|
|
93
82
|
|
|
94
|
-
- **`ux-design`** — the Constraint-First method +
|
|
95
|
-
- **`ux-presets`** — reference
|
|
83
|
+
- **`ux-design`** — the Constraint-First method + Direction playbook (auto-injected by the hook when active).
|
|
84
|
+
- **`ux-presets`** — reference presets for Step 0: neutral Web/Mobile DESIGN.md starters, three style-direction starters (S1 Editorial print, S2 Ledger, S3 Warm consumer — APCA-verified pairs), the shadcn/Material/Radix reuse table, and a CSS-only `:root` fallback. Reference only — no bundled CSS.
|
|
96
85
|
- **`ux-capture`** — the Step 4 render-and-inspect capture playbook: local headless-Chrome capture read inline vs daemon-rendered `web_screenshot`, LAN IP/host.docker.internal addressing, SSRF-blocked daemons, cloudflared tunnel as last resort.
|
|
86
|
+
- **`ux-routing`** — the model-routing table for delegating design steps (Define/Generate/Inspect/Audit) across agy/Gemini, Claude, DeepSeek, GLM, Kimi. Not injected.
|
|
87
|
+
|
|
88
|
+
## Benchmark
|
|
89
|
+
|
|
90
|
+
`bench/` holds a design-quality harness: three fixed briefs (landing, dashboard, mobile), `run.sh` (headless `pi` run with the same model + screenshot capture), and a fixed scoring rubric. Used to measure output quality across pi-ux versions; results are gitignored.
|
|
97
91
|
|
|
98
92
|
## Configuration
|
|
99
93
|
|
package/extensions/index.js
CHANGED
|
@@ -38,7 +38,9 @@ export function parseUxCommand(text, defaultMode = DEFAULT_MODE) {
|
|
|
38
38
|
const normalizedText = String(text || "").trim().toLowerCase();
|
|
39
39
|
|
|
40
40
|
if (!normalizedText) {
|
|
41
|
-
|
|
41
|
+
// Reset to the configured default, whatever it is — a deliberate
|
|
42
|
+
// `/ux default off` must survive bare `/ux` too.
|
|
43
|
+
return { type: "set-mode", mode: fallback };
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
const [primary, secondary] = normalizedText.split(/\s+/);
|
|
@@ -90,7 +92,7 @@ function auditParametersSchema() {
|
|
|
90
92
|
};
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
function formatAuditResult(result) {
|
|
95
|
+
export function formatAuditResult(result) {
|
|
94
96
|
const lines = [];
|
|
95
97
|
lines.push(result.pass ? "✅ UX AUDIT PASSED" : "❌ UX AUDIT FAILED");
|
|
96
98
|
lines.push("");
|
|
@@ -113,6 +115,17 @@ function formatAuditResult(result) {
|
|
|
113
115
|
for (const m of s.missingFocusVisible) lines.push(` ✗ ${m}`);
|
|
114
116
|
for (const m of s.missingDisabled) lines.push(` ✗ ${m}`);
|
|
115
117
|
for (const m of s.missingReducedMotion || []) lines.push(` ✗ ${m}`);
|
|
118
|
+
if (!s.pass) {
|
|
119
|
+
if (s.missingFocusVisible.length || s.missingDisabled.length) {
|
|
120
|
+
// Interactive selectors ARE present, yet focus/disabled rules failed —
|
|
121
|
+
// for a fragment those rules may simply live in another file.
|
|
122
|
+
lines.push(" ℹ If this is a fragment, states rules may live in another file — pass the COMPLETE stylesheet.");
|
|
123
|
+
} else if (s.hasInteractive === false) {
|
|
124
|
+
// No interactive selectors: the only possible failure is reduced-motion.
|
|
125
|
+
// Say how to fix it, and only conditionally suggest the fragment case.
|
|
126
|
+
lines.push(" ℹ Motion needs a prefers-reduced-motion fallback. If this is the complete stylesheet, add one; if it is a fragment, audit the COMPLETE stylesheet.");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
116
129
|
|
|
117
130
|
const st = result.gates.slopTells;
|
|
118
131
|
lines.push(st.pass ? "✓ Slop tells" : "✗ Slop tells");
|
|
@@ -157,7 +170,7 @@ export default function uxExtension(pi) {
|
|
|
157
170
|
name: "ux_audit",
|
|
158
171
|
label: "UX Slop Audit",
|
|
159
172
|
description:
|
|
160
|
-
"Run deterministic slop-audit gates on CSS: APCA contrast (perceptual; WCAG sidecar), off-system token values (hardcoded hex / ad-hoc shadows), missing interaction states (:focus-visible / :disabled + prefers-reduced-motion), and named AI slop tells (glassmorphism, gradient orbs, neon glow, default-card, tracked-out eyebrows, tinted near-black). No model needed — all gates are computable. In strict mode,
|
|
173
|
+
"Run deterministic slop-audit gates on CSS: APCA contrast (perceptual; WCAG sidecar), off-system token values (hardcoded hex / ad-hoc shadows), missing interaction states (:focus-visible / :disabled + prefers-reduced-motion), and named AI slop tells (glassmorphism, gradient orbs, neon glow, default-card, tracked-out eyebrows, tinted near-black). No model needed — all gates are computable. In strict mode, handoff is blocked until this passes. AUDIT THE COMPLETE STYLESHEET, not fragments. If no contrast pairs are supplied, they are auto-extracted from rules that declare both colour and background.",
|
|
161
174
|
promptSnippet: "Run deterministic UX slop-audit (APCA contrast + tokens + states + slop tells)",
|
|
162
175
|
promptGuidelines: [
|
|
163
176
|
"Contrast, token-coverage, and slop-tells are computable, not judgement — use this tool instead of eyeballing or calling a vision model.",
|
package/hooks/ux-audit.js
CHANGED
|
@@ -206,7 +206,7 @@ function scanOffSystem(css, tokens) {
|
|
|
206
206
|
// .test() flake across calls, so use plain includes().
|
|
207
207
|
function scanStates(css) {
|
|
208
208
|
css = css.replace(/\/\*[\s\S]*?\*\//g, ' '); // dead code must not fail the gate
|
|
209
|
-
const findings = { missingFocusVisible: [], missingDisabled: [], missingReducedMotion: [] };
|
|
209
|
+
const findings = { missingFocusVisible: [], missingDisabled: [], missingReducedMotion: [], hasInteractive: false };
|
|
210
210
|
|
|
211
211
|
// Motion needs a reduced-motion fallback regardless of interactive elements
|
|
212
212
|
// (a hero fade-in on a page with no buttons still needs one).
|
|
@@ -219,6 +219,7 @@ function scanStates(css) {
|
|
|
219
219
|
const hasInteractive =
|
|
220
220
|
/\b(?:button|a|input|select|textarea)\b/i.test(css) || /\[role\s*=\s*"?button"?\]/i.test(css);
|
|
221
221
|
if (!hasInteractive) return findings;
|
|
222
|
+
findings.hasInteractive = true;
|
|
222
223
|
|
|
223
224
|
if (!css.includes(':focus-visible')) findings.missingFocusVisible.push('no :focus-visible rule for interactive elements');
|
|
224
225
|
if (!css.includes(':disabled')) findings.missingDisabled.push('no :disabled rule for interactive elements');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacnh85/pi-ux",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Anti-slop UI/UX design discipline for your Pi agent — anchors a lintable DESIGN.md, runs deterministic slop-audit gates (APCA contrast + tokens + states + slop tells), works with text-only models, ships reference design-system presets.",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Anti-slop UI/UX design discipline for your Pi agent — anchors a lintable DESIGN.md, derives a design direction (mood, type voice, color mood, signature), runs deterministic slop-audit gates (APCA contrast + tokens + states + slop tells), works with text-only models, ships reference design-system presets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"pi",
|
|
@@ -12,9 +12,93 @@ description: >
|
|
|
12
12
|
|
|
13
13
|
Judge captures at viewer resolution (1×–3×); never chase sub-visible precision.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**Disable entrance animations when capturing**: add
|
|
16
|
+
`--force-prefers-reduced-motion` to headless Chrome (or emulate the media
|
|
17
|
+
query). Pages rightly use staggered page-load reveals with `opacity:0`
|
|
18
|
+
backwards-fill — captured mid-animation they screenshot as blank sections,
|
|
19
|
+
and you will "fix" content that isn't broken. The same forced query doubles
|
|
20
|
+
as a reduced-motion audit: with animations off, every section must still be
|
|
21
|
+
fully visible and readable.
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
**Capture at the brief's target viewport.** Web pages: 1280–1440 wide. App
|
|
24
|
+
screens and mobile-first briefs: the width the brief names (usually 390) at
|
|
25
|
+
its target height (~844) — but know your tooling: many headless Chrome builds
|
|
26
|
+
**clamp window width to 500px**, so a "390 capture" secretly renders at 500
|
|
27
|
+
and crops (see the layout probe below for the wrapper that does it honestly).
|
|
28
|
+
If content overflows or dead-ends at the target size, the page is broken —
|
|
29
|
+
**fix the page. Never widen the viewport to make a problem invisible.**
|
|
30
|
+
|
|
31
|
+
## Layout probe (required for fixed-viewport briefs)
|
|
32
|
+
|
|
33
|
+
Two Chrome facts make naive mobile checks lie:
|
|
34
|
+
|
|
35
|
+
1. **Headless Chrome clamps window width to 500px.** A `--window-size=390`
|
|
36
|
+
capture renders the page at 500px and crops the PNG to 390 — cuts at the
|
|
37
|
+
right edge are the CROP, not your CSS. Detect it:
|
|
38
|
+
`--dump-dom` a page containing `window.innerWidth` — 500 at a 390 request
|
|
39
|
+
means clamped.
|
|
40
|
+
2. `window.innerWidth` therefore never reports the true mobile viewport.
|
|
41
|
+
|
|
42
|
+
The honest way to see and measure a 390px screen: a **wrapper page with a
|
|
43
|
+
390×844 iframe** (an iframe IS a true 390px CSS viewport, immune to the
|
|
44
|
+
clamp), rendered at a 500px window with `--allow-file-access-from-files`:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cp index.html /tmp/page.html
|
|
48
|
+
cat > /tmp/wrapper.html <<'EOF'
|
|
49
|
+
<!DOCTYPE html><html><head><meta charset="utf-8"><style>
|
|
50
|
+
body{margin:0;background:#888}#frame{width:390px;height:844px;border:0;outline:2px solid #000}
|
|
51
|
+
</style></head><body>
|
|
52
|
+
<iframe id="frame" src="./page.html"></iframe>
|
|
53
|
+
<pre id="out">measuring…</pre>
|
|
54
|
+
<script>
|
|
55
|
+
const f=document.getElementById('frame');
|
|
56
|
+
f.addEventListener('load',()=>{
|
|
57
|
+
const d=f.contentDocument;
|
|
58
|
+
document.getElementById('out').textContent=
|
|
59
|
+
'page scrollWidth:'+d.documentElement.scrollWidth+' / viewport:390';
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
</body></html>
|
|
63
|
+
EOF
|
|
64
|
+
"$CHROME" --headless --disable-gpu --allow-file-access-from-files \
|
|
65
|
+
--virtual-time-budget=6000 --window-size=500,900 \
|
|
66
|
+
--screenshot=/tmp/mobile-390.png "file:///tmp/wrapper.html"
|
|
67
|
+
"$CHROME" --headless --disable-gpu --allow-file-access-from-files \
|
|
68
|
+
--virtual-time-budget=6000 --window-size=500,900 \
|
|
69
|
+
--dump-dom "file:///tmp/wrapper.html" | grep -o 'page scrollWidth:[^<]*'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Pass = scrollWidth 390 / viewport:390**, and the screenshot shows the true
|
|
73
|
+
mobile render (grey letterbox on the right is the wrapper, not your page).
|
|
74
|
+
If scrollWidth exceeds 390, fix the CSS (min-width on rows/grid, an
|
|
75
|
+
unbreakable string, a fixed-width column) and probe again — on the delivered
|
|
76
|
+
file, after the last edit.
|
|
77
|
+
|
|
78
|
+
**Alternative: puppeteer-core device emulation** (when node ≥18 and npm are
|
|
79
|
+
available — no wrapper file, and it gives true `fullPage` + a JS overflow
|
|
80
|
+
probe): `npm i puppeteer-core` once, then launch with
|
|
81
|
+
`executablePath` pointing at installed Chrome and
|
|
82
|
+
`page.setViewport({ width: 390, height: 844, deviceScaleFactor: 2 })`.
|
|
83
|
+
`setViewport` is real device-metrics emulation — immune to the window clamp.
|
|
84
|
+
Probe with `document.documentElement.scrollWidth` via `page.evaluate` before
|
|
85
|
+
screenshotting; `page.screenshot({ fullPage: true })` for the tall capture.
|
|
86
|
+
|
|
87
|
+
## Default — web_screenshot (pi-web ≥0.7.0, auto local detection)
|
|
88
|
+
|
|
89
|
+
`web_screenshot` auto-routes localhost/LAN/file URLs to the locally installed
|
|
90
|
+
headless Chrome and returns the PNG inline — no daemon, no manual commands:
|
|
91
|
+
|
|
92
|
+
- `web_screenshot url="http://localhost:PORT" width=390 height=844` — the
|
|
93
|
+
model sees the render; pass `width`/`height` explicitly for mobile targets.
|
|
94
|
+
- `full_page=true` captures a tall 8000px window; `wait_for` settles JS via
|
|
95
|
+
`--virtual-time-budget`; `engine="local"` forces local on a public URL.
|
|
96
|
+
- `web_pdf` works the same way (`--print-to-pdf`) for full-content archival.
|
|
97
|
+
- If Chrome is missing: `web_status` shows `localChrome.path`; set `CHROME_PATH`.
|
|
98
|
+
|
|
99
|
+
## Fallback — manual headless Chrome (pi-web <0.7.0 or if the tool errors)
|
|
100
|
+
|
|
101
|
+
Headless Chrome writes the PNG; the `read` tool shows it inline (multimodal models see it). Keep `--window-size` at the target viewport (e.g. `390,844` for a phone screen) — never widen it to hide overflow.
|
|
18
102
|
|
|
19
103
|
- Linux: `google-chrome --headless --screenshot=/tmp/shot.png --window-size=1280,800 http://localhost:PORT` (or `chromium`)
|
|
20
104
|
- macOS: `"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless --screenshot=/tmp/shot.png --window-size=1280,800 http://localhost:PORT`
|
|
@@ -4,11 +4,13 @@ description: >
|
|
|
4
4
|
Anti-slop UI/UX design discipline for AI-generated interfaces. Enforces
|
|
5
5
|
industrial-design principles (Dieter Rams: honest, thorough to the last
|
|
6
6
|
detail, as little design as possible) so output is a defensible system, not
|
|
7
|
-
statistical-default slop (purple glow, shadow-as-texture, missing states)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
statistical-default slop (purple glow, shadow-as-texture, missing states) —
|
|
8
|
+
and equally not the correct-but-forgettable default (Inter, blue accent,
|
|
9
|
+
timid sizes). Covers the Constraint-First method: own the system via a
|
|
10
|
+
lintable DESIGN.md, write a 5-field brief, DERIVE A DIRECTION from the
|
|
11
|
+
subject (mood, type voice, color mood, signature element), generate inside
|
|
12
|
+
constraints, normalise, render-and-inspect with vision, pass a measurable
|
|
13
|
+
slop-audit gate. Works deterministically with text-only models
|
|
12
14
|
(DeepSeek-v4, GLM-5.2, Kimi K3); agy/Gemini/Claude is optional polish, never
|
|
13
15
|
the review gate. Use when designing or building any UI — web, mobile, or
|
|
14
16
|
desktop. Active via /ux lite|strict|off.
|
|
@@ -16,11 +18,20 @@ argument-hint: ""
|
|
|
16
18
|
license: MIT
|
|
17
19
|
---
|
|
18
20
|
|
|
19
|
-
# UX Design Discipline — Anti-Slop, Industrial-Design Method
|
|
21
|
+
# UX Design Discipline — Direction + Anti-Slop, Industrial-Design Method
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
Design work has two moves, and skipping either produces slop:
|
|
24
|
+
|
|
25
|
+
1. **Discipline** — never drift into the statistical default: tokens only, named
|
|
26
|
+
elevation, full states, none of the banned tells. (Steps 0–5 + the audit gate.)
|
|
27
|
+
2. **Direction** — always commit to a visual point of view drawn from the
|
|
28
|
+
subject, before any markup. (Step 1.5 + the Direction playbook.)
|
|
29
|
+
|
|
30
|
+
Discipline without direction gives the correct-but-forgettable page — Inter,
|
|
31
|
+
a blue accent, white cards, timid sizes — that no design lead would ship.
|
|
32
|
+
Direction without discipline gives purple glow and missing states. The
|
|
33
|
+
repo-root **DESIGN.md** anchors the system; the **direction brief** anchors
|
|
34
|
+
the taste. You produce both.
|
|
24
35
|
|
|
25
36
|
## The anti-slop guardrail (hard rules)
|
|
26
37
|
|
|
@@ -53,11 +64,11 @@ deterministic gate and the agent both read.
|
|
|
53
64
|
1. **Repo-root `DESIGN.md` exists** → read it; use its tokens verbatim. This is the happy path.
|
|
54
65
|
2. **An existing system is already wired in** (shadcn/MUI/Radix theme, Tailwind config) → reuse it — YAGNI applies to design systems too.
|
|
55
66
|
3. **A reference site exists** (brand to match, look to echo) → `web_extract` it and distill its design DNA (4–6 core hex, type roles, radius/spacing rhythm) into a DESIGN.md starter, lint it, then work inside it.
|
|
56
|
-
4. **Nothing exists** → do NOT block. Pick a
|
|
67
|
+
4. **Nothing exists** → do NOT block. Pick a preset from the `ux-presets` skill (B1 Web / B2 Mobile neutral, or a style-direction starter — infer from the task; **ASK only if web-vs-mobile is genuinely unclear**, because it changes touch targets, hover, and safe areas). Use it **in-context as the implicit system** for this generation, then offer to persist it to repo-root `DESIGN.md` (don't auto-write — that's an unrequested file mutation). **A preset is a floor, not an identity:** Step 1.5 must still bend its display face, neutrals, and accent until the finished page could not be mistaken for the stock preset. If your page could be the untouched preset, you did not design.
|
|
57
68
|
5. **No preset fits** → generate a DESIGN.md once with `agy_execute mode=plan pro-high` (Gemini); thereafter text-only models are sufficient.
|
|
58
69
|
|
|
59
70
|
The non-negotiables a preset/system must define before any screen is written:
|
|
60
|
-
- **Tokens:** colour (one accent + neutrals), type scale, spacing (8px grid), radius, elevation.
|
|
71
|
+
- **Tokens:** colour (one accent + tinted neutrals), type scale, spacing (8px grid), radius, elevation.
|
|
61
72
|
- **Elevation scale:** 3–5 named levels only. Map every shadow to one.
|
|
62
73
|
- **State contract:** every interactive component declares all states (web: + `:hover`/`:focus-visible`; mobile: + `:active`/pressed, NO hover); motion ships with a `prefers-reduced-motion` fallback.
|
|
63
74
|
|
|
@@ -75,10 +86,29 @@ All five are required. If you can't produce inventory + states with confidence,
|
|
|
75
86
|
4. **Required interaction states** per interactive component.
|
|
76
87
|
5. **One reference** screen/component that already feels like the product (brand tone).
|
|
77
88
|
|
|
78
|
-
**Generic-default check (before building):** if there is no DESIGN.md, sketch a mini plan first — 4–6 named hex, type roles, a one-line layout concept or ASCII wireframe, one principle. Then ask: *would I produce this same plan for any similar brief?* Every part that answers yes is a default, not a choice — revise it and say what changed.
|
|
79
|
-
|
|
80
89
|
**Scale to the task:** internal single-file tools → one-line brief + one draft is fine; user-facing product UI → full brief + 2–3 variants. The gates are the same either way.
|
|
81
90
|
|
|
91
|
+
### Step 1.5 — Derive a direction (before any markup)
|
|
92
|
+
|
|
93
|
+
This is the taste step, and it is not optional for user-facing UI. From the
|
|
94
|
+
brief, extract the subject's **material** and commit to a direction brief.
|
|
95
|
+
Test for a real direction: *two different designers following it must produce
|
|
96
|
+
visibly different pages.* If they would produce the same page, you wrote a
|
|
97
|
+
default, not a direction — revise.
|
|
98
|
+
|
|
99
|
+
1. **Subject material** — industry, material, era, energy. "B2B invoicing" is a category; "the accountant's desk — paper, ink, ruled columns, stamps" is material. Design the material.
|
|
100
|
+
2. **Mood adjectives** — three, committed ("calm, ruled, exact"). "Modern, clean, minimal" are the absence of mood — banned as answers.
|
|
101
|
+
3. **Visual concept** — one line naming the world this page lives in ("an expedition logbook, not a travel-agency template"; "a ledger book, not an admin panel").
|
|
102
|
+
4. **Type voice** — display + body pairing from the Direction playbook table (or a deliberate off-table choice). Never default to Inter/system-ui when the subject has a voice.
|
|
103
|
+
5. **Color mood** — temperature, neutral tint, the one accent, written as hex (playbook below).
|
|
104
|
+
6. **Signature element** — the one memorable thing (playbook below). Exactly one.
|
|
105
|
+
|
|
106
|
+
Say the direction in one line before writing markup —
|
|
107
|
+
`Direction: <concept> · type <display>+<body> · palette <hex> · signature: <element>` —
|
|
108
|
+
then generate inside it. When you catch yourself mid-build reaching for a
|
|
109
|
+
stock choice, ask the generic-default question: *would I produce this same
|
|
110
|
+
plan for any similar brief?* Every yes is a default masquerading as a choice.
|
|
111
|
+
|
|
82
112
|
### Step 2 — Generate fast, converge early
|
|
83
113
|
Generate several variants, commit early to the direction with the clearest hierarchy under real content.
|
|
84
114
|
- **Variants are files, not vibes:** write 2–3 drafts as separate HTML files (`.ux/drafts/<slug>-{a,b,c}.html`), screenshot each, view the images side by side, and commit to one direction before touching real code. When redesigning existing UI, first capture a BEFORE screenshot of the current page as reference context.
|
|
@@ -88,61 +118,128 @@ Generate several variants, commit early to the direction with the clearest hiera
|
|
|
88
118
|
Replace ad-hoc colours with tokens, remap shadows to the elevation scale, snap spacing to the rhythm, turn one-offs into component variants.
|
|
89
119
|
- Gate: sample 10 components, verify token mapping. If fewer than 8 map cleanly, stop and repair the baseline.
|
|
90
120
|
|
|
91
|
-
### Step 4 — Render & Inspect (vision verify
|
|
121
|
+
### Step 4 — Render & Inspect (vision verify)
|
|
92
122
|
|
|
93
123
|
The deterministic gates check CSS text; they never see the page. When the
|
|
94
|
-
generating model is multimodal (GLM-5.3, Claude, Gemini)
|
|
95
|
-
|
|
96
|
-
models
|
|
124
|
+
generating model is multimodal (GLM-5.3, Claude, Gemini) this step is
|
|
125
|
+
**REQUIRED in strict mode** — you see your own output, which is the single
|
|
126
|
+
biggest quality lever for flash-tier models. Skipping it is a gate failure
|
|
127
|
+
even when `ux_audit` passes. (Text-only models, or genuinely no capture path
|
|
128
|
+
→ skip; the deterministic gates are the whole loop.)
|
|
97
129
|
|
|
98
130
|
1. **Reference-first.** If the task gives a URL or screenshot as the design target, capture it with `web_screenshot` (pi-web 0.6.2+ returns the PNG inline) BEFORE generating. Every visual judgment is made against that reference.
|
|
99
|
-
2. **Inspect your own build.** Serve the UI (dev server or `python3 -m http.server`), then capture and LOOK — default: local headless Chrome + `read` (renders inline; commands in the `ux-capture` skill
|
|
100
|
-
3. **
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
131
|
+
2. **Inspect your own build.** Serve the UI (dev server or `python3 -m http.server`), then capture and LOOK — default: local headless Chrome + `read` (renders inline; commands in the `ux-capture` skill); alternative: `web_screenshot` at a daemon-reachable address (full playbook in `ux-capture`).
|
|
132
|
+
3. **The LOOK checklist** — at the **brief's target viewport** (mobile briefs: capture at exactly 390 wide; never widen the capture to make a problem invisible — that is cheating the loop). Fix every failure in one batch, re-capture, repeat (2-loop convergence trigger, then move on):
|
|
133
|
+
- **Squint test:** three distinguishable levels of hierarchy? Does the eye land first where it should?
|
|
134
|
+
- **Dead zones:** any region with nothing for the eye? Whitespace piling up on one side? On app screens: does content leave a large empty region below the last element at the target height?
|
|
135
|
+
- **Monotony:** consecutive sections with identical weight/background? Every content group in the same box?
|
|
136
|
+
- **Timidity:** cover the logo — could this page belong to anyone? Then amplify the display scale or the signature; the direction is not coming through.
|
|
137
|
+
- **Type & overflow at target width:** display sizes actually large? measure comfortable? **any horizontal scroll, cut-off text, or squeezed badges at the target width?** orphans, cramped labels?
|
|
138
|
+
- **Mood:** is the palette's temperature visible at a glance, or is it generic white+blue?
|
|
139
|
+
4. **Visibility baseline.** Judge at what a viewer sees at 1×–3×. Nothing sub-visible can fail, and nothing sub-visible may be produced — no ±1px claims, no per-pixel diffs, no instrument-read values on either side.
|
|
140
|
+
5. **Gates stay final.** `ux_audit` (Step 5) remains the blocking authority; vision settles only what looking can settle.
|
|
104
141
|
|
|
105
142
|
### Step 5 — Slop-audit gate (blocks handoff on fail)
|
|
106
143
|
|
|
107
144
|
Run `ux_audit` on the generated CSS. The contrast gate reports **APCA Lc**
|
|
108
145
|
(perceptual, primary — Lc ≥75 body / ≥45 large-bold / ≥30 non-text) with a
|
|
109
146
|
WCAG 2.x ratio sidecar for compliance reporting. APCA catches dark-theme +
|
|
110
|
-
thin-type slop that the legacy WCAG ratio misses.
|
|
111
|
-
tells (glassmorphism, gradient orbs, neon glow, default-card).
|
|
147
|
+
thin-type slop that the legacy WCAG ratio misses.
|
|
112
148
|
|
|
113
|
-
| Gate | Pass | Fail action |
|
|
149
|
+
| Gate (implemented in `ux_audit`) | Pass | Fail action |
|
|
114
150
|
|---|---|---|
|
|
115
|
-
| Token coverage | ≥8/10 components map to tokens | Pause, repair baseline |
|
|
116
|
-
| Shadow recipes | ≤3 named recipes on core surfaces | Collapse to named elevations |
|
|
117
151
|
| Contrast (APCA) | Lc ≥75 body, ≥45 large-bold, ≥30 non-text (WCAG sidecar shown) | Block handoff until fixed |
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
| Slop tells | no glassmorphism / orbs / glow / default-card / 1px-gray-border / tracked-out eyebrow / tinted near-black bg | Refactor: space → bg shift → elevation before a border |
|
|
152
|
+
| Tokens | no hardcoded hex outside `:root` definitions; no ad-hoc box-shadows built from raw values | Move values into tokens / named elevations |
|
|
153
|
+
| States + motion | interactive elements have `:focus-visible` + `:disabled`; any transition/animation ships a `prefers-reduced-motion` fallback | Keep in draft |
|
|
154
|
+
| Slop tells | no glassmorphism / gradient orbs / neon glow / default-card / 1px-gray-border / tracked-out eyebrow / tinted near-black bg | Refactor: space → bg shift → elevation before a border |
|
|
155
|
+
|
|
156
|
+
**Model-side checks** (not mechanically gated — you verify): token mapping ≥8/10 sampled components; ≤3 named shadow recipes; no duplicate components. A 4th slop gate flags named AI tells automatically.
|
|
157
|
+
|
|
158
|
+
## Direction playbook (the positive layer)
|
|
121
159
|
|
|
122
|
-
|
|
160
|
+
The guardrail stops bad; the playbook produces good. Pull from it in Step 1.5.
|
|
123
161
|
|
|
124
|
-
|
|
162
|
+
### Typography voice
|
|
125
163
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
never the review gate.
|
|
164
|
+
Choose deliberately; 1–2 families with clearly distinct roles. Inter/system-ui
|
|
165
|
+
is the statistical default this method exists to escape — reach past it unless
|
|
166
|
+
the subject is genuinely neutral infrastructure. Pairings that work (Google
|
|
167
|
+
Fonts; first = display, second = body):
|
|
131
168
|
|
|
132
|
-
|
|
|
169
|
+
| Subject voice | Pairing | Why it works |
|
|
133
170
|
|---|---|---|
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
|
|
|
137
|
-
|
|
|
138
|
-
|
|
|
139
|
-
|
|
|
140
|
-
|
|
141
|
-
|
|
171
|
+
| Editorial / literary / journal | **Newsreader** + **Source Sans 3** | serif display at 500–600, tight leading; body stays quiet |
|
|
172
|
+
| Expedition / outdoors / heritage | **Bricolage Grotesque 800** + **Newsreader** | heavy grotesque display over a serif body reads "printed field guide" |
|
|
173
|
+
| Financial / ledger / legal | **Spline Sans** + **Spline Sans Mono** | mono for every numeral, `tabular-nums`; ruled borders do the branding |
|
|
174
|
+
| Technical / infra / dev tool | **IBM Plex Sans** + **IBM Plex Mono** | personality from weight contrast + hairline rules, not decoration |
|
|
175
|
+
| Warm consumer / food / home | **DM Sans** 700–800 display + body | geometric warmth; personality from color + radius + scale |
|
|
176
|
+
| Dense data / dashboard | Spline Sans pair or IBM Plex pair | personality from rhythm and status-color discipline |
|
|
177
|
+
|
|
178
|
+
Cautions: the cream+Fraunces+terracotta and Space-Grotesk-on-dark looks are
|
|
179
|
+
named cliché clusters below. Off-table picks that still carry voice:
|
|
180
|
+
Archivo, Schibsted Grotesk, Libre Caslon Text, Spectral (displays);
|
|
181
|
+
Public Sans, Instrument Sans, Work Sans, Outfit (bodies).
|
|
182
|
+
|
|
183
|
+
Numbers that make type feel designed: marketing h1 ≥ `clamp(2.75rem, 7vw, 6.75rem)`
|
|
184
|
+
(≈96–108px at 1440 — the hero must dominate at 3–4× body size or the scale
|
|
185
|
+
step is wrong); section h2 ≥ 2rem; stat/metric numerals ≥ 2rem with
|
|
186
|
+
`tabular-nums`; display weight 700–800 against body 400; body 1rem–1.125rem/1.6
|
|
187
|
+
at 45–75ch; display letter-spacing −0.01 to −0.025em. Timid sizes are the #1
|
|
188
|
+
"no feel" symptom — when in doubt, bigger display, fewer words.
|
|
189
|
+
|
|
190
|
+
### Color mood construction
|
|
191
|
+
|
|
192
|
+
- **Temperature first:** warm or cool page? Then tint EVERY neutral with the mood hue at very low chroma — warm paper `#F6F4EE`, green-black ink `#22302A`, blue-gray desk `#F3F1EA`. Never pure `#fff`/`#000`.
|
|
193
|
+
- **One accent, posture committed:** deep + saturated (vermilion, forest, indigo, oxblood) beats bright + default. Pure blue `#0066FF` on white IS the default look. Test the accent's APCA pair before committing to it.
|
|
194
|
+
- **Support colors only when they encode meaning** (success/danger/warning), each with a soft tinted background for badges — never as decoration.
|
|
195
|
+
- **Bands give rhythm:** 2–3 background treatments across a page (paper → tinted → dark ink → paper). A dark band mid-page is a strong, cheap rhythm marker. Alternating white sections are not rhythm, they're fog.
|
|
196
|
+
- **Surface budget: three surfaces, one family.** Base, one warm/tinted mid, one inverse (dark) — all tinted by the same mood hue — plus the accent reserved for CTAs and marks. If the header/nav introduces a color the rest of the page never uses, delete it: the header inherits the base surface. Five unrelated surfaces read as template collage.
|
|
197
|
+
|
|
198
|
+
### Composition anatomy
|
|
199
|
+
|
|
200
|
+
- **Hierarchy = scale + weight + color contrast**, not shadow boxes. If hierarchy needs a shadow, the scale is broken.
|
|
201
|
+
- **Hero formula:** kicker (small, real information) → one big claim (display face, ≥3rem, ≤9 words) → one sub (≤2 lines) → one primary action. No gradient-blob backgrounds; the whitespace and type ARE the design.
|
|
202
|
+
- **Rows beat card grids** for repeated content (trips, features, invoices, episodes) — and a row is a **fixed column grid, not flowing text**: 4–5 columns at identical x-positions across all rows (identity left, one datum per middle column, terminal value — price/CTA — right-aligned on a shared axis), hairline separators, equal row heights. Metadata never wraps to a second line; if it doesn't fit the column, the copy is too long. If you must card, vary the span — one wide, two narrow.
|
|
203
|
+
- **Shaped whitespace:** asymmetric gutters, a deliberately wide margin around one element, rag that breathes. Whitespace is a material you place, not what's left over.
|
|
204
|
+
- **Depth via composition** — overlap, scale steps, band shifts — not glow or shadow-piles.
|
|
205
|
+
|
|
206
|
+
### The signature element
|
|
207
|
+
|
|
208
|
+
Exactly one memorable element, drawn from the subject, **scaled like
|
|
209
|
+
composition rather than framed like content**: ≥25% of the viewport wide,
|
|
210
|
+
allowed to bleed off the canvas edge, set at reduced contrast so it reads as
|
|
211
|
+
atmosphere — never a small bordered "illustration card" smaller than the
|
|
212
|
+
headline it sits beside. Forms that work: an oversized glyph or numeral from
|
|
213
|
+
the subject's own writing system, a rotated stamp/seal, index numerals treated
|
|
214
|
+
as design objects, a contour/texture system that fills the hero, one
|
|
215
|
+
full-bleed moment. Everything else stays quiet. If you cannot name your
|
|
216
|
+
signature element, you don't have one — and the page will be forgettable.
|
|
217
|
+
|
|
218
|
+
### The punctuation kit (authored details, 2–3 per viewport)
|
|
219
|
+
|
|
220
|
+
Small deliberate marks are the difference between "clean" and "authored":
|
|
221
|
+
|
|
222
|
+
- one accent-colored terminal on the H1 (a colored period, a final word);
|
|
223
|
+
- pull quotes framed by a bracket, rule, or oversized mark — not a floating italic slab;
|
|
224
|
+
- one stamp/seal/badge marking the page's scarcity or guarantee claim;
|
|
225
|
+
- texture glyphs from the subject's language on repeated items (JP kanji beside route names, §, №, coordinates);
|
|
226
|
+
- terminal marks as data affordances (an arrow on prices/rows) — but NEVER appended to every link/button (that is the template-chrome cliché below).
|
|
227
|
+
|
|
228
|
+
Placeholder monograms (initials in a circle) are not punctuation — replace
|
|
229
|
+
them with role labels and one real credential line.
|
|
230
|
+
|
|
231
|
+
### Default vs directed (feel the difference)
|
|
232
|
+
|
|
233
|
+
- *Default:* `Inter`, `#111` on `#fff`, `h1{font-size:2.5rem}`, blue button, three equal cards.
|
|
234
|
+
- *Directed (same brief, "ledger" concept):* Spline Sans + mono `tabular-nums`, desk `#F3F1EA`, ruled table borders, one rotated `OVERDUE` stamp badge, forest accent `#1E6B50`.
|
|
235
|
+
|
|
236
|
+
Same effort. One is a page; the other is a template.
|
|
142
237
|
|
|
143
|
-
|
|
238
|
+
## Model routing
|
|
144
239
|
|
|
145
|
-
|
|
240
|
+
The who-does-what table lives in the `ux-routing` skill (not injected). Always
|
|
241
|
+
true: **the deterministic gate reviews; a multimodal model looks at its own
|
|
242
|
+
render; taste lives in the direction brief, not the model choice.**
|
|
146
243
|
|
|
147
244
|
## Banned anti-patterns
|
|
148
245
|
|
|
@@ -158,7 +255,7 @@ screen stays in draft until refactored.
|
|
|
158
255
|
- Shadows as texture (drifting blur/opacity per component). Shadows = named elevation only.
|
|
159
256
|
- Magic pixel values; off-scale font sizes; ad-hoc accent colours.
|
|
160
257
|
- Shipping a component without `focus-visible` + `disabled` states.
|
|
161
|
-
- Prompting "make it modern/clean" with no DESIGN.md — the single biggest slop trigger.
|
|
258
|
+
- Prompting "make it modern/clean" with no DESIGN.md and no direction — the single biggest slop trigger.
|
|
162
259
|
|
|
163
260
|
### Named cliché clusters (credit: anthropics/skills frontend-design)
|
|
164
261
|
All legitimate for *some* briefs — but they are defaults, not choices, and appear regardless of subject. Where the brief pins a direction, follow it exactly; where an axis is free, don't spend it here:
|
|
@@ -172,8 +269,8 @@ All legitimate for *some* briefs — but they are defaults, not choices, and app
|
|
|
172
269
|
## Taste rules
|
|
173
270
|
|
|
174
271
|
- **Ground it in the subject.** Distinctive choices come from the brief's industry, materials, and vernacular — a toy for kids and a trading dashboard should not share a visual language. If the subject is unclear, confirm it before designing.
|
|
175
|
-
- **Typography carries personality.** Choose typefaces deliberately per project (1–2 families, clearly distinct roles); body lines under ~80 chars. Never accent a single word of a headline; no ALL-CAPS labels by default; structural devices (numbers, rules, eyebrows) only when they encode real information — `01 / 02 / 03` is for actual sequences.
|
|
272
|
+
- **Typography carries personality.** Choose typefaces deliberately per project (1–2 families, clearly distinct roles — the playbook table is the starting point, not the ceiling); body lines under ~80 chars. Never accent a single word of a headline; no ALL-CAPS labels by default; structural devices (numbers, rules, eyebrows) only when they encode real information — `01 / 02 / 03` is for actual sequences.
|
|
176
273
|
- **Motion: one orchestrated moment.** A single page-load sequence or reveal lands better than effects scattered everywhere; fade-and-slide-up on every section is an AI tell. Motion that answers an action (opening, confirming) is welcome. Always ship a `prefers-reduced-motion` fallback.
|
|
177
|
-
- **Design writing is design.** Use the user's words, not system words ("notifications", not "webhook config"). CTAs say what happens ("Save changes", not "Submit"); one name per action across the flow. Errors direct instead of apologising; empty states invite action.
|
|
178
|
-
- **Spend boldness in one place.** One memorable element; everything around it quiet and disciplined. Quality floor without announcing it: responsive, visible keyboard focus, reduced motion, accessible contrast.
|
|
274
|
+
- **Design writing is design.** Use the user's words, not system words ("notifications", not "webhook config"). CTAs say what happens ("Save changes", not "Submit"); one name per action across the flow. Errors direct instead of apologising; empty states invite action. In a screenshot test, placeholder names ("Acme", "Lorem", "Feature One") read as template — write real content even in drafts.
|
|
275
|
+
- **Spend boldness in one place.** One memorable element (the signature); everything around it quiet and disciplined. Quality floor without announcing it: responsive, visible keyboard focus, reduced motion, accessible contrast.
|
|
179
276
|
- **Data-viz rules (dashboards & charts).** Chart fills must pass non-text contrast (≥3:1) against their track. Adjacent categorical fills must be nameably different, not opacity steps; cap ramps at 7. Secondary series ≤0.85 opacity or a muted token; today/selected gets full accent. Numeric cells get `font-variant-numeric: tabular-nums`. Empty/zero chart states show an axis or "no data" slot, never a blank canvas. Tables: row hover on bg, right-aligned numerics, sentence-case headers.
|
|
@@ -3,11 +3,12 @@ name: ux-presets
|
|
|
3
3
|
description: >
|
|
4
4
|
Reference design-system presets for ux-design. Anti-slop shortcuts: instead of
|
|
5
5
|
inventing tokens, elevation, and state contracts from scratch, reuse one of
|
|
6
|
-
these battle-tested systems (shadcn/ui, Material 3, Radix)
|
|
7
|
-
medium-tuned DESIGN.md starter (Web or Mobile)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
these battle-tested systems (shadcn/ui, Material 3, Radix), drop in a
|
|
7
|
+
medium-tuned DESIGN.md starter (Web or Mobile), or adopt a style-direction
|
|
8
|
+
starter (Editorial print, Ledger, Warm consumer) that already carries a point
|
|
9
|
+
of view. Use when starting a new UI project with no existing design system,
|
|
10
|
+
when Step 0 (Own the system) of the ux-design method has nothing to reuse,
|
|
11
|
+
or when asked for a starter token set. These are REFERENCES only —
|
|
11
12
|
pi-ux never bundles CSS.
|
|
12
13
|
argument-hint: ""
|
|
13
14
|
license: MIT
|
|
@@ -172,7 +173,6 @@ Map EVERY shadow to a named level — never invent blur/opacity per component:
|
|
|
172
173
|
```
|
|
173
174
|
|
|
174
175
|
### B2 — Mobile preset (touch-first, native / RN / mobile web)
|
|
175
|
-
|
|
176
176
|
```markdown
|
|
177
177
|
---
|
|
178
178
|
name: Mobile Baseline
|
|
@@ -275,6 +275,130 @@ is nearer than the page — keep it that way.
|
|
|
275
275
|
- iOS vs Android: follow the host platform — iOS HIG (SF symbols, larger corner radius, sheet) vs Material 3 (FAB, ripple, top app bar).
|
|
276
276
|
```
|
|
277
277
|
|
|
278
|
+
## Option B+ — style-direction starters (a floor WITH a point of view)
|
|
279
|
+
|
|
280
|
+
B1/B2 are deliberately neutral — they pass the gates but carry no identity, and
|
|
281
|
+
a project that keeps them untouched ships the "correct but forgettable" page.
|
|
282
|
+
The three starters below pair the same gate-hardened token structure with a
|
|
283
|
+
**subject material and type voice**, so Step 1.5 starts from a direction
|
|
284
|
+
instead of a blank. Pick the one whose material matches the brief; then still
|
|
285
|
+
bend it (Step 1.5): swap the accent to the subject's world, adjust the mood
|
|
286
|
+
adjectives, and commit a signature element of your own. All pairs below were
|
|
287
|
+
verified against the APCA ladder (Lc ≥75 body · ≥45 large · ≥60 small-bold
|
|
288
|
+
badges).
|
|
289
|
+
|
|
290
|
+
### S1 — Editorial print (journal, heritage brand, long-form, portfolio)
|
|
291
|
+
|
|
292
|
+
Material: the printed page — warm paper, ink, hairline rules. Near-square
|
|
293
|
+
corners; separation by rules and whitespace, elevation almost never. Display
|
|
294
|
+
**Newsreader** (500–600, tight leading) over **Source Sans 3** body.
|
|
295
|
+
|
|
296
|
+
```yaml
|
|
297
|
+
name: Editorial Print
|
|
298
|
+
colors:
|
|
299
|
+
bg: "#F6F2E9" # warm paper
|
|
300
|
+
surface: "#EEE8DA" # shaded paper band
|
|
301
|
+
text: "#20241F" # ink
|
|
302
|
+
text-muted: "#565B50"
|
|
303
|
+
accent: "#7D2B25" # oxblood — links, rules, stamps
|
|
304
|
+
accent-hover: "#67241E"
|
|
305
|
+
border: "#DBD3C2" # hairline
|
|
306
|
+
danger: "#9B2C20"
|
|
307
|
+
typography:
|
|
308
|
+
body: { fontFamily: "Source Sans 3", fontSize: 1.0625rem, lineHeight: 1.65 }
|
|
309
|
+
h1: { fontFamily: Newsreader, fontSize: 3rem, fontWeight: 600, lineHeight: 1.1 }
|
|
310
|
+
h2: { fontFamily: Newsreader, fontSize: 2.1rem, fontWeight: 600 }
|
|
311
|
+
label: { fontFamily: "Source Sans 3", fontSize: 0.85rem, fontWeight: 600 }
|
|
312
|
+
rounded: { sm: 2px, md: 2px, lg: 3px } # print is square
|
|
313
|
+
components:
|
|
314
|
+
ruled-row: { borderTop: "1.5px solid {colors.text}", padding: "{spacing.md} 0" }
|
|
315
|
+
link: { color: "{colors.accent}", textDecorationThickness: "1px" }
|
|
316
|
+
button-quiet: { border: "1.5px solid {colors.text}", color: "{colors.text}", rounded: "{rounded.sm}" }
|
|
317
|
+
stamp: { color: "{colors.accent}", border: "2px solid {colors.accent}", transform: "rotate(-2deg)" }
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Do: rules over shadows (border hierarchy); generous margins around display
|
|
321
|
+
type; numerals and folios as design objects. Don't: shadows bigger than
|
|
322
|
+
`--elev-sm`; rounded-2xl softness; dark mode (this system IS paper).
|
|
323
|
+
|
|
324
|
+
### S2 — Ledger (finance, invoicing, dense data, legal, ops dashboards)
|
|
325
|
+
|
|
326
|
+
Material: the accountant's desk — desk paper, cards, ruled columns, stamps.
|
|
327
|
+
Every numeral in the mono face with `font-variant-numeric: tabular-nums`.
|
|
328
|
+
Display **Spline Sans** + **Spline Sans Mono** for all data.
|
|
329
|
+
|
|
330
|
+
```yaml
|
|
331
|
+
name: Ledger
|
|
332
|
+
colors:
|
|
333
|
+
bg: "#F3F1EA" # desk
|
|
334
|
+
surface: "#FCFBF7" # card
|
|
335
|
+
text: "#1C211E" # ink
|
|
336
|
+
text-muted: "#5B625D"
|
|
337
|
+
accent: "#1E6B50" # ledger green — buttons, positive
|
|
338
|
+
accent-text: "#17573F" # green as TEXT on desk/card (Lc 79-81)
|
|
339
|
+
accent-hover: "#155640"
|
|
340
|
+
danger: "#B0362A" # stamp text on #F4E3E0 (small-bold ≥600)
|
|
341
|
+
warning: "#8A6210" # stamp text on #F2EAD4 (small-bold ≥600)
|
|
342
|
+
border: "#E0DDD2"
|
|
343
|
+
rule: "#1C211E" # 1.5px structural rules — the branding
|
|
344
|
+
typography:
|
|
345
|
+
body: { fontFamily: "Spline Sans", fontSize: 0.9rem, lineHeight: 1.5 }
|
|
346
|
+
data: { fontFamily: "Spline Sans Mono", fontWeight: 500, fontVariantNumeric: tabular-nums }
|
|
347
|
+
h1: { fontFamily: "Spline Sans", fontSize: 1.5rem, fontWeight: 700, letterSpacing: "-0.015em" }
|
|
348
|
+
label: { fontFamily: "Spline Sans Mono", fontSize: 0.6875rem, fontWeight: 500, letterSpacing: "0.08em", textTransform: uppercase }
|
|
349
|
+
rounded: { sm: 3px, md: 6px, lg: 8px }
|
|
350
|
+
components:
|
|
351
|
+
panel: { backgroundColor: "{colors.surface}", border: "1.5px solid {colors.rule}", rounded: "{rounded.md}" }
|
|
352
|
+
kpi-value: { font: "{typography.data}", fontSize: 1.6875rem, fontWeight: 600 }
|
|
353
|
+
status-paid: { color: "{colors.accent-text}", backgroundColor: "#E2EEE7", fontWeight: 700, fontSize: "0.65rem" }
|
|
354
|
+
status-overdue: { color: "{colors.danger}", backgroundColor: "#F4E3E0", fontWeight: 700, fontSize: "0.65rem", transform: "rotate(-2deg)" }
|
|
355
|
+
table-head: { font: "{typography.label}", borderBottom: "1.5px solid {colors.rule}" }
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Do: structural 1.5px rules; status badges as stamps (soft bg + dark text);
|
|
359
|
+
KPI strip as one ruled panel, not four floating cards. Don't: pastel
|
|
360
|
+
dashboards with soft shadows everywhere; chart fills lighter than 3:1 against
|
|
361
|
+
the track; intercom-blue accents.
|
|
362
|
+
|
|
363
|
+
### S3 — Warm consumer (food, home, family, habits, kitchen/bath apps)
|
|
364
|
+
|
|
365
|
+
Material: warm kitchen linen. Chunky friendly radii, one deep green spine,
|
|
366
|
+
freshness told in color (fresh/expiring/expired), oversized action. Display
|
|
367
|
+
**DM Sans** 700–800.
|
|
368
|
+
|
|
369
|
+
```yaml
|
|
370
|
+
name: Warm Consumer
|
|
371
|
+
colors:
|
|
372
|
+
bg: "#FAF6EE" # linen
|
|
373
|
+
surface: "#FFFFFF" # card
|
|
374
|
+
text: "#27302A"
|
|
375
|
+
text-muted: "#5C6457" # passes on BOTH linen and white
|
|
376
|
+
accent: "#2F6B3C" # basil — FAB, primary actions
|
|
377
|
+
accent-pressed: "#245430"
|
|
378
|
+
accent-soft: "#E5F0E2"
|
|
379
|
+
danger: "#A93222" # expired badge text on #F8E3DD (bold)
|
|
380
|
+
warning: "#8A5D0B" # expiring badge text on #F7ECD4 (bold)
|
|
381
|
+
border: "#EAE4D6"
|
|
382
|
+
typography:
|
|
383
|
+
body: { fontFamily: "DM Sans", fontSize: 1rem, lineHeight: 1.45 }
|
|
384
|
+
h1: { fontFamily: "DM Sans", fontSize: 1.4375rem, fontWeight: 800, letterSpacing: "-0.02em" }
|
|
385
|
+
badge: { fontFamily: "DM Sans", fontSize: 0.65625rem, fontWeight: 700, letterSpacing: "0.03em" }
|
|
386
|
+
rounded: { sm: 12px, md: 16px, lg: 22px }
|
|
387
|
+
components:
|
|
388
|
+
list-row: { backgroundColor: "{colors.surface}", border: "1.5px solid {colors.border}", rounded: "{rounded.md}", padding: "12px 14px" }
|
|
389
|
+
category-spine: { position: absolute, left: 0, width: 5px, backgroundColor: "{category-color}" }
|
|
390
|
+
fab: { backgroundColor: "{colors.accent}", color: "#F2F7EE", rounded: 999px, padding: "16px 26px", fontWeight: 800 }
|
|
391
|
+
badge-soon: { color: "{colors.warning}", backgroundColor: "#F7ECD4" }
|
|
392
|
+
badge-expired: { color: "{colors.danger}", backgroundColor: "#F8E3DD" }
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Do: category color spines (5px, left edge); FAB as the page's one loud
|
|
396
|
+
element; header bands in the accent color. Don't: neon or candy gradients;
|
|
397
|
+
hover-only feedback (touch medium — use B2's tap rules); body below 16px.
|
|
398
|
+
|
|
399
|
+
Mobile adaptation: any S-starter keeps its palette/type and takes B2's
|
|
400
|
+
mechanics (44pt targets, `:active` states, safe areas, thumb zone).
|
|
401
|
+
|
|
278
402
|
## Option C — minimal `:root` token set (CSS-only, no DESIGN.md)
|
|
279
403
|
|
|
280
404
|
For CSS-only projects that do not adopt DESIGN.md, drop this compact baseline
|
|
@@ -330,11 +454,13 @@ into the project's `:root`. It satisfies the token + elevation + state gates of
|
|
|
330
454
|
|
|
331
455
|
1. Run **Step 0** of ux-design. If the project already has a DESIGN.md / tokens / elevation / states → use them.
|
|
332
456
|
2. If not, pick **Option A** — reuse an installed system (shadcn/Material/Radix) when the stack matches, OR pull a named style (Claymorphism, Brutalism, …) from the design.md library. Both reuse-before-invent; both still must pass `ux_audit`.
|
|
333
|
-
3. Otherwise pick **Option B** — infer the medium (Web B1 vs Mobile B2) from the task, use the starter in-context as the implicit system, and keep generating. Only ASK if web-vs-mobile is unclear.
|
|
457
|
+
3. Otherwise pick **Option B** — infer the medium (Web B1 vs Mobile B2) from the task, use the starter in-context as the implicit system, and keep generating. Only ASK if web-vs-mobile is unclear. When the subject's material clearly matches a style-direction starter (**S1 Editorial print / S2 Ledger / S3 Warm consumer**), prefer it over the neutral B-starter — it hands Step 1.5 a direction for free.
|
|
334
458
|
4. Only for CSS-only projects that won't adopt DESIGN.md, use **Option C** (`:root` block).
|
|
335
459
|
5. Generate screens with any text-only model (DeepSeek-v4 / GLM-5.2 / Kimi K3) INSIDE these constraints.
|
|
336
460
|
6. Run `ux_audit` with the CSS + the most common text colour pairs before handoff.
|
|
337
461
|
|
|
462
|
+
**Floors, not identities:** whichever starter you pick, Step 1.5 of ux-design still bends it — accent toward the subject's world, mood adjectives committed, one signature element named. The finished page must not be recognisable as the stock starter.
|
|
463
|
+
|
|
338
464
|
## Audit-ready pairs
|
|
339
465
|
|
|
340
466
|
```jsonc
|
|
@@ -354,3 +480,4 @@ into the project's `:root`. It satisfies the token + elevation + state gates of
|
|
|
354
480
|
- Not a CSS framework — reference only. Don't bundle it; copy what you need.
|
|
355
481
|
- Not themeable at runtime — it's a baseline to extend, not a product.
|
|
356
482
|
- Not opinionated about the accent — `accent` / `{colors.accent}` is the ONE knob you turn for brand.
|
|
483
|
+
- Not an identity — even the S-starters are floors. A finished page that looks exactly like its starter means Step 1.5 was skipped.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ux-routing
|
|
3
|
+
description: >
|
|
4
|
+
Reference table for dividing UX work across models in the Pi ecosystem —
|
|
5
|
+
who defines the DESIGN.md scaffold, who generates variants, who renders and
|
|
6
|
+
inspects, what reviews. Consult when delegating design work to other models
|
|
7
|
+
(agy/Gemini, Claude, DeepSeek, Kimi) or when deciding whether the main model
|
|
8
|
+
should do a step itself. Not injected into every turn by design; the
|
|
9
|
+
always-true core rule lives in ux-design: the deterministic gate reviews, a
|
|
10
|
+
multimodal model looks at its own render, and taste lives in the direction
|
|
11
|
+
brief, not in the model choice.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Model routing for design work (who does what)
|
|
15
|
+
|
|
16
|
+
Split the work along each model's strength. **The inversion rule:** the
|
|
17
|
+
cheaper/weaker the model, the MORE you must externalise constraints. Taste
|
|
18
|
+
lives in the direction brief, not the weights.
|
|
19
|
+
|
|
20
|
+
**The deterministic-first principle:** the gate is mechanical (DESIGN.md lint +
|
|
21
|
+
`ux_audit`), not a vision-LLM call. Text-only models now lead frontend
|
|
22
|
+
(Kimi K3 is #1 on the Arena.ai Frontend Code Arena, ahead of Claude Fable 5) —
|
|
23
|
+
inside a locked system they produce non-slop. agy/Gemini/Claude is optional,
|
|
24
|
+
never the review gate.
|
|
25
|
+
|
|
26
|
+
| Step | Best tool/model | Why |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| **Define system** (DESIGN.md: tokens, elevation, type) | Reuse a preset (ux-presets) OR `agy_execute mode=plan pro-high` (Gemini) **once** | Preset is cheapest. Gemini = strongest visual reasoning for the one-time scaffold. |
|
|
29
|
+
| **Derive direction** (Step 1.5) | The generating model itself, after reading the subject material | Direction needs the subject brief, not a bigger model. |
|
|
30
|
+
| **Lint system** | `npx @google/design.md lint DESIGN.md` (shell-out) | Deterministic token-ref + contrast + structure validation. |
|
|
31
|
+
| **Per-screen brief** | Main Pi model: **GLM-5.2** | 1M ctx holds the whole DESIGN.md while scoping one screen |
|
|
32
|
+
| **Generate variants** | Main Pi model: **DeepSeek-v4**, **GLM-5.2**, or **Kimi K3** | Text-only models lead frontend inside a locked system; cheaper than vision calls. |
|
|
33
|
+
| **Normalise into system** | Main Pi model: **DeepSeek-v4** or **GLM-5.2** | Long context, token remapping, mechanical precision |
|
|
34
|
+
| **Render & inspect** | The generating model itself, when multimodal (GLM-5.3, Claude, Gemini) via `web_screenshot` | Eyes on your own output beat rules in a prompt — flash-tier models produce notably better UI when they see the rendered result (inline image blocks) |
|
|
35
|
+
| **Slop audit** | `ux_audit` tool (deterministic) + DESIGN.md lint | Contrast (APCA) + tokens + states + slop tells are computable, not judgement |
|
|
36
|
+
| **Optional polish** (never a gate) | `agy_execute mode=accept-edits sonnet` (Claude) or `opus` | Only if brand-fit is uncertain after the deterministic gate passes. NOT required. |
|
|
37
|
+
|
|
38
|
+
**Cross-family rule:** Gemini/Claude produce → deterministic gate reviews. Don't
|
|
39
|
+
spend vision-model quota on what `ux_audit` computes for free. agy review is a
|
|
40
|
+
fallback for aesthetic uncertainty, never the gate.
|
|
41
|
+
|
|
42
|
+
DeepSeek/GLM/Kimi K3 are safe for design **only inside a fully-specified
|
|
43
|
+
system** (DESIGN.md or preset + direction brief). If no system exists yet,
|
|
44
|
+
create it once (preset + Step 1.5 direction, or agy), then text-only models
|
|
45
|
+
are sufficient for every generation thereafter.
|