qualia-framework 4.4.0 → 5.1.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/AGENTS.md +24 -0
- package/CLAUDE.md +12 -63
- package/README.md +24 -18
- package/agents/builder.md +13 -33
- package/agents/plan-checker.md +18 -0
- package/agents/planner.md +17 -0
- package/agents/verifier.md +70 -0
- package/agents/visual-evaluator.md +132 -0
- package/bin/cli.js +64 -23
- package/bin/install.js +375 -29
- package/bin/qualia-ui.js +208 -1
- package/bin/slop-detect.mjs +362 -0
- package/bin/state.js +218 -2
- package/docs/erp-contract.md +5 -0
- package/docs/install-redesign-builder-prompt.md +290 -0
- package/docs/install-redesign-pilot.md +234 -0
- package/docs/playwright-loop-builder-prompt.md +185 -0
- package/docs/playwright-loop-design-notes.md +108 -0
- package/docs/playwright-loop-pilot-results.md +170 -0
- package/docs/playwright-loop-review-2026-05-03.md +65 -0
- package/docs/playwright-loop-tester-prompt.md +213 -0
- package/docs/reviews/matt-pocock-skills-analysis.md +300 -0
- package/guide.md +9 -5
- package/hooks/env-empty-guard.js +74 -0
- package/hooks/pre-compact.js +19 -9
- package/hooks/pre-deploy-gate.js +8 -2
- package/hooks/pre-push.js +26 -12
- package/hooks/supabase-destructive-guard.js +62 -0
- package/hooks/vercel-account-guard.js +91 -0
- package/package.json +2 -1
- package/rules/design-brand.md +114 -0
- package/rules/design-laws.md +148 -0
- package/rules/design-product.md +114 -0
- package/rules/design-rubric.md +157 -0
- package/rules/grounding.md +4 -0
- package/skills/qualia-build/SKILL.md +40 -46
- package/skills/qualia-discuss/SKILL.md +51 -68
- package/skills/qualia-handoff/SKILL.md +1 -0
- package/skills/qualia-issues/SKILL.md +151 -0
- package/skills/qualia-map/SKILL.md +78 -35
- package/skills/qualia-new/REFERENCE.md +139 -0
- package/skills/qualia-new/SKILL.md +85 -124
- package/skills/qualia-optimize/REFERENCE.md +202 -0
- package/skills/qualia-optimize/SKILL.md +72 -237
- package/skills/qualia-plan/SKILL.md +58 -65
- package/skills/qualia-polish/SKILL.md +180 -136
- package/skills/qualia-polish-loop/REFERENCE.md +265 -0
- package/skills/qualia-polish-loop/SKILL.md +201 -0
- package/skills/qualia-polish-loop/fixtures/broken.html +117 -0
- package/skills/qualia-polish-loop/fixtures/clean.html +196 -0
- package/skills/qualia-polish-loop/scripts/loop.mjs +302 -0
- package/skills/qualia-polish-loop/scripts/playwright-capture.mjs +197 -0
- package/skills/qualia-polish-loop/scripts/score.mjs +176 -0
- package/skills/qualia-report/SKILL.md +141 -180
- package/skills/qualia-research/SKILL.md +28 -33
- package/skills/qualia-road/SKILL.md +103 -0
- package/skills/qualia-ship/SKILL.md +1 -0
- package/skills/qualia-task/SKILL.md +1 -1
- package/skills/qualia-test/SKILL.md +50 -2
- package/skills/qualia-triage/SKILL.md +152 -0
- package/skills/qualia-verify/SKILL.md +63 -104
- package/skills/qualia-zoom/SKILL.md +51 -0
- package/skills/zoho-workflow/SKILL.md +64 -0
- package/templates/CONTEXT.md +36 -0
- package/templates/DESIGN.md +229 -435
- package/templates/PRODUCT.md +95 -0
- package/templates/decisions/ADR-template.md +30 -0
- package/tests/bin.test.sh +451 -7
- package/tests/state.test.sh +58 -0
- package/skills/qualia-design/SKILL.md +0 -169
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ~/.claude/hooks/vercel-account-guard.js — block deploys from wrong Vercel account.
|
|
3
|
+
//
|
|
4
|
+
// PreToolUse hook on `Bash`. Reads the proposed command from the Claude Code
|
|
5
|
+
// hook payload (stdin JSON: tool_input.command) and exits 2 to BLOCK, 0 to
|
|
6
|
+
// allow. Triggers only when the command matches `vercel --prod` or `vercel deploy`.
|
|
7
|
+
//
|
|
8
|
+
// Allowed teams are read from ~/.claude/.vercel-allowed-teams (one slug per
|
|
9
|
+
// line, mode 0600). Missing config file = fail-open with stderr warning.
|
|
10
|
+
//
|
|
11
|
+
// Cross-platform (Windows/macOS/Linux). No external dependencies.
|
|
12
|
+
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const os = require("os");
|
|
16
|
+
const { spawnSync } = require("child_process");
|
|
17
|
+
|
|
18
|
+
const _traceStart = Date.now();
|
|
19
|
+
|
|
20
|
+
function _trace(result, extra) {
|
|
21
|
+
try {
|
|
22
|
+
const traceDir = path.join(os.homedir(), ".claude", ".qualia-traces");
|
|
23
|
+
if (!fs.existsSync(traceDir)) fs.mkdirSync(traceDir, { recursive: true });
|
|
24
|
+
const entry = {
|
|
25
|
+
hook: "vercel-account-guard",
|
|
26
|
+
result,
|
|
27
|
+
timestamp: new Date().toISOString(),
|
|
28
|
+
duration_ms: Date.now() - _traceStart,
|
|
29
|
+
...extra,
|
|
30
|
+
};
|
|
31
|
+
const file = path.join(traceDir, `${new Date().toISOString().split("T")[0]}.jsonl`);
|
|
32
|
+
fs.appendFileSync(file, JSON.stringify(entry) + "\n");
|
|
33
|
+
} catch {}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Read hook payload from stdin.
|
|
37
|
+
let command = "";
|
|
38
|
+
try {
|
|
39
|
+
if (!process.stdin.isTTY) {
|
|
40
|
+
const raw = fs.readFileSync(0, "utf8");
|
|
41
|
+
if (raw) command = (JSON.parse(raw).tool_input || {}).command || "";
|
|
42
|
+
}
|
|
43
|
+
} catch {}
|
|
44
|
+
|
|
45
|
+
if (!command) {
|
|
46
|
+
_trace("allow", { reason: "no-command" });
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Only act on vercel deploy commands.
|
|
51
|
+
if (!/vercel\s+(--prod|deploy)/.test(command)) {
|
|
52
|
+
_trace("allow", { reason: "not-vercel-deploy" });
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Read allowed teams (one slug per line).
|
|
57
|
+
const teamsFile = path.join(os.homedir(), ".claude", ".vercel-allowed-teams");
|
|
58
|
+
let allowed;
|
|
59
|
+
try {
|
|
60
|
+
allowed = fs.readFileSync(teamsFile, "utf8").split(/\r?\n/).map(l => l.trim()).filter(Boolean);
|
|
61
|
+
} catch {
|
|
62
|
+
allowed = [];
|
|
63
|
+
}
|
|
64
|
+
if (allowed.length === 0) {
|
|
65
|
+
console.error("No .vercel-allowed-teams configured -- skipping account check");
|
|
66
|
+
_trace("skipped", { reason: "no-config" });
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Run `vercel whoami` to get the active account (fail-open on error).
|
|
71
|
+
const r = spawnSync("vercel", ["whoami"], {
|
|
72
|
+
encoding: "utf8", timeout: 5000, shell: process.platform === "win32",
|
|
73
|
+
});
|
|
74
|
+
const actual = ((r.status === 0 && !r.error) ? (r.stdout || "") : "").trim();
|
|
75
|
+
if (!actual) {
|
|
76
|
+
console.error("vercel whoami failed or empty -- skipping account check");
|
|
77
|
+
_trace("skipped", { reason: "whoami-unavailable" });
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (allowed.includes(actual)) {
|
|
82
|
+
_trace("allow", { actual, allowed_count: allowed.length });
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Block: wrong account.
|
|
87
|
+
const msg = `BLOCKED: Wrong Vercel account/team. Active: '${actual}'. Allowed: ${allowed.join(", ")}. Run \`vercel switch ${allowed[0]}\` first, or add this team to ~/.claude/.vercel-allowed-teams.`;
|
|
88
|
+
console.error(msg);
|
|
89
|
+
console.log(msg);
|
|
90
|
+
_trace("block", { actual, allowed_count: allowed.length, reason: "wrong-account" });
|
|
91
|
+
process.exit(2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qualia-framework",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Claude Code workflow framework by Qualia Solutions. Plan, build, verify, ship.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"qualia-framework": "./bin/cli.js"
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"tests/",
|
|
43
43
|
"docs/",
|
|
44
44
|
"CLAUDE.md",
|
|
45
|
+
"AGENTS.md",
|
|
45
46
|
"guide.md"
|
|
46
47
|
],
|
|
47
48
|
"engines": {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
globs: ["*.tsx", "*.jsx", "*.css", "*.scss", "*.html", "*.vue", "*.svelte"]
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Design — Brand register
|
|
6
|
+
|
|
7
|
+
**When this register applies:** marketing pages, landing pages, campaign sites, portfolios, brand microsites, anything where the design IS the product.
|
|
8
|
+
|
|
9
|
+
**The bar:** distinctiveness. Memorable beats safe. If a visitor can't remember what your site looked like an hour later, you failed.
|
|
10
|
+
|
|
11
|
+
This register inherits all of `design-laws.md`. The rules below add what changes when distinctiveness, not familiarity, is the goal.
|
|
12
|
+
|
|
13
|
+
## The brand brief (what the agent commits to first)
|
|
14
|
+
|
|
15
|
+
Before any code, the agent writes a 4-line brief and waits for user confirmation:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Aesthetic direction: [editorial · brutalist · luxury · maximalist · retro-futuristic · organic · terminal-native · sci-fi · pastoral · industrial · ...]
|
|
19
|
+
Color strategy: [Restrained · Committed · Full palette · Drenched]
|
|
20
|
+
Scene sentence: [single concrete sentence — who, where, ambient light, mood]
|
|
21
|
+
Differentiation: [what someone will remember 24 hours later]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This is mandatory. Skipping it produces generic output that ignores the brand.
|
|
25
|
+
|
|
26
|
+
## Reflex-reject aesthetic lanes
|
|
27
|
+
|
|
28
|
+
The currently-saturated AI-design families. Reject by default unless the user explicitly asks for one and you confirmed they understand it's a saturated lane.
|
|
29
|
+
|
|
30
|
+
| Lane | Tells | Reject because |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| **Generic SaaS-cream** | Off-white bg, soft shadows, rounded cards, slate text, one-color accent | Notion clone aesthetic. Every AI tool builds this first |
|
|
33
|
+
| **Vercel-terminal-native** | Dark + monospace + green/blue accents + grain | The 2024-2025 v0 aesthetic. Saturated |
|
|
34
|
+
| **Crypto-neon** | Pure black + electric accent + glow + grid | Web3 cliché |
|
|
35
|
+
| **Healthcare-clinical** | White + teal + rounded sans + soft icons | Every health app |
|
|
36
|
+
| **Fintech-navy-gold** | Navy + gold accent + serif headings | Every bank, every wealth tool |
|
|
37
|
+
| **Sleek-corporate** | Inter + blue + gradient mesh + isometric illustrations | Every B2B SaaS landing 2020-2024 |
|
|
38
|
+
|
|
39
|
+
If the project lands in one of these by default, the slop test failed at second-order. Rework.
|
|
40
|
+
|
|
41
|
+
## Brand register — additional laws
|
|
42
|
+
|
|
43
|
+
### Typography
|
|
44
|
+
|
|
45
|
+
- Pick a **distinctive display face**. Editorial serifs (Fraunces, Reckless, Ogg, Editorial New, Mazius). Geometric grotesques (Migra, Söhne, Gambarino). Variable display fonts (Authentic Sans, Departure Mono).
|
|
46
|
+
- Pair the display with a **refined body**. JetBrains Mono, Geist, Inter Display (NOT Inter), Söhne, Untitled Sans, Mona Sans, Switzer.
|
|
47
|
+
- **Banned display fonts:** Inter, Roboto, Arial, system-ui, Space Grotesk, Montserrat, Poppins, Lato, Open Sans. These are the AI defaults — and the worst signal of "designer didn't choose this."
|
|
48
|
+
- **Variable fonts encouraged.** A single variable font with axis-driven weight/optical-size is more interesting than three static cuts.
|
|
49
|
+
|
|
50
|
+
### Color
|
|
51
|
+
|
|
52
|
+
- Brand register defaults to **Committed** color strategy. One saturated color carries identity.
|
|
53
|
+
- **Drenched** is a strong choice for hero sections. Don't be afraid.
|
|
54
|
+
- Accent should be **sharp** — not a desaturated pastel-of-the-brand. The accent is the loudest element on the page.
|
|
55
|
+
|
|
56
|
+
### Layout
|
|
57
|
+
|
|
58
|
+
- **Asymmetry > symmetry.** Real design has tension. Centered-everything is the AI default.
|
|
59
|
+
- **Diagonal flow, overlap, grid-breaking elements** when they serve the design.
|
|
60
|
+
- **Scale contrast.** Hero text at clamp(4rem, 9vw, 8rem) next to body at 1rem creates drama. Hero at 2.5rem next to body at 1rem is timid.
|
|
61
|
+
- **Negative space is content.** Generous whitespace > dense layouts for marketing.
|
|
62
|
+
|
|
63
|
+
### Motion
|
|
64
|
+
|
|
65
|
+
- One **signature motion** that gives the site personality:
|
|
66
|
+
- Parallax scroll-triggered reveal
|
|
67
|
+
- Magnetic buttons
|
|
68
|
+
- Cursor-following element
|
|
69
|
+
- Letter-by-letter text reveal on hero
|
|
70
|
+
- Scroll-driven scrubbed scene
|
|
71
|
+
- Morphing shapes between sections
|
|
72
|
+
- Subtle elsewhere. Restraint everywhere except the signature.
|
|
73
|
+
- Use the Web Animations API or Framer Motion for orchestrated sequences. CSS keyframes for one-offs.
|
|
74
|
+
|
|
75
|
+
### Backgrounds and visual details
|
|
76
|
+
|
|
77
|
+
This is where Brand earns its register. Allowed and encouraged:
|
|
78
|
+
|
|
79
|
+
- Gradient meshes (CSS or SVG)
|
|
80
|
+
- Noise textures (subtle, 2-5% opacity)
|
|
81
|
+
- Geometric patterns (SVG, not images)
|
|
82
|
+
- Layered transparencies
|
|
83
|
+
- Dramatic shadows (with brand-tinted color, not gray)
|
|
84
|
+
- Decorative borders (sparingly)
|
|
85
|
+
- Custom cursors (page-specific, not site-wide)
|
|
86
|
+
- Grain overlays on images
|
|
87
|
+
- Scroll-progress indicators (interesting ones, not generic top bars)
|
|
88
|
+
|
|
89
|
+
Don't use all of these on one page. Pick 1-2 that serve the brief.
|
|
90
|
+
|
|
91
|
+
## Brand-specific anti-patterns
|
|
92
|
+
|
|
93
|
+
In addition to `design-laws.md` absolute bans:
|
|
94
|
+
|
|
95
|
+
- **Hero pattern: text-center + gradient bg + 2 CTAs + screenshot below.** The single most overused brand pattern of 2020-2025.
|
|
96
|
+
- **Three-column feature grid in section 2.** The default AI brand layout.
|
|
97
|
+
- **"Trusted by" logo strip in faded gray.** Lazy social proof. If the logos matter, design with them. If they don't, omit.
|
|
98
|
+
- **Parallax scroll on every section.** Heavy-handed. Use it for the signature, nowhere else.
|
|
99
|
+
- **Animated dots / pulsing orbs / blinking status indicators.** Decorative noise.
|
|
100
|
+
- **Generic "Get Started" / "Learn More" CTA pair.** Always specify the action.
|
|
101
|
+
- **Stock photography of diverse smiling people in offices.** Use illustration, abstract imagery, product screenshots, or no imagery at all.
|
|
102
|
+
|
|
103
|
+
## Brand register success criteria
|
|
104
|
+
|
|
105
|
+
If you can answer YES to all of these, the brand register passed:
|
|
106
|
+
|
|
107
|
+
- [ ] A designer would call this **distinctive** — not "polished" but **specific**
|
|
108
|
+
- [ ] The aesthetic direction is identifiable in 3 seconds
|
|
109
|
+
- [ ] The color strategy is one of the four (and reads that way)
|
|
110
|
+
- [ ] The scene sentence and the page agree
|
|
111
|
+
- [ ] One signature motion, executed well
|
|
112
|
+
- [ ] No reflex-reject lane (first or second order)
|
|
113
|
+
- [ ] All anti-references avoided
|
|
114
|
+
- [ ] Implementation matches vision (effort proportional to ambition)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
globs: ["*.tsx", "*.jsx", "*.css", "*.scss", "*.html", "*.vue", "*.svelte"]
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Design Laws
|
|
6
|
+
|
|
7
|
+
The non-negotiable rules every Qualia frontend honors. Both registers (Brand and Product) inherit from this file. Skip nothing.
|
|
8
|
+
|
|
9
|
+
These laws exist because AI-generated UI has identifiable defaults — Inter font, purple gradients, identical card grids, gray-on-gray, modal-as-first-thought — and shipping the defaults is shipping AI slop. Each rule below has a specific failure mode it prevents.
|
|
10
|
+
|
|
11
|
+
## 1. Color: OKLCH only
|
|
12
|
+
|
|
13
|
+
Use the OKLCH color space for every color in the codebase. RGB / hex / HSL are translated values, not source values.
|
|
14
|
+
|
|
15
|
+
```css
|
|
16
|
+
/* yes */
|
|
17
|
+
--bg: oklch(0.16 0.012 220);
|
|
18
|
+
--accent: oklch(0.78 0.14 196);
|
|
19
|
+
|
|
20
|
+
/* no */
|
|
21
|
+
--bg: #1a1d22;
|
|
22
|
+
--accent: #00ced1;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Why:** OKLCH is perceptually uniform. Equal lightness numbers look equally light to the eye, regardless of hue. Hex doesn't give you that — `#777777` and `#7777ff` are nominally equal lightness but the blue reads darker.
|
|
26
|
+
|
|
27
|
+
**Reduce chroma at the extremes.** As lightness approaches 0 or 100, high chroma reads garish. Cap chroma at 0.04 below L=0.20 and above L=0.92.
|
|
28
|
+
|
|
29
|
+
**Never use `#000` or `#fff`.** Tint every neutral toward the brand hue with chroma 0.005–0.01. Pure black/white reads as untuned and generic — the dead giveaway of "designer didn't pick this color, the framework did."
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
/* tinted neutrals */
|
|
33
|
+
--text: oklch(0.94 0.006 220); /* 220 is the brand hue */
|
|
34
|
+
--bg: oklch(0.16 0.012 220);
|
|
35
|
+
--muted: oklch(0.62 0.010 220);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 2. Color strategy: pick one, commit to it
|
|
39
|
+
|
|
40
|
+
Before picking colors, pick a strategy. Four steps on a commitment axis:
|
|
41
|
+
|
|
42
|
+
| Strategy | Rule | When |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| **Restrained** | Tinted neutrals + one accent at ≤10% surface coverage | Product default; brand minimalism |
|
|
45
|
+
| **Committed** | One saturated color carries 30–60% of the surface | Brand default for identity-driven pages |
|
|
46
|
+
| **Full palette** | 3–4 named roles, each used deliberately | Brand campaigns; product data viz |
|
|
47
|
+
| **Drenched** | The surface IS the color | Brand heroes; campaign pages |
|
|
48
|
+
|
|
49
|
+
The "≤10% accent" rule is **Restrained only**. Committed / Full palette / Drenched exceed it on purpose. Don't collapse every design to Restrained by reflex.
|
|
50
|
+
|
|
51
|
+
## 3. Theme: write a scene sentence
|
|
52
|
+
|
|
53
|
+
Dark vs light is never a default. Not dark "because tools look cool dark." Not light "to be safe." Before choosing, write **one sentence of physical scene**: who uses this, where, under what ambient light, in what mood.
|
|
54
|
+
|
|
55
|
+
| Vague (no answer) | Concrete (forces an answer) |
|
|
56
|
+
|---|---|
|
|
57
|
+
| "Observability dashboard" | "SRE glancing at incident severity on a 27-inch monitor at 2am in a dim room" |
|
|
58
|
+
| "Customer support tool" | "Agent at a coffee-lit desk juggling 4 chats while a coworker asks a question" |
|
|
59
|
+
| "Marketing site" | "Founder showing the page to an investor on an iPad in a sunlit lobby" |
|
|
60
|
+
|
|
61
|
+
Run the sentence, not the category. If the sentence doesn't force an answer, add detail until it does.
|
|
62
|
+
|
|
63
|
+
## 4. Typography
|
|
64
|
+
|
|
65
|
+
- Body line length capped at 65–75ch
|
|
66
|
+
- Hierarchy through scale + weight contrast (≥1.25 ratio between adjacent steps)
|
|
67
|
+
- Avoid flat scales — if h1 is 32px and h2 is 28px, the hierarchy is invisible
|
|
68
|
+
- Letter-spacing as a semantic signal:
|
|
69
|
+
- Display headlines: tight (-0.02em to -0.04em)
|
|
70
|
+
- Body: neutral (0)
|
|
71
|
+
- Labels and badges: open (+0.04em to +0.08em)
|
|
72
|
+
- Category tags: wide (+0.08em to +0.14em)
|
|
73
|
+
|
|
74
|
+
## 5. Layout
|
|
75
|
+
|
|
76
|
+
- **Vary spacing for rhythm.** Same padding everywhere is monotony. Tight within groups, generous between sections.
|
|
77
|
+
- **Cards are the lazy answer.** Use them only when they're truly the best affordance. Nested cards are always wrong.
|
|
78
|
+
- **Container depth max 2.** Card → content. Not card → panel → pill → content.
|
|
79
|
+
- **Don't wrap everything in a container.** Most things don't need one.
|
|
80
|
+
- **Full-width with fluid padding.** No hardcoded `max-width: 1200px` or `max-w-7xl`. Use `clamp(1rem, 5vw, 4rem)` for horizontal padding.
|
|
81
|
+
|
|
82
|
+
## 6. Motion
|
|
83
|
+
|
|
84
|
+
- Don't animate CSS layout properties (`width`, `height`, `top`, `left`, `margin`, `padding`). Animate `transform` and `opacity`.
|
|
85
|
+
- Ease out with exponential curves: `cubic-bezier(0.22, 1, 0.36, 1)` (out-quart) or `cubic-bezier(0.16, 1, 0.3, 1)` (out-expo). No bounce. No elastic.
|
|
86
|
+
- One signature motion per page > scattered micro-interactions
|
|
87
|
+
- Always respect `prefers-reduced-motion: reduce`
|
|
88
|
+
|
|
89
|
+
## 7. Copy
|
|
90
|
+
|
|
91
|
+
- Every word earns its place
|
|
92
|
+
- No restated headings
|
|
93
|
+
- No intros that repeat the title
|
|
94
|
+
- **No em dashes.** Use commas, colons, semicolons, periods, or parentheses. Also not `--`.
|
|
95
|
+
- CTAs name the action. "Download invoice" not "Get Started". "Continue setup" not "Learn More".
|
|
96
|
+
|
|
97
|
+
## 8. Absolute bans (match-and-refuse)
|
|
98
|
+
|
|
99
|
+
If you're about to write any of these, rewrite the element with different structure. No exceptions.
|
|
100
|
+
|
|
101
|
+
| Banned pattern | Why | Rewrite with |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| **Side-stripe borders** — `border-left: 4px` colored as accent on cards / list items / callouts | Decorative noise, never semantic | Full borders, background tints, leading numbers/icons, or nothing |
|
|
104
|
+
| **Gradient text** — `background-clip: text` with a gradient | Decorative, never meaningful | Single solid color. Emphasis via weight or size |
|
|
105
|
+
| **Glassmorphism by default** — blur + glass cards used everywhere | Trendy slop from 2023-2024 | Rare and purposeful, or nothing |
|
|
106
|
+
| **Hero-metric template** — big number, small label, three supporting stats, gradient accent | SaaS cliché | Vary layout per metric. Don't grid them |
|
|
107
|
+
| **Identical card grids** — same-sized cards with icon + heading + text repeated 3+ times | The default AI hero pattern | Vary card sizes, layouts, content shapes |
|
|
108
|
+
| **Modal as first thought** — every action goes through a modal | Lazy interaction design | Inline expansion, progressive disclosure, dedicated route |
|
|
109
|
+
|
|
110
|
+
## 9. The AI slop test (run at two altitudes)
|
|
111
|
+
|
|
112
|
+
If someone could look at this interface and say "AI made that" without doubt, it failed.
|
|
113
|
+
|
|
114
|
+
**First-order check.** If someone could guess the theme + palette from the category alone, the first reflex won:
|
|
115
|
+
- Observability → dark blue
|
|
116
|
+
- Healthcare → white + teal
|
|
117
|
+
- Finance → navy + gold
|
|
118
|
+
- Crypto → neon on black
|
|
119
|
+
|
|
120
|
+
If yes, rework the scene sentence and color strategy until the answer isn't obvious from the domain.
|
|
121
|
+
|
|
122
|
+
**Second-order check.** If someone could guess the aesthetic family from category-plus-anti-references, the trap one tier deeper won:
|
|
123
|
+
- AI workflow tool that's not SaaS-cream → editorial-typographic
|
|
124
|
+
- Fintech that's not navy-and-gold → terminal-native dark mode
|
|
125
|
+
- Health app that's not white-and-teal → soft-pastel
|
|
126
|
+
|
|
127
|
+
The first reflex was avoided; the second wasn't. Rework until both answers are not obvious. The Brand register's reflex-reject aesthetic lanes catch the currently-saturated families.
|
|
128
|
+
|
|
129
|
+
## 10. Single-icon-family rule
|
|
130
|
+
|
|
131
|
+
Pick one icon family per project and enforce it. Lucide OR Heroicons OR Phosphor OR Radix Icons. Never mixed. Mixing icon families is the visual equivalent of mixing fonts.
|
|
132
|
+
|
|
133
|
+
## 11. Anti-references are mandatory
|
|
134
|
+
|
|
135
|
+
Every PRODUCT.md must list 3–5 sites the project should NOT look like. Anti-references are more useful than positive references because they pin down what the design is reacting against.
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Anti-references:
|
|
139
|
+
- generic SaaS-cream (Notion clones)
|
|
140
|
+
- terminal-nostalgia (vercel/v0 dark)
|
|
141
|
+
- corporate-navy fintech (most banks)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 12. The implementation must match the vision
|
|
145
|
+
|
|
146
|
+
Maximalist designs need elaborate code. Minimalist designs need restraint, precision, and careful attention to spacing and typography. The failure mode is mismatch: elaborate animation on a "clean minimal" claim, or flat execution of an ambitious concept.
|
|
147
|
+
|
|
148
|
+
If you wrote 200 lines of CSS to render a "minimal" landing page, the page is not minimal. If your "bold and maximalist" page uses 4 colors and `text-center`, it's not bold.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
globs: ["*.tsx", "*.jsx", "*.css", "*.scss", "*.html", "*.vue", "*.svelte"]
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Design — Product register
|
|
6
|
+
|
|
7
|
+
**When this register applies:** app UI, admin consoles, dashboards, internal tools, settings pages, anything where the design SERVES the product.
|
|
8
|
+
|
|
9
|
+
**The bar:** earned familiarity. Fluent users of Linear, Figma, Notion, Stripe, Raycast should trust the interface on first glance. Distinctiveness is allowed but never at the cost of usability.
|
|
10
|
+
|
|
11
|
+
This register inherits all of `design-laws.md`. The rules below add what changes when familiarity, not distinctiveness, is the goal.
|
|
12
|
+
|
|
13
|
+
## The product brief (what the agent commits to first)
|
|
14
|
+
|
|
15
|
+
Before any code, the agent writes a 4-line brief and waits for user confirmation:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Reference apps: [Linear · Stripe · Notion · Figma · Raycast · Vercel · Plaid Dashboard · Pylon · ...]
|
|
19
|
+
Color strategy: [Restrained — almost always]
|
|
20
|
+
Scene sentence: [physical scene — focus on context of use, not aesthetic mood]
|
|
21
|
+
Density: [comfortable · standard · compact · ultra-compact]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Reference apps anchor the visual language. Pick 2-3 that share the user mental model and the density.
|
|
25
|
+
|
|
26
|
+
## Density target
|
|
27
|
+
|
|
28
|
+
Product UIs have to choose density. State it explicitly.
|
|
29
|
+
|
|
30
|
+
| Density | Examples | Use when |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| **Comfortable** | Notion, Linear (default), Mailchimp | Knowledge work, low-frequency |
|
|
33
|
+
| **Standard** | Stripe Dashboard, Vercel | Mainstream SaaS |
|
|
34
|
+
| **Compact** | Linear (compact mode), Figma left rail | Power users, workflow tools |
|
|
35
|
+
| **Ultra-compact** | Bloomberg Terminal, Trader UIs | Information-dense, professional users |
|
|
36
|
+
|
|
37
|
+
Higher density = smaller font sizes (down to 12px), tighter padding, more rows per viewport. Don't pretend the app is comfortable when it's compact, or vice versa. Match the user's actual workflow.
|
|
38
|
+
|
|
39
|
+
## Product register — additional laws
|
|
40
|
+
|
|
41
|
+
### Typography
|
|
42
|
+
|
|
43
|
+
- **One typeface family is fine** for product. Variable fonts shine here (Inter Display, Geist, Switzer, Mona Sans, Söhne, Untitled Sans).
|
|
44
|
+
- Banned (per laws): Inter (regular cut), Roboto, Arial, system-ui, Space Grotesk
|
|
45
|
+
- Body: 13-15px depending on density (comfortable=15, standard=14, compact=13, ultra-compact=12)
|
|
46
|
+
- Hierarchy through weight (400 body, 500 labels, 600 headings) more than size
|
|
47
|
+
- Tabular numerals for any numeric data: `font-feature-settings: "tnum" 1`
|
|
48
|
+
- Truncate long strings with ellipsis + tooltip — never let layout collapse on a long username
|
|
49
|
+
|
|
50
|
+
### Color
|
|
51
|
+
|
|
52
|
+
- **Restrained is the default.** Tinted neutrals + one accent.
|
|
53
|
+
- **Semantic colors required:** success (green) / warning (amber) / error (red) / info (blue). Always paired with non-color signal (icon, label, pattern).
|
|
54
|
+
- Status indicators must work for color-blind users. Run the design through a deuteranopia simulator.
|
|
55
|
+
- Background depth: 3 surface levels max (`bg`, `surface`, `elevated`). Subtle differences (oklch L delta of 0.03-0.05).
|
|
56
|
+
|
|
57
|
+
### Layout
|
|
58
|
+
|
|
59
|
+
- **Density first, aesthetics second.** A beautiful UI a user can't navigate quickly is a failed product UI.
|
|
60
|
+
- **Predictable layout.** Sidebar left, main center, optional right rail. Don't reinvent navigation patterns.
|
|
61
|
+
- **Sticky headers** for long-scroll views (tables, lists)
|
|
62
|
+
- **Keyboard-first.** Every action accessible via keyboard. Power users will memorize shortcuts; expose them.
|
|
63
|
+
- **Fitts's Law.** Frequently-used controls go to screen edges. Action buttons in tight clusters.
|
|
64
|
+
|
|
65
|
+
### Components
|
|
66
|
+
|
|
67
|
+
| Pattern | Product register rule |
|
|
68
|
+
|---|---|
|
|
69
|
+
| **Buttons** | 3 variants max: primary / secondary / ghost. Destructive variant where needed. No "outline + filled + ghost + soft + subtle" proliferation. |
|
|
70
|
+
| **Inputs** | Always have visible labels (not placeholder-only). Validation inline, on blur. Error messages specific and actionable. |
|
|
71
|
+
| **Tables** | Tabular numerals. Right-align numbers. Sort indicators on hover, persistent on active. Sticky headers. |
|
|
72
|
+
| **Empty states** | Always include: icon, single sentence of context, primary action |
|
|
73
|
+
| **Loading** | Skeleton (not spinner) for known-shape content. Spinner only for unknown duration. |
|
|
74
|
+
| **Empty data** | Never "No results." Always "No invoices yet — try [action]." |
|
|
75
|
+
| **Toasts** | Auto-dismiss at 5s minimum. Always dismissible. Errors don't auto-dismiss. |
|
|
76
|
+
| **Modals** | Last resort (per laws). When unavoidable: trap focus, ESC to close, restore focus on close. |
|
|
77
|
+
|
|
78
|
+
### Motion
|
|
79
|
+
|
|
80
|
+
- Restrained. Product UI motion exists to communicate state changes, not to delight.
|
|
81
|
+
- Hover transitions: 100-150ms ease-out
|
|
82
|
+
- Panel slides: 200ms ease-out
|
|
83
|
+
- Page transitions: 0ms (yes, zero) for navigation between similar views. Motion when it would disorient should be skipped.
|
|
84
|
+
- Skeleton shimmer: 1500ms loop, subtle
|
|
85
|
+
- No bounce. No elastic. No overshoot.
|
|
86
|
+
|
|
87
|
+
### States (every interactive element)
|
|
88
|
+
|
|
89
|
+
- Default
|
|
90
|
+
- Hover (within 100ms)
|
|
91
|
+
- Focus (visible ring, 2px+ offset, contrasting color)
|
|
92
|
+
- Active/pressed (subtle scale or color shift)
|
|
93
|
+
- Disabled (opacity 0.5, `cursor: not-allowed`, `aria-disabled="true"`)
|
|
94
|
+
- Loading (per-element spinner or skeleton)
|
|
95
|
+
- Error (inline message + `aria-describedby`)
|
|
96
|
+
|
|
97
|
+
Every state on every interactive element. No exceptions.
|
|
98
|
+
|
|
99
|
+
## Product register success criteria
|
|
100
|
+
|
|
101
|
+
If you can answer YES to all of these, the product register passed:
|
|
102
|
+
|
|
103
|
+
- [ ] A user fluent in the reference apps would feel oriented in 5 seconds
|
|
104
|
+
- [ ] Density is consistent across the app (one chosen value, applied)
|
|
105
|
+
- [ ] Keyboard navigation reaches every action
|
|
106
|
+
- [ ] Empty / loading / error states present on every async surface
|
|
107
|
+
- [ ] Color is never the sole information signal
|
|
108
|
+
- [ ] Tabular numerals on numeric columns
|
|
109
|
+
- [ ] No reinvented navigation pattern
|
|
110
|
+
- [ ] Implementation matches vision (precision and restraint, not flourish)
|
|
111
|
+
|
|
112
|
+
## When in doubt
|
|
113
|
+
|
|
114
|
+
Ask: "Would Linear ship this?" If the answer is no, rework. Linear is not the only standard, but it is the highest commonly-cited bar for product design in 2025-2026 — fluency, restraint, density, motion all dialed in. Most product work will land within ±10% of Linear's bar; be honest about which side.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
globs: ["*.tsx", "*.jsx", "*.css", "*.scss", "*.html", "*.vue", "*.svelte"]
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Design Rubric
|
|
6
|
+
|
|
7
|
+
Anchored 1-5 scoring across 8 dimensions. Used by the verifier agent to score frontend phases. Used by `/qualia-polish --critique` for read-only audits.
|
|
8
|
+
|
|
9
|
+
## How to score
|
|
10
|
+
|
|
11
|
+
Every dimension is scored 1-5 with EVIDENCE on the next line. Score without evidence is rejected.
|
|
12
|
+
|
|
13
|
+
Anchored definitions:
|
|
14
|
+
- **1** — Fails. Hard violation. WCAG fails, broken layout, absolute-ban hit.
|
|
15
|
+
- **2** — Below acceptable. Functions but signals "AI generated this."
|
|
16
|
+
- **3** — Acceptable. Ships. Not memorable, not embarrassing.
|
|
17
|
+
- **4** — Good. Specific choices visible. A designer would approve.
|
|
18
|
+
- **5** — Excellent. Distinctive. Worth screenshotting and sharing.
|
|
19
|
+
|
|
20
|
+
**Default to 3.** Only score above 3 if you can cite a specific design principle the work excels at. Only score below 3 if you can quote evidence of a violation.
|
|
21
|
+
|
|
22
|
+
If a vision model is critiquing screenshots, it must score against this rubric. Without anchoring, vision models say "looks great!" to everything.
|
|
23
|
+
|
|
24
|
+
## Scope guard
|
|
25
|
+
|
|
26
|
+
Score only what is scope-relevant. A `/qualia-polish src/components/Button.tsx` review scores Typography, Color, States, Motion, Microcopy. Skip Layout Originality and Container Depth — they're component-internal concerns at most.
|
|
27
|
+
|
|
28
|
+
A whole-app `/qualia-polish` scores all 8 dimensions across multiple representative routes.
|
|
29
|
+
|
|
30
|
+
## The 8 dimensions
|
|
31
|
+
|
|
32
|
+
### 1. Typography
|
|
33
|
+
|
|
34
|
+
| Score | Criteria |
|
|
35
|
+
|---|---|
|
|
36
|
+
| 1 | Inter / Roboto / Arial / system-ui / Space Grotesk as primary. Or single weight throughout. |
|
|
37
|
+
| 2 | Project font loaded but only one weight, no scale, no letter-spacing variation. |
|
|
38
|
+
| 3 | Project font with 2-3 weights, basic scale (h1/h2/body), readable. |
|
|
39
|
+
| 4 | Distinctive display + refined body pair. Letter-spacing varied semantically (tight headlines, open labels). Tabular numerals on numeric data. |
|
|
40
|
+
| 5 | Variable font with axis-driven weight/optical-size. Hierarchy through weight + scale + letter-spacing combined. Line lengths capped at 65-75ch. |
|
|
41
|
+
|
|
42
|
+
Evidence format: `font-family: "<name>" used at line N, scale 14/16/24/40 visible, weights 400/500/700`
|
|
43
|
+
|
|
44
|
+
### 2. Color cohesion
|
|
45
|
+
|
|
46
|
+
| Score | Criteria |
|
|
47
|
+
|---|---|
|
|
48
|
+
| 1 | Hex values scattered in JSX. No CSS variables. Pure `#000` or `#fff`. |
|
|
49
|
+
| 2 | CSS variables defined but unused (raw hex still appears). RGB used. |
|
|
50
|
+
| 3 | All colors as CSS variables. Tinted neutrals (no pure black/white). One color strategy implied. |
|
|
51
|
+
| 4 | OKLCH throughout. Strategy explicit (Restrained / Committed / Full / Drenched). WCAG AA verified. |
|
|
52
|
+
| 5 | OKLCH with documented strategy. Chroma reduced at extremes. Brand-tinted shadows. APCA contrast verified for dense text. |
|
|
53
|
+
|
|
54
|
+
Evidence: count of CSS custom properties for color, presence of OKLCH, evidence of strategy commitment
|
|
55
|
+
|
|
56
|
+
### 3. Spatial rhythm
|
|
57
|
+
|
|
58
|
+
| Score | Criteria |
|
|
59
|
+
|---|---|
|
|
60
|
+
| 1 | Arbitrary px values everywhere. No system. |
|
|
61
|
+
| 2 | 8px grid roughly followed. Same padding everywhere. |
|
|
62
|
+
| 3 | 8px grid consistently. Tight within groups, generous between sections. |
|
|
63
|
+
| 4 | Fluid `clamp()` padding. Spacing varies by content type. Vertical rhythm identifiable. |
|
|
64
|
+
| 5 | Spatial system reads as composed — different spacing for different relationships. Generous negative space. Asymmetry where intentional. |
|
|
65
|
+
|
|
66
|
+
Evidence: presence of spacing tokens, padding values divisible by 4, `clamp()` usage
|
|
67
|
+
|
|
68
|
+
### 4. Layout originality
|
|
69
|
+
|
|
70
|
+
| Score | Criteria |
|
|
71
|
+
|---|---|
|
|
72
|
+
| 1 | Three-column feature grid in section 2. Card grid of 3 identical items. Centered hero with gradient. |
|
|
73
|
+
| 2 | Standard hero + sections + footer. Symmetric throughout. |
|
|
74
|
+
| 3 | Some layout variation across sections. No card monotony. |
|
|
75
|
+
| 4 | Asymmetry, full-bleed, varied component sizes within a section. Negative space used. |
|
|
76
|
+
| 5 | Layout reads as composed by a designer. Diagonal flow, overlap, scale contrast. Each section a different shape. |
|
|
77
|
+
|
|
78
|
+
Evidence: file:line references showing layout choices
|
|
79
|
+
|
|
80
|
+
### 5. Shadow & depth hierarchy
|
|
81
|
+
|
|
82
|
+
| Score | Criteria |
|
|
83
|
+
|---|---|
|
|
84
|
+
| 1 | Single shadow value applied to everything (or no shadows). Nested cards. |
|
|
85
|
+
| 2 | 2 shadow levels but applied inconsistently. |
|
|
86
|
+
| 3 | 3 elevation levels (subtle / medium / pronounced). Brand-tinted, not pure gray. |
|
|
87
|
+
| 4 | Shadows match elevation semantically. Different shadow for hover than rest. |
|
|
88
|
+
| 5 | Multi-layer shadows (offset + ambient + directional). Brand-hue tinted at chroma 0.02-0.04. Spatial logic clear. |
|
|
89
|
+
|
|
90
|
+
Evidence: count of distinct `box-shadow` values, OKLCH chroma in shadow color
|
|
91
|
+
|
|
92
|
+
### 6. Motion intent
|
|
93
|
+
|
|
94
|
+
| Score | Criteria |
|
|
95
|
+
|---|---|
|
|
96
|
+
| 1 | No transitions. Or: bounce/elastic everywhere. Animates layout properties. |
|
|
97
|
+
| 2 | Default browser transitions on hover. No stagger. |
|
|
98
|
+
| 3 | Considered transitions on interactive elements. Respects `prefers-reduced-motion`. |
|
|
99
|
+
| 4 | One signature motion identified and executed. Stagger on entrance. Easing curves chosen (ease-out-quart or expo). |
|
|
100
|
+
| 5 | Orchestrated motion that communicates structure. Scroll-driven if appropriate. Restraint elsewhere. |
|
|
101
|
+
|
|
102
|
+
Evidence: easing curves used, presence of `prefers-reduced-motion`, file:line of signature motion
|
|
103
|
+
|
|
104
|
+
### 7. Microcopy specificity
|
|
105
|
+
|
|
106
|
+
| Score | Criteria |
|
|
107
|
+
|---|---|
|
|
108
|
+
| 1 | "Get Started" / "Learn More" / "Welcome to <product>" / "Click here". Lorem ipsum. |
|
|
109
|
+
| 2 | Generic but functional ("Save", "Cancel", "Submit"). Empty states say "No results." |
|
|
110
|
+
| 3 | Action-named CTAs ("Download invoice"). Empty states with one-sentence context. |
|
|
111
|
+
| 4 | Voice consistent with brand. Errors specific and actionable ("This file is 12MB; max is 10MB. Compress and retry."). |
|
|
112
|
+
| 5 | Microcopy you'd quote. Voice unmistakable. Every word earned. |
|
|
113
|
+
|
|
114
|
+
Evidence: grep for banned phrases, sample of empty/error states
|
|
115
|
+
|
|
116
|
+
### 8. Container depth & nesting
|
|
117
|
+
|
|
118
|
+
| Score | Criteria |
|
|
119
|
+
|---|---|
|
|
120
|
+
| 1 | Card → panel → pill → content (depth ≥ 4). Side-stripe borders. Gradient text. |
|
|
121
|
+
| 2 | Card-on-card pattern (depth 3). Most things wrapped in containers. |
|
|
122
|
+
| 3 | Container depth ≤ 2. No decorative side-stripes. |
|
|
123
|
+
| 4 | Containers used semantically only. Things that don't need a container don't have one. |
|
|
124
|
+
| 5 | Layout reads as elements arranged on a surface, not boxes inside boxes. |
|
|
125
|
+
|
|
126
|
+
Evidence: max DOM nesting depth on cards/panels, grep for `border-left` decorative usage
|
|
127
|
+
|
|
128
|
+
## Aggregate score
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
total = sum of dimension scores (max 40)
|
|
132
|
+
average = total / count_of_scored_dimensions
|
|
133
|
+
phase_pass = ALL scored dimensions ≥ 3
|
|
134
|
+
phase_fail = ANY scored dimension < 3
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
A phase fails if any dimension is below 3. Same gate as functional verification.
|
|
138
|
+
|
|
139
|
+
## Output format (verifier agent contract)
|
|
140
|
+
|
|
141
|
+
```markdown
|
|
142
|
+
## Design Rubric — Phase {N}
|
|
143
|
+
|
|
144
|
+
| Dim | Score | Evidence |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| Typography | 4 | `app/page.tsx:14` Fraunces + JetBrains Mono pair, weights 400/500/700, clamp() scale visible |
|
|
147
|
+
| Color cohesion | 3 | All CSS vars in `app/globals.css:8-22`. OKLCH used. Strategy: Restrained. WCAG AA verified |
|
|
148
|
+
| Spatial rhythm | 5 | Fluid clamp padding throughout, varied within section, generous between |
|
|
149
|
+
| Layout originality | 2 | `app/page.tsx:42-78` is a three-column feature grid in section 2 — first-order slop. Rework. |
|
|
150
|
+
| ... | ... | ... |
|
|
151
|
+
|
|
152
|
+
**Aggregate:** 28/40 (avg 3.5)
|
|
153
|
+
**Phase verdict:** FAIL — Layout Originality at 2 blocks the phase.
|
|
154
|
+
**Fix priority:** Rework section 2. Replace three-column grid with varied-height layout per `design-brand.md` §Layout.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
If the score < 3, the verifier writes the diagnosis. The fix is the next builder's task.
|
package/rules/grounding.md
CHANGED