Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Anti-slop skill for PRODUCT UI - dashboards, data tables, forms, multi-step flows, settings, list/detail, app shells. The agent reads the surface, budgets the frame first, and ships dense interfaces that are correct at every edge case (overflow, long labels, empty/error/loading states, i18n expansion, keyboard/a11y). House-system-first; maps to Carbon/Polaris/Atlaskit/Fluent/Primer/Material3/Radix-shadcn/Ant. The counterpart to taste-skill, which owns landing/portfolio/marketing.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 249% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 249% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 250% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 590% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 1135% | 0% |
> Dashboards, data tables, forms, wizards, settings, list/detail, admin consoles, app shells. NOT landing pages, portfolios, or marketing (that is taste-skill). NOT charts (that is dataviz). NOT native mobile. > Every rule is contextual and mechanical. It fires from the surface you are building, not automatically. Read the surface, budget the frame, then pull only what fits. > Companion contract: taste-skill explicitly hands off "dashboards / dense product UI / data tables / multi-step forms" to "the right tool." This skill is that tool. If a brief is half marketing and half product (a landing page with an embedded live dashboard), use taste-skill for the hero/marketing sections and this skill for the product surface. Never run both on the same component.
Marketing UI lives on first impression. Product UI lives on the hundredth use, under real data, by someone doing a job. The slop failure mode is different: not "templated aesthetic," but prototype that dies on contact with real data - a table that scrolls the whole page sideways, a button that truncates its own label, a single grey "no data" box reused for three different situations, a form that clears itself when the user fat-fingers one field.
Before any code, state: "Reading this as: a \<surface type> for \<user> at \<density>, \<data volume>, consequence \<level>, built on \<design system | house tokens | none>."
Examples:
The single fastest way to make a product screen look like an AI prototype is to wrap every record in a Card with a Badge. Dense data that the user scans, filters, sorts, or selects belongs in rows (Table for columnar, List/Item for single-line), edge-to-edge, with dividers and 32-40px row height. Card is a widget container (KPI tile, chart panel, settings group, gallery entry), never a list-item wrapper. Material3 lists; Carbon] If your first instinct on a list of records is a Card grid, stop and re-read this line.
The one question worth asking is almost always about data volume or consequence (they change the architecture): "roughly how many rows at the high end?" or "is this action reversible?" Everything else, infer from the Product Read and state your assumption. Do not interview.
The model's defaults for product UI are: Card-soup, fixed-width <table> that overflows the page, disabled submit button as the only validation, one generic empty state, z-index: 9999, and a full-page spinner on every load. Each has a specific correct replacement below. When you catch yourself reaching for one, that is the signal to open the matching section.
After the Product Read, set three dials. Unlike taste-skill (VARIANCE / MOTION / DENSITY tuned for expressive marketing), product dials are tuned for information work. Motion is deliberately near-absent in product UI; it is feedback, not decoration.
DENSITY: 2 - 1 = comfortable/consumer, 2 = standard, 3 = compact/cockpit. Drives row height, padding scale, font size, and whether you offer a density switcher.DATA_COMPLEXITY: 2 - 1 = simple list (plain Table/List), 2 = sortable+filterable+selectable table, 3 = enterprise grid (frozen columns, virtualization, column management, live data).CONSEQUENCE: 2 - 1 = informational/read, 2 = reversible mutations, 3 = destructive/irreversible/financial. Drives confirmation defaults, autosave eligibility, error tone, and how loud "unsaved changes" gets.Baseline: 2 / 2 / 2 (a standard SaaS product table with a detail view). Override from the Product Read.
| Signal | DENSITY | DATA_COMPLEXITY | CONSEQUENCE | |---|---|---|---| | "internal admin / ops tool / expert daily driver" | 3 | 2-3 | 2 | | "enterprise data console / grid / analytics" | 3 | 3 | 2 | | "consumer settings / account / onboarding" | 1 | 1 | 1-2 | | "SaaS product dashboard (default)" | 2 | 2 | 2 | | "billing / delete / deploy / permissions surface" | match | match | 3 | | "observability / logs / live metrics" | 3 | 3 | 1 |
| Preset | DENSITY | DATA_COMPLEXITY | CONSEQUENCE | Frame | |---|---|---|---|---| | Internal admin tool | 3 | 2 | 2 | AppShell + SideNav, inspector on select | | SaaS product dashboard | 2 | 2 | 2 | AppShell + SideNav or TopNav + TabList | | Enterprise data console | 3 | 3 | 2 | AppShell + SideNav, full-height grid | | Consumer settings/account | 1 | 1 | 1-2 | AppShell + settings template (nav + form panels) | | Multi-step wizard | 1 | 1 | 2-3 | Centered column, stepper, no distracting chrome |
Use these exact names as global variables in reasoning. DENSITY gates row height and the density-switcher decision (Section 10.A). DATA_COMPLEXITY gates how far down the table deep-dive (Section 7) you go. CONSEQUENCE gates Sections 6.G, 8.E, 8.F, 9.A. Do not invent aliases.
Product UI is almost never hand-rolled CSS. Pick the foundation from the host app first, the archetype second. One system per app. Do not mix Carbon with Fluent, or drop shadcn into a Polaris tree.
If the app already has a design system, that system wins, and your first job is to find out what it actually ships rather than assuming. Most mature systems already solve the dense-surface problems this skill is about, so you rarely drop to raw CSS. Guessing at component names and props is the single most common way an agent produces code that looks right and does not compile.
Before writing UI, resolve each of these against the host system's real API (its CLI, its docs site, or its .d.ts files):
| Need | What to look for | How to resolve it | |---|---|---| | Full-page shell | An app-shell primitive plus its nav slots | System docs "layout" or "app shell" page | | Multi-pane tool / inspector | A layout + side-panel pair, not two floated divs | Docs "layout" page; check whether the panel is resizable | | Columnar data | The system's Table and its sort/select/pagination plugins | Component reference, not a blog example | | Sticky / frozen columns | A dedicated hook or plugin (most systems ship one) | Search the docs for "sticky" or "frozen" before hand-rolling | | Resizable columns | A column-resize hook | Same | | Pagination footer | Table pagination + page-size control | Component reference | | Search + filter toolbar | A toolbar/filter component distinct from plain search | Component reference | | Single-line records | A List / Item pair | Component reference | | Empty region | An EmptyState component | Component reference | | Status / metadata | A status indicator distinct from Badge | Token/status docs |
Workflow, always: find the closest page or block template the system already ships, study its layout skeleton, then read the real props for every component you use. Never invent props. Prefer the system's own layout components over raw <div>s so spacing stays on the system's scale. Use design tokens for every value (var(--color-*), spacing tokens), never raw hex or px. Reserve Badge for counts and enumerated states; status belongs on a status indicator.
The house-system bridge (resolve the tension): the CSS mechanics in Sections 5-10 (scroll ownership, position:sticky headers, frozen-column offsets, z-index tiers, min-width:0) are the contract a mature system's hooks already satisfy under the hood. Inside such a system, reach for the hook or plugin first, because a real sticky-column hook gives you the offset, the solid background, and the shadow divider correctly. Drop to the raw CSS contract only (a) to verify the system did it right when something bleeds through, or (b) when you are outside a design system entirely. Never hand-roll a sticky column with inline styles inside a system Table when the hook exists.
| Archetype / brief | Reach for | Why | |---|---|---| | IBM-style enterprise analytics, dense grids | @carbon/react + @carbon/styles | Most mature data-density + 2x-grid + empty/loading patterns carbon] | | Microsoft / enterprise SaaS, DataGrid | @fluentui/react-components (v9) | Official Fluent 2, Overflow primitives, tokens fluent] | | Atlassian / Jira-style product | @atlaskit/* + @atlaskit/tokens | Semantic spacing tokens chosen by meaning atlaskit] | | Merchant/admin, resource lists | Polaris (@shopify/polaris or web components) | IndexTable, four-states philosophy, error content polaris] | | GitHub-style devtool chrome | @primer/react / @primer/css | PageLayout landmarks, Blankslate, Truncate primer] | | Adaptive/canonical layouts, consumer-ish | @material/web + Material 3 tokens | Window-size classes, list-detail/supporting-pane material3] | | Own-the-code lean internal tool | Radix Primitives + shadcn/ui + TanStack Table | Headless a11y primitives, you own styling radix, tanstack] | | Enterprise React data-dense fast | Ant Design or Mantine + Mantine React Table | Batteries-included Table/Form ant, mantine] | | Lean keyboard-first (Linear/Vercel vibe) | Radix/shadcn + custom tokens | Density + command palette + restraint linear, vercel] |
Honesty rule: if the app is one of these, use the official package and its Table/Form/Dialog. Do not recreate Carbon's data table in raw CSS. Do not import a system's tokens then override 90% of them.
Budget the frame in pixels before writing one line of content. Real applications are built top-down: pick the shell, name its regions, give each an explicit px budget, decide its container policy and responsive behavior, then fill. Content-first layout (write sections, wrap each in a Card) produces a padded scroll column that reads as a prototype. most systems' layout docs]
| Region | Budget | |---|---| | Side nav | 240-280px | | Icon rail (collapsed nav) | 64-72px | | Detail / inspector panel | 340-420px (resizable 320-480) | | Filter / facet rail | 220-260px | | Row height | 32px compact / 40px standard / 48px comfortable dense systems budget 32-40; Carbon ships 5 relative tiers, no fixed px] | | Header / top nav | 48-64px (common app-bar convention) |
Name every region with a real landmark and know the difference primer PageLayout]:
resizable. Overlay it below ~1024px instead of compressing Content.| Archetype | Container policy | |---|---| | Tracker / work tool (issues, tickets, CRM) | Rows only. Grouped edge-to-edge lists, zero cards. | | Console / observability | Card grid for dashboard widgets; Table for everything else. | | Messaging / feed | Rows and bubbles. No cards in the stream. | | Media / gallery | Card grid (ClickableCard) + dense metadata rows in detail. | | Settings / forms | FormLayout sections; Card only to group dangerous/billing actions. |
Banned: a Card per list item (card soup), stacked full-width Cards as page structure, nested Cards, Badge as decoration.
overflow:auto). The page <body> must never scroll horizontally. A wide table scrolls inside its own container, not by pushing the whole app sideways. Sticky headers use position:sticky scoped to that scroll wrapper, never position:fixed to the viewport. carbon, ant table-scroll, tanstack] If two nested elements both scroll the same axis, you have a bug.position:sticky silently does nothing if no ancestor establishes a scroll boundary. A sticky table header requires the table's scroll container to have a real max-height. css mechanics]min-width:auto, which refuses to shrink below content size, so text-overflow:ellipsis silently no-ops and the child pushes the layout wider instead. Any truncating text inside a flex row needs min-width:0 on the text-holding child. This is the most common reason "my ellipsis isn't working." overflow-truncation]container-type + @container, not viewport media queries, for anything that renders in more than one region width. joshwcomeau, responsive-layout]font-variant-numeric: tabular-nums so digits align. Proportional digits in a metrics column is a Tell.Product UI is where real strings arrive: a 90-character file path, a German button label, a customer name that is one word or forty. Truncation is a scalpel, not a default.
Never truncate primary/identifying content: titles, entity names, unique IDs, error and validation messages, page headers, button labels. Truncation is for secondary/supplementary content only (descriptions, secondary metadata, breadcrumb middles). carbon overflow, primer truncate] If an identifying string is too long, wrap it, give it more space, or use middle-truncation that preserves both ends (for paths/IDs) - never a trailing ellipsis that hides the part that distinguishes two records.
A hover-only title tooltip is not an accessible fallback: it fails keyboard-only users, touch users, and speech-recognition users. Provide the full text on focus as well as hover, or expandable inline, or in the detail view. carbon, primer]
Never truncate a button label. Size the button to its content ("hug contents") with a min-width floor for tap-target accessibility. If the label is long (or translated), wrap to at most 2 lines before you ever truncate, and re-evaluate the label copy. A button reading "Save chang..." is broken work. Icon-only buttons need an aria-label and a tooltip. overflow-truncation, i18n]
overflow:hidden; text-overflow:ellipsis; white-space:nowrap; plus min-width:0 on every flex ancestor between the text and the scroll/row container (Section 4). Without the min-width:0 chain, the CSS is inert.
-webkit-line-clamp)All four properties together or none work: display:-webkit-box; -webkit-line-clamp:N; -webkit-box-orient:vertical; overflow:hidden;. Never put padding on the clamped element itself - the clamp math ignores padding and text bleeds. If you need padding, wrap the clamp in a padded parent, or use a fixed-height fallback (line-height * N). css-tricks line-clampin]
Real data is 0, 1, or 1,000,000 - design all three, not just the demo's "3."
overflow-wrap:anywhere, never a trailing ellipsis that hides the distinguishing tail, and never let the token blow out its column width.minimumVisible floor, move the rest into a "More" menu, render in reverse DOM order so the first item wins. fluent]Every data surface has more than a happy path. Design the full matrix at three granularities: page, component/region, and field. The slop failure is reusing one grey box for every non-happy state.
For any region that loads data, design ideal / loading / empty / error before you ship the ideal. polaris four-states, nngroup]
Never reuse one empty component/message across these:
carbon empty-states, atlaskit empty-state] For tiny tiles/side-panels, a centered text-only empty state is the exception to "mirror the layout."
A load failure is always an ErrorState, never an EmptyState. Error copy is specific, plain-language, actionable (what to do next), and carries an error code/id for support. Route all of them through one shared ErrorState component so tone and layout stay consistent; never ship a bare "An error occurred." polaris error-messages, nngroup]
Fields have their own micro-states: pending async validation (Section 8.D), inline error, success confirmation, saving/saved (autosave, Section 8.E). Do not let a field-level failure silently no-op.
Between "loaded" and "failed" live the states real networks produce:
Marketing UI has no concept of these; product UI lives on them:
Gate depth by DATA_COMPLEXITY. A simple list needs 7.A-7.B; an enterprise grid needs all of it. Inside a design system, use its Table plus the system's plugins/hooks; the mechanics below are the contract those hooks satisfy, and your guide when you are outside one.
One wrapper owns both axes (overflow:auto) with a bounded height; the page never scrolls sideways (Section 4). Sticky header via position:sticky; top:0 scoped to that wrapper. carbon, ant table-scroll]
position:sticky + explicit left:0 (or right:0) offset + z-index >= 10 (above the scrolling body) + a solid, non-transparent background that also covers hover and selected-row states + an inset box-shadow divider, never a CSS border (borders disappear or double-render under sticky scroll). Miss the solid background and the scrolling content bleeds through the frozen column. ant, mantine-react-table column-pinning, tanstack]
Virtualize past ~100-200 rows. Required DOM shape: two wrappers - outer = viewport-height scrollable, inner = full virtual-content height (a spacer). Never size the <table> element itself to the full virtual height - it distorts row heights and collapses the sticky header (which cannot escape a too-short parent). tanstack virtual #640] Do not attach per-row style objects or per-row menu instances during virtual render (perf death); use columnResizeMode:"onEnd" unless memoized. ant, mantine]
history.pushState() so the back button restores scroll position (>90% of implementations get this wrong). On mobile, load one larger flat batch (15-30 items), never chunk-lazy-load mid-scroll (fetches during active scroll are unreliable). nngroup infinite-scrolling, baymard]Cells hold ragged real data. Truncate secondary cells per Section 5 (with focus-reachable full text), never identifying cells. Right-align numeric columns and use tabular-nums (Section 4). Empty cell = a consistent "not set" placeholder (an em-dash is banned; use "-" or "Not set"), never a blank that looks like a render bug.
Top-aligned labels by default (fastest to scan, best for localization and mobile). Right-aligned labels only for dense enterprise forms where vertical space is scarce. Left-aligned only to deliberately slow the user down on a careful task. Never right-align labels on a localized product (expansion breaks the column). lukew web-form, nngroup web-form-design]
Validate on blur and on submit, not on every keystroke of an incomplete value (do not scream "invalid email" at the first character). The exception is a live positive helper like password-strength. Show inline errors next to the field and an aggregate error summary at the top on submit, never only one of the two. nngroup errors-forms, polaris inline-error]
For forms with 5+ fields, never make a disabled Submit the only feedback - the user cannot tell what is missing. Let them submit, then show the aggregate error count and focus the first error. A disabled Submit is acceptable only on trivial (<3 field) forms. Always preserve user input after a failed submit - never clear the form. forms-flows, nngroup]
Debounce it, and cancel superseded requests with AbortSignal - an uncancelled slow response can overwrite a newer, correct result (username-availability race). forms-flows]
Confirmation dialogs for delete/deploy/charge default keyboard focus to the safe action (Cancel), not the destructive one. Use role="alertdialog", aria-modal="true", and aria-describedby pointing at the warning text. For high-consequence deletes, require typing the resource name to enable the destructive button. On close, return focus to the element that opened the dialog (or the next logical element). w3 alertdialog, radix dialog]
Focus-trap while open, return focus on close, close on Escape, close on overlay click only when non-destructive. Dialog.Title and Dialog.Description are never omitted even if visually hidden (screen readers announce them). radix dialog, w3 dialog-modal]
Default ~5000ms, pause on hover and focus, swipe-dismiss threshold ~50px, dismissible manually. Distinguish foreground announcements (user's own action succeeded) from background ones (something changed) for screen-reader priority. Never put a destructive-confirm inside a toast. radix toast]
Full keyboard contract: arrow-key navigation, Home/End, type-ahead, roving tabindex (Section 10.D). Tab moves between a composite widget and the next content; arrows move within it. Menu/tab items that should stay keyboard-reachable use aria-disabled, not native disabled (which removes them from the tab order). radix, w3 grid]
9999/99999 is a Tell.position:fixed/sticky with a z-index, opacity < 1, transform / filter / will-change naming a context property, contain, container-type, isolation:isolate, top-layer elements. mdn stacking-context]<dialog>, popover API) where available.Offer >= 3 density presets (comfortable / standard / compact) as a real switch that swaps row-height and padding tokens, not a scaled-down zoom. i18n-a11y-density] Carbon ships 5 row heights; pick the set your DENSITY dial needs.
Design containers with headroom for translation, because English is one of the shortest UI languages:
Never hardcode fixed-width text containers (Section 4). Use flex/grid so text reflows instead of clipping. Pseudo-localization (wrap strings in accented padding) is a fast smoke test that catches ~80% of expansion bugs. crowdin, i18n-a11y-density]
Mirror directional icons only (back/forward arrows, chevrons, progress direction). Never mirror universal icons (clock, play, checkmark, logos). Numerals keep LTR shaping even inside RTL text. The primary action button moves to the left (the natural end of an RTL reading scan). Progress bars fill right-to-left. crowdin]
tabindex="0", all others -1; the grid container itself is never a tab stop.w3 grid data-grids] If you use a real design-system Table (Carbon, Fluent, Polaris, Ant), this is handled - do not reinvent it. Reach for the contract only when hand-building a grid.
:focus-visible, never a bare :focus that flashes on mouse click; never remove focus outlines without replacing them.aria-label.01/02/2025.Dense products overwhelm on first run. Reveal power progressively; earn complexity.
Progressive disclosure (the default mechanic):
First-run (mandatory):
Coach marks, tours, checklists (banned unless dismissible):
Empty-to-productive path: every empty state's CTA leads somewhere that actually creates value in <=3 steps. An onboarding checklist that just links to docs is decoration.
When the Product Read matches one of these, scaffold the whole skeleton, not a fragment:
Full component implementations live in the host system, not duplicated here: reach them via your host system's template/skeleton command or its docs (Appendix B). Three high-value skeletons are worked below because they encode the contracts most often gotten wrong; treat them as the canonical shape, adapt to the host system.
Block 1 - Index + detail-drawer (frame-first):
tsx// Budget the frame first: nav 256 | table flex | inspector 380 <AppShell sideNav={<SideNav>{/* nav */}</SideNav>} contentPadding={0}> <Layout> <LayoutContent> <ToolbarTableFilter /* search + status/priority filters + overflow (cap 5) */ /> {/* applied-filter chips row + Clear all (Section 7.F) */} <Table plugins={[useTableStickyColumns, useTableColumnResize /*, pagination */]} /* selection persists by row id across page/sort/filter (Section 7.E) */ /> {/* pagination footer; three empty states + ErrorState wired per Section 6 */} </LayoutContent> <LayoutPanel width={380} resizable={{minSizePx: 320, maxSizePx: 480}} hasDivider isScrollable> {selected ? <DetailFields item={selected} /> : <EmptyState title="Nothing selected" />} </LayoutPanel> </Layout> </AppShell> // Below ~1024px: LayoutPanel overlays Content; SideNav becomes MobileNav.
Block 2 - Table scroll + sticky header + frozen column (the CSS contract a system's hooks satisfy):
css/* ONE wrapper owns both scroll axes; page body never scrolls sideways (Section 4). */ .table-scroll { overflow: auto; max-block-size: 70vh; } /* bounded height => sticky works */ .table-scroll thead th { position: sticky; inset-block-start: 0; z-index: 20; } /* Frozen first column: sticky + offset + z + SOLID THEME-TOKEN bg + shadow divider, never border */ .col-frozen { position: sticky; inset-inline-start: 0; z-index: 10; background: var(--color-surface); /* must also cover hover/selected rows */ box-shadow: inset -1px 0 0 var(--color-border); /* not border-right (double-renders on scroll) */ }
Block 3 - Truncation contract (single-line + multi-line):
css.cell { min-inline-size: 0; } /* required on EVERY flex ancestor or ellipsis no-ops */ .cell .text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .clamp { /* all four props; NO padding on this node */ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
columnResizeMode:"onEnd" unless memoized; virtualize past ~100-200 rows.prefers-reduced-motion for everything.Token-driven, one theme locked per app (same discipline as taste-skill Section 8/11: no region flips to inverted mid-app). Use the host system's semantic layer tokens (var(--color-*), Carbon $layer/$border-subtle, etc.), never hardcoded greys. Test tables, frozen columns (the solid background must be a theme token, or it bleeds in dark mode), overlays, and focus rings in both modes. Set the theme once at the shell root, never per-section.
Distinct from taste-skill's landing-page tells. These are the signatures of a model faking a product screen. Hard bans unless the brief genuinely demands one.
<table> that scrolls the page sideways: no scroll-owning wrapper. (Section 4)title. (Section 5.A-B)disabled Submit as the only validation on a 5+-field form. (Section 8.C)9999, 99999) instead of a tiered token scale. (Section 9.D)disabled on menu/tab items that should stay keyboard-reachable (use aria-disabled). (Section 9.C)—) anywhere visible. Banned in headers, cells, labels, empty/error copy, tooltips, and as the "not set" cell placeholder. Use a hyphen (-). This is the single most-violated Tell; zero tolerance. (taste-skill Section 9.G, house rule)App shell / PageLayout (Header / Content / Pane / Sidebar / Footer) · canonical layouts (list-detail / supporting-pane / feed) · master-detail + inspector · IndexTable / Resource List / DataGrid / Table · frozen (pinned) columns · virtualization · roving tabindex · stacking context · container query · skeleton screen (shape-mirroring) · Blankslate / EmptyState / ErrorState · Overflow / priority-plus · applied-filter chips / faceted search · alertdialog · optimistic vs. pessimistic update · density mode · pseudo-localization · command palette · workspace switcher · job/status tray.
Before touching visual style, audit for the correctness bugs that matter more than aesthetics:
:focus, icon buttons without labels, disabled submit as only signal.Fix these first. Only then apply the density/token/visual polish. Preserve the host design system; do not swap Carbon for shadcn as a "redesign."
taste-skill: marketing hero sections, landing pages, portfolios, editorial, illustration-heavy brand surfaces. If a screen is half product, half marketing, split it: taste-skill for the marketing sections, this skill for the product surface. Never run both on one component.dataviz: chart/graph authoring (color, encoding, axes). This skill covers the table and the dashboard frame around the charts, not the chart internals.Run every box. If any fails, the surface is not done.
role=alertdialog, returns focus on close?:focus-visible, icon buttons labelled, aria-disabled not disabled on keyboard-reachable menu items, 44px/24px targets?<div> for layout, tokens only, Badge for counts only, status via a status indicator?If a single box cannot be honestly ticked, fix it before delivering.
bash# House design system - resolve the real API before writing UI. # Most systems ship a CLI or a docs site; use whichever exists. # 1. find the closest shipped page/block template # 2. study its layout skeleton # 3. read real props for every component (never invent props) # IBM Carbon npm install @carbon/react @carbon/styles # Microsoft Fluent UI React v9 npm install @fluentui/react-components # Atlassian Atlaskit (per-component + tokens) npm install @atlaskit/tokens @atlaskit/dynamic-table # Shopify Polaris npm install @shopify/polaris # GitHub Primer React npm install @primer/react styled-components # Radix Primitives + shadcn/ui + TanStack Table npm install @radix-ui/react-dialog @radix-ui/react-toast @tanstack/react-table npx shadcn@latest add table dialog # Material Web (Material 3) npm install @material/web # Ant Design / Mantine + Mantine React Table npm install antd npm install @mantine/core @mantine/form mantine-react-table
Consolidated primary sources:
Frame / layout
Overflow / truncation
State matrix
Data table
Forms / flows
Overlays / layering / a11y
Tokens / density / i18n
Modern minimal
Other measured skills in the registry, with their headline benchmark lift.