--- description: "UI/UX standards for {{PROJECT_NAME}}" globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"] alwaysApply: false --- # UI / UX Standards — {{PROJECT_NAME}} ## Theme & design tokens (single source of truth) - Define **one** light/dark `ThemeData` (and optional `ThemeExtension`s for brand spacing, radii, semantic colors). Feature code reads `Theme.of(context)` only. - **Colors:** `colorScheme` / extensions — never hex/`Color(...)` literals in widgets except inside the theme definition file(s). - **Typography:** `textTheme` / `primaryTextTheme` — never raw `TextStyle(fontSize:, fontFamily:)` in feature UI. - **Spacing & shapes:** `ThemeExtension` or documented constants consumed consistently — avoid one-off magic numbers for padding/radius. ## Loading states - Every async operation MUST show a loading skeleton (shimmer), NOT a spinner unless < 300ms - Use `shimmer` package with a shimmer that matches the final layout shape - Never show a blank screen during loading — skeleton must fill the same space as the content ## Empty states - Every list/grid MUST have a distinct empty state widget: illustration + headline + CTA - Empty state is different from error state — never reuse the same widget for both - Empty state copy: positive framing ("No items yet — add your first one") ## Error states - Every async failure MUST show: error message + retry button - Never swallow errors silently - Error text: user-friendly, never expose stack traces or raw API messages ## Navigation & transitions - Use `IndexedStack` for bottom nav tabs — preserves scroll position - Named routes only — never `Navigator.push(context, MaterialPageRoute(...))` - Page transitions: use `CustomTransitionPage` with `FadeTransition` for modal sheets ## Responsive layout - Use `LayoutBuilder` or `MediaQuery` for breakpoints, not hardcoded pixel values - Minimum touch target: 48×48 logical pixels (Material guideline) - Test on 375px (iPhone SE) and 414px (iPhone Pro Max) widths minimum ## Haptics - Use `HapticFeedback.lightImpact()` on primary CTAs - Use `HapticFeedback.selectionClick()` on toggle/checkbox interactions - Never add haptics to destructive actions without confirmation ## Accessibility - All interactive widgets must have a `Semantics` label or `tooltip` - Minimum contrast ratio: 4.5:1 (WCAG AA) - Test with TalkBack / VoiceOver before each release {{HIGH_CONTRAST_UX_LINE}}