feat: refresh shared skills catalog

This commit is contained in:
vasily committed 2026-10-30 12:40:16 +08:00
1 parent 98bb35e6fb
commit 418792d1ec
49 files changed
+2273 -2390

No files matched your search

@@ -1,6 +1,7 @@
# WCAG 2.1 AA Accessibility Audit Guide
# Accessibility audit reference
Comprehensive checklist for building accessible web interfaces. Every requirement maps to WCAG 2.1 Level AA success criteria.
Select relevant checks for the changed flow. This reference mixes WCAG requirements
with stricter design recommendations; those recommendations are not AA gates.
---
@@ -141,8 +142,11 @@ The first rule of ARIA: do not use ARIA if a native HTML element provides the be
}
```
- Focus indicators must meet 3:1 contrast ratio against adjacent colors (WCAG 2.4.11)
- Minimum focus indicator area: at least 2px perimeter around the component
- Keep focus visible and not entirely obscured by authored content
([WCAG 2.2 AA 2.4.11](https://www.w3.org/WAI/WCAG22/Understanding/focus-not-obscured-minimum.html)).
- A 2px-perimeter area and 3:1 focused/unfocused contrast are stronger
[AAA focus-appearance criteria](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html),
not the definition of AA 2.4.11. Apply non-text contrast where required.
### Modal Focus Trapping
@@ -300,18 +304,14 @@ panel: id="panel-id", role="region", aria-labelledby="trigger-id"
## 7. prefers-reduced-motion
### Global Reset
### Targeted motion reduction
```css
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
html { scroll-behavior: auto; }
/* Apply to components with nonessential movement; keep state feedback. */
.decorative-motion { animation: none; }
.moving-control { transition-property: color, background-color, opacity; }
}
```
@@ -402,7 +402,7 @@ npx playwright test --project=accessibility
| `aria-hidden="true"` on focusable elements | Remove from tab order or remove `aria-hidden` |
| Missing `lang` attribute on `<html>` | Set `<html lang="en">` (or appropriate language code) |
| Autoplaying video/audio with sound | Require user interaction to start, or mute by default with controls |
| Tiny tap targets on mobile | Minimum 44x44 CSS pixels for touch targets |
| Tiny tap targets on mobile | Aim for 44px touch targets; AA 2.5.8 minimum is 24px with exceptions |
| Using `title` attribute as primary label | `title` is unreliable; use `aria-label` or visible `<label>` |
| Links that say "click here" or "read more" | Descriptive link text: "Read the accessibility guide" |
| Missing form error summary | On submit failure, show summary of all errors at top of form |
@@ -526,11 +526,12 @@ Use the playback speed controls (25%, 10%) for detailed inspection.
### Real device testing
Touch interactions feel completely different on a real phone versus a trackpad simulator.
Always test gestures, drag interactions, and spring animations on physical devices.
For gesture-sensitive changes, test on a physical device when available; otherwise
use the available simulator/emulator and state the verification limit.
### Fresh eyes check
Review animations with fresh eyes the next day. What felt right at 11pm during
For a dedicated polish pass, a later review can help. What felt right at 11pm during
development often feels too fast, too slow, or too dramatic the next morning.
### The checklist
@@ -0,0 +1,506 @@
# Design recipes
Optional examples for new designs. Existing components, tokens, user preferences,
and platform conventions override these recipes. Aesthetic suggestions are not
accessibility requirements or completion gates. Read only relevant sections.
### 2. The Priority Stack
When implementing UI, work through these priorities in order. Higher priorities are non-negotiable; lower priorities are polish that compounds quality.
| Priority | Level | What It Means |
|----------|-------|---------------|
| **Accessibility** | CRITICAL | Contrast 4.5:1, keyboard nav, ARIA semantics, visible focus rings. Ship nothing that excludes users. |
| **Performance** | HIGH | WebP/AVIF images, lazy loading below fold, CLS < 0.1, transform-only animations on the compositor thread. |
| **Typography** | HIGH | Font smoothing, text-wrap balance/pretty, tabular-nums for data, 65ch max line length. |
| **Layout & Spatial** | HIGH | 4/8px grid, concentric border radius, optical alignment over geometric. |
| **Color & Theme** | MEDIUM | HSL custom properties, semantic tokens, dark mode pairs tested separately. |
| **Motion & Interaction** | MEDIUM | Frequency-based animation decisions, 150-300ms durations, ease-out default. |
| **Polish & Details** | LOW | Layered shadows over borders, press feedback on buttons, staggered enter animations. |
Never skip a CRITICAL/HIGH item to chase a LOW item. A beautifully animated button that fails keyboard navigation is a net negative.
---
### 3. Aesthetic Direction
For a new design, choose a direction suited to the audience and task. The most common failure mode in AI-generated UI is convergence on the same safe, forgettable look.
### Pick a Tone
Choose one and commit fully:
- **Brutally minimal** — generous whitespace, monospace type, stark contrast, near-zero decoration
- **Maximalist chaos** — layered textures, clashing type scales, dense information, intentional visual noise
- **Retro-futuristic** — CRT glow effects, monospace terminals, scan lines, neon on dark
- **Organic / natural** — earth tones, rounded shapes, paper textures, hand-drawn accents
- **Luxury / refined** — serif headlines, muted palettes, ample negative space, subtle gold or cream accents
- **Editorial / magazine** — dramatic type hierarchy, full-bleed imagery, grid-breaking layouts
- **Playful / bold** — bright primaries, chunky borders, exaggerated shadows, bouncy motion
### Match Complexity to Vision
Maximalist design demands elaborate code — layered backgrounds, complex grid structures, multiple font stacks. Minimalist design demands surgical precision — every pixel of spacing matters more when there's nothing to hide behind.
### Visual Texture
Add depth through: gradient meshes, noise/grain overlays (`filter: url(#noise)`), layered transparencies, subtle background patterns, duotone image treatments.
Use existing project tokens and components even without a DESIGN.md.
---
### 4. Typography Essentials
Typography is the single highest-leverage design element. Get it right and mediocre layouts still feel good. Get it wrong and nothing else saves it.
### Root Setup
```css
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
```
Apply font smoothing to the root layout. On macOS, the default sub-pixel rendering makes text appear heavier than the designer intended.
### Text Wrapping
```css
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
}
p, li, dd, blockquote {
text-wrap: pretty;
}
```
`balance` distributes heading lines evenly. `pretty` avoids orphaned words in body text.
### Numeric Display
```css
.data-value, .price, .counter, [data-numeric] {
font-variant-numeric: tabular-nums;
}
```
Use `tabular-nums` for any number that updates dynamically — prices, counters, table columns. Without it, layout shifts as digit widths change.
### Scale and Rhythm
- **Base size**: 16px minimum for body text. Never go below 14px for any readable content.
- **Line height**: 1.5-1.75 for body text, 1.1-1.3 for large headings.
- **Max line length**: `max-width: 65ch` for body text. Long lines destroy readability.
- **Type scale**: Pick a consistent scale and stick to it: 12 / 14 / 16 / 18 / 24 / 32 / 48 / 64.
### Font Pairing
Pair a distinctive display font with a refined body font. The display font carries personality; the body font carries readability. Use `font-weight` for hierarchy within a family:
- **Headings**: 600-700 (semibold to bold)
- **Body**: 400 (regular)
- **Labels / UI**: 500 (medium)
Always include font stack fallbacks:
```css
--font-display: "Instrument Serif", "Georgia", serif;
--font-body: "Söhne", "Helvetica Neue", sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", monospace;
```
---
## 5. Color & Theme
### HSL Custom Properties (shadcn Pattern)
```css
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
```
Define semantic tokens: primary, secondary, destructive, muted, accent, background, foreground. Reference colors by semantic name — never hardcode hex values in components.
### Dark Mode
```css
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... desaturated, lighter tonal variants — NOT simply inverted */
}
```
Dark mode is not "invert colors." Use desaturated, lighter tonal variants. Backgrounds go dark but not pure black (`#000`). Text goes light but not pure white (`#fff`). Test contrast separately for dark mode — what passes in light may fail in dark.
### Contrast Requirements
- **WCAG AA minimum**: 4.5:1 for normal text, 3:1 for large text (18px+ bold or 24px+ regular)
- Never convey information by color alone — always pair with an icon, label, or pattern
- Test with browser devtools contrast checker or axe-core
### Color Confidence
Dominant colors with sharp accents outperform timid, evenly-distributed palettes. Pick one or two hero colors and let the rest of the palette recede. A confident palette has clear hierarchy; an uncertain palette spreads color evenly and feels flat.
---
## 6. Spatial Design
### Concentric Border Radius
This is the single most common thing that makes nested UI elements feel "off":
```
outer_radius = inner_radius + padding
```
```css
/* Correct: concentric */
.card { border-radius: 16px; padding: 8px; }
.card-inner { border-radius: 8px; } /* 16 - 8 = 8 */
/* Wrong: same radius on parent and child */
.card { border-radius: 12px; }
.card-inner { border-radius: 12px; } /* Looks bloated */
```
When geometric centering looks off, align optically. Play/pause icons, dropdown carets, and asymmetric glyphs often need 1-2px manual nudges to look centered.
### Shadows Over Borders
Layer multiple transparent `box-shadow` values for natural depth instead of using borders:
```css
.elevated {
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.04),
0 2px 4px rgba(0, 0, 0, 0.04),
0 4px 8px rgba(0, 0, 0, 0.04);
}
```
Multiple shadows at different spreads mimic how light works. A single hard shadow looks artificial.
### Image Outlines
Add a subtle inset outline to images and media for consistent depth against varied backgrounds:
```css
img, video {
outline: 1px solid rgba(0, 0, 0, 0.06);
outline-offset: -1px;
}
```
### Spacing Scale
Use a 4px / 8px base incremental system. A 4px-based scale is one possible default:
`4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 96 / 128`
### Hit Areas
Aim for comfortable 44x44px touch targets where practical. If the visual element is smaller, extend the hit area with a pseudo-element:
```css
.small-button::before {
content: "";
position: absolute;
inset: -8px;
}
```
### Z-Index Scale
Define a layered scale and never use arbitrary values:
```css
--z-base: 0;
--z-dropdown: 10;
--z-sticky: 20;
--z-overlay: 40;
--z-modal: 100;
--z-toast: 1000;
```
---
### 7. Motion & Interaction
### The Frequency-Based Decision Framework
This is the most important mental model for animation decisions:
| Frequency | Examples | Animation |
|-----------|----------|-----------|
| **100+ times/day** | Keyboard shortcuts, command palette actions, tab switches | **None.** Zero animation. Instant. |
| **Tens of times/day** | Hover effects, list item navigation, toggles | **Remove or drastically reduce.** 50-100ms max. |
| **Occasional** | Modals, drawers, toasts, page transitions | **Standard animation.** 150-300ms. |
| **Rare / first-time** | Onboarding, celebrations, empty states | **Can add delight.** 300-500ms, more elaborate. |
High-frequency animations feel sluggish. Low-frequency animations without motion feel jarring. Match the animation budget to usage frequency.
### Custom Easing Curves
Custom easing curves are an option when motion helps the interaction:
```css
:root {
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}
```
### Duration Guide
| Element | Duration |
|---------|----------|
| Buttons, toggles | 100-160ms |
| Tooltips | 125-200ms |
| Dropdowns, popovers | 150-250ms |
| Modals, drawers | 200-500ms |
| Page transitions | 250-400ms |
Keep motion responsive; choose duration and easing for the interaction.
### Enter/Exit Asymmetry
Exits should be softer and faster than enters. An enter animation at 250ms should have its exit at 150-200ms.
### Split and Stagger Enter Animations
When multiple elements enter the viewport, stagger them by semantic chunks with ~50-100ms delay:
```css
.stagger-item {
animation: fadeSlideIn 300ms var(--ease-out) both;
}
.stagger-item:nth-child(1) { animation-delay: 0ms; }
.stagger-item:nth-child(2) { animation-delay: 60ms; }
.stagger-item:nth-child(3) { animation-delay: 120ms; }
```
### Scale Animations
Never animate from `scale(0)`. Start from `scale(0.9)` or higher, combined with opacity:
```css
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
```
### Press Feedback
Optional press feedback, when consistent with the design and reduced-motion policy:
```css
button:active {
transform: scale(0.97);
}
```
### Interruptibility
Use CSS transitions (not keyframe animations) for interactive state changes. Transitions can be interrupted mid-way; keyframes cannot. This matters for hover states, toggles, and any element the user might interact with rapidly.
### Popover Origin
Make popovers transform-origin aware — they should grow from their trigger element, not from center. Exception: modals always originate from center.
### Tooltip Hover Delay
Skip the tooltip delay on subsequent hovers. If the user has already waited for one tooltip, show the next one immediately.
### Reduced Motion
```css
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.decorative-motion { animation: none; }
.moving-control { transition-property: color, background-color, opacity; }
}
```
Respect `prefers-reduced-motion`. Reduce animations — don't eliminate opacity and color transitions entirely, as those provide important feedback.
### Hover Gate
Gate hover animations behind a media query so touch devices don't trigger stuck hover states:
```css
@media (hover: hover) and (pointer: fine) {
.card:hover { transform: translateY(-2px); }
}
```
> Reference `animation-playbook.md` for deep dives on spring physics, gesture-driven animation, and complex choreography.
---
### 8. Component Craft
### Primitives
In a React project already using Radix, its primitives can provide accessible foundations. Use CVA (class-variance-authority) for type-safe component variants:
```tsx
import { cva } from "class-variance-authority";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input hover:bg-accent hover:text-accent-foreground",
ghost: "hover:bg-accent hover:text-accent-foreground",
},
size: {
sm: "h-9 px-3 text-sm",
default: "h-10 px-4 py-2",
lg: "h-11 px-8 text-lg",
},
},
defaultVariants: { variant: "default", size: "default" },
}
);
```
### Button
- Scale on press (`transform: scale(0.97)` on `:active`)
- Visible focus ring (never `outline: none` without replacement)
- Loading state with spinner replacing label, maintaining button dimensions
- Disabled state at `opacity: 0.5` with `pointer-events: none`
### Card
- Concentric border radius between card and inner elements
- Layered shadows (not borders) for depth
- Hover state: subtle elevation change (`translateY(-1px)` + shadow increase)
### Dialog / Modal
- Focus trap (keyboard cannot escape to elements behind)
- ESC to close, click outside overlay to close
- `transform-origin: center`, fade + scale enter animation
- `aria-modal="true"`, `role="dialog"`, `aria-labelledby`
### Form
- Visible labels always — never placeholder-only inputs
- Error messages near the field with `aria-live="polite"` for screen readers
- Progressive disclosure: show advanced fields only when needed
- Use the project's form and runtime validation libraries
### Theming
Use the project's semantic color tokens. Wrap client-interactive components in server components for Next.js App Router compatibility.
> Reference `component-patterns.md` for the full component catalog with copy-paste implementations.
---
## 9. Accessibility Essentials
### Semantic HTML First
Use `<button>`, `<nav>`, `<main>`, `<header>`, `<footer>`, `<article>`, `<section>` before reaching for ARIA. A `<button>` gives you keyboard handling, focus management, and screen reader semantics for free. A `<div onClick>` gives you none of that.
### Keyboard Navigation
- **Tab / Shift+Tab**: move between focusable elements
- **Enter / Space**: activate buttons and links
- **Arrow keys**: navigate within lists, menus, tabs, radio groups
- **Escape**: close modals, popovers, dropdowns
- **Home / End**: jump to first/last item in lists
### Focus Management
- Visible focus rings on all interactive elements — NEVER use `outline: none` without a replacement
- Trap focus inside modals (Tab wraps within the modal, not behind it)
- Restore focus to the trigger element when a modal/popover closes
- Use `focus-visible` to show rings only for keyboard users, not mouse clicks:
```css
:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
```
### ARIA Attributes
- `aria-label` for icon-only buttons: `<button aria-label="Close menu">X</button>`
- `aria-labelledby` to associate headings with sections
- `aria-describedby` to link help text or error messages to inputs
- `aria-live="polite"` for dynamic content updates (toast messages, form errors)
- `aria-hidden="true"` for decorative elements (icons next to text labels)
- `aria-expanded` for toggleable elements (dropdowns, accordions)
### Color and Contrast
- WCAG AA: 4.5:1 for normal text, 3:1 for large text
- Never use color as the sole indicator — pair with icons, text, or patterns
- Test in both light and dark modes
### Images and Media
- Descriptive `alt` text for meaningful images: `alt="Dashboard showing 23% revenue growth"`
- Empty `alt=""` for purely decorative images
- Captions for video, transcripts for audio
### Navigation Aids
- **Skip link**: first focusable element, hidden until focused:
```html
<a href="#main-content" class="sr-only focus:not-sr-only">
Skip to main content
</a>
```
- **Heading hierarchy**: sequential h1 through h6, no level skips. One `<h1>` per page.
### Touch Targets
- Aim for 44x44px touch targets; WCAG 2.2 AA minimum is 24px with exceptions
- 8px minimum spacing between adjacent touch targets
- Extend small visual elements with invisible padding or pseudo-elements
### Testing
- **Automated**: relevant axe-core checks when available; a Lighthouse score alone does not prove conformance
- **Manual**: full keyboard-only navigation test
- **Screen reader**: test with VoiceOver (macOS) or NVDA (Windows)
- **Visual**: zoom to 200%, check nothing breaks or overlaps
> Reference `accessibility-checklist.md` for the full audit guide with pass/fail criteria.
---
@@ -1,6 +1,8 @@
# Pre-Delivery Review Checklist
Extended 30-item checklist for UI implementation quality. Run through this before marking any UI task as complete.
Optional polish checklist. Select items relevant to the changed flow. Existing design
conventions override aesthetic suggestions; accessibility requirements remain applicable.
Do not run every item for a small edit.
## Typography (6 items)