Use when building or restyling the AUTHENTICATED MockForge app (app.mockforge.dev / crates/mockforge-ui) — dashboards, data tables, settings, forms, and multi-step wizards. This is the product-UI complement to the marketing taste-skill (design-taste-frontend / mockforge-marketing-design), which explicitly excludes dense product UI. Invoke before adding or changing admin-UI components.
Installation
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Use when building or restyling the AUTHENTICATED MockForge app (app.mockforge.dev / crates/mockforge-ui) — dashboards, data tables, settings, forms, and multi-step wizards. This is the product-UI complement to the marketing taste-skill (design-taste-frontend / mockforge-marketing-design), which explicitly excludes dense product UI. Invoke before adding or changing admin-UI components.
MockForge Product UI
For the authenticated product app (the admin UI in crates/mockforge-ui/ui, served at app.mockforge.dev): dashboards, data tables, settings panels, forms, multi-step wizards, detail views.
Not for marketing/landing/docs pages — use mockforge-marketing-design (the taste-skill) for those. The taste-skill is explicitly out of scope for dashboards and tables (its Section 13); this skill fills that gap.
Shares the brand (burnt-orange #D35400, slate-navy #2C3E50, Geist + JetBrains Mono) so the app feels like the same product as the site — but product UI is denser, status-rich, and state-complete, which changes several rules.
0. The real stack (build on it, don't fight it)
Confirmed from crates/mockforge-ui/ui/package.json + source:
Component primitives: Radix UI — dialog, dropdown-menu, select, switch, tabs, toast, context-menu, slot. Plus CVA (class-variance-authority) + clsx + tailwind-merge. That combination is the shadcn/ui pattern: own your components, built on Radix + Tailwind, variants via CVA, classes merged via a cn() helper.
Icons: lucide-react. (Note: the marketing site uses Phosphor; the app uses lucide. Keep one family per surface — in the app, use lucide, do not introduce Phosphor.)
No @tanstack/react-table yet — complex tables are hand-rolled. No react-hook-form — forms use local state + zod.
0.A MUI is already converged via a shim — do not "migrate" it
~22 screens still import { ... } from '@mui/material' / '@mui/icons-material', but real MUI is NOT installed.vite.config.ts aliases those imports to in-repo shims:
@mui/material → src/components/ui/mui-shim.tsx (~2200 lines reimplementing the MUI API on shadcn/ui + Tailwind + the brand tokens, e.g. sx color keywords map to hsl(var(--primary))).
So those screens already render as Tailwind/shadcn with brand tokens — the app is one system under the hood. Verified live: dashboard and shim-backed pages (e.g. Hosted Mocks) render on-brand with correct empty/error states. Directive:
Never reinstall real @mui/*. If a @mui/*-imported component misbehaves or looks off, fix it in the shim (one file, fixes every screen) — do not reach for the real package.
Build genuinely new UI directly against the native Tailwind/Radix/CVA components (skip the shim).
Migrating a screen's imports from @mui/material onto native components is optional cleanup, not a consistency fix — the shim already makes it Tailwind. Only do it when you're touching the screen anyway and it simplifies things. "Net MUI usage" is cosmetic (import paths), not a real second design system.
0.B Dependency verification
Before importing a new library, check package.json. Prefer the libraries already present (Radix, lucide, sonner, chart.js, TanStack Query, zustand, zod). For complex tables, add @tanstack/react-table (headless, pairs with the existing system) rather than a heavyweight grid — output the install command first.
1. Dials for product UI (different from marketing)
Marketing baseline is VARIANCE 8 / MOTION 6 / DENSITY 4. Product UI is the opposite shape:
DESIGN_VARIANCE: 3 — predictable, aligned, scannable. Users operate this daily; surprise is a cost, not a delight. Symmetric grids, consistent card/section rhythm.
MOTION_INTENSITY: 2-3 — functional only: state transitions, optimistic feedback, skeleton shimmer, menu/dialog enter. No scroll-reveal, no decorative animation — data must be visible immediately, not faded in on scroll.
VISUAL_DENSITY: 6-7 — tight, information-rich. py-3/py-4 section rhythm, compact tables, text-sm as the workhorse size. Whitespace is earned, not default.
2. Tokens & color (status-rich, unlike marketing)
The marketing "max one accent" rule does not apply. Product UI must communicate state, so it needs a small, fixed semantic palette:
Neutrals: slate/zinc ramp. Surfaces, borders, text. Body text darker than marketing (readability at small sizes): text-slate-700/text-slate-200 (dark).
Semantic (lock these, use everywhere consistently):
success = emerald-600 / emerald-400 (dark)
warning = amber-600 / amber-400
error/destructive = red-600 / red-400
info = blue-600 / blue-400
Never rely on color alone for status — pair with an icon and/or label (a11y + colorblind). A green dot needs "Healthy" next to it.
Radius: one scale — inputs/buttons rounded-lg (8px), cards/panels rounded-xl (12px), pills rounded-full. Be consistent.
Dark mode is first-class (operators work at night). Tailwind 4 dark strategy as configured in the app; design and test both modes. Off-black surfaces (zinc-900/950), never pure #000.
3. App shell & page structure
Shell: left sidebar nav (sections + active state in brand-orange, collapsible on small screens) + slim top bar (context, environment switcher, account/notifications) + scrollable content region. Sidebar/topbar are persistent chrome; only the content region scrolls.
Page header: page title (left) + primary actions (right, primary action is the orange button, secondaries are outline/ghost). Optional breadcrumb above title. Keep on one row at desktop.
Content width: product content is full-width within the shell (not max-w-7xl centered like marketing). Use a comfortable inner max (max-w-[1600px]) only on very wide monitors so line lengths and tables stay sane.
Filters/toolbar: above tables/lists — search, filter chips, view toggles, density toggle. Sticky if the list is long.
Pick the right engine: simple, static, < ~50 rows → semantic <table> with Tailwind. Sorting / filtering / pagination / column control / virtualization → @tanstack/react-table (headless; you keep full Tailwind control). Don't hand-roll sort/filter/paginate logic that TanStack Table already solves.
Header: sticky (sticky top-0), subtle background (bg-slate-50/dark:bg-slate-900), uppercase-small or medium-weight labels, sortable columns show a lucide chevron/arrow and are keyboard-activatable.
Rows: separate with divide-y hairlines (one separator between rows — notborder-t+border-b on every row). Row hover (hover:bg-slate-50/50) for scannability. Row height compact by default; offer a density toggle (comfortable/compact).
Alignment & type: text left; numbers/IDs/dates right-aligned and font-mono with tabular figures (tabular-nums) so columns line up. Truncate long cells with truncate + title/tooltip; don't wrap unpredictably.
Row actions: a trailing actions column — an icon button opening a Radix DropdownMenu, or inline icon buttons for 1-2 frequent actions. Destructive actions confirm (see §5).
Selection: checkbox column with a header select-all; show a bulk-action bar when ≥1 selected.
States (mandatory — never ship only the happy path):
Loading: skeleton rows that match column widths (not a centered spinner).
Empty: a composed empty state — icon + one line of what this is + a primary action to populate it. Distinguish "no data yet" from "no results for this filter" (the latter offers "Clear filters").
Error: inline error row/panel with a retry, plus a sonner toast for transient fetch failures.
Pagination or virtualization: paginate, or virtualize (TanStack Virtual) past ~100 rows. Never render thousands of DOM rows.
No giant fixed-track progress bars or decorative dots in cells — show real values; a tiny inline bar without a heavy background track is fine for ratios.
5. Forms & multi-step wizards
Field layout: label above input (gap-1.5), helper text below in muted, error text below in red-600/dark:red-400 with an icon. Never placeholder-as-label.
Components: Radix Select/Switch/Tabs/Dialog/RadioGroup-style built with CVA; consistent input styling (border, focus:ring-brand-orange/40, disabled state). Reuse the existing input components — don't restyle inputs per form.
Validation:zod schema per form; validate on blur + on submit; show all errors inline; focus the first invalid field. Disable submit while invalid only if it won't trap the user — prefer showing errors on submit attempt.
Async submit: button shows pending state (spinner + disabled), use TanStack Query mutations; on success a sonner success toast + navigate/close; on error inline + toast. Optimistic updates where safe, with rollback on failure.
Destructive actions: Radix AlertDialog confirmation ("Delete mock server X?") with the destructive button in red and the safe action focused by default. Never delete on a single un-confirmed click.
Multi-step wizards:
Show a stepper (numbered steps with current/done/upcoming states; done steps get a check). Steps are labeled by their content ("Choose protocol", "Configure", "Review"), not "Step 1/2/3".
Validate per step before allowing Next; keep entered data when going Back (don't reset state). Persist wizard state in a store (zustand) or form state so a misclick doesn't lose work.
Back/Next at the bottom; primary (Next/Finish) on the right. Final step is a Review summary before the committing action.
Long/optional flows: allow "Save draft" / resume where it makes sense.
6. Every state, always (the product discipline)
For any data-driven view, implement the full set — this is the #1 thing that separates product UI from a demo:
Loading → skeletons shaped like the final content (cards, table rows, chart placeholder). No bare spinners for full sections.
Empty → composed, actionable, distinguishes empty-dataset vs empty-filter.
Error → inline, with retry; transient ops also toast (sonner).
Partial/stale → TanStack Query isFetching background refresh indicator; don't flash the whole page on refetch.
Success / optimistic → immediate feedback, rollback on failure.
Permission/disabled → explain why a disabled action is disabled (tooltip), don't just grey it out silently.
7. Dashboards, metrics & charts
Stat tiles: label + large font-mono tabular-nums value + optional delta (▲/▼ with semantic color + the comparison period). Real data only — no fabricated precision (92.4%, 4.1×) unless it comes from the API; mark mocked sample data clearly in dev.
Charts:react-chartjs-2. Primary series in brand-orange; multi-series use a defined categorical palette (don't random-pick colors per chart). Always: axis labels, units, legend when >1 series, accessible tooltips, empty/loading states. Respect dark mode (grid/axis colors). Don't chart-junk — no 3D, no needless gradients.
Card grid: consistent card (rounded-xl border bg-white dark:bg-slate-900 p-4/6 shadow-sm); align tiles to a grid; group related metrics. Cards earn their elevation — for pure data, hairline divide/border sections can beat boxes.
8. Navigation, feedback, motion
Sidebar nav: sections with clear active state (orange left-border or filled pill), lucide icons + labels, collapsible; keyboard navigable. Current route highlighted.
Tabs: Radix Tabs for in-page section switching; don't deep-nest tabs-in-tabs.
Toasts (sonner): transient confirmations and async errors. Don't toast things the user can already see; don't use toasts for errors that belong inline in a form.
Motion: functional and quick (120–200ms). State changes, dialog/menu enter (Radix-driven), optimistic toggles, skeleton shimmer, layout shifts on list reorder. No scroll-reveal, parallax, or decorative loops in the app. Honor prefers-reduced-motion.
9. Accessibility (product owes more than marketing)
Radix primitives give you focus management, roving tabindex, ARIA, escape/dismiss — use them instead of hand-rolling menus/dialogs/selects.
Keyboard: every action reachable; tables support arrow/tab navigation for interactive cells; visible focus rings (focus-visible:ring-2 ring-brand-orange/50).
Contrast: WCAG AA for text and UI; check small text-sm/muted text especially in dark mode.
Status never by color alone (icon + label). Forms: label association (htmlFor/id), aria-invalid, error text linked via aria-describedby.
10. Reuse & consistency
Use the project's cn() (clsx + tailwind-merge) helper and CVA variants; don't invent one-off class soups that drift from existing components.
Extend the shared component library (button/input/card/dialog/table) rather than restyling inline per screen.
Keep tokens in the Tailwind config / CSS variables; reference them, don't hardcode hexes in components (except the documented brand values).
When in doubt, match an existing well-built screen rather than inventing a new pattern.
11. Pre-flight check (run before shipping product UI)
Built in the Tailwind + Radix + CVA system; real MUI never reinstalled (any @mui/* imports resolve to the in-repo shim — fix issues there, §0.A)?
Icons are lucide (not Phosphor); one family in this surface?
All states present: loading (skeleton), empty (actionable, filter-aware), error (inline + toast), success/optimistic, disabled-explained?
Tables: sticky header, divide-y (not double borders per row), numbers right-aligned font-mono tabular-nums, row actions in a menu, destructive confirmed, paginated/virtualized past ~100 rows, TanStack Table for sort/filter?
this skill: low variance, dense, lucide icons, semantic status palette, every-state discipline, functional motion.
Same brand, opposite ergonomics. If a screen is a marketing surface that happens to live behind auth (e.g., an upgrade/billing splash), the marketing taste rules can apply to that splash specifically — but the surrounding app chrome stays product-UI.