UI Development
PopChoice UI work uses one shared design workshop with app-specific stories and a small shared component package. Keep the workflow incremental: document the contract, add stories near the code that owns the workflow, then extract primitives only when reuse is real.
Storybook Topology
Use one Storybook runner for the monorepo. The existing runner lives in apps/web and should load:
apps/web/src/**/*.stories.*apps/backoffice/src/**/*.stories.*packages/ui/src/**/*.stories.*
This is a normal Storybook setup: stories are discovered by glob, so the runner can document components from multiple workspaces. The constraints are operational rather than conceptual:
- Stories must not depend on live production services.
- App-specific providers, CSS, and mocks should be opt-in per story or decorator.
- Shared primitives belong in
packages/ui; domain workflows stay in their owning app. - Backoffice stories should cover dense operator states: long movie names, missing TMDB ids, unavailable queue data, bulk confirmation open, failed jobs, and mobile/narrow widths.
If the single runner becomes too heavy, split later by package composition rather than duplicating the whole setup up front.
Shared UI Package Direction
packages/ui contains source-owned, domain-free primitives that can be consumed by both apps/web
and apps/backoffice. Keep it dependency-light and extract only reusable interface atoms here:
packages/ui
├── src
│ ├── badge.tsx
│ ├── breadcrumb.tsx
│ ├── button.tsx
│ ├── dialog.tsx
│ ├── table.tsx
│ ├── tabs.tsx
│ ├── utils.ts
│ └── storybook
└── package.jsonUse package exports such as:
{
"exports": {
".": "./src/index.ts",
"./button": "./src/button.tsx",
"./badge": "./src/badge.tsx",
"./breadcrumb": "./src/breadcrumb.tsx",
"./dialog": "./src/dialog.tsx",
"./tabs": "./src/tabs.tsx",
"./table": "./src/table.tsx",
"./utils": "./src/utils.ts"
}
}Apps that render shared primitives must include packages/ui/src in Tailwind source scanning and,
for Next.js, list @pop-choice/ui in transpilePackages.
shadcn/ui Policy
Use shadcn/ui as the source-code baseline for common primitives, not as a black-box dependency. The CLI copies component source into the repo, which fits PopChoice because we need operator-dense backoffice variants and branded web variants.
When initializing shadcn in the monorepo:
- Prefer
packages/uias the install target for reusable primitives. - Keep app
components.jsonfiles aligned with the shared package aliases. - Use Tailwind CSS v4 configuration style: leave
tailwind.configempty incomponents.json. - Use the same
style,iconLibrary, andbaseColoracross app and shared configs. - Prefer Radix-backed components unless there is a concrete reason to choose Base UI.
Current shadcn-derived primitives:
Button,ButtonLink,buttonVariantsBadgeBreadcrumb,BreadcrumbList,BreadcrumbItem,BreadcrumbLink,BreadcrumbPage,BreadcrumbSeparator,BreadcrumbEllipsisDialog,DialogContent,DialogHeader,DialogFooter,DialogTitle,DialogDescription,DialogTrigger,DialogCloseTabsNav,TabsLinkTableScroll,Table,DataTable,TableEmptyRow
Next likely primitives:
tooltipfield,input,textarea,select,checkbox,switchalert-dialogfor destructive/batch confirmations when the base dialog contract is not enoughdropdown-menu,popover,command/comboboxfor operator search and filterspagination,skeleton,empty
Do not put domain components such as CatalogHealthOverview, RepairBatchItemsTable, or
ManualMovieMetadataPanel into packages/ui until they have a second real consumer.
Backoffice UI Rules
- Backoffice is wired for Tailwind CSS v4 through
apps/backoffice/postcss.config.mjsandapps/backoffice/src/app/globals.css. - Prefer Tailwind utilities first; add custom CSS only for reusable layout primitives, complex table behavior, or app-level tokens.
- Existing global CSS can remain while screens are migrated incrementally; new components should prove why a custom selector is needed before adding one.
- Each backoffice screen should expose one next operator action before secondary diagnostics.
- Use neutral colors for counts, warning colors for work that needs attention, and green only for genuinely healthy or completed states.
- Keep diagnostics visually quieter than actions.
- Confirmation UI must be contained inside its panel or use a dialog; it must not depend on a narrow action-column width.
- Add stories or browser tests for long labels, unavailable data, open confirmations, and narrow viewports before merging UI workflow changes.
Critique Artifacts
Run $impeccable critique <target> for meaningful UI changes, but keep generated critique
snapshots local. The .impeccable/ directory is ignored because timestamped tool output makes PRs
noisy and ages faster than the implementation. Carry forward only the decisions that should outlive
the run:
- Update
DESIGN.mdfor visual principles, tone, hierarchy, and component rules. - Update this guide for reusable UI workflow or primitive guidance.
- Update
docs/ROADMAP-ARCHITECTURE.mdor GitHub issues for deferred work. - Summarize critique score, fixed items, and accepted follow-ups in the PR description when useful.