qualia-framework 3.1.0 → 3.2.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/CLAUDE.md +4 -3
- package/README.md +5 -10
- package/agents/planner.md +0 -52
- package/agents/verifier.md +32 -180
- package/bin/cli.js +9 -403
- package/bin/install.js +61 -113
- package/bin/qualia-ui.js +15 -0
- package/bin/state.js +6 -200
- package/bin/statusline.js +4 -4
- package/hooks/auto-update.js +30 -8
- package/hooks/branch-guard.js +2 -23
- package/hooks/migration-guard.js +0 -23
- package/hooks/pre-compact.js +0 -20
- package/hooks/pre-deploy-gate.js +0 -39
- package/hooks/pre-push.js +0 -20
- package/hooks/session-start.js +44 -0
- package/package.json +4 -5
- package/skills/qualia/SKILL.md +0 -1
- package/skills/qualia-build/SKILL.md +0 -18
- package/skills/qualia-design/SKILL.md +8 -14
- package/skills/qualia-learn/SKILL.md +4 -27
- package/skills/qualia-optimize/SKILL.md +417 -0
- package/skills/qualia-polish/SKILL.md +117 -167
- package/skills/qualia-report/SKILL.md +8 -17
- package/skills/qualia-review/SKILL.md +41 -126
- package/skills/qualia-verify/SKILL.md +1 -1
- package/templates/DESIGN.md +102 -440
- package/templates/plan.md +0 -14
- package/tests/bin.test.sh +6 -20
- package/tests/hooks.test.sh +7 -76
- package/tests/state.test.sh +11 -189
- package/docs/erp-contract.md +0 -161
- package/hooks/block-env-edit.js +0 -52
- package/rules/infrastructure.md +0 -87
- package/skills/qualia-help/SKILL.md +0 -60
- package/skills/qualia-test/SKILL.md +0 -134
- package/templates/help.html +0 -476
- package/tests/runner.js +0 -1956
package/templates/DESIGN.md
CHANGED
|
@@ -1,475 +1,137 @@
|
|
|
1
1
|
# Design System — {Project Name}
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
> Format inspired by [awesome-design-md](https://github.com/VoltAgent/awesome-design-md).
|
|
3
|
+
> Generated during project setup. This is the source of truth for all frontend work.
|
|
4
|
+
> Builder agents read this before writing any component. Update it as design evolves.
|
|
6
5
|
|
|
7
|
-
##
|
|
6
|
+
## Brand
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
like a luxury watch brand crossed with a fintech terminal. The custom variable font
|
|
13
|
-
runs at weight 300 for headlines — light as confidence, not weakness." -->
|
|
8
|
+
- **Tone:** {dark-bold | clean-minimal | colorful-playful | corporate-professional}
|
|
9
|
+
- **Personality:** {1-2 sentences describing the feel — e.g., "Confident and technical, like a Bloomberg terminal meets a luxury brand"}
|
|
10
|
+
- **Industry:** {e.g., fintech, healthcare, SaaS, e-commerce}
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
**Key Characteristics:**
|
|
18
|
-
- {Font choice and why — e.g., "Outfit at weight 300 for headlines — geometric, modern, not overused"}
|
|
19
|
-
- {Color signature — e.g., "Deep charcoal (#1a1a2e) backgrounds with copper (#c9784e) accents"}
|
|
20
|
-
- {Shadow approach — e.g., "Warm-tinted multi-layer shadows, not flat or neutral gray"}
|
|
21
|
-
- {Border-radius philosophy — e.g., "Conservative 4-8px, nothing pill-shaped"}
|
|
22
|
-
- {Layout approach — e.g., "Full-bleed sections, asymmetric grids, no card monotony"}
|
|
23
|
-
- {One signature detail — e.g., "Noise texture overlay at 2% on hero, grain filter on images"}
|
|
24
|
-
|
|
25
|
-
## 2. Color Palette & Roles
|
|
26
|
-
|
|
27
|
-
<!-- Every color must have a name, hex value, AND its role. No unnamed colors in code. -->
|
|
28
|
-
|
|
29
|
-
### Primary
|
|
30
|
-
- **{Brand Color Name}** (`#{hex}`): Primary brand identity. Used for {where}.
|
|
31
|
-
- **{Heading Color}** (`#{hex}`): All headings. Not pure black — {warm/cool undertone}.
|
|
32
|
-
- **{Background}** (`#{hex}`): Page background.
|
|
33
|
-
|
|
34
|
-
### Accent & CTA
|
|
35
|
-
- **{Accent Name}** (`#{hex}`): CTAs, interactive highlights, links.
|
|
36
|
-
- **{Accent Hover}** (`#{hex}`): Hover state for accent elements.
|
|
37
|
-
- **{Secondary Accent}** (`#{hex}`): {role — badges, decorative, gradients}.
|
|
38
|
-
|
|
39
|
-
### Neutral Scale
|
|
40
|
-
- **{Text Primary}** (`#{hex}`): Body text, paragraphs.
|
|
41
|
-
- **{Text Muted}** (`#{hex}`): Secondary text, descriptions, captions.
|
|
42
|
-
- **{Text Subtle}** (`#{hex}`): Placeholders, hints, disabled text.
|
|
43
|
-
- **{Border Default}** (`#{hex}`): Card borders, dividers.
|
|
44
|
-
- **{Border Subtle}** (`#{hex}`): Faint separators, inactive states.
|
|
45
|
-
|
|
46
|
-
### Semantic
|
|
47
|
-
- **Success** (`#{hex}`): With icon. Background: `{rgba}`. Border: `{rgba}`.
|
|
48
|
-
- **Warning** (`#{hex}`): With icon. Background: `{rgba}`. Border: `{rgba}`.
|
|
49
|
-
- **Error** (`#{hex}`): With icon. Background: `{rgba}`. Border: `{rgba}`.
|
|
50
|
-
- **Info** (`#{hex}`): With icon. Background: `{rgba}`. Border: `{rgba}`.
|
|
51
|
-
|
|
52
|
-
### Shadow Colors
|
|
53
|
-
- **Primary Shadow** (`{rgba}`): {e.g., "Brand-tinted — warm brown-gray, not neutral"}.
|
|
54
|
-
- **Secondary Shadow** (`{rgba}`): Reinforcement layer for depth.
|
|
55
|
-
- **Ambient Shadow** (`{rgba}`): Soft lift for subtle elevation.
|
|
56
|
-
|
|
57
|
-
### CSS Variables
|
|
12
|
+
## Color System
|
|
58
13
|
|
|
59
14
|
```css
|
|
60
15
|
:root {
|
|
61
|
-
/*
|
|
62
|
-
--color-primary:
|
|
63
|
-
--color-primary-hover:
|
|
64
|
-
--color-primary-subtle:
|
|
65
|
-
|
|
66
|
-
/* Accent */
|
|
67
|
-
--color-accent:
|
|
68
|
-
--color-accent-hover:
|
|
69
|
-
|
|
70
|
-
/*
|
|
71
|
-
--color-bg:
|
|
72
|
-
--color-bg-subtle:
|
|
73
|
-
--color-bg-muted:
|
|
74
|
-
|
|
75
|
-
/*
|
|
76
|
-
--color-text:
|
|
77
|
-
--color-
|
|
78
|
-
--color-
|
|
79
|
-
|
|
80
|
-
/* Borders */
|
|
81
|
-
--color-border: #{hex};
|
|
82
|
-
--color-border-subtle: #{hex};
|
|
16
|
+
/* Primary — brand identity */
|
|
17
|
+
--color-primary: {value};
|
|
18
|
+
--color-primary-hover: {value};
|
|
19
|
+
--color-primary-subtle: {value}; /* backgrounds, badges */
|
|
20
|
+
|
|
21
|
+
/* Accent — CTAs, highlights */
|
|
22
|
+
--color-accent: {value};
|
|
23
|
+
--color-accent-hover: {value};
|
|
24
|
+
|
|
25
|
+
/* Neutral — text, borders, backgrounds */
|
|
26
|
+
--color-bg: {value};
|
|
27
|
+
--color-bg-subtle: {value}; /* cards, raised surfaces */
|
|
28
|
+
--color-bg-muted: {value}; /* disabled, secondary surfaces */
|
|
29
|
+
--color-text: {value};
|
|
30
|
+
--color-text-muted: {value}; /* secondary text */
|
|
31
|
+
--color-text-subtle: {value}; /* placeholders, hints */
|
|
32
|
+
--color-border: {value};
|
|
33
|
+
--color-border-subtle: {value};
|
|
83
34
|
|
|
84
35
|
/* Semantic */
|
|
85
|
-
--color-success:
|
|
86
|
-
--color-warning:
|
|
87
|
-
--color-error:
|
|
88
|
-
--color-info:
|
|
89
|
-
|
|
90
|
-
/* Shadows */
|
|
91
|
-
--shadow-primary: {rgba};
|
|
92
|
-
--shadow-secondary: {rgba};
|
|
93
|
-
--shadow-ambient: {rgba};
|
|
36
|
+
--color-success: {value};
|
|
37
|
+
--color-warning: {value};
|
|
38
|
+
--color-error: {value};
|
|
39
|
+
--color-info: {value};
|
|
94
40
|
}
|
|
95
41
|
|
|
96
|
-
/* Dark mode
|
|
42
|
+
/* Dark mode overrides */
|
|
97
43
|
[data-theme="dark"] {
|
|
98
|
-
--color-bg:
|
|
99
|
-
--color-bg-subtle:
|
|
100
|
-
--color-text:
|
|
101
|
-
--color-text-muted: #{hex};
|
|
102
|
-
--color-border: #{hex};
|
|
44
|
+
--color-bg: {value};
|
|
45
|
+
--color-bg-subtle: {value};
|
|
46
|
+
--color-text: {value};
|
|
103
47
|
/* ... override all tokens ... */
|
|
104
48
|
}
|
|
105
49
|
```
|
|
106
50
|
|
|
107
|
-
|
|
108
|
-
- Body text on background: {ratio} (must be >= 4.5:1)
|
|
109
|
-
- Muted text on background: {ratio} (must be >= 4.5:1)
|
|
110
|
-
- Accent on background: {ratio} (must be >= 3:1 for large text)
|
|
51
|
+
## Typography
|
|
111
52
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
53
|
+
```css
|
|
54
|
+
:root {
|
|
55
|
+
/* Font families */
|
|
56
|
+
--font-display: '{Display Font}', {fallback}; /* Hero text, page titles */
|
|
57
|
+
--font-heading: '{Heading Font}', {fallback}; /* Section headings */
|
|
58
|
+
--font-body: '{Body Font}', {fallback}; /* Paragraphs, UI text */
|
|
59
|
+
--font-mono: '{Mono Font}', monospace; /* Code, data */
|
|
60
|
+
|
|
61
|
+
/* Scale */
|
|
62
|
+
--text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.8rem);
|
|
63
|
+
--text-sm: clamp(0.875rem, 0.8rem + 0.4vw, 0.9rem);
|
|
64
|
+
--text-base: clamp(1rem, 0.5rem + 1.5vw, 1.125rem);
|
|
65
|
+
--text-lg: clamp(1.125rem, 0.75rem + 1.2vw, 1.25rem);
|
|
66
|
+
--text-xl: clamp(1.25rem, 0.75rem + 1.5vw, 1.5rem);
|
|
67
|
+
--text-2xl: clamp(1.5rem, 0.75rem + 2.25vw, 2rem);
|
|
68
|
+
--text-3xl: clamp(1.875rem, 1rem + 2.5vw, 2.5rem);
|
|
69
|
+
--text-4xl: clamp(2.25rem, 1rem + 3vw, 3rem);
|
|
70
|
+
--text-5xl: clamp(3rem, 1rem + 4vw, 4rem);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
118
73
|
|
|
119
74
|
**Google Fonts import:** `{URL}`
|
|
120
75
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
### Hierarchy Table
|
|
76
|
+
## Spacing Scale
|
|
124
77
|
|
|
125
|
-
|
|
126
|
-
|------|------|------|--------|-------------|----------------|-------|
|
|
127
|
-
| Display Hero | {display} | {clamp(2.5rem, 1rem + 4vw, 4rem)} | {weight} | 1.05 | {-0.02em} | Largest text on site |
|
|
128
|
-
| Display | {display} | {clamp(2rem, 1rem + 3vw, 3rem)} | {weight} | 1.1 | {-0.015em} | Section heroes |
|
|
129
|
-
| H1 | {heading} | {clamp(1.75rem, 1rem + 2.5vw, 2.5rem)} | {weight} | 1.15 | {-0.01em} | Page titles |
|
|
130
|
-
| H2 | {heading} | {clamp(1.5rem, 0.75rem + 2vw, 2rem)} | {weight} | 1.2 | normal | Section titles |
|
|
131
|
-
| H3 | {heading} | {clamp(1.25rem, 0.75rem + 1.5vw, 1.5rem)} | {weight} | 1.25 | normal | Subsection titles |
|
|
132
|
-
| Body Large | {body} | {1.125rem} | 400 | 1.6 | normal | Intro text, feature descriptions |
|
|
133
|
-
| Body | {body} | {1rem} | 400 | 1.6 | normal | Standard reading text |
|
|
134
|
-
| Body Small | {body} | {0.875rem} | 400 | 1.5 | normal | Secondary text |
|
|
135
|
-
| Caption | {body} | {0.75rem} | 500 | 1.4 | {0.02em} | Labels, metadata |
|
|
136
|
-
| Button | {body} | {0.875rem–1rem} | 500 | 1.0 | {0.01em} | Button text |
|
|
137
|
-
| Code | {mono} | {0.875rem} | 500 | 1.7 | normal | Code blocks |
|
|
78
|
+
Based on 8px grid: `4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 96 / 128`
|
|
138
79
|
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
- Body text max-width: `65ch`. Everything else: fluid full-width.
|
|
144
|
-
- Min body font size: 16px. Never smaller for reading text.
|
|
80
|
+
- **Within components:** 8–16px
|
|
81
|
+
- **Between related elements:** 16–24px
|
|
82
|
+
- **Between sections:** 48–96px
|
|
83
|
+
- **Page padding:** `clamp(1rem, 5vw, 4rem)` horizontal
|
|
145
84
|
|
|
146
|
-
##
|
|
147
|
-
|
|
148
|
-
### Buttons
|
|
85
|
+
## Motion
|
|
149
86
|
|
|
150
|
-
**
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
- Hover: `var(--color-accent-hover)`, transition 150ms ease-out
|
|
157
|
-
- Active: `transform: scale(0.98)`
|
|
158
|
-
- Focus: `2px solid var(--color-accent)` offset 2px
|
|
159
|
-
- Disabled: opacity 0.5, `cursor: not-allowed`, `aria-disabled="true"`
|
|
87
|
+
- **Approach:** {minimal | subtle | expressive}
|
|
88
|
+
- **Page load:** staggered fade-up, 60ms delay between elements
|
|
89
|
+
- **Hover/focus:** 150ms ease-out
|
|
90
|
+
- **Expand/collapse:** 250ms ease-in-out
|
|
91
|
+
- **Page transitions:** 400ms ease-out
|
|
92
|
+
- **Reduced motion:** all non-essential animations disabled
|
|
160
93
|
|
|
161
|
-
|
|
162
|
-
- Background: transparent
|
|
163
|
-
- Text: `var(--color-accent)`
|
|
164
|
-
- Border: `1px solid var(--color-border)`
|
|
165
|
-
- Hover: `var(--color-bg-subtle)` background
|
|
166
|
-
|
|
167
|
-
**Ghost**
|
|
168
|
-
- Background: transparent
|
|
169
|
-
- Text: `var(--color-text)`
|
|
170
|
-
- Hover: `var(--color-bg-subtle)` background
|
|
171
|
-
|
|
172
|
-
**Destructive**
|
|
173
|
-
- Background: `var(--color-error)`
|
|
174
|
-
- Text: `#ffffff`
|
|
175
|
-
- Use: Only for irreversible actions with confirmation
|
|
176
|
-
|
|
177
|
-
**Sizes:** sm (32px height), md (40px), lg (48px)
|
|
178
|
-
|
|
179
|
-
### Cards & Surfaces
|
|
180
|
-
|
|
181
|
-
- Background: `var(--color-bg-subtle)`
|
|
182
|
-
- Border: `1px solid var(--color-border-subtle)`
|
|
183
|
-
- Radius: {8px}
|
|
184
|
-
- Shadow (resting): `var(--shadow-ambient) 0px 4px 12px`
|
|
185
|
-
- Shadow (hover): `var(--shadow-primary) 0px 8px 24px, var(--shadow-secondary) 0px 4px 12px`
|
|
186
|
-
- Transition: shadow 200ms ease-out
|
|
187
|
-
- **No identical card grids** — vary layout, size, and emphasis
|
|
188
|
-
|
|
189
|
-
### Inputs & Forms
|
|
94
|
+
## Component Patterns
|
|
190
95
|
|
|
96
|
+
### Buttons
|
|
97
|
+
- **Primary:** solid {accent} background, white text, rounded-{radius}
|
|
98
|
+
- **Secondary:** bordered, transparent background
|
|
99
|
+
- **Ghost:** text-only, hover background
|
|
100
|
+
- **Destructive:** red variant for dangerous actions
|
|
101
|
+
- **Sizes:** sm (32px), md (40px), lg (48px)
|
|
102
|
+
|
|
103
|
+
### Inputs
|
|
104
|
+
- Border: 1px solid var(--color-border)
|
|
105
|
+
- Focus: 2px ring var(--color-primary)
|
|
106
|
+
- Error: red border + error text below with `aria-describedby`
|
|
191
107
|
- Height: 40px (md), 48px (lg)
|
|
192
|
-
- Border: `1px solid var(--color-border)`
|
|
193
|
-
- Radius: {6px}
|
|
194
|
-
- Focus: `2px solid var(--color-accent)` ring
|
|
195
|
-
- Error: `var(--color-error)` border + error text below with `aria-describedby`
|
|
196
|
-
- Label: visible `<label>` with `htmlFor` — never placeholder-only
|
|
197
|
-
- Placeholder: `var(--color-text-subtle)`
|
|
198
|
-
|
|
199
|
-
### Badges / Status
|
|
200
|
-
|
|
201
|
-
- Padding: 2px 8px
|
|
202
|
-
- Radius: {4px}
|
|
203
|
-
- Font: caption size, weight 500
|
|
204
|
-
- Success: `var(--color-success)` bg at 0.15 alpha, text at full, border at 0.3 alpha
|
|
205
|
-
- Warning: same pattern with `var(--color-warning)`
|
|
206
|
-
- Error: same pattern with `var(--color-error)`
|
|
207
|
-
|
|
208
|
-
### Navigation
|
|
209
|
-
|
|
210
|
-
- {Sticky header with backdrop-filter blur(12px) / Fixed sidebar / etc.}
|
|
211
|
-
- Logo: {left-aligned / centered}
|
|
212
|
-
- Links: {font, size, weight, color}
|
|
213
|
-
- Active indicator: {underline / background / border-bottom}
|
|
214
|
-
- Mobile: hamburger with drawer, 44px touch target
|
|
215
|
-
- CTA button: accent color, right-aligned
|
|
216
|
-
|
|
217
|
-
### Toasts / Notifications
|
|
218
|
-
|
|
219
|
-
- Position: {top-right / bottom-center}
|
|
220
|
-
- `aria-live="polite"` for info, `"assertive"` for errors
|
|
221
|
-
- Auto-dismiss: 5s minimum, dismissible via close button
|
|
222
|
-
- Semantic coloring matching badge pattern
|
|
223
|
-
|
|
224
|
-
## 5. Layout & Spacing
|
|
225
|
-
|
|
226
|
-
### Spacing Scale (8px grid)
|
|
227
|
-
|
|
228
|
-
`4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 96 / 128`
|
|
229
|
-
|
|
230
|
-
| Context | Value |
|
|
231
|
-
|---------|-------|
|
|
232
|
-
| Within components | 8–16px |
|
|
233
|
-
| Between related elements | 16–24px |
|
|
234
|
-
| Between sections | `clamp(3rem, 8vw, 6rem)` |
|
|
235
|
-
| Page horizontal padding | `clamp(1rem, 5vw, 4rem)` |
|
|
236
|
-
| Component gap | `clamp(1rem, 3vw, 2rem)` |
|
|
237
|
-
|
|
238
|
-
### Grid & Layout Strategy
|
|
239
|
-
|
|
240
|
-
- **Full-width layouts** — no hardcoded `max-width: 1200px` caps
|
|
241
|
-
- Prose/reading content: `max-width: 65ch`
|
|
242
|
-
- Feature sections: varied layouts (side-by-side, staggered, asymmetric, full-bleed)
|
|
243
|
-
- {Specific grid: e.g., "12-column CSS grid with fluid gutters"}
|
|
244
|
-
- Break symmetry where it serves design — offset, overlap, diagonal flow
|
|
245
|
-
|
|
246
|
-
### Whitespace Philosophy
|
|
247
|
-
|
|
248
|
-
{e.g., "Dense data, generous chrome. Financial tables are tightly packed, but the UI frame
|
|
249
|
-
around them breathes. Sections alternate between dense content and generous breathing room."}
|
|
250
108
|
|
|
251
|
-
###
|
|
109
|
+
### Cards/Surfaces
|
|
110
|
+
- Background: var(--color-bg-subtle)
|
|
111
|
+
- Border: 1px solid var(--color-border-subtle) or shadow
|
|
112
|
+
- Border-radius: {value}
|
|
113
|
+
- No identical card grids — vary layout and emphasis
|
|
252
114
|
|
|
253
|
-
|
|
254
|
-
|-------|-------|-----|
|
|
255
|
-
| Micro | 2px | Subtle rounding, inline elements |
|
|
256
|
-
| Standard | {4-6px} | Buttons, inputs, badges — the workhorse |
|
|
257
|
-
| Comfortable | {8px} | Cards, containers |
|
|
258
|
-
| Large | {12px} | Featured cards, hero elements |
|
|
259
|
-
| Full | 9999px | Avatars, pills (use sparingly) |
|
|
115
|
+
## Responsive Approach
|
|
260
116
|
|
|
261
|
-
|
|
117
|
+
- **Strategy:** mobile-first
|
|
118
|
+
- **Primary breakpoints:** 768px (tablet), 1024px (desktop)
|
|
119
|
+
- **Navigation:** hamburger on mobile, expanded on desktop
|
|
120
|
+
- **Layout:** single column mobile → multi-column desktop
|
|
121
|
+
- **Touch targets:** 44px minimum on mobile
|
|
262
122
|
|
|
263
|
-
|
|
264
|
-
|-------|--------|-----|
|
|
265
|
-
| Flat (0) | none | Page background, inline content |
|
|
266
|
-
| Subtle (1) | `var(--shadow-ambient) 0px 1px 3px` | Resting cards, slight lift |
|
|
267
|
-
| Standard (2) | `var(--shadow-primary) 0px 4px 12px` | Cards, panels |
|
|
268
|
-
| Elevated (3) | `var(--shadow-primary) 0px 8px 24px, var(--shadow-secondary) 0px 4px 12px` | Dropdowns, popovers, hover cards |
|
|
269
|
-
| Overlay (4) | `var(--shadow-primary) 0px 16px 48px, var(--shadow-secondary) 0px 8px 24px` | Modals, dialogs, floating panels |
|
|
270
|
-
| Focus ring | `0 0 0 2px var(--color-accent)` | Keyboard focus (non-negotiable) |
|
|
123
|
+
## Visual Effects
|
|
271
124
|
|
|
272
|
-
|
|
273
|
-
|
|
125
|
+
- {e.g., "Noise texture overlay at 3% opacity on hero sections"}
|
|
126
|
+
- {e.g., "Glass-morphism on floating elements: backdrop-blur-xl bg-white/80"}
|
|
127
|
+
- {e.g., "Gradient mesh on hero: radial-gradient from primary to transparent"}
|
|
128
|
+
- {e.g., "Decorative geometric shapes as background accents"}
|
|
274
129
|
|
|
275
|
-
##
|
|
130
|
+
## Anti-Patterns (Don't Do This)
|
|
276
131
|
|
|
277
|
-
|
|
278
|
-
-
|
|
279
|
-
-
|
|
280
|
-
-
|
|
281
|
-
-
|
|
282
|
-
-
|
|
283
|
-
- Use `cursor: pointer` on every clickable element
|
|
284
|
-
- Use CSS variables for every color — zero scattered hex values in components
|
|
285
|
-
- Use `clamp()` for fluid typography and spacing
|
|
286
|
-
- Test at 320px, 768px, 1024px, 1440px before shipping
|
|
287
|
-
|
|
288
|
-
### Don't
|
|
289
|
-
- {e.g., "Don't use bold (700) for headlines — this brand uses light weights"}
|
|
290
|
-
- {e.g., "Don't use pill-shaped buttons (border-radius: 9999px) — conservative rounding only"}
|
|
291
|
-
- {e.g., "Don't use warm accents (orange, yellow) for interactive elements"}
|
|
292
|
-
- Don't use Inter, Roboto, Arial, Helvetica, system-ui, Space Grotesk
|
|
293
|
-
- Don't use blue-purple gradients (AI slop tell #1)
|
|
294
|
-
- Don't use identical card grids — vary layout and emphasis
|
|
295
|
-
- Don't use `outline: none` without a visible focus replacement
|
|
296
|
-
- Don't use `max-width: 1200px` or `1280px` containers — go full-width fluid
|
|
297
|
-
- Don't use placeholder-only form inputs — always pair with visible `<label>`
|
|
298
|
-
- Don't ship gray-on-gray text (#999 on #fff fails WCAG)
|
|
299
|
-
|
|
300
|
-
## 8. Responsive Behavior
|
|
301
|
-
|
|
302
|
-
### Breakpoints
|
|
303
|
-
|
|
304
|
-
| Name | Width | Key Changes |
|
|
305
|
-
|------|-------|-------------|
|
|
306
|
-
| Mobile | < 640px | Single column, stacked nav, reduced heading sizes |
|
|
307
|
-
| Tablet | 640–1023px | 2-column grids, condensed nav |
|
|
308
|
-
| Desktop | 1024–1279px | Full layout, expanded nav |
|
|
309
|
-
| Large | >= 1280px | Maximum content width, generous margins |
|
|
310
|
-
|
|
311
|
-
### Collapsing Strategy
|
|
312
|
-
|
|
313
|
-
| Element | Desktop | Tablet | Mobile |
|
|
314
|
-
|---------|---------|--------|--------|
|
|
315
|
-
| Navigation | Full horizontal + CTA | Condensed or hamburger | Hamburger + drawer |
|
|
316
|
-
| Hero | {Side-by-side / full-width} | {Stacked, padded} | {Stacked, full-width} |
|
|
317
|
-
| Feature sections | {3-column / asymmetric} | {2-column} | {Single column stacked} |
|
|
318
|
-
| Sidebar | Always visible | Collapsible | Hidden, overlay |
|
|
319
|
-
| Tables | Full table | Scroll with sticky col | Card view |
|
|
320
|
-
| Modals | Centered, max-width | Centered, 80% width | Full screen |
|
|
321
|
-
|
|
322
|
-
### Touch Targets
|
|
323
|
-
- All interactive elements: 44x44px minimum (48px recommended)
|
|
324
|
-
- Adequate spacing between tap targets (8px minimum gap)
|
|
325
|
-
- Mobile nav toggle: prominent, easy to reach
|
|
326
|
-
|
|
327
|
-
### Image Behavior
|
|
328
|
-
- `next/image` with explicit `width`/`height` (prevent layout shift)
|
|
329
|
-
- Responsive `srcset` for different densities
|
|
330
|
-
- `max-width: 100%`, `height: auto` on all images
|
|
331
|
-
- Lazy-load below-fold images
|
|
332
|
-
|
|
333
|
-
## 9. Agent Prompt Guide
|
|
334
|
-
|
|
335
|
-
### Quick Color Reference
|
|
336
|
-
- Primary CTA: {Name} (`#{hex}`)
|
|
337
|
-
- CTA Hover: {Name} (`#{hex}`)
|
|
338
|
-
- Page background: {Name} (`#{hex}`)
|
|
339
|
-
- Heading text: {Name} (`#{hex}`)
|
|
340
|
-
- Body text: {Name} (`#{hex}`)
|
|
341
|
-
- Muted text: {Name} (`#{hex}`)
|
|
342
|
-
- Border: {Name} (`#{hex}`)
|
|
343
|
-
- Link: {Name} (`#{hex}`)
|
|
344
|
-
- Dark section: {Name} (`#{hex}`)
|
|
345
|
-
- Success: (`#{hex}`)
|
|
346
|
-
- Error: (`#{hex}`)
|
|
347
|
-
|
|
348
|
-
### Example Component Prompts
|
|
349
|
-
|
|
350
|
-
**Hero section:**
|
|
351
|
-
"{Describe: background color, headline font/size/weight/color/spacing, subtitle treatment, CTA button specs, layout}"
|
|
352
|
-
|
|
353
|
-
**Card:**
|
|
354
|
-
"{Describe: background, border, radius, shadow (exact rgba), title font treatment, body text treatment}"
|
|
355
|
-
|
|
356
|
-
**Form:**
|
|
357
|
-
"{Describe: input border/radius/height, focus ring, label treatment, error state, submit button}"
|
|
358
|
-
|
|
359
|
-
### Iteration Checklist
|
|
360
|
-
1. Always use CSS variables — never hardcode colors in components
|
|
361
|
-
2. {Font rule — e.g., "Enable ss01 on all display font text"}
|
|
362
|
-
3. {Weight rule — e.g., "Default to 300; use 400 only for UI/buttons"}
|
|
363
|
-
4. {Shadow rule — e.g., "Always use branded rgba, never neutral gray shadows"}
|
|
364
|
-
5. {Heading rule — e.g., "Heading color is deep navy, not black — warmth matters"}
|
|
365
|
-
6. {Radius rule — e.g., "Stay within 4-8px range except avatars"}
|
|
366
|
-
|
|
367
|
-
## 10. Accessibility (Non-Negotiable)
|
|
368
|
-
|
|
369
|
-
### Perceivable
|
|
370
|
-
- [ ] All images: descriptive `alt` text (decorative: `alt=""` + `aria-hidden="true"`)
|
|
371
|
-
- [ ] Color contrast: 4.5:1 normal text, 3:1 large text (18px+ bold / 24px+)
|
|
372
|
-
- [ ] Color contrast: 3:1 for UI components and graphical objects
|
|
373
|
-
- [ ] Information not conveyed by color alone — icons, text, patterns as supplements
|
|
374
|
-
- [ ] Text resizable to 200% without loss of content
|
|
375
|
-
- [ ] Content reflows at 320px (no horizontal scroll)
|
|
376
|
-
|
|
377
|
-
### Operable
|
|
378
|
-
- [ ] All functionality available via keyboard (Tab, Enter, Space, Escape, Arrows)
|
|
379
|
-
- [ ] No keyboard traps (except focus traps in modals)
|
|
380
|
-
- [ ] Visible focus indicator on every interactive element
|
|
381
|
-
- [ ] Skip navigation link: `<a href="#main" class="sr-only focus:not-sr-only">`
|
|
382
|
-
- [ ] Touch targets: 44x44px minimum
|
|
383
|
-
- [ ] Page titles are descriptive and unique
|
|
384
|
-
|
|
385
|
-
### Understandable
|
|
386
|
-
- [ ] Form inputs have visible `<label>` linked via `htmlFor`
|
|
387
|
-
- [ ] Error messages identify the field and describe the fix
|
|
388
|
-
- [ ] Required fields: `aria-required="true"` + visual indicator
|
|
389
|
-
- [ ] `<html lang="en">` set
|
|
390
|
-
- [ ] Consistent navigation across pages
|
|
391
|
-
|
|
392
|
-
### Robust
|
|
393
|
-
- [ ] Semantic HTML: `<nav>`, `<main>`, `<article>`, `<section>`, `<header>`, `<footer>`
|
|
394
|
-
- [ ] One `<h1>` per page, sequential heading order
|
|
395
|
-
- [ ] ARIA used correctly when HTML semantics aren't enough
|
|
396
|
-
- [ ] Dynamic content: `aria-live="polite"` for updates, `"assertive"` for errors
|
|
397
|
-
- [ ] Modals: focus trap, close on Escape, `aria-modal="true"`, restore focus on close
|
|
398
|
-
|
|
399
|
-
## 11. Hardening Criteria
|
|
400
|
-
|
|
401
|
-
Before shipping, stress-test:
|
|
402
|
-
|
|
403
|
-
- [ ] **Long text:** 200-character username, 3-paragraph description — does layout hold?
|
|
404
|
-
- [ ] **Empty everywhere:** all lists empty, all data missing — helpful empty states?
|
|
405
|
-
- [ ] **Error everywhere:** every fetch fails — error states visible and recoverable?
|
|
406
|
-
- [ ] **320px viewport:** nothing overflows, clips, or overlaps
|
|
407
|
-
- [ ] **Keyboard only:** Tab through entire app — everything reachable, focus visible?
|
|
408
|
-
- [ ] **Slow network:** loading states visible? Content streams, doesn't flash?
|
|
409
|
-
- [ ] **RTL text:** if applicable, does layout mirror correctly?
|
|
410
|
-
- [ ] **Zoom 200%:** content still usable, nothing hidden?
|
|
411
|
-
|
|
412
|
-
## 12. Anti-Slop Detection
|
|
413
|
-
|
|
414
|
-
<!-- These patterns are checked by /qualia-polish grep scans. Any matches = mandatory fix. -->
|
|
415
|
-
|
|
416
|
-
| Pattern | Check | Fix |
|
|
417
|
-
|---------|-------|-----|
|
|
418
|
-
| Generic fonts | `grep -rn "Inter\|Roboto\|Arial\|Helvetica\|system-ui\|Space.Grotesk"` | Replace with project fonts |
|
|
419
|
-
| Hardcoded containers | `grep -rn "max-w-7xl\|max-w-\[1200\|max-width.*1200"` | Use fluid `clamp()` padding |
|
|
420
|
-
| Blue-purple gradients | `grep -rn "from-blue.*to-purple\|from-purple.*to-blue"` | Use brand colors |
|
|
421
|
-
| Card grid monotony | `grep -rn "grid-cols-3\|grid-cols-4"` in component files | Vary layout and emphasis |
|
|
422
|
-
| Scattered hex colors | `grep -rn "text-\[#\|bg-\[#\|border-\[#"` count > 5 | Use CSS variables |
|
|
423
|
-
| Missing focus styles | `grep -rn "outline: none\|outline:none"` | Add visible focus replacement |
|
|
424
|
-
| Placeholder-only inputs | `<input>` without adjacent `<label>` | Add visible label |
|
|
425
|
-
|
|
426
|
-
## Motion Reference
|
|
427
|
-
|
|
428
|
-
### Duration Table
|
|
429
|
-
|
|
430
|
-
| Action | Duration | Easing |
|
|
431
|
-
|--------|----------|--------|
|
|
432
|
-
| Micro-feedback (press, toggle) | 100ms | ease-out |
|
|
433
|
-
| Hover/focus | 150ms | ease-out |
|
|
434
|
-
| Tooltip, dropdown | 200ms | ease-out |
|
|
435
|
-
| Expand/collapse | 250ms | ease-in-out |
|
|
436
|
-
| Page element enter | 300ms | `cubic-bezier(0, 0, 0.2, 1)` |
|
|
437
|
-
| Page/route transition | 400ms | `cubic-bezier(0.4, 0, 0.2, 1)` |
|
|
438
|
-
| Complex orchestration | 500–800ms | staggered |
|
|
439
|
-
|
|
440
|
-
### Easing Curves
|
|
441
|
-
|
|
442
|
-
```css
|
|
443
|
-
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
|
|
444
|
-
--ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
|
|
445
|
-
--ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
|
|
446
|
-
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
### Stagger Pattern
|
|
450
|
-
|
|
451
|
-
```css
|
|
452
|
-
@keyframes fadeUp {
|
|
453
|
-
from { opacity: 0; transform: translateY(12px); }
|
|
454
|
-
to { opacity: 1; transform: translateY(0); }
|
|
455
|
-
}
|
|
456
|
-
.stagger > * { animation: fadeUp 300ms var(--ease-decelerate) both; }
|
|
457
|
-
.stagger > *:nth-child(1) { animation-delay: 0ms; }
|
|
458
|
-
.stagger > *:nth-child(2) { animation-delay: 60ms; }
|
|
459
|
-
.stagger > *:nth-child(3) { animation-delay: 120ms; }
|
|
460
|
-
.stagger > *:nth-child(4) { animation-delay: 180ms; }
|
|
461
|
-
.stagger > *:nth-child(5) { animation-delay: 240ms; }
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
### Reduced Motion (mandatory)
|
|
465
|
-
|
|
466
|
-
```css
|
|
467
|
-
@media (prefers-reduced-motion: reduce) {
|
|
468
|
-
*, *::before, *::after {
|
|
469
|
-
animation-duration: 0.01ms !important;
|
|
470
|
-
animation-iteration-count: 1 !important;
|
|
471
|
-
transition-duration: 0.01ms !important;
|
|
472
|
-
scroll-behavior: auto !important;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
```
|
|
132
|
+
- No Inter, Roboto, Arial, system-ui fonts
|
|
133
|
+
- No blue-purple gradients
|
|
134
|
+
- No identical card grids
|
|
135
|
+
- No generic stock-photo heroes
|
|
136
|
+
- No hardcoded max-width containers (use fluid layouts)
|
|
137
|
+
- No gray-on-gray low-contrast text
|
package/templates/plan.md
CHANGED
|
@@ -26,17 +26,3 @@ Goal: {what must be true when done}
|
|
|
26
26
|
- [ ] {truth 1 — what the user can do}
|
|
27
27
|
- [ ] {truth 2}
|
|
28
28
|
- [ ] {truth 3}
|
|
29
|
-
|
|
30
|
-
## Verification Contract
|
|
31
|
-
|
|
32
|
-
### Contract for Task 1 — {title}
|
|
33
|
-
**Check type:** {file-exists | grep-match | command-exit | behavioral}
|
|
34
|
-
**Command:** `{exact command the verifier will run}`
|
|
35
|
-
**Expected:** {what the output should be}
|
|
36
|
-
**Fail if:** {what constitutes failure}
|
|
37
|
-
|
|
38
|
-
### Contract for Task 2 — {title}
|
|
39
|
-
**Check type:** {file-exists | grep-match | command-exit | behavioral}
|
|
40
|
-
**Command:** `{exact command}`
|
|
41
|
-
**Expected:** {expected output}
|
|
42
|
-
**Fail if:** {failure condition}
|
package/tests/bin.test.sh
CHANGED
|
@@ -139,7 +139,7 @@ cat > "$TMP/.claude/.qualia-config.json" <<'EOF'
|
|
|
139
139
|
"code": "QS-FAWZI-01",
|
|
140
140
|
"installed_by": "Fawzi Goussous",
|
|
141
141
|
"role": "OWNER",
|
|
142
|
-
"version": "
|
|
142
|
+
"version": "3.1.0",
|
|
143
143
|
"installed_at": "2026-04-10"
|
|
144
144
|
}
|
|
145
145
|
EOF
|
|
@@ -423,7 +423,7 @@ cat > "$TMP/.claude/.qualia-config.json" <<'EOF'
|
|
|
423
423
|
"code": "QS-FAWZI-01",
|
|
424
424
|
"installed_by": "Fawzi Goussous",
|
|
425
425
|
"role": "OWNER",
|
|
426
|
-
"version": "
|
|
426
|
+
"version": "3.1.0",
|
|
427
427
|
"installed_at": "2026-04-10"
|
|
428
428
|
}
|
|
429
429
|
EOF
|
|
@@ -476,10 +476,10 @@ else
|
|
|
476
476
|
fail_case "CLAUDE.md role substitution"
|
|
477
477
|
fi
|
|
478
478
|
|
|
479
|
-
# 31. All
|
|
479
|
+
# 31. All 7 hooks installed (block-env-edit.js retired in v3.2.0)
|
|
480
480
|
HOOK_COUNT=$(ls "$TMP/.claude/hooks/"*.js 2>/dev/null | wc -l)
|
|
481
|
-
if [ "$HOOK_COUNT" -eq
|
|
482
|
-
pass "
|
|
481
|
+
if [ "$HOOK_COUNT" -eq 7 ]; then
|
|
482
|
+
pass "7 hooks installed in hooks/"
|
|
483
483
|
else
|
|
484
484
|
fail_case "hook count" "got $HOOK_COUNT"
|
|
485
485
|
fi
|
|
@@ -494,21 +494,7 @@ else
|
|
|
494
494
|
fail_case "settings.json contents"
|
|
495
495
|
fi
|
|
496
496
|
|
|
497
|
-
# 33.
|
|
498
|
-
if grep -q 'block-env-edit.js' "$TMP/.claude/settings.json" \
|
|
499
|
-
&& grep -q 'branch-guard.js' "$TMP/.claude/settings.json" \
|
|
500
|
-
&& grep -q 'migration-guard.js' "$TMP/.claude/settings.json" \
|
|
501
|
-
&& grep -q 'pre-push.js' "$TMP/.claude/settings.json" \
|
|
502
|
-
&& grep -q 'pre-deploy-gate.js' "$TMP/.claude/settings.json" \
|
|
503
|
-
&& grep -q 'auto-update.js' "$TMP/.claude/settings.json" \
|
|
504
|
-
&& grep -q 'session-start.js' "$TMP/.claude/settings.json" \
|
|
505
|
-
&& grep -q 'pre-compact.js' "$TMP/.claude/settings.json"; then
|
|
506
|
-
pass "settings.json has all 8 hooks wired"
|
|
507
|
-
else
|
|
508
|
-
fail_case "settings.json missing hooks"
|
|
509
|
-
fi
|
|
510
|
-
|
|
511
|
-
# 34. Lowercase code works (resolveTeamCode normalizes)
|
|
497
|
+
# 33. Lowercase code works (resolveTeamCode normalizes)
|
|
512
498
|
TMP=$(mktmp)
|
|
513
499
|
echo "qs-fawzi-01" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out.log" 2>&1
|
|
514
500
|
EXIT=$?
|