Sidenav (app-shell.tsx): - Each NavGroup now carries an icon (Building2 / Database / Plug / MessageSquare / Eye / Sparkles) rendered on the LEFT of the group header, with the chevron moved to the RIGHT. Header typography switched to caption + uppercase + tracking-wider muted, matching pristine-ui's main-branch app-shell. Same change applied to the mobile sheet's group headers. /ai mobile fixes (ai.tsx): - Composer container honors iOS safe-area inset (pb-[max(0.75rem,env(safe-area-inset-bottom))]) so the input clears the home indicator and stays above the soft keyboard. - Composer toolbar wraps on narrow viewports (flex-wrap + gap-y-1) so the agent / model / reasoning / voice chips don't clip. - Empty-state card uses px-4 sm:px-8 instead of hard px-8. - MessageRow's 56px turn-number gutter collapses below sm: prose flows full-width on phone, two-column layout returns at sm+. /ai desktop centering: - Console wrapper opts out of AppShell's [&>*:first-child]:lg:pr-72 (the page-header clearance for the floating top-right pill) via lg:!pr-0. The /ai surface has no top-right page-header controls, so the inherited padding was shifting the chat column ~144px left of the visible viewport center. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
components/
Component layers in this project.
ui/ shadcn primitives — token-driven, reskinnable per design system
forms/ composed form widgets
data/ data display (tables, filters, empty states)
layout/ app shell, page chrome, navigation wrappers
marketing/ landing and marketing blocks
[system]/ design-system-specific components (e.g. m3/, apple/)
The one rule
Custom components import from ui/, never the reverse.
ui/ is the primitive layer. It must stay reskinnable by swapping tokens in
app/themes/*.css alone. If a component can't be expressed that way (M3
ripple, Apple segmented control, etc.), it belongs in a system-specific
folder — not ui/ and not the shared folders above.
Tokens, not values
Every custom component should reference semantic tokens:
- Colors:
bg-primary,text-muted-foreground,border-border - Radius:
rounded-md,rounded-lg - Fonts:
font-sans,font-heading
Hardcoded hex, oklch, or px values are a bug — they break theming.