UI Component Library¶
Design system¶
shadcn/ui, configured via components.json:
shadcn isn't a library you install — it's a CLI that copies component source into your repo, so each component is editable. The 50 primitives under src/components/ui/ are all generated this way, then customised in place.
CSS approach¶
- Tailwind 3 as the primary styling layer
- Design tokens via CSS variables (HSL palette in
src/index.css), wired intotailwind.config.tsascolors: { background: "hsl(var(--background))", ... } class-variance-authority(cva) +tailwind-merge(cn(...)) for variant-based component APIs (e.g.,<Button variant="destructive">)tailwindcss-animatefor keyframe animations- Two
.cssfiles outside Tailwind:App.css(largely empty),pages/Accounts.css(page-specific overrides) - Dark mode:
darkMode: ["class"]configured; no theme toggle in the UI at audit time
Font¶
fontFamily: { sans: ["SF Pro Display", "-apple-system", "BlinkMacSystemFont", "system-ui", "sans-serif"] }
SF Pro Display is Apple's system font. It is not bundled — there's no @font-face rule in the repo. The browser uses it on macOS / iOS only; on other systems it falls back to the next entries.
Component inventory¶
Primitives (src/components/ui/ — 50 files)¶
accordion alert-dialog alert aspect-ratio avatar
badge breadcrumb button calendar card
carousel chart checkbox collapsible command
context-menu custom-button dialog drawer dropdown-menu
form hover-card input-otp input label
menubar navigation-menu pagination popover progress
radio-group resizable scroll-area select separator
sheet sidebar skeleton slider sonner
switch table tabs textarea toaster
toast toggle-group toggle tooltip use-toast
custom-button.tsx is the only non-default — a project-specific extension on top of shadcn's button.
App components¶
src/components/
├── auth/
│ ├── LoginForm.tsx
│ └── ProtectedRoute.tsx
├── layout/
│ ├── AppLayout.tsx # sidebar + main area shell
│ └── Sidebar.tsx # nav + logout + account switcher (uses lucide icons, shadcn Popover/Tooltip)
└── shared/ # small reusable bits
This is a small, healthy component tree. No prop-drilling-from-hell observed in spot-checks.
Forms¶
react-hook-formfor form state@hookform/resolvers+zodfor validation- shadcn
<Form>(which composesFormProvider+FormField+FormLabel+FormMessage) as the canonical pattern
Adoption is partial at audit time — verify whether every form (Login, profile edit, account create/edit, etc.) goes through react-hook-form + zod. Some pages may still use raw useState + manual validation.
Theming¶
- Light theme defined via
--background,--foreground,--primary, etc. CSS vars insrc/index.css - Dark theme class support is wired up (
darkMode: ["class"]in Tailwind config) but no theme switcher exists in the UI next-themesis inpackage.json(^0.3.0) but not yet mounted anywhere I can see at audit time — declared, not used
Storybook / sandbox¶
None. There is no isolated component-development environment. Engineers iterate on components by mounting them in a page and refreshing.
Patterns to follow¶
When adding a new UI component:
- Prefer extending an existing shadcn primitive over writing from scratch (
custom-button.tsxis the pattern) - Variants via
cvarather than per-className props - Always wrap with
cn(...)for className composition - Use lucide icons (already imported throughout) rather than adding a new icon library
- For new forms, use
react-hook-form + zod + shadcn Form— don't roll your own
Recommendations¶
- Add a theme toggle (or remove
next-themesif dark mode isn't planned) - Self-host SF Pro Display OR change the default to something predictable on non-Apple platforms (e.g.,
Inter) - Set up Storybook when the component count exceeds ~75 (currently ~55 — getting close)
- Audit form consistency — pick one pattern (react-hook-form + zod) and apply universally
- Reconsider
Accounts.css— page-specific stylesheets defeat Tailwind's atomic approach. Inline the styles as Tailwind classes if possible.