Skip to content

Foundations

App audit

Every component in the app on main, and what happens to it. This is the list the migration is driven from, and the reason the library grew a Layout and a Marketing category: the landing nav, the app nav and four sidebars had each independently decided what a header is.

App components

50

Audited

Already covered

16

Delete the app copy

New library parts

15

Built for this

Compose in the app

8

No new CSS

Already covered New in the library Compose in the app Stays in the app

“Stays in the app” is not a lesser verdict. A design system that ships your editorial planner has stopped being a design system. What changes for those rows is that none of them should write CSS any more — every one is expressible as library parts plus layout, and that is the actual test of whether this library is finished.

Site chrome

The marketing shell. Every one of these was hand-written CSS against global classes defined in a stylesheet somewhere else, which is why the landing nav and the app nav had drifted into two different ideas of what a header is.

In the appVerdictUse
SiteNav The burger menu was a non-modal <dialog> with a hand-rolled scrim button and no focus containment. Now showModal, so Escape and the top layer come from the platform. New in the librarySiteHeader
SiteFooter Link columns become data. The oversized wordmark keeps its aria-hidden; the link colour comes up from 45% white to a token that passes. New in the librarySiteFooter
LegalFooter Compose in the appSiteFooter legal snippet
CookieBanner Reject now matches Accept in size, variant and weight at every width — including the narrow one, where banners usually start cheating. New in the libraryCookieBar
LangToggle Already coveredSelect or SegmentedControl
TopbarCta Compose in the appButton + SiteHeader cta snippet
PageHead Meta tags. Not a UI component at all. Stays in the app

App shell

Five components were splitting one job between them: two sidebars, two mobile navs and a top bar, each with its own collapse logic and its own idea of which element scrolls.

In the appVerdictUse
AppSidebar, DashboardSidebar, BrandsSidebar, SettingsSidebar Four rails, one component. Collapse is a width change on a variable rather than four different trees. New in the librarySidebar + SidebarSection + SidebarItem
DashboardMobileNav, BrandsMobileNav The drawer is the same rail, not a second component to keep in sync. New in the librarySidebar overlay
PageTopBar The overflow-into-a-menu behaviour was the good idea worth keeping; it now measures its own width rather than the viewport, because the bar is the sidebar’s complement. New in the libraryTopBar
LegalLayout, ToolPage Compose in the appAppShell + TopBar
WorkbenchTabBar Already coveredTabs + TabPanels
BrandProjectSwitcher, ScopePicker Compose in the appMenu + MenuItem + Avatar
AppEntryShimmer, WorkbenchPageShimmer Already coveredSkeleton

Micro components

The small ones, where a library earns most of its keep: each was five to forty lines in the app, each got one accessibility or typographic detail wrong, and each was about to be written a second time.

In the appVerdictUse
AnimatedNum, PlanStatNum, StatsTiles, PerfCards, ToolStats Tabular figures, a delta that names both its direction and whether that direction is welcome, and an optional Sparkline at the same width. New in the libraryStat (+ Counter)
PresenceStack Same two-face cap, but the overflow count is now derived from the same array as the faces, so "+3" cannot disagree with what is rendered. New in the libraryAvatarGroup
status pill (in SiteFooter), WarningCenter dots A bare coloured dot fails SC 1.4.1. The label is now mandatory; labelHidden moves it out of the layout, never out of the accessibility tree. New in the libraryStatusDot
ToolKeywordTable, PostRow, MediaReviewStatsPanel Native markup, wrapped. The scroll region is focusable and labelled, which is what lets a keyboard user reach an off-screen column. New in the libraryTable
(none — this was missing everywhere) New in the libraryBreadcrumb
(empty lists were ad-hoc paragraphs) New in the libraryEmptyState
VideoScoreRing Already coveredCircularProgress
PlatformMixBars, LeadsTrend A ranked list is a horizontal bar with the label on the same line, not a div with an inline width — that version loses the value column and the meter semantics. New in the libraryRankBars, LineChart
PublishHeatmap Sequential ramp, a scale legend, and empty cells that are visibly not zero. New in the libraryHeatmap
ChatQueueChip Already coveredChip + Dialog
IconRow Compose in the appChip + Tooltip
BrandMark, PlatformGlyph, AiSurfaceGlyph, PixelPattern Brand assets. A design system should not ship somebody’s logo. Stays in the app

Chat

Done in the previous pass — the app’s own chat was the reference the library components were rebuilt against.

In the appVerdictUse
ChatPrompt Already coveredPromptInput
ChatColumn, ChatWidget Compose in the appChatMessage + AppShell
ChatToolChips Already coveredChatToolCall
ChatLiveStatus Already coveredChatThinking + ChatToolCall
ChatMessageActions, ChatUserMessageActions Already coveredChatMessage actions snippet
compaction divider (in the thread page) Already coveredChatCompaction
ChatImageLightbox A zoomable lightbox is a Dialog with one image in it. Compose in the appDialog
ChatSessionMemory, ChatSources, ChatPlanCard, ChatQuestionsCard Stays in the appAccordion, Card, List

Overlays, forms and feedback

Mostly already covered. The app is on shadcn-svelte primitives here, so the migration is a rename plus deleting the wrapper.

In the appVerdictUse
ui/dialog, ui/sheet Already coveredDialog, BottomSheet
ui/dropdown-menu Already coveredMenu + MenuItem
ui/tooltip, ui/collapsible, ui/tabs Already coveredTooltip, Accordion, Tabs
ui/input, ui/label, ui/select Already coveredTextField, Field, Select
ui/badge, ui/card, ui/avatar, ui/separator, ui/skeleton Already coveredBadge, Card, Avatar, Divider, Skeleton
ui/sidebar The shadcn sidebar carries its own context, provider and cookie persistence. Replaced wholesale. New in the librarySidebar
SetupDialog, DeleteBrandDialog, ConnectClaudeDialog, CreateContentModal Compose in the appDialog + Field + Button
PromptHistoryDrawer Already coveredBottomSheet or Sidebar overlay
PageTipsBubble, GuidedTour, OnboardingChecklist Stays in the appPopover, List, Progress

Product surfaces

These stay in the app: they are business logic, and a design system that ships them stops being a design system. What changes is that none of them should write CSS any more — every one is now expressible as library parts plus layout.

In the appVerdictUse
PostCard, PostRow, TopPostCard, PostEditor Stays in the appCard, Table, Field
EditorialPlanCards, PlanCards, PlanDocumentView, PlanSidePanel Stays in the appCard, List, BottomSheet
AdsOverview, AdsCampaignList, AdsStats, AdsReadiness Stays in the appStat, Table, Progress
GrowthReadiness, HubOverview, HubOverviewCard, StrategyTabs Stays in the appCard, Tabs, Stat
KnowledgeGraph, KnowledgeConnectors, PublishHeatmap Stays in the appCustom viz on tokens
VideoReviewPanel, VideoScoreNote, MediaReviewStatsPanel Stays in the appCircularProgress, Table
DashboardMockup, ServiceMockup, SocialPostMockup, HowItWorks, WhyUs, PainLanding, LandingFaq Page-specific marketing copy and layout. The showcase pages demonstrate the pattern; the content is not the library’s business. Stays in the appMarketing sections

What is still open

Named honestly rather than quietly skipped. None of these blocked a row above; all of them would earn their place.

  • Donut / part-to-whole — deliberately not built yet. Every caller so far is comparing close values, which is the one thing a donut is worst at; RankBars with share answers the same question and can be read. It goes in when a real at-a-glance split of ≤ 4 segments turns up.
  • Drawer — a side sheet for desktop. Sidebar overlay covers the navigation case, and BottomSheet covers the phone case, but a right-hand inspector panel is neither.
  • Steps — onboarding checklists and guided tours. Three app components want it; none of them agree on what a step is yet, which is a reason to wait.
  • FileDropzone — the upload surfaces share no code today, and each one handles drag state slightly differently.
  • PricingCardPlanCards is the only caller, so it is a marketing section for now, not a component.