whale-igniter 1.3.0 → 1.3.2
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/commands/ignite.js +2 -1
- package/dist/version.js +1 -1
- package/package.json +3 -1
- package/packs/atlas/instructions.md +3 -0
- package/packs/atlas/manifest.json +8 -0
- package/packs/atlas/rules.md +4 -0
- package/packs/forge/instructions.md +3 -0
- package/packs/forge/manifest.json +8 -0
- package/packs/forge/rules.md +4 -0
- package/packs/lighthouse/instructions.md +3 -0
- package/packs/lighthouse/manifest.json +8 -0
- package/packs/lighthouse/rules.md +4 -0
- package/packs/scribe/instructions.md +3 -0
- package/packs/scribe/manifest.json +8 -0
- package/packs/scribe/rules.md +4 -0
- package/packs/selene/instructions.md +3 -0
- package/packs/selene/manifest.json +7 -0
- package/packs/selene/rules.md +4 -0
- package/ui-references/INDEX.md +51 -0
- package/ui-references/data-display.md +160 -0
- package/ui-references/feedback.md +190 -0
- package/ui-references/forms.md +192 -0
- package/ui-references/layout.md +155 -0
- package/ui-references/navigation.md +188 -0
- package/ui-references/surface.md +188 -0
package/dist/commands/ignite.js
CHANGED
|
@@ -46,7 +46,8 @@ export async function igniteCommand(projectName = "whale-project", options = {})
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
// ---- Step 1: scaffold workspace -------------------------------------------
|
|
49
|
-
const
|
|
49
|
+
const resolvedProjectName = config.projectName ?? projectName;
|
|
50
|
+
const target = await initCommand(resolvedProjectName, { config, silent: true });
|
|
50
51
|
const targetRel = path.relative(process.cwd(), target) || ".";
|
|
51
52
|
console.log(ui.ok(`Workspace created at ${ui.path(targetRel)}`));
|
|
52
53
|
// ---- Step 1b: record free-text project intent as first decision ------------
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = "1.3.
|
|
1
|
+
export const PACKAGE_VERSION = "1.3.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whale-igniter",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "CLI-first operational intelligence. Bootstraps and adopts AI-readable project context so agents like Claude Code, Codex and Cursor understand your design system from the first commit. Includes an MCP server, a file watcher, deterministic insights, and an opt-in AI bridge (Selene).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
12
|
"brand",
|
|
13
|
+
"packs",
|
|
14
|
+
"ui-references",
|
|
13
15
|
"README.md",
|
|
14
16
|
"LICENSE",
|
|
15
17
|
"docs/ROADMAP.md"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "atlas",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Product strategy, decision logs and roadmaps.",
|
|
5
|
+
"status": "stub",
|
|
6
|
+
"requiresAI": false,
|
|
7
|
+
"auditNote": "No generator or validator commands implemented. Decision logging is handled by whale decision (core), not this pack."
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lighthouse",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Accessibility audits, contrast and semantic validation.",
|
|
5
|
+
"status": "stub",
|
|
6
|
+
"requiresAI": false,
|
|
7
|
+
"auditNote": "Validators directory is empty. No runtime rules implemented in v1.3. Roadmapped for v1.4."
|
|
8
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# UI References — Index
|
|
2
|
+
|
|
3
|
+
This library is a curated set of component quality benchmarks drawn from the best production design systems. Consult it when building any UI component: before writing code to understand the quality bar, and when reviewing code to catch common mistakes.
|
|
4
|
+
|
|
5
|
+
AI agents: read this file first to locate the right category, then read that category file for component-specific guidance.
|
|
6
|
+
|
|
7
|
+
## Categories
|
|
8
|
+
|
|
9
|
+
| File | Covers | When to consult |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| [forms.md](forms.md) | Button, Input, Select, Checkbox, Radio, Toggle, Textarea | Any user input element |
|
|
12
|
+
| [navigation.md](navigation.md) | Nav bar, Tabs, Sidebar, Breadcrumb, Dropdown menu, Pagination, Command palette | Any wayfinding or navigation element |
|
|
13
|
+
| [feedback.md](feedback.md) | Toast, Modal, Alert, Progress, Loading states, Skeleton, Empty state | Any element that communicates system status |
|
|
14
|
+
| [surface.md](surface.md) | Card, Popover, Tooltip, Sheet, Avatar, Separator, Badge | Any container or overlay surface |
|
|
15
|
+
| [layout.md](layout.md) | Container, Stack, Grid, Responsive patterns, Sidebar layout | Any structural layout element |
|
|
16
|
+
| [data-display.md](data-display.md) | Table, List, Code block, Stats, Timeline | Any element that presents data |
|
|
17
|
+
|
|
18
|
+
## Minimum design system
|
|
19
|
+
|
|
20
|
+
A project is considered to have a design system when it has all of the following components implemented to the quality bar described in their respective files:
|
|
21
|
+
|
|
22
|
+
- **Button** — with default, hover, focus-visible, active, disabled, loading states and icon variant
|
|
23
|
+
- **Input** — with label, placeholder, error, disabled states and accessible labeling
|
|
24
|
+
- **Select** — keyboard-navigable, with accessible option list
|
|
25
|
+
- **Checkbox** and **Radio** — with indeterminate state for checkbox
|
|
26
|
+
- **Card** — as a reusable surface with consistent padding and radius
|
|
27
|
+
- **Modal / Dialog** — with focus trap, Escape to close, and accessible title
|
|
28
|
+
- **Toast / Notification** — dismissible, accessible, with distinct severity levels
|
|
29
|
+
|
|
30
|
+
These seven families are the minimum. Everything else is project-specific.
|
|
31
|
+
|
|
32
|
+
## How this library is structured
|
|
33
|
+
|
|
34
|
+
Each category file follows the same format per component:
|
|
35
|
+
|
|
36
|
+
1. One paragraph on what the component is and why it matters.
|
|
37
|
+
2. **Reference implementations** — two to four links to high-quality real-world implementations with one sentence on what each gets right.
|
|
38
|
+
3. **Minimum quality bar** — a checklist of states and behaviors the component must have to ship.
|
|
39
|
+
4. **Common mistakes** — a short list of patterns to avoid.
|
|
40
|
+
|
|
41
|
+
## Reference source tiers
|
|
42
|
+
|
|
43
|
+
When citing implementations, this library prioritizes:
|
|
44
|
+
|
|
45
|
+
- **Tier 1** (cited frequently): Linear, Vercel/Geist, Radix UI, shadcn/ui, Sonner, Headless UI, Park UI
|
|
46
|
+
- **Tier 2** (cited when category-appropriate): Material Design 3, Apple HIG, Carbon (IBM), Polaris (Shopify), Fluent (Microsoft), Atlassian, GitHub Primer
|
|
47
|
+
- **Tier 3** (cited for niche or emerging needs): Mantine, Ark UI, Origin UI, Animata
|
|
48
|
+
|
|
49
|
+
## Contributing
|
|
50
|
+
|
|
51
|
+
To add a component or update a reference link, edit the relevant category file directly. The format per component is fixed — add new components at the end of the file or insert them in frequency order. Do not use AI generation to create entries; references must point to real, stable implementations you have verified.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Data Display — UI References
|
|
2
|
+
|
|
3
|
+
Data display components present information that the user reads but does not directly edit. The quality bar is high: data must be scannable, sortable when appropriate, and never overflow or truncate silently. Accessibility concerns shift from interaction to comprehension: screen readers must be able to navigate tables and lists in a meaningful order.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table
|
|
8
|
+
|
|
9
|
+
A structured grid of rows and columns. Tables are for relational data where both the row and column identity matter. Do not use tables for layout.
|
|
10
|
+
|
|
11
|
+
### Reference implementations
|
|
12
|
+
|
|
13
|
+
- **[shadcn/ui Table](https://ui.shadcn.com/docs/components/table)** — semantic HTML (`<table>`, `<thead>`, `<tbody>`, `<th scope="col">`) with Tailwind styling; the correct baseline.
|
|
14
|
+
- **[Radix UI Table](https://www.radix-ui.com/themes/docs/components/table)** — themed table with built-in row hover, column alignment helpers, and condensed/standard density variants.
|
|
15
|
+
- **[TanStack Table](https://tanstack.com/table/latest)** — the leading headless table library; handles sorting, filtering, pagination, and virtual scrolling for large datasets. Consult for any table with more than a few dozen rows.
|
|
16
|
+
- **[Linear's issue list](https://linear.app)** — observe: not a `<table>` but a virtual list; shows how sorting, filtering, and row selection are handled in a list-as-table pattern common in modern apps.
|
|
17
|
+
|
|
18
|
+
### Minimum quality bar
|
|
19
|
+
|
|
20
|
+
- [ ] Semantic HTML: `<table>`, `<thead>`, `<tbody>`, `<tfoot>` (if total row), `<tr>`, `<th>`, `<td>`
|
|
21
|
+
- [ ] Column headers: `<th scope="col">` with a text description; not just icons
|
|
22
|
+
- [ ] Row headers (if applicable): `<th scope="row">` on the first cell of each row
|
|
23
|
+
- [ ] `<caption>` element or `aria-label` on the table describing what it contains
|
|
24
|
+
- [ ] Sortable columns: `aria-sort="ascending"` / `"descending"` / `"none"` on the column header
|
|
25
|
+
- [ ] Horizontal scroll on small viewports: wrap in `overflow-x: auto` container — do not collapse columns silently
|
|
26
|
+
- [ ] Row selection: checkbox in first column; `aria-checked` on the row (or the checkbox); keyboard: Space to toggle row
|
|
27
|
+
- [ ] Sticky header: `position: sticky; top: 0` — works with `overflow-x: auto` using the right scroll container
|
|
28
|
+
- [ ] Empty state: a single full-width `<td colspan="N">` with an empty state component, not a blank table
|
|
29
|
+
|
|
30
|
+
### Common mistakes
|
|
31
|
+
|
|
32
|
+
- Using `<div>` grid for tabular data — loses semantic meaning; screen readers cannot navigate by row/column
|
|
33
|
+
- Sortable columns with only visual state and no `aria-sort` — screen reader users can't tell the current sort
|
|
34
|
+
- Truncating cell content silently with `overflow: hidden` — use `text-overflow: ellipsis` and a tooltip for the full value
|
|
35
|
+
- No mobile strategy — either allow horizontal scroll, stack into a card per row, or use a different component for small viewports
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## List
|
|
40
|
+
|
|
41
|
+
An ordered or unordered sequence of items. Lists are the most common data structure in UI: navigation items, search results, notifications, file trees. Quality lists are scannable and navigable.
|
|
42
|
+
|
|
43
|
+
### Reference implementations
|
|
44
|
+
|
|
45
|
+
- **[Radix UI List](https://www.radix-ui.com/themes/docs/components/data-list)** — key-value data list pattern; good for detail views and settings pages.
|
|
46
|
+
- **[GitHub Primer ActionList](https://primer.style/components/action-list/react/alpha)** — the most complete reference for a list of interactive items (like a menu, but also used for navigation and selection): dividers, header groups, leading/trailing visuals, single/multi-select.
|
|
47
|
+
- **[TanStack Virtual](https://tanstack.com/virtual/latest)** — for lists with >100 items; virtual scrolling renders only visible items.
|
|
48
|
+
|
|
49
|
+
### Minimum quality bar
|
|
50
|
+
|
|
51
|
+
- [ ] Semantic HTML: `<ul>` for unordered, `<ol>` for ordered — not `<div>` + `<div>`
|
|
52
|
+
- [ ] Each item is `<li>`; interactive items inside `<li>` use `<a>` or `<button>`, not the `<li>` itself
|
|
53
|
+
- [ ] List roles override when semantics matter: `role="listbox"` + `role="option"` for a selectable list
|
|
54
|
+
- [ ] Keyboard navigation for interactive lists: arrow keys navigate items (roving tabindex or `aria-activedescendant`)
|
|
55
|
+
- [ ] Selected item: `aria-selected="true"` and a visual highlight
|
|
56
|
+
- [ ] Long lists: virtual scrolling or pagination — do not render 1000+ DOM nodes
|
|
57
|
+
- [ ] Grouped lists: each group has a visible heading and `role="group"` with `aria-label` pointing to that heading
|
|
58
|
+
|
|
59
|
+
### Common mistakes
|
|
60
|
+
|
|
61
|
+
- `<ul>` with `list-style: none` without `role="list"` — some screen readers strip list semantics when list style is removed (add `role="list"` to restore)
|
|
62
|
+
- Interactive items as `<li>` with `onClick` — the `<li>` is not an interactive role; use `<button>` or `<a>` inside
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Code Block
|
|
67
|
+
|
|
68
|
+
A formatted display of code, command output, or configuration. Must preserve whitespace, be readable at any content length, and allow copying.
|
|
69
|
+
|
|
70
|
+
### Reference implementations
|
|
71
|
+
|
|
72
|
+
- **[shadcn/ui](https://ui.shadcn.com)** — observe the code blocks throughout the documentation: copy button in the top-right corner, syntax highlighting, scrollable horizontally, no word wrap.
|
|
73
|
+
- **[Vercel/Geist Code](https://vercel.com/geist/code)** — inline code and block code variants; the inline variant for single-line snippets within prose.
|
|
74
|
+
- **[Shiki](https://shiki.matsu.io/)** — the leading syntax highlighter for static output (no runtime needed); generates semantic token HTML. Use for server-rendered or build-time highlighted code.
|
|
75
|
+
|
|
76
|
+
### Minimum quality bar
|
|
77
|
+
|
|
78
|
+
- [ ] `<pre><code>` elements — `<pre>` preserves whitespace; `<code>` signals that the content is code
|
|
79
|
+
- [ ] `tabindex="0"` on `<pre>` so keyboard users can scroll the code block
|
|
80
|
+
- [ ] Horizontal scroll on overflow — never wrap code unless the language is prose-like
|
|
81
|
+
- [ ] Copy button in the top-right corner with visual confirmation ("Copied!")
|
|
82
|
+
- [ ] Language label visible (e.g. "TypeScript", "bash") for multi-snippet docs
|
|
83
|
+
- [ ] Dark mode: code blocks often look better with a dark background in both light and dark themes
|
|
84
|
+
- [ ] `aria-label` on the `<pre>` if the language label is not a `<caption>` equivalent
|
|
85
|
+
|
|
86
|
+
### Common mistakes
|
|
87
|
+
|
|
88
|
+
- `white-space: pre-wrap` on code — wraps long lines in a way that breaks indentation context
|
|
89
|
+
- No horizontal scroll (`overflow-x: hidden`) on code blocks — silently truncates code
|
|
90
|
+
- Copy button that disappears after animation — show "Copied!" for at least 1.5 seconds
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Stats / Metrics
|
|
95
|
+
|
|
96
|
+
A display of key numbers or metrics with labels and optional context (trend, change, unit). Common in dashboards, reports, and analytics views.
|
|
97
|
+
|
|
98
|
+
### Reference implementations
|
|
99
|
+
|
|
100
|
+
- **[shadcn/ui example: Dashboard](https://ui.shadcn.com/examples/dashboard)** — the stat card pattern: large number, label below, trend indicator (up/down arrow + percentage).
|
|
101
|
+
- **[Vercel/Geist](https://vercel.com/geist/components)** — observe the analytics pages: stat value, label, optional sparkline in the card.
|
|
102
|
+
- **[Polaris KPICard](https://polaris.shopify.com)** — handles trend indicators, unit suffixes, comparison to previous period.
|
|
103
|
+
|
|
104
|
+
### Minimum quality bar
|
|
105
|
+
|
|
106
|
+
- [ ] Number has sufficient contrast and type scale — stat values should be the largest text element in the card
|
|
107
|
+
- [ ] Unit is part of the label or the value (`12.4k`, `$1,234`, `94%`), not ambiguous
|
|
108
|
+
- [ ] Trend indicator (if present): arrow + percentage; green/red with a text label ("Up 12%") — never color-only
|
|
109
|
+
- [ ] Large numbers are formatted with locale-appropriate separators (`toLocaleString()` or `Intl.NumberFormat`)
|
|
110
|
+
- [ ] Loading state is a skeleton with the same dimensions as the loaded state
|
|
111
|
+
|
|
112
|
+
### Common mistakes
|
|
113
|
+
|
|
114
|
+
- Trend indicator that uses color alone (just a green or red number) — inaccessible; add an up/down arrow or "▲ 12%" text
|
|
115
|
+
- Stats that don't communicate the time period — `$12,400` means nothing without "last 30 days" or similar context
|
|
116
|
+
- Numbers that reflow unpredictably on value change — use `tabular-nums` font feature to prevent layout shift when digits change
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Timeline
|
|
121
|
+
|
|
122
|
+
A chronological sequence of events, displayed as a vertical list with connecting lines and time markers. Used in audit logs, activity feeds, version histories.
|
|
123
|
+
|
|
124
|
+
### Reference implementations
|
|
125
|
+
|
|
126
|
+
- **[GitHub's commit history](https://github.com)** — observe: timestamp, author, branch indicator; condensed view with expand; the connecting line between events.
|
|
127
|
+
- **[Linear activity log](https://linear.app)** — observe in issue detail: avatar + action description + relative time; grouped by day; expandable for details.
|
|
128
|
+
- **[Atlassian Activity Streams](https://atlassian.design)** — enterprise-quality timeline with status changes, user attribution, and grouping.
|
|
129
|
+
|
|
130
|
+
### Minimum quality bar
|
|
131
|
+
|
|
132
|
+
- [ ] Semantic: use `<ol>` (ordered list) — timeline events are ordered
|
|
133
|
+
- [ ] Each event: timestamp, actor (if applicable), event description
|
|
134
|
+
- [ ] Time shown as relative ("3 minutes ago") with absolute time in a tooltip (`title` attribute or `aria-label`)
|
|
135
|
+
- [ ] Connecting line is decorative (`aria-hidden`); the list semantic conveys the sequence
|
|
136
|
+
- [ ] Long timelines: load more on scroll or paginate — do not render hundreds of events on mount
|
|
137
|
+
- [ ] Events grouped by day: group heading is a visible date, `role="separator"` divider
|
|
138
|
+
|
|
139
|
+
### Common mistakes
|
|
140
|
+
|
|
141
|
+
- Connecting line as part of the layout that breaks when the viewport narrows
|
|
142
|
+
- Relative timestamps that never update — "5 minutes ago" should update as the user stays on the page
|
|
143
|
+
- No empty state for timelines with no history — "No activity yet" prevents confusion
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Badge / Tag (Data Context)
|
|
148
|
+
|
|
149
|
+
A small label attached to data items to communicate category, status, or type. See [surface.md](surface.md) for the component itself; this section covers data-display-specific usage patterns.
|
|
150
|
+
|
|
151
|
+
### Usage patterns in data display
|
|
152
|
+
|
|
153
|
+
- **Status badges in tables**: align all status badges in the same column; use a fixed-width column so the rest of the row doesn't shift when status changes.
|
|
154
|
+
- **Category tags in lists**: tags that filter the list when clicked become `<button>` elements, not `<span>` — clicking must have a visible effect.
|
|
155
|
+
- **Priority indicators**: if using colored dots or badges for priority (urgent/high/medium/low), always accompany color with a text label or icon — do not rely on color alone.
|
|
156
|
+
|
|
157
|
+
### Reference implementations
|
|
158
|
+
|
|
159
|
+
- **[Linear's priority + status labels](https://linear.app)** — observe in issue list: priority dot + label, status as a colored chip with text; keyboard accessible in filter panels.
|
|
160
|
+
- **[GitHub labels](https://github.com)** — colored label with text; filter behavior when clicked in issues list; editable in label management UI.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Feedback — UI References
|
|
2
|
+
|
|
3
|
+
Feedback components communicate system status. They answer the user's question "did that work?" They must be timely (not delayed), clear (no ambiguous language), and unobtrusive (not blocking work the user is trying to do). A toast that the user misses, a modal that takes five clicks to dismiss, or an error message with jargon all represent feedback component failures.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Toast / Notification
|
|
8
|
+
|
|
9
|
+
A brief, auto-dismissing message that confirms an action or communicates a system event. Appears without interrupting the user's current task.
|
|
10
|
+
|
|
11
|
+
### Reference implementations
|
|
12
|
+
|
|
13
|
+
- **[Sonner](https://sonner.emilkowal.ski/)** — the current best-in-class toast library: smooth enter/exit animations, stacking with expansion on hover, rich content with actions, correct `role="status"` for non-urgent messages and `role="alert"` for errors. Study the source for the animation and stacking patterns.
|
|
14
|
+
- **[shadcn/ui Toast (Sonner)](https://ui.shadcn.com/docs/components/sonner)** — Sonner integration with the shadcn/ui theming system.
|
|
15
|
+
- **[Radix UI Toast](https://www.radix-ui.com/primitives/docs/components/toast)** — alternative implementation; observe how it handles `swipeDirection` and the viewport positioning.
|
|
16
|
+
- **[Linear's toasts](https://linear.app)** — observe in the app: appear bottom-left, short duration, action button inline, stack without expanding (up to ~3 visible), oldest auto-dismisses when stack is full.
|
|
17
|
+
|
|
18
|
+
### Minimum quality bar
|
|
19
|
+
|
|
20
|
+
- [ ] Non-critical toasts: `role="status"`, `aria-live="polite"` — screen reader announces after current speech finishes
|
|
21
|
+
- [ ] Error/warning toasts: `role="alert"`, `aria-live="assertive"` — screen reader announces immediately
|
|
22
|
+
- [ ] Auto-dismiss after 4–6 seconds; longer for messages with actions
|
|
23
|
+
- [ ] Dismiss button always present; hover pauses auto-dismiss timer
|
|
24
|
+
- [ ] Stacking: at most 3–5 toasts visible; oldest dismiss when stack is full
|
|
25
|
+
- [ ] Positioned away from interactive elements (bottom corner, not covering nav)
|
|
26
|
+
- [ ] Keyboard: Tab reaches the toast's action/dismiss button while it is visible
|
|
27
|
+
- [ ] Mobile: full-width at bottom, swipe-to-dismiss gesture
|
|
28
|
+
|
|
29
|
+
### Common mistakes
|
|
30
|
+
|
|
31
|
+
- Using `alert` role for success messages — causes screen readers to interrupt unnecessarily
|
|
32
|
+
- Auto-dismissing error messages — errors must persist until acknowledged
|
|
33
|
+
- Toasts covering important page content (especially modals or forms mid-submission)
|
|
34
|
+
- No action button for "undo" actions — missed opportunity to recover from mistakes
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Modal / Dialog
|
|
39
|
+
|
|
40
|
+
A focused overlay that interrupts the user's workflow to demand attention or a decision. Use sparingly: modals block the background and demand immediate resolution.
|
|
41
|
+
|
|
42
|
+
### Reference implementations
|
|
43
|
+
|
|
44
|
+
- **[Radix UI Dialog](https://www.radix-ui.com/primitives/docs/components/dialog)** — the reference implementation: focus trap, `role="dialog"`, `aria-modal="true"`, Escape to close, scroll lock on body, correct overlay with `aria-hidden` on background content.
|
|
45
|
+
- **[shadcn/ui Dialog](https://ui.shadcn.com/docs/components/dialog)** — Radix Dialog with Tailwind; includes `DialogHeader`, `DialogTitle` (required), `DialogDescription` (recommended), `DialogFooter` pattern.
|
|
46
|
+
- **[Headless UI Dialog](https://headlessui.com/react/dialog)** — alternative, with the `initialFocus` ref pattern for controlling which element receives focus when the modal opens.
|
|
47
|
+
|
|
48
|
+
### Minimum quality bar
|
|
49
|
+
|
|
50
|
+
- [ ] `role="dialog"` and `aria-modal="true"` on the dialog container
|
|
51
|
+
- [ ] `aria-labelledby` pointing to the dialog title; `aria-describedby` pointing to the description
|
|
52
|
+
- [ ] Focus trap: Tab and Shift+Tab cycle only within the modal while open
|
|
53
|
+
- [ ] First focusable element receives focus on open (or explicitly set with `autoFocus`)
|
|
54
|
+
- [ ] Escape closes the modal; clicking the overlay closes it; focus returns to the trigger
|
|
55
|
+
- [ ] Body scroll is locked while modal is open; background content is `aria-hidden="true"`
|
|
56
|
+
- [ ] Destructive modal: the destructive action is not the default focused button
|
|
57
|
+
- [ ] Animation: fade + scale in, fade + scale out; respects `prefers-reduced-motion`
|
|
58
|
+
|
|
59
|
+
### Common mistakes
|
|
60
|
+
|
|
61
|
+
- Modal without a visible title — `DialogTitle` is required even if visually hidden; without it screen readers have no context
|
|
62
|
+
- Focus returning to page top (not the trigger) after modal closes
|
|
63
|
+
- Modals that cannot be dismissed without taking an action — always provide a way out
|
|
64
|
+
- Nested modals — almost always a design problem that should be solved differently
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Alert / Banner
|
|
69
|
+
|
|
70
|
+
An inline message that communicates status, warnings, or errors within the page content. Unlike toasts, alerts do not auto-dismiss and are integrated into the layout.
|
|
71
|
+
|
|
72
|
+
### Reference implementations
|
|
73
|
+
|
|
74
|
+
- **[shadcn/ui Alert](https://ui.shadcn.com/docs/components/alert)** — clean implementation with icon + title + description pattern; default and destructive variants.
|
|
75
|
+
- **[Radix UI Callout](https://www.radix-ui.com/themes/docs/components/callout)** — four severity levels (info, success, warning, error) with icon and text.
|
|
76
|
+
- **[Polaris Banner](https://polaris.shopify.com/components/feedback-indicators/banner)** — good reference for how to handle actionable alerts (with dismiss, with action button, with expansion for details).
|
|
77
|
+
- **[GitHub Primer Flash](https://primer.style/components/flash/react/alpha)** — persistent alerts with full-width option for page-level messages.
|
|
78
|
+
|
|
79
|
+
### Minimum quality bar
|
|
80
|
+
|
|
81
|
+
- [ ] Four severity levels: info, success, warning, error — visually distinct beyond color (icon, border, label)
|
|
82
|
+
- [ ] `role="alert"` only for genuinely urgent messages that need immediate announcement; `role="status"` for success and info
|
|
83
|
+
- [ ] Dismissible alerts have a close button with `aria-label="Dismiss"`
|
|
84
|
+
- [ ] Actionable alerts: action button is secondary style (not primary — the alert should not compete with the page's primary action)
|
|
85
|
+
- [ ] Error alerts that accompany form validation: positioned near (or above) the form, not at page top
|
|
86
|
+
|
|
87
|
+
### Common mistakes
|
|
88
|
+
|
|
89
|
+
- `role="alert"` on every informational message — causes constant interruptions for screen reader users
|
|
90
|
+
- Color alone to convey severity — red text, yellow text without icon or label are inaccessible
|
|
91
|
+
- Alerts that appear and disappear too quickly — users miss them or can't read the message
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Progress Indicator
|
|
96
|
+
|
|
97
|
+
Communicates how far along a process is. Two types: determinate (known progress) and indeterminate (working but no ETA).
|
|
98
|
+
|
|
99
|
+
### Reference implementations
|
|
100
|
+
|
|
101
|
+
- **[Radix UI Progress](https://www.radix-ui.com/primitives/docs/components/progress)** — `role="progressbar"`, `aria-valuenow`, `aria-valuemin`, `aria-valuemax` correctly implemented; smooth transition on value change.
|
|
102
|
+
- **[shadcn/ui Progress](https://ui.shadcn.com/docs/components/progress)** — clean linear progress bar; the animated indeterminate variant shows the pattern.
|
|
103
|
+
|
|
104
|
+
### Minimum quality bar
|
|
105
|
+
|
|
106
|
+
- [ ] Determinate: `role="progressbar"` with `aria-valuenow`, `aria-valuemin="0"`, `aria-valuemax="100"`, `aria-valuetext` for human-readable labels ("Step 2 of 5")
|
|
107
|
+
- [ ] Indeterminate: `aria-valuenow` omitted; CSS animation for visual motion; respects `prefers-reduced-motion` (reduce animation, don't remove it)
|
|
108
|
+
- [ ] Multi-step progress: step labels visible at all steps; completed steps visually distinct from future steps
|
|
109
|
+
- [ ] Color: progress fill must pass 3:1 contrast against the track
|
|
110
|
+
|
|
111
|
+
### Common mistakes
|
|
112
|
+
|
|
113
|
+
- Indeterminate progress bar staying for too long — show estimated time after 3+ seconds or switch to a spinner with a status message
|
|
114
|
+
- Progress that jumps backwards (data re-loading or error recovery) — disorienting; hold the last value if you can't go backwards meaningfully
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Loading State
|
|
119
|
+
|
|
120
|
+
Communicates that the system is working. Must be immediate (appear within 100ms of an action) and honest (not appear when there is nothing to load).
|
|
121
|
+
|
|
122
|
+
### Reference implementations
|
|
123
|
+
|
|
124
|
+
- **[Vercel/Geist Spinner](https://vercel.com/geist/spinner)** — minimal spinner; used inline and as a page-level overlay; the spinning animation is smooth.
|
|
125
|
+
- **[Radix UI Spinner (Themes)](https://www.radix-ui.com/themes/docs/components/spinner)** — several sizes, accessible.
|
|
126
|
+
- **[Linear loading](https://linear.app)** — observe: spinner appears within 100ms, replaces content (not overlays it in most cases), disappears immediately when content is ready.
|
|
127
|
+
|
|
128
|
+
### Minimum quality bar
|
|
129
|
+
|
|
130
|
+
- [ ] Appears within 100ms of triggering the async operation
|
|
131
|
+
- [ ] `role="status"` and `aria-label="Loading"` so screen readers announce it
|
|
132
|
+
- [ ] Replaces or overlays only the content being loaded — not the entire page for partial updates
|
|
133
|
+
- [ ] `prefers-reduced-motion`: reduce animation speed or show a static icon instead of spinning
|
|
134
|
+
- [ ] Spinner disappears immediately when load completes — no artificial minimum duration
|
|
135
|
+
|
|
136
|
+
### Common mistakes
|
|
137
|
+
|
|
138
|
+
- Loading overlay covering the full viewport for a small update in one section
|
|
139
|
+
- Minimum loading time ("show spinner for at least 500ms") — adds latency without benefit
|
|
140
|
+
- No loading state at all for operations that take >300ms — users think nothing happened and click again
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Skeleton
|
|
145
|
+
|
|
146
|
+
A placeholder that previews the shape of content while it loads. More useful than a generic spinner for content-heavy layouts.
|
|
147
|
+
|
|
148
|
+
### Reference implementations
|
|
149
|
+
|
|
150
|
+
- **[shadcn/ui Skeleton](https://ui.shadcn.com/docs/components/skeleton)** — minimal base implementation: pulse animation, neutral background, matches the shape of the content it replaces.
|
|
151
|
+
- **[Polaris SkeletonPage](https://polaris.shopify.com/components/feedback-indicators/skeleton-page)** — full-page skeleton with SkeletonBodyText, SkeletonDisplayText, SkeletonThumbnail; good reference for compositional approach.
|
|
152
|
+
|
|
153
|
+
### Minimum quality bar
|
|
154
|
+
|
|
155
|
+
- [ ] Matches the approximate layout and shape of the real content
|
|
156
|
+
- [ ] Pulse animation (opacity oscillation) is gentler than shimmer (left-to-right gradient) for users with motion sensitivity
|
|
157
|
+
- [ ] `aria-hidden="true"` on skeleton elements — they are decorative; screen readers should not read them
|
|
158
|
+
- [ ] Use `aria-busy="true"` on the container while loading; `aria-busy="false"` when content is ready
|
|
159
|
+
- [ ] Skeleton does not shift layout when content loads — reserve space accurately
|
|
160
|
+
|
|
161
|
+
### Common mistakes
|
|
162
|
+
|
|
163
|
+
- Skeleton that looks nothing like the real content — causes layout shift and disorientation when content appears
|
|
164
|
+
- Skeleton persisting after content has loaded — even briefly, confusing to users
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Empty State
|
|
169
|
+
|
|
170
|
+
The state of a view when there is no data to display. One of the most neglected components, and one of the most important for user retention.
|
|
171
|
+
|
|
172
|
+
### Reference implementations
|
|
173
|
+
|
|
174
|
+
- **[Linear's empty states](https://linear.app)** — observe in fresh workspace: illustration or icon, clear heading, specific explanation of why it's empty, one actionable next step. Never just "No items found."
|
|
175
|
+
- **[Polaris EmptyState](https://polaris.shopify.com/components/layout-and-structure/empty-state)** — image + heading + description + primary action + (optional) secondary action; the structure to follow.
|
|
176
|
+
- **[GitHub empty states](https://github.com)** — observe in repositories, issues, projects: different copy for "nothing exists yet" vs "nothing matches your filter" — those are two different problems.
|
|
177
|
+
|
|
178
|
+
### Minimum quality bar
|
|
179
|
+
|
|
180
|
+
- [ ] Distinct copy for "nothing exists yet" vs "nothing matches current filters" — different user problems
|
|
181
|
+
- [ ] At least one call to action ("Create your first X", "Clear filters")
|
|
182
|
+
- [ ] Visual treatment: icon or illustration (not a blank white box)
|
|
183
|
+
- [ ] When filtered and empty: show what filter is active; offer "Clear all filters"
|
|
184
|
+
- [ ] Error state (couldn't load) is distinct from empty state (loaded but empty) — error needs a retry
|
|
185
|
+
|
|
186
|
+
### Common mistakes
|
|
187
|
+
|
|
188
|
+
- Generic "No results" with no context or action — leaves users stranded
|
|
189
|
+
- Empty state that looks like a bug — provide enough context that users trust the app is working correctly
|
|
190
|
+
- Same empty state for all situations — loading error, empty dataset, and filter-with-no-results are three different states
|