--- description: "UI/UX standards for {{PROJECT_NAME}} — always applied" alwaysApply: true --- # UI / UX Standards — {{PROJECT_NAME}} ## 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