Documentation Index
Fetch the complete documentation index at: https://grantmaster.dev/llms.txt
Use this file to discover all available pages before exploring further.
Component Library and Design System
GrantMaster uses a design system built on Tailwind CSS 4 with design tokens defined in the @theme block of src/index.css. Colors use OKLCH for perceptually uniform scales.
Tabs
- Use
PremiumTabs for section-level tab navigation inside feature pages and settings panels.
- Use
PremiumTabs variant="card" when the tab strip should sit inside a card container.
- Use
PageTabs for top-level page navigation shells only.
PageTabs must be route-driven for major workspace sections. The URL is the source of truth; do not keep the active tab in cross-route component state.
- Always pass an explicit
ariaLabel and a stable idBase when the tab content is rendered externally.
- Render exactly one matching external
tabpanel for the active route. The tab strip owns navigation; the page owns content.
TabNavigation and ModernTabs are retired and should not be used for new work.
🎨 Global Design Tokens
All tokens are defined in src/index.css under @theme. Always use these tokens rather than hardcoded hex values.
Brand Colors (OKLCH)
| Token | Scale | Usage |
|---|
primary-50 .. primary-950 | 11-step OKLCH scale | Brand blue. Anchor: #2e5af5. Never hardcode blue-*. |
success | Semantic | emerald-* equivalent. Approvals, confirmations. |
warning | Semantic | amber-* equivalent. Cautionary states. |
error | Semantic | rose-* equivalent. Destructive/reject. |
info | Semantic | Informational highlights. |
Surface / Neutrals
| Token | Hex (light) | Usage |
|---|
surface-50 | #f8fafc | Lightest background |
surface-100 | #f1f5f9 | Page background |
surface-200 | #e2e8f0 | Card borders |
surface-300 | #cbd5e1 | Dividers |
surface-400 | #94a3b8 | Placeholder text |
surface-500 | #64748b | Muted text |
surface-600 | #475569 | Secondary text |
surface-700 | #334155 | Dark card backgrounds |
surface-800 | #1e293b | Dark mode surfaces |
surface-900 | #0f172a | Dark mode background |
surface-950 | #020617 | OLED black |
These map to slate-* Tailwind classes. Never use gray-*.
Spacing & Radius
| Token | Value | Usage |
|---|
--radius-xs | 0.25rem | Small chips |
--radius-sm | 0.375rem | Small inputs |
--radius-md | 0.5rem | Buttons |
--radius-lg | 0.75rem | Default |
--radius-xl | 1rem | Standard card radius |
--radius-2xl | 1.5rem | Large modals/sections |
Shadows
| Token | Usage |
|---|
--shadow-sm | Subtle elevation |
--shadow-md | Card default |
--shadow-lg | Elevated panels |
--shadow-xl | Modals/overlays |
--shadow-premium | Premium card appearance |
--shadow-glass | Glassmorphism |
Motion Tokens (:root)
| Token | Value | Usage |
|---|
--duration-micro | 150ms | Hover, focus |
--duration-small | 200ms | Toggles, selections |
--duration-medium | 300ms | Modals, page transitions |
--duration-large | 400ms | Complex reveals |
--duration-slow | 500ms | Staggered sequences |
--ease-default | cubic-bezier(0.16, 1, 0.3, 1) | Smooth deceleration |
--ease-bounce | cubic-bezier(0.34, 1.56, 0.64, 1) | Playful overshoot |
--ease-smooth | cubic-bezier(0.4, 0, 0.2, 1) | Material-style |
Note: Decorative CSS animations are globally disabled. Only functional animations (spinners, skeleton shimmer) are whitelisted.
🧱 Core Components
Every main page must be wrapped in <PageLayout> and use <PageHeader> for the title. Never write manual <h1> tags.
For resolver-driven pages, prefer <PageShell> so breadcrumbs, actions, and tabs stay consistent across sections.
- Breadcrumbs should show the ancestor path only. Do not repeat the current tab/page label in the breadcrumb line and the title line.
- The title line should reflect the active route/tab title only once.
- Reuse spacing tokens from the shared shell:
gap-6 between header/content regions, mb-6 below page tabs, and card surfaces from shared primitives rather than local inline values.
Always use <Button> from @/components/ui/button. Never raw <button> with inline classes.
default: Primary action (one per screen).
destructive: Delete/reject.
success: Approve/confirm.
warning: Cautionary.
outline / secondary / ghost / link: Lower emphasis.
- Use
isLoading + loadingText props for async states.
3. Modals & Side-Overs
- Use
Modal for focused, short interactions (e.g., “Confirm Delete”).
- Use
Sheet (right-side panel) for complex data entry or detailed views.
4. Status & Badges
<StatusBadge> with mapDomainStatus() for status indicators.
<Badge> for non-status labels.
5. Cards
Card from shadcn for composable layouts.
CardVariants / StatCard for presets and metric displays.
- Never write card styling inline.
<SecondarySidebar> for left-side sub-navigation on tabbed pages.
- Flat items via
items prop; grouped items via groups prop.
- Enable
searchable for 10+ items.
Governance Matrix (Allowed vs Disallowed)
Use this quick matrix during implementation and review. The design system is the default; raw HTML is the exception.
| Concern | Allowed | Disallowed |
|---|
| Buttons | <Button> variants from @/components/ui/button | Raw <button> in feature files that import Button |
| Text inputs | <Input> / <Textarea> / design-system Select | Raw text-like <input> when Input is available in file |
| Empty states | <EmptyState> or <SurfaceEmptyState> with action copy | Plain "No data" / "No results" text blocks |
| Loading states | SkeletonLoader presets (DashboardSkeleton, TablePageSkeleton, etc.) | Hand-rolled animate-pulse scaffolds in feature screens |
| Page titles | <PageHeader> or <SurfacePageHeader> | Manual page-level <h1> blocks |
| Search/filter bars | <SurfaceToolbar> or approved wrappers (SearchFilterBar) | One-off control rows with custom spacing and state copy |
| KPI strips | <SurfaceMetricGrid> / MetricCard | Ad-hoc stat tiles |
| Status labels | <StatusBadge> + mapDomainStatus() | Local status-to-color helpers |
| Primary color usage | primary-* token scale | blue-* utilities in tokenized files |
| Semantic status colors | emerald-* success, amber-* warning, rose-* danger | green-*, yellow-*, red-* status mapping |
✨ Animations
We use the motion library (npm package motion@^12, formerly Framer Motion) for intentional animations only. Decorative CSS animations are globally disabled in src/index.css.
🛠️ Implementation Rules
- Don’t build it twice: Check
src/components/ui/ before creating a new UI element.
- Accessibility (a11y): Every button must have an
aria-label if it only contains an icon. Use semantic HTML (<nav>, <main>, <aside>).
Route-driven tab strips must expose role="tablist" / role="tab" / role="tabpanel" with linked IDs and keyboard navigation for ArrowLeft, ArrowRight, Home, and End.
Inputs and selects need an associated label or aria-labelledby; placeholders are not labels.
- Dark Mode: Support both light and dark modes. Use shadcn CSS variables for theme-aware colors. Ensure all colors have high contrast ratios (minimum 4.5:1).
- Responsive: Use Tailwind breakpoints (
sm:, md:, lg:). Test every component on a mobile view.
Sidebar, page tabs, and page content must stack cleanly without overlap at narrower widths; horizontal overflow should be intentional and controlled.
- Icons: Use
lucide-react exclusively. Size conventions: w-4 h-4 in buttons/badges, w-5 h-5 in section headers, w-8 h-8 in stat cards.