@nqlib/nqui 0.5.6 → 0.6.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/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/theme-appearance-menu.d.ts +9 -0
- package/dist/components/theme-appearance-menu.d.ts.map +1 -0
- package/dist/components/theme-toggle.d.ts +2 -0
- package/dist/components/theme-toggle.d.ts.map +1 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -1
- package/dist/components/ui/toggle-group.d.ts.map +1 -1
- package/dist/elevation-debate.html +286 -0
- package/dist/nqui.cjs.js +15 -13
- package/dist/nqui.es.js +2742 -2637
- package/dist/styles.css +151 -255
- package/docs/components/README.md +2 -1
- package/docs/components/nqui-scroll-area.md +69 -0
- package/docs/nqui-skills/AGENT_PROMPT.md +190 -0
- package/docs/nqui-skills/COMPONENTS_INDEX.md +51 -1
- package/docs/nqui-skills/COMPOSITION.md +321 -0
- package/docs/nqui-skills/ELEVATION.md +154 -0
- package/docs/nqui-skills/EVAL.md +148 -0
- package/docs/nqui-skills/HUMAN_GUIDE.md +18 -0
- package/docs/nqui-skills/MIGRATION.md +133 -0
- package/docs/nqui-skills/MOTION.md +189 -0
- package/docs/nqui-skills/README.md +2 -0
- package/docs/nqui-skills/READ_BUDGET.md +60 -0
- package/docs/nqui-skills/RECIPES.md +735 -0
- package/docs/nqui-skills/SKILL.md +58 -1
- package/docs/nqui-skills/STATES.md +154 -0
- package/docs/nqui-skills/THEMING.md +203 -0
- package/docs/nqui-skills/WRITING.md +205 -0
- package/docs/nqui-skills/adapt/SKILL.md +5 -2
- package/docs/nqui-skills/animate/SKILL.md +5 -2
- package/docs/nqui-skills/audit/SKILL.md +5 -2
- package/docs/nqui-skills/bolder/SKILL.md +5 -2
- package/docs/nqui-skills/clarify/SKILL.md +5 -2
- package/docs/nqui-skills/colorize/SKILL.md +5 -2
- package/docs/nqui-skills/delight/SKILL.md +5 -4
- package/docs/nqui-skills/distill/SKILL.md +5 -2
- package/docs/nqui-skills/impeccable/SKILL.md +0 -16
- package/docs/nqui-skills/impeccable/reference/INDEX.md +26 -0
- package/docs/nqui-skills/layout/SKILL.md +5 -2
- package/docs/nqui-skills/nqui-components/SKILL.md +32 -9
- package/docs/nqui-skills/nqui-composition/SKILL.md +148 -0
- package/docs/nqui-skills/nqui-data-tables/SKILL.md +127 -0
- package/docs/nqui-skills/nqui-design-system/SKILL.md +22 -1
- package/docs/nqui-skills/nqui-install/SKILL.md +1 -0
- package/docs/nqui-skills/overdrive/SKILL.md +5 -2
- package/docs/nqui-skills/polish/SKILL.md +5 -4
- package/docs/nqui-skills/quieter/SKILL.md +5 -2
- package/docs/nqui-skills/shape/SKILL.md +5 -2
- package/docs/nqui-skills/typeset/SKILL.md +5 -2
- package/package.json +2 -1
- package/scripts/cli.js +2 -0
- package/scripts/install-claude-skills.js +109 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nqui-elevation
|
|
3
|
+
description: The 2-surface + spacing hybrid model for elevation. Caps inline nesting at 2 surfaces; past that, spacing + type weight do the work. One additional "elevated" surface reserved for modals/popovers/sheets. Read before designing any layered UI.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# nqui Elevation — The 2+1 Surface Hybrid
|
|
7
|
+
|
|
8
|
+
The single most consequential design-system decision is **how you express elevation** (this-is-deeper-than-that). nqui's answer is the **hybrid model**: 2 alternating inline surfaces + 1 elevated surface for overlays + spacing for everything else.
|
|
9
|
+
|
|
10
|
+
This file is the rule. Apply it to every layered UI.
|
|
11
|
+
|
|
12
|
+
## The model in one diagram
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌─ Surface A (page) ──────────────────────────────┐
|
|
16
|
+
│ │
|
|
17
|
+
│ ┌─ Surface B (card) ─────────────────────────┐ │
|
|
18
|
+
│ │ │ │
|
|
19
|
+
│ │ Section title (uppercase label) │ │
|
|
20
|
+
│ │ ──spacing-- │ │
|
|
21
|
+
│ │ Section content │ │
|
|
22
|
+
│ │ │ │
|
|
23
|
+
│ │ ────── Separator (optional) ────── │ │
|
|
24
|
+
│ │ │ │
|
|
25
|
+
│ │ Another section │ │
|
|
26
|
+
│ │ ──spacing-- │ │
|
|
27
|
+
│ │ Sub-detail (indent + smaller text) │ │
|
|
28
|
+
│ │ │ │
|
|
29
|
+
│ │ ⛔ NO third nested surface │ │
|
|
30
|
+
│ │ │ │
|
|
31
|
+
│ └────────────────────────────────────────────┘ │
|
|
32
|
+
│ │
|
|
33
|
+
└─────────────────────────────────────────────────┘
|
|
34
|
+
|
|
35
|
+
┌─ Surface Elevated (modal / popover) ─┐
|
|
36
|
+
│ Only for overlays, never inline. │
|
|
37
|
+
└──────────────────────────────────────┘
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## The 3 tokens (and only 3)
|
|
41
|
+
|
|
42
|
+
| Token | Tailwind class | When to use |
|
|
43
|
+
|-------|---------------|-------------|
|
|
44
|
+
| `surface-a` | `bg-background` | Page background, alternates with B |
|
|
45
|
+
| `surface-b` | `bg-muted/40` (or `bg-muted/30` for a subtler step) | Cards, panels, regions that need to read as a distinct topic |
|
|
46
|
+
| `surface-elevated` | `bg-popover` + `shadow-lg` | Modals, sheets, popovers, dropdowns — anything physically "above" the page |
|
|
47
|
+
|
|
48
|
+
**These are the only legitimate inline surface tokens.** If you find yourself wanting a fourth, the answer is *not* a new shade — the answer is more spacing, a stronger label, or a different layout.
|
|
49
|
+
|
|
50
|
+
## The depth rule
|
|
51
|
+
|
|
52
|
+
**Maximum 2 inline surfaces. Period.**
|
|
53
|
+
|
|
54
|
+
| Depth | Carrier | Example |
|
|
55
|
+
|-------|---------|---------|
|
|
56
|
+
| Page → card | Surface flip (A → B) | Page is `bg-background`, card is `bg-muted/40` |
|
|
57
|
+
| Inside card → sub-section | **Spacing only** (`gap-6` between sections + uppercase label) | No new surface. `<h3 className="text-xs uppercase tracking-wider">Section</h3>` + content |
|
|
58
|
+
| Section → detail group | **Spacing only** (`gap-4`) or one horizontal `Separator` | No new surface |
|
|
59
|
+
| Sub-detail | **Type weight + indentation** | `text-sm` → `text-xs text-muted-foreground` |
|
|
60
|
+
| Modal over content | `surface-elevated` (the ONLY third allowed) | `Dialog`, `Sheet`, `Popover` |
|
|
61
|
+
|
|
62
|
+
## When you want a third inline surface — the decision tree
|
|
63
|
+
|
|
64
|
+
You almost never want this. When the impulse hits, walk this tree:
|
|
65
|
+
|
|
66
|
+
1. **Is this a genuinely separate topic from its parent?**
|
|
67
|
+
- **No** → use spacing + a label. (Most cases land here.)
|
|
68
|
+
- **Yes** → continue.
|
|
69
|
+
|
|
70
|
+
2. **Could this content move to its own route, sheet, or popover?**
|
|
71
|
+
- **Yes** → move it. The hierarchy will be cleaner.
|
|
72
|
+
- **No** → continue.
|
|
73
|
+
|
|
74
|
+
3. **Does this content benefit from being closed by default?**
|
|
75
|
+
- **Yes** → use `Accordion` or `Collapsible`. The closed state IS the separation; no surface needed.
|
|
76
|
+
- **No** → continue.
|
|
77
|
+
|
|
78
|
+
4. **Are you building a stacked-modal or genuinely physical depth metaphor?**
|
|
79
|
+
- **Yes** → use `surface-elevated`. This is the legitimate exception.
|
|
80
|
+
- **No** → STOP. Flatten. Your hierarchy is over-engineered.
|
|
81
|
+
|
|
82
|
+
## What spacing/typography do instead of color
|
|
83
|
+
|
|
84
|
+
The discipline is: **make spacing, weight, and indentation do the work that color was doing**. The hierarchy is preserved; it just expresses through different signals.
|
|
85
|
+
|
|
86
|
+
| Old (color-based) | New (space + type) |
|
|
87
|
+
|-------------------|-------------------|
|
|
88
|
+
| Nested card with `bg-muted/60` | `<section className="mt-6"><h3 className="text-xs uppercase">Title</h3>...</section>` |
|
|
89
|
+
| Card-inside-card with border | Outer card + horizontal `Separator` between sections |
|
|
90
|
+
| Three-color metadata strip | One surface, columns laid out with `gap-6`, label text in muted color |
|
|
91
|
+
| Deep nested settings groups | `<FieldSet>` with `<FieldLegend>` — no extra surface |
|
|
92
|
+
|
|
93
|
+
**Spacing scale to use as the hierarchy signal:**
|
|
94
|
+
- `gap-2` (8px) — within a row, between tight related elements (label + value)
|
|
95
|
+
- `gap-4` (16px) — between fields in a form, between cards in a grid
|
|
96
|
+
- `gap-6` (24px) — between sub-sections within a single surface
|
|
97
|
+
- `gap-8` (32px) — between major sections within a surface
|
|
98
|
+
- `gap-12` (48px) — between top-level page regions
|
|
99
|
+
|
|
100
|
+
The *variation* of spacing IS the hierarchy. Don't make everything the same gap.
|
|
101
|
+
|
|
102
|
+
## When continuous (5-shade) elevation is the right call
|
|
103
|
+
|
|
104
|
+
This rule has principled exceptions. Continuous is correct when:
|
|
105
|
+
|
|
106
|
+
1. **Stacked modals** — modal opening from modal. Each must feel physically above. (iOS does this; macOS does this.)
|
|
107
|
+
2. **Bloomberg-class data UIs** — when you genuinely have 4+ semantic depth levels and density is the dominant design constraint. Most products don't have this.
|
|
108
|
+
3. **File-system metaphors** — where depth IS the content (Finder columns, tree views with visualized hierarchy).
|
|
109
|
+
|
|
110
|
+
If your screen isn't one of these three, you don't need continuous.
|
|
111
|
+
|
|
112
|
+
## When single-surface (no nesting) is the right call
|
|
113
|
+
|
|
114
|
+
Even more refined than 2-surface alternation:
|
|
115
|
+
|
|
116
|
+
1. **Focus-mode editors** — iA Writer, Apple Notes detail, Things 3 task detail. No nesting at all.
|
|
117
|
+
2. **Reading views** — long-form text, documentation, blog posts.
|
|
118
|
+
3. **Single-concept screens** — a page that does one thing, deeply.
|
|
119
|
+
|
|
120
|
+
For these, ONE surface + generous spacing + careful typography is the most refined option. Use it when the content's job is sustained attention rather than navigation between sub-regions.
|
|
121
|
+
|
|
122
|
+
## Anti-patterns this rule eliminates
|
|
123
|
+
|
|
124
|
+
| ❌ Old pattern | ✅ Hybrid pattern |
|
|
125
|
+
|---------------|-------------------|
|
|
126
|
+
| `<Card><Card><Card>...</Card></Card></Card>` (nested) | Outer Card → sections separated by `gap-6` + label, no inner cards |
|
|
127
|
+
| 5-token elevation scale (`bg-1` through `bg-5`) | 2 alternating + 1 elevated, total 3 tokens |
|
|
128
|
+
| "Slightly darker shade for the sub-region" | More space, uppercase label, no shade |
|
|
129
|
+
| `border-l-4 border-accent` on a sub-section | Uppercase label + spacing |
|
|
130
|
+
| `shadow-md` on a flat inline card | No shadow — shadows are for elevated surfaces only |
|
|
131
|
+
| `bg-muted/20` inside `bg-muted/40` inside `bg-background` | `bg-background` (A) → `bg-muted/40` (B) → spacing for the third level |
|
|
132
|
+
|
|
133
|
+
## The check before shipping any layered UI
|
|
134
|
+
|
|
135
|
+
Walk the screen from outside in. Count surface changes.
|
|
136
|
+
|
|
137
|
+
- [ ] Did I use more than 2 distinct inline surface colors? If yes, collapse to 2.
|
|
138
|
+
- [ ] Did I nest a card inside a card inside a card? If yes, flatten one level.
|
|
139
|
+
- [ ] Did I add a shade just because "this section needed to feel distinct"? If yes, use spacing + label instead.
|
|
140
|
+
- [ ] Is my elevated surface (Modal/Sheet/Popover) using `shadow-lg` + `bg-popover`? If not, fix.
|
|
141
|
+
- [ ] Are my section gaps varied (some `gap-4`, some `gap-6`, some `gap-8`)? If everything is the same, the hierarchy is monotonous.
|
|
142
|
+
|
|
143
|
+
## Why this works (the principle)
|
|
144
|
+
|
|
145
|
+
Color is a heavy signal. The eye uses it for category ("this is selected", "this is destructive", "this is a warning"). When you use color to signal **depth**, you're spending a category-signaling slot on a hierarchy job that spacing handles for free.
|
|
146
|
+
|
|
147
|
+
The hybrid model frees color to do what color is best at:
|
|
148
|
+
- Brand/accent: one primary
|
|
149
|
+
- Semantic: success / warning / error
|
|
150
|
+
- State: selected / hover / focus
|
|
151
|
+
|
|
152
|
+
And spacing does what spacing is best at: rhythm, grouping, breathing room — which is what makes UI feel calm and intentional.
|
|
153
|
+
|
|
154
|
+
This is why Linear, Things 3, Apple Notes feel different from Material Design dashboards. Not because they have prettier colors. Because they spent fewer signals on hierarchy, leaving more bandwidth for the content.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nqui-eval
|
|
3
|
+
description: Eval set + grading rubric for AI agents using nqui. Run before each release. The skills folder is theory until the eval validates it produces consistently good output. Without this, nqui is documentation, not infrastructure.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# nqui Eval — How We Measure "Production AI Ready"
|
|
7
|
+
|
|
8
|
+
The skills folder claims that AI agents using nqui produce reliably good output. **Without an eval, that's an unfalsifiable claim.** This file is the eval set + grading rubric. Run it before any release that touches tokens, components, or skills.
|
|
9
|
+
|
|
10
|
+
## How to run
|
|
11
|
+
|
|
12
|
+
1. Load **only** the nqui skills folder + `AGENT_PROMPT.md` into a fresh agent context (Claude API, GPT-4, etc.)
|
|
13
|
+
2. Send each prompt below verbatim, in a fresh conversation
|
|
14
|
+
3. Let the agent build the code (no follow-up clarifying questions allowed in the eval — measure cold output)
|
|
15
|
+
4. Score using the rubric below
|
|
16
|
+
5. Track per-prompt scores over time. Regression = something broke.
|
|
17
|
+
|
|
18
|
+
## Grading rubric (per prompt, max 10)
|
|
19
|
+
|
|
20
|
+
| Category | Max | What to check |
|
|
21
|
+
|----------|-----|---------------|
|
|
22
|
+
| **Composition** | 2 | Right named pattern picked? Surface cap respected? Three-surface rule followed? |
|
|
23
|
+
| **State coverage** | 2 | All required states designed? Empty / loading / error present where needed? |
|
|
24
|
+
| **Component selection** | 1 | Right component for the situation? (Dialog vs AlertDialog, ToggleGroup vs RadioGroup, etc.) |
|
|
25
|
+
| **Writing** | 1 | Copy specific (not "Submit" / "Are you sure?")? No exclamation marks? Sentence case? |
|
|
26
|
+
| **Motion** | 1 | Restrained (default: none)? No banned curves? `prefers-reduced-motion` respected? |
|
|
27
|
+
| **Anti-patterns avoided** | 2 | Zero from the 10 hard-banned (nested cards, gradient text, hero metric template, etc.)? |
|
|
28
|
+
| **Code quality** | 1 | Readable, uses kit conventions, no custom CSS where component exists? |
|
|
29
|
+
|
|
30
|
+
**Pass threshold: 8/10 average across all prompts. Below 7 average = block release.**
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## The eval set (12 prompts)
|
|
35
|
+
|
|
36
|
+
Each prompt simulates a real user request that the kit is expected to handle. Coverage spans the long tail of common product surfaces.
|
|
37
|
+
|
|
38
|
+
### 1. Auth: login form
|
|
39
|
+
> Build a login screen with email + password. Include "forgot password?" link and a primary sign-in button. The user should see clear error messages if auth fails.
|
|
40
|
+
|
|
41
|
+
**Looks for:** `FieldSet` + `Field` + `Input` + `FieldError`. Sentence-case labels. Specific error copy ("Incorrect email or password" not "Auth failed"). One primary button + `outline` for secondary. Loading state on submit button. NO `Dialog` (this is a page).
|
|
42
|
+
|
|
43
|
+
### 2. Auth: OAuth + email signup
|
|
44
|
+
> Build a signup page that offers Google OAuth and email signup. Show a divider between OAuth providers and the email form.
|
|
45
|
+
|
|
46
|
+
**Looks for:** OAuth buttons as `variant="outline"` with provider name + brand-name (no `Submit`). `Separator` with text label. Email form uses same `Field` pattern as login. Reasonable security copy ("We'll never share your email").
|
|
47
|
+
|
|
48
|
+
### 3. Settings: account preferences
|
|
49
|
+
> Build an account settings page with: display name, email, time zone selector, weekly digest toggle, two-factor toggle, change password button, danger zone (delete account).
|
|
50
|
+
|
|
51
|
+
**Looks for:** `FieldSet` + `FieldGroup`. `Switch` for toggles. `Select` for time zone. Danger zone uses `AlertDialog` for delete confirmation with SPECIFIC stakes (not "Are you sure?"). Surface cap respected — no card per field.
|
|
52
|
+
|
|
53
|
+
### 4. Dashboard: deploy/build monitor
|
|
54
|
+
> Build a deployment dashboard showing recent deploys with status (building, success, failed), commit info, and a detail panel when one is selected.
|
|
55
|
+
|
|
56
|
+
**Looks for:** Master-detail pattern. Responsive switch to `Sheet` below 900px. `Tracker` for build history visualization. `Badge` for status. Real status copy ("Building" / "Failed" not "Status 1"). Empty + loading states for the list.
|
|
57
|
+
|
|
58
|
+
### 5. Billing: pricing + checkout
|
|
59
|
+
> Build a pricing page with 3 tiers (Free / Pro / Team). Selecting a paid tier opens a Sheet with order summary and a payment form (card number, expiry, CVC).
|
|
60
|
+
|
|
61
|
+
**Looks for:** Pricing cards with ONE recommended (subtle emphasis, not screaming). `Sheet` for checkout (long form). `Field` + `InputOTP` or formatted `Input`. Specific button copy ("Pay $49/month" not "Submit"). Real-feeling totals.
|
|
62
|
+
|
|
63
|
+
### 6. Search: filterable list
|
|
64
|
+
> Build a project search page with a search box, status filter (open/closed/all), assignee filter, and results list. Show "no results" state when nothing matches.
|
|
65
|
+
|
|
66
|
+
**Looks for:** Search input with leading icon. `ToggleGroup` for status (NOT RadioGroup). `Combobox` for assignee. Filter container uses `bg-muted/30`. `Empty` w/ "clear filter" action (not just apology). Result count + sort.
|
|
67
|
+
|
|
68
|
+
### 7. File upload: multi-file with progress
|
|
69
|
+
> Build a file uploader that accepts multiple files via drag-and-drop or file picker, shows per-file progress, and handles errors per file (e.g., file too large).
|
|
70
|
+
|
|
71
|
+
**Looks for:** Drop zone with active state on dragover. Per-file `Progress`. Per-file status (uploading/success/error). Specific error messages per file. Remove button per file. No alarming language for fails.
|
|
72
|
+
|
|
73
|
+
### 8. Data table: sortable + paginated
|
|
74
|
+
> Build a customers data table with columns: name, email, plan, signup date, MRR. Make it sortable, paginated, with row selection for bulk actions.
|
|
75
|
+
|
|
76
|
+
**Looks for:** `DataTable` with header sort indicators. Sticky header on scroll. Bulk action bar appears on selection (sticky pill at bottom). `Pagination` component. Real-feeling column widths.
|
|
77
|
+
|
|
78
|
+
### 9. Onboarding: 4-step wizard
|
|
79
|
+
> Build a 4-step onboarding wizard: workspace name → invite teammates → choose integrations → review and start. Show progress, allow back navigation, validate each step.
|
|
80
|
+
|
|
81
|
+
**Looks for:** `Progress` or step indicator. ONE primary action per step ("Continue" / "Skip for now" / "Start"). Back is `variant="outline"`. Validation prevents advance. `Alert` for step errors. Centered single card on each step.
|
|
82
|
+
|
|
83
|
+
### 10. Notification preferences
|
|
84
|
+
> Build a notification settings page where the user can choose, per category (mentions, comments, weekly digest, security alerts), whether to receive Email / Push / Slack — like a matrix.
|
|
85
|
+
|
|
86
|
+
**Looks for:** Table-like or grid layout with `Switch` or `Checkbox` per cell. Clear column headers. Row labels explain each category. "Apply to all" affordance per row. No nested cards.
|
|
87
|
+
|
|
88
|
+
### 11. Chat / message thread
|
|
89
|
+
> Build a chat interface with a message list (showing user vs assistant alternation), an input at the bottom with send button, and a typing indicator when the assistant is responding.
|
|
90
|
+
|
|
91
|
+
**Looks for:** Distinct visual for user vs assistant (NOT just color — also alignment or avatar). `Item` or custom message block. Auto-scroll to bottom. Loading dots while assistant responds. Send on Enter (Shift+Enter for newline).
|
|
92
|
+
|
|
93
|
+
### 12. Error / empty page
|
|
94
|
+
> Build a 404 page with a clear message, helpful next actions (go home, search, contact), and the site's normal navigation.
|
|
95
|
+
|
|
96
|
+
**Looks for:** `Empty` component or similar. Specific, friendly copy ("This page moved or was deleted") not "404 Not Found". 2-3 actions, ONE primary. Page chrome still present (user isn't stranded).
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Common failure modes to watch for
|
|
101
|
+
|
|
102
|
+
These are the patterns the eval should catch reliably. If multiple prompts surface the same failure, fix the underlying skill/recipe.
|
|
103
|
+
|
|
104
|
+
| Failure mode | Symptom in output | Fix in |
|
|
105
|
+
|--------------|------------------|--------|
|
|
106
|
+
| Three-surface nesting | `<Card><Card><Card>` or `bg-muted/60` inside `bg-muted/40` | `ELEVATION.md`, lint rule |
|
|
107
|
+
| Empty state forgotten | List renders blank when 0 items | `STATES.md`, component template |
|
|
108
|
+
| Generic copy | "Submit", "An error occurred", "Are you sure?" | `WRITING.md` |
|
|
109
|
+
| Bouncy/elastic motion | overshoot easing on any element | `MOTION.md` |
|
|
110
|
+
| Wrong component | RadioGroup in toolbar, Dialog for destructive confirm | `COMPONENTS_INDEX.md` decision tables |
|
|
111
|
+
| Side-stripe status | `border-l-4 border-yellow-500` | `RECIPES.md` Status Pill recipe |
|
|
112
|
+
| Two primary buttons | Two `variant="default"` Buttons in one surface | `nqui-composition/SKILL.md` |
|
|
113
|
+
| Wall-of-cards layout | every section wrapped in Card | `COMPOSITION.md` |
|
|
114
|
+
| Tooltip-as-label | icon button with only Tooltip context | A11y rule in `AGENT_PROMPT.md` |
|
|
115
|
+
| Custom CSS where component exists | hand-rolled dropdown instead of `Select` | `AGENT_PROMPT.md` "never reach outside the kit" |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Running the eval — what to do with results
|
|
120
|
+
|
|
121
|
+
After running 12 prompts:
|
|
122
|
+
|
|
123
|
+
1. **Sum scores.** Average ≥ 8 = ship. 7-8 = warn, fix obvious gaps. < 7 = block release.
|
|
124
|
+
2. **Per-prompt analysis.** Which prompts scored low? Each low score points to a missing recipe or unclear rule.
|
|
125
|
+
3. **Failure clustering.** If 5 prompts all forgot empty states, the issue is STATES.md isn't being read — restructure the routing in `READ_BUDGET.md`.
|
|
126
|
+
4. **Anti-pattern leak detection.** If "no nested cards" was violated in 3+ prompts, add a lint rule, don't just update docs.
|
|
127
|
+
5. **Trend over time.** Re-run after token changes / new components / doc rewrites. Did the score go up? Down? That's the signal.
|
|
128
|
+
|
|
129
|
+
## When to run
|
|
130
|
+
|
|
131
|
+
- **Before any release** that touches: tokens, component APIs, skills folder structure, AGENT_PROMPT
|
|
132
|
+
- **After any breaking visual change** (we just shipped Card-no-shadow and chart palette flip — should have re-run)
|
|
133
|
+
- **Monthly** as a baseline regression check
|
|
134
|
+
- **Before adding a new component** — make sure existing patterns still work
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## What this eval does NOT measure
|
|
139
|
+
|
|
140
|
+
- Performance (runtime, bundle size) — separate concern
|
|
141
|
+
- Accessibility audits (run axe-core separately)
|
|
142
|
+
- Cross-browser rendering (visual regression suite)
|
|
143
|
+
- Long-form copy quality (eval is short prompts)
|
|
144
|
+
- Multi-page app coherence (eval is per-screen)
|
|
145
|
+
|
|
146
|
+
These are valuable. They're not in scope here. The eval measures: **"given a prompt, does the agent produce something a Linear designer wouldn't be embarrassed by?"**
|
|
147
|
+
|
|
148
|
+
That's the bar nqui ships to.
|
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
Use this file for **wayfinding** before diving into `README.md` (long) or dozens of `nqui-*.md` files. Paths below are from the **nqui package**; after `npx @nqlib/nqui init-skills`, the same files live under **`.cursor/nqui-skills/components/`**.
|
|
4
4
|
|
|
5
|
+
## Composition first (product UI)
|
|
6
|
+
|
|
7
|
+
**Read:** [`COMPOSITION.md`](./COMPOSITION.md) — when to use Card, ToggleGroup vs RadioGroup, three-surface cap, anti-patterns.
|
|
8
|
+
|
|
9
|
+
**Dev app (`npm run dev` in `packages/nqui`):**
|
|
10
|
+
|
|
11
|
+
| Route | Purpose |
|
|
12
|
+
|-------|---------|
|
|
13
|
+
| `/` | **Recipes hub** — start here |
|
|
14
|
+
| `/patterns` | Commerce dashboard recipe |
|
|
15
|
+
| `/recipes/settings` | Settings form recipe |
|
|
16
|
+
| `/catalog` | Component variant catalog |
|
|
17
|
+
| `/design-system` | Tokens |
|
|
18
|
+
|
|
19
|
+
Do **not** learn layout from the catalog alone — it is API reference, not composition guidance.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
5
23
|
| I’m building… | Start here | Then open |
|
|
6
24
|
|---------------|------------|-----------|
|
|
7
25
|
| **Forms** (login, settings, checkout) | `docs/components/README.md` → Forms sections | One of `nqui-field.md`, `nqui-input.md`, `nqui-select.md`, `nqui-combobox.md`, … |
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nqui-migration
|
|
3
|
+
description: Breaking changes consumers must be aware of when updating. Each entry includes what changed, why, and how to migrate. Keep this file additive — never edit historical entries, only append new ones.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# nqui Migration Notes
|
|
7
|
+
|
|
8
|
+
When upgrading nqui, scan the latest version section for breaking changes BEFORE running `pnpm install`. Visual regressions disguise themselves as "the new design"; they aren't.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## v0.6 (current) — Design system rebuild
|
|
13
|
+
|
|
14
|
+
This release applies the **2+1 elevation rule** to the token system, fixes several real bugs in color tokens, and tightens motion. Most changes are visual — runtime behavior is unchanged.
|
|
15
|
+
|
|
16
|
+
### 🔴 Breaking visual changes
|
|
17
|
+
|
|
18
|
+
#### 1. Card lost its default shadow
|
|
19
|
+
**What:** `.nqui-card` no longer applies `box-shadow`. Card surfaces now rely on border + background contrast for separation.
|
|
20
|
+
|
|
21
|
+
**Why:** The 2+1 elevation rule (see `ELEVATION.md`) reserves shadows for ELEVATED surfaces only (Dialog, Sheet, Popover, DropdownMenu). Inline cards with shadows produced template-y, decorative depth that competed with content.
|
|
22
|
+
|
|
23
|
+
**Migration:**
|
|
24
|
+
- If your app depends on Card lift, wrap with `.nqui-elevated` instead — but ask first whether you actually need elevation. Most "needs a shadow" instincts are better solved with `bg-muted/40` (surface B).
|
|
25
|
+
- If you have custom CSS adding shadow to Card, remove it — that's now the system default.
|
|
26
|
+
|
|
27
|
+
#### 2. Chart palette changed from 5-blues to categorical
|
|
28
|
+
**What:** `--chart-1` through `--chart-5` used to be 5 shades of blue (hue 251-265). Now they're distinct hues: blue / orange / green / purple / amber.
|
|
29
|
+
|
|
30
|
+
**Why:** 5 shades of blue is useless for categorical data — you can't tell series apart. The new palette is for *categorical* (different categories, different statuses). For *sequential* (ordinal intensity) data, override per the `THEMING.md` guide.
|
|
31
|
+
|
|
32
|
+
**Migration:**
|
|
33
|
+
- Audit any chart that depended on the old blue palette. Visualizations may look completely different now.
|
|
34
|
+
- If you specifically need sequential blues, override `--chart-*` in your project (see `THEMING.md` → Chart palette).
|
|
35
|
+
- This may shift the visual identity of dashboards — review screenshots before deploying.
|
|
36
|
+
|
|
37
|
+
#### 3. Light mode sidebar flipped direction
|
|
38
|
+
**What:** `--sidebar` in light mode was `oklch(0.953)` (DARKER than the `0.982` page background). Now `oklch(0.988)` (LIGHTER than page).
|
|
39
|
+
|
|
40
|
+
**Why:** macOS, Linear, Vercel all do "sidebar lighter than page" — the sidebar lifts forward. Dark mode already did this. Light mode was inverted. Now they match.
|
|
41
|
+
|
|
42
|
+
**Migration:**
|
|
43
|
+
- Sidebar will look subtly different — lighter, more lifted. Verify your custom sidebar content still has enough contrast.
|
|
44
|
+
- `--sidebar-accent` and `--sidebar-border` also adjusted to remain visible against the lighter sidebar.
|
|
45
|
+
|
|
46
|
+
#### 4. Accent token darker in light mode
|
|
47
|
+
**What:** `--accent` was `oklch(0.895)`, lighter than `--muted` (`0.914`). Now `oklch(0.880)`, properly darker for hover/selection contrast.
|
|
48
|
+
|
|
49
|
+
**Why:** Real bug — accent was LIGHTER than the muted surface, so hover states *reduced* contrast instead of increasing it. Now hover is properly visible.
|
|
50
|
+
|
|
51
|
+
**Migration:**
|
|
52
|
+
- Hover and selection states on Items, list rows, etc. will appear darker than before. This is correct behavior. Don't override back to the old value.
|
|
53
|
+
|
|
54
|
+
#### 5. Warning hue shifted (65 → 80)
|
|
55
|
+
**What:** Warning was OKLCH hue 65 (olive/khaki, despite the comment claiming "orange"). Now hue 80 (true amber).
|
|
56
|
+
|
|
57
|
+
**Why:** Bug. Warning badges and alerts looked slightly sickly. Now they read as amber/warning.
|
|
58
|
+
|
|
59
|
+
**Migration:**
|
|
60
|
+
- Visual shift, no API change. Verify warning states still read as intended in your theme.
|
|
61
|
+
|
|
62
|
+
#### 6. Success hue shifted (142 → 135)
|
|
63
|
+
**What:** Subtle 7° hue shift to harmonize with warm-paper background.
|
|
64
|
+
|
|
65
|
+
**Why:** Hue 142 was cool emerald — clashed with the warm light-mode palette. Hue 135 is a slightly warmer green that fits.
|
|
66
|
+
|
|
67
|
+
**Migration:**
|
|
68
|
+
- Subtle. Existing green badges will look very slightly warmer. No action needed unless you've customized success colors.
|
|
69
|
+
|
|
70
|
+
#### 7. Dark mode border softened (0.34 → 0.27)
|
|
71
|
+
**What:** `--border` in dark mode was `oklch(0.34)` (drawn lines). Now `oklch(0.27)` (subtle folds).
|
|
72
|
+
|
|
73
|
+
**Why:** Match Linear / Vercel dark-mode convention. Borders should read as "where surfaces meet," not as drawn lines.
|
|
74
|
+
|
|
75
|
+
**Migration:**
|
|
76
|
+
- Dark mode borders will be more subtle. Verify forms and panels still have enough definition. If a specific surface NEEDS a stronger border, opt in per-component.
|
|
77
|
+
|
|
78
|
+
#### 8. Semantic foreground tokens aligned with `--foreground`
|
|
79
|
+
**What:** `--primary-foreground`, `--success-foreground`, `--destructive-foreground`, etc. were all `oklch(0.98)` (near-pure white). Now `oklch(0.92)` (matching the global softened foreground).
|
|
80
|
+
|
|
81
|
+
**Why:** Eye-strain consistency. 0.98 white on saturated backgrounds was harsh.
|
|
82
|
+
|
|
83
|
+
**Migration:**
|
|
84
|
+
- Text on colored buttons (primary/destructive/success) is now slightly softer. If contrast looks too low against a custom brand color, re-verify per `THEMING.md` checklist.
|
|
85
|
+
|
|
86
|
+
#### 9. Tooltip z-index corrected (9998 → 70)
|
|
87
|
+
**What:** `--z-tooltip` was `9998` (extreme value, fighting other systems). Now `70` (matches documented scale).
|
|
88
|
+
|
|
89
|
+
**Why:** Was a patch that never got reconciled. The actual stacking scale documents tooltip at 70.
|
|
90
|
+
|
|
91
|
+
**Migration:**
|
|
92
|
+
- If you have custom z-index values above 70 that depended on tooltip being at 9998 to overlay them, they will now incorrectly cover tooltips. Refactor to use the `--z-*` semantic scale.
|
|
93
|
+
|
|
94
|
+
#### 10. Motion: checkbox bounce removed
|
|
95
|
+
**What:** Checkbox `transition` was `cubic-bezier(0.68, -0.55, 0.265, 1.55)` (elastic overshoot). Now standard ease-out.
|
|
96
|
+
|
|
97
|
+
**Why:** Elastic/overshoot curves are explicitly banned per `MOTION.md`. They feel dated and AI-flavored.
|
|
98
|
+
|
|
99
|
+
**Migration:**
|
|
100
|
+
- Checkboxes now snap rather than bounce. This is intentional. If you preferred the bounce, you preferred the wrong thing.
|
|
101
|
+
|
|
102
|
+
### 🟠 Internal restructuring (no API change, may affect overrides)
|
|
103
|
+
|
|
104
|
+
- `src/styles/elevation.css` renamed to `src/styles/z-index.css` — the file is z-index tokens only, not surface depth. Elevation now lives in `ELEVATION.md` (philosophy) + `shadows.css` (shadow tokens).
|
|
105
|
+
- New file `src/styles/motion.css` — duration + easing tokens + `prefers-reduced-motion` media query.
|
|
106
|
+
- New file `src/styles/shadows.css` — `--shadow-elevated`, `--shadow-modal`, `--shadow-focus` tokens.
|
|
107
|
+
- New semantic surface aliases: `--surface-a`, `--surface-b`, `--surface-elevated` (alias the existing `--background`, `--muted`, `--popover`).
|
|
108
|
+
- `--popover` is now `var(--card)` (was a duplicated value pretending to be a separate token).
|
|
109
|
+
- Sheet transitions changed from `duration-500`/`duration-300` to `duration-250`/`duration-200` for tighter feel.
|
|
110
|
+
- Navigation menu chevron animation: `duration-300` → `duration-200`.
|
|
111
|
+
- Enhanced progress bar: `duration-300` → `duration-200`.
|
|
112
|
+
|
|
113
|
+
### ✅ Additive (not breaking)
|
|
114
|
+
|
|
115
|
+
- New skill: `nqui-composition/SKILL.md` — Apple-inspired craft principles.
|
|
116
|
+
- New skill docs: `RECIPES.md`, `STATES.md`, `WRITING.md`, `MOTION.md`, `ELEVATION.md`, `READ_BUDGET.md`, `AGENT_PROMPT.md`, `EVAL.md`, `THEMING.md`, `MIGRATION.md` (this file).
|
|
117
|
+
- New showcase routes: `/recipes/tracker`, `/recipes/elevation`.
|
|
118
|
+
- Updated `SKILL.md` Agent Build Protocol (now 10 steps, plus the "30-second Linear designer test").
|
|
119
|
+
|
|
120
|
+
### Recommended migration path
|
|
121
|
+
|
|
122
|
+
1. **Read the breaking changes above.** Identify which apply to your app.
|
|
123
|
+
2. **Run your visual regression suite** if you have one. (If you don't — this release is a good reason to set one up.)
|
|
124
|
+
3. **Audit chart-using pages first** — palette change is the most visible.
|
|
125
|
+
4. **Audit Card-using pages** — shadow removal may reveal places that depended on lift.
|
|
126
|
+
5. **Verify dark mode** — multiple dark token changes, easier to miss than light.
|
|
127
|
+
6. **Run the eval** (see `EVAL.md`) against your most important pages — make sure the kit still produces good output for your common patterns.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Earlier versions
|
|
132
|
+
|
|
133
|
+
(Append entries for prior versions above this line, newest first, as future releases land.)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nqui-motion
|
|
3
|
+
description: Motion design as a system, not decoration. Easing vocabulary, timing scale, choreography, reduced-motion. Use when adding ANY animation or transition. Without this, motion is either absent (cold) or busy (chaotic).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# nqui Motion — Choreography, Not Decoration
|
|
7
|
+
|
|
8
|
+
Motion is design. It either serves comprehension (showing where something came from, conveying hierarchy, providing feedback) or it's noise. This file is the vocabulary.
|
|
9
|
+
|
|
10
|
+
## Three motion principles
|
|
11
|
+
|
|
12
|
+
1. **Motion has a job.** Every animation answers one of: *Where did this come from? Where did it go? What changed? What's happening right now?* If you can't answer, cut the animation.
|
|
13
|
+
2. **Motion respects continuity.** Things appear from their source (Sheet slides from edge, Popover scales from its trigger, Toast slides from the toast region — never from random space).
|
|
14
|
+
3. **Motion gets out of the way.** A confirmation animation that takes 600ms means the user waits 600ms before moving on. Make motion fast unless it's communicating something significant.
|
|
15
|
+
|
|
16
|
+
## The timing scale
|
|
17
|
+
|
|
18
|
+
Use named tokens, not arbitrary milliseconds. These cover 95% of UI motion.
|
|
19
|
+
|
|
20
|
+
| Token | Duration | When to use |
|
|
21
|
+
|-------|----------|-------------|
|
|
22
|
+
| **instant** | 0ms (no animation) | State toggles where motion would be a delay (`checked`, `disabled`) |
|
|
23
|
+
| **micro** | 100ms | Hover states, focus rings, color shifts on press |
|
|
24
|
+
| **quick** | 150ms | Most state changes: opening dropdowns, button presses, small reveals |
|
|
25
|
+
| **standard** | 200-250ms | Modal/Sheet/Drawer entries, page transitions, accordion expansions |
|
|
26
|
+
| **dramatic** | 300-400ms | One-off attention moments — initial empty state appearing, success confirmation, onboarding cues |
|
|
27
|
+
| **never** | ≥500ms | Almost nothing. If you reach for 500ms+, you're decorating, not communicating |
|
|
28
|
+
|
|
29
|
+
**Default if unsure:** 150ms (`quick`). Most UI motion should be this fast.
|
|
30
|
+
|
|
31
|
+
## The easing vocabulary
|
|
32
|
+
|
|
33
|
+
Easing curves communicate **physicality** — they tell the eye whether something is decelerating naturally (good) or moving mechanically (bad).
|
|
34
|
+
|
|
35
|
+
| Curve | Tailwind / CSS | When |
|
|
36
|
+
|-------|---------------|------|
|
|
37
|
+
| **ease-out** (`cubic-bezier(0.16, 1, 0.3, 1)`) | `ease-out` | **Default for entrances.** Things decelerate as they arrive — feels natural |
|
|
38
|
+
| **ease-in** (`cubic-bezier(0.4, 0, 1, 1)`) | `ease-in` | **Default for exits.** Things accelerate as they leave — out of the way |
|
|
39
|
+
| **ease-in-out** | `ease-in-out` | Reserved for symmetric back-and-forth (toggle that animates same way both directions) |
|
|
40
|
+
| **linear** | `linear` | Only for continuous motion (loading bar, spinner, marquee) — never for state changes |
|
|
41
|
+
| **spring** (react-spring / framer) | — | Only when modeling physical objects (drag, drawer pull). Subtle, not bouncy |
|
|
42
|
+
|
|
43
|
+
**Banned curves:** elastic, bounce, anything with overshoot > 10%. They look dated and tacky. Real objects decelerate smoothly; they don't sproing.
|
|
44
|
+
|
|
45
|
+
## Animate this, never animate that
|
|
46
|
+
|
|
47
|
+
| ✅ Animate | ❌ Never animate |
|
|
48
|
+
|------------|------------------|
|
|
49
|
+
| `opacity` | `width` / `height` (use grid-row tricks if you need height transition) |
|
|
50
|
+
| `transform` (translate, scale, rotate) | `top` / `left` / `right` / `bottom` |
|
|
51
|
+
| `filter: blur()` (sparingly) | `padding` / `margin` |
|
|
52
|
+
| `background-color` (subtle, fast) | `box-shadow` (very expensive — use opacity on a shadow layer instead) |
|
|
53
|
+
| `color` | `font-size` |
|
|
54
|
+
| CSS variables (modern, GPU-friendly) | Layout properties that cause reflow |
|
|
55
|
+
|
|
56
|
+
**Why:** transform and opacity are GPU-composited and free. Layout properties cause reflow and jank, especially during scroll.
|
|
57
|
+
|
|
58
|
+
## What to animate, when
|
|
59
|
+
|
|
60
|
+
### Entrance / exit (mount, unmount)
|
|
61
|
+
- **Modal/Dialog:** scale from 95% + fade in, 200ms ease-out
|
|
62
|
+
- **Sheet:** translate from off-screen edge, 250ms ease-out (in), 200ms ease-in (out)
|
|
63
|
+
- **Popover/Tooltip:** scale from 95% + fade, 150ms ease-out, origin at trigger
|
|
64
|
+
- **Toast:** slide in from toast region (usually bottom or top-right), 200ms ease-out
|
|
65
|
+
- **Empty state on first appearance:** fade in + translate up 4px, 300ms ease-out
|
|
66
|
+
- **List items appearing:** stagger 30ms per item, fade + translate up 8px each, ease-out
|
|
67
|
+
|
|
68
|
+
### State changes
|
|
69
|
+
- **Selection (item in a list):** background color shift, 150ms
|
|
70
|
+
- **Button press:** scale 0.97 for 100ms, then back — micro-affordance
|
|
71
|
+
- **Hover:** background or color shift, 100ms — never transform
|
|
72
|
+
- **Focus ring:** instant appear, fade out at 150ms
|
|
73
|
+
|
|
74
|
+
### Attention direction (use sparingly)
|
|
75
|
+
- **Skeleton → real content:** crossfade 150ms (avoid the "snap")
|
|
76
|
+
- **Optimistic item appearing:** fade in at 70% opacity, then to 100% when confirmed
|
|
77
|
+
- **Error shake:** translate x ±4px, 4 cycles of 50ms each — only for form submission errors with severity
|
|
78
|
+
|
|
79
|
+
### Continuous (loading)
|
|
80
|
+
- **Spinner:** 1 full rotation per 900ms, linear
|
|
81
|
+
- **Skeleton shimmer:** 1.5s loop, ease-in-out, very subtle (8-12% opacity range)
|
|
82
|
+
- **Loading bar (indeterminate):** 1.5s loop, ease-in-out
|
|
83
|
+
- **Progress bar (determinate):** transition `width` change with 200ms ease-out (this is one of the few times width animation is OK because it represents real progress)
|
|
84
|
+
|
|
85
|
+
## Choreography — when multiple things move
|
|
86
|
+
|
|
87
|
+
Bad: everything animates at once → chaos.
|
|
88
|
+
Good: a clear sequence the eye can follow.
|
|
89
|
+
|
|
90
|
+
### Stagger
|
|
91
|
+
When N similar things appear (list items, cards, nav items), animate them sequentially with a small delay (20-40ms between each). Stagger creates a sense of rhythm and avoids the "everything pops at once" feel.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
{items.map((item, i) => (
|
|
95
|
+
<div
|
|
96
|
+
key={item.id}
|
|
97
|
+
style={{ animationDelay: `${i * 30}ms` }}
|
|
98
|
+
className="animate-fade-in-up"
|
|
99
|
+
>
|
|
100
|
+
...
|
|
101
|
+
</div>
|
|
102
|
+
))}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Cap stagger at ~8 items.** Beyond that, the last items appear so late it feels broken. For larger lists, skip the stagger or use a much smaller delay (10ms).
|
|
106
|
+
|
|
107
|
+
### Sequence (different things, in order)
|
|
108
|
+
When a complex element appears, parts can arrive in sequence — but only when sequence conveys meaning:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
modal appears:
|
|
112
|
+
backdrop fades in (150ms)
|
|
113
|
+
→ modal scales in (200ms, starts at 50ms — backdrop catches up)
|
|
114
|
+
→ modal content fades in (150ms, starts at 200ms)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Default to simultaneous unless sequence is meaningful.** Don't sequence parts of a single UI element — that fragments perception.
|
|
118
|
+
|
|
119
|
+
## Reduced motion (mandatory)
|
|
120
|
+
|
|
121
|
+
Respect `prefers-reduced-motion: reduce`. This is not optional — vestibular disorders are real.
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
@media (prefers-reduced-motion: reduce) {
|
|
125
|
+
*,
|
|
126
|
+
*::before,
|
|
127
|
+
*::after {
|
|
128
|
+
animation-duration: 0.01ms !important;
|
|
129
|
+
animation-iteration-count: 1 !important;
|
|
130
|
+
transition-duration: 0.01ms !important;
|
|
131
|
+
scroll-behavior: auto !important;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**What survives reduced motion:**
|
|
137
|
+
- Opacity transitions (fade in/out) — these don't cause vestibular issues
|
|
138
|
+
- Color transitions — fine
|
|
139
|
+
- Subtle scale (≤ 5% change) — fine
|
|
140
|
+
|
|
141
|
+
**What gets disabled:**
|
|
142
|
+
- All translate-based animations (slide-in, slide-up)
|
|
143
|
+
- Spinners (replace with text "Loading…")
|
|
144
|
+
- Parallax, scroll-driven motion
|
|
145
|
+
- Auto-playing carousels
|
|
146
|
+
|
|
147
|
+
**The pattern:** wrap motion in a media query and provide a static alternative:
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
const prefersReducedMotion = useReducedMotion()
|
|
151
|
+
|
|
152
|
+
<div className={prefersReducedMotion ? "" : "animate-slide-in"}>
|
|
153
|
+
{content}
|
|
154
|
+
</div>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Anti-patterns (the AI-motion ones)
|
|
158
|
+
|
|
159
|
+
These are the motion patterns that immediately signal AI-generated:
|
|
160
|
+
|
|
161
|
+
| ❌ Anti-pattern | ✅ Use instead |
|
|
162
|
+
|----------------|---------------|
|
|
163
|
+
| Bouncing/elastic everything | Smooth ease-out, no overshoot |
|
|
164
|
+
| 500ms+ for routine state changes | 150ms |
|
|
165
|
+
| Animating layout (width/height/padding) | Transform + opacity |
|
|
166
|
+
| Sequential typewriter text reveal | Just render the text |
|
|
167
|
+
| Hover scales > 1.05 | Hover background shift only |
|
|
168
|
+
| Wobbly button presses | scale(0.97) for 100ms, done |
|
|
169
|
+
| Marquee scrolling text in non-marketing UI | Truncate with ellipsis |
|
|
170
|
+
| Pulsing CTAs to "draw attention" | Better hierarchy, not motion |
|
|
171
|
+
| Spinning logos | Why? Just why? |
|
|
172
|
+
| Hero "shimmer" effects on every gradient | One shimmer, on one element, occasionally |
|
|
173
|
+
| Per-letter text animation in product UI | Reserve for marketing landing pages |
|
|
174
|
+
|
|
175
|
+
## The motion audit
|
|
176
|
+
|
|
177
|
+
Before shipping:
|
|
178
|
+
|
|
179
|
+
- [ ] Every animation answers one of the four "why" questions (where from / where to / what changed / what's happening)
|
|
180
|
+
- [ ] No animation is longer than 250ms unless it's the rare "dramatic" moment
|
|
181
|
+
- [ ] Animating only `transform` and `opacity` (no layout props)
|
|
182
|
+
- [ ] Easing is ease-out for entrances, ease-in for exits (never linear except for continuous)
|
|
183
|
+
- [ ] `prefers-reduced-motion: reduce` is respected — verified with the OS toggle
|
|
184
|
+
- [ ] No bounce, elastic, or overshoot curves anywhere
|
|
185
|
+
- [ ] Stagger capped at ~8 items
|
|
186
|
+
- [ ] No infinite/looping animation outside of loading states
|
|
187
|
+
- [ ] Hover states are color/background shifts, not transforms
|
|
188
|
+
|
|
189
|
+
If anything moves "to look cool" rather than to serve comprehension, cut it.
|