26

Every dashboard prompt I tried gave me the identical gray-card-with-a-number-and-a-tiny-line-chart layout, and a table that was just a styled div with none of the behavior that makes a table useful.

I rewrote the ask to demand real table behavior (sort, filter, paginate, sticky header, keyboard nav) with a distinct visual point of view I got to name, and to build it around a typed column definition so I can reuse it. It came out looking intentional and actually worked with 5k rows.

How do you get variety in the look without it drifting into something unusable?

THE PROMPT
Build a reusable data table component for a React + TypeScript dashboard. Behavior first, then a deliberate visual identity, NOT the default shadcn gray-card look.

API: driven by a typed `ColumnDef<T>[]` where each column declares key, header, an optional cell renderer, sortable, and align. The table is generic over the row type T; no `any`.

BEHAVIOR (all required, no placeholders):
- Sorting: click header to sort asc/desc/none, multi-column with shift-click. Sort is stable.
- Filtering: a per-column filter and a global search box.
- Pagination: page size selector; must stay smooth at 5,000 rows (virtualize the body if needed).
- Sticky header, resizable columns, and full keyboard navigation (arrow keys move the focused cell, Enter activates).
- Empty, loading, and error states are first-class, not afterthoughts.

ACCESSIBILITY: proper table semantics (role, scope, aria-sort on sorted headers). Focus is visible and never trapped.

VISUAL IDENTITY: pick ONE point of view and commit (e.g. dense terminal-inspired, or airy editorial with generous line-height). Justify the type scale and spacing. Do not output generic gray cards.

CONSTRAINT: strict TS, builds clean, and works with an unknown-in-advance row shape. Deliver the component, the ColumnDef type, and a usage example with 3 columns.
"pick one point of view and commit" is the anti-slop line i needed. the gray boxes are gray because nobody told the model to have an opinion.designbynight 1 month ago
add a comment

3 Answers

22

The typed ColumnDef is the reusable core, that's the part I'd keep across projects. On variety without breaking usability: constrain the visual freedom to tokens, tell it to pick the identity but express it only through a small set of design tokens (type scale, spacing unit, one accent, border treatment). That way 'editorial' vs 'terminal' changes the tokens, not the layout logic, so it can't wander into something unusable.

THE PROMPT
Constrain the visual identity to a design-token set: one spacing unit, a modular type scale, one accent color, and a border style. The identity is expressed only by these tokens so behavior and layout stay constant.
16

Good that a11y is in the prompt and not bolted on. Two things I'd tighten: aria-sort must be set to 'none' explicitly on sortable-but-unsorted columns (not just omitted), and if you virtualize the body, screen readers lose the row count, so add aria-rowcount/aria-rowindex on the virtualized rows. Otherwise the keyboard story is great but the SR story quietly breaks at 5k rows.

12

Resizable columns plus sticky header plus virtualization is a real layout trap, they fight over the same widths. Add a rule that column widths come from a single source of truth (a widths array in state) that both the header and the virtualized rows read from, otherwise the header and body drift out of alignment the moment you resize. Learned that one the hard way.

Your Answer