## Summary Stacked on `overview-revamp` (now rebased against `dev`). Introduces a first-class `DataGrid` component in `@stackframe/dashboard-ui-components`, migrates every dashboard table off the legacy `DesignDataTable` / hand-rolled `<Table>` pattern to it, and ships a matching dashboard design guide. Since the last writeup the `DataGrid` runtime has been substantially rewritten: the virtualizer now supports `rowHeight="auto"` with `estimatedRowHeight`, every column can opt into `cellOverflow: "wrap"`, the toolbar + header stick under a configurable `stickyTop`, and the seeded dummy data has been fleshed out so the migrated surfaces render with realistic density. The AI-analytics prompt was also extended with full schema docs for the auth / team / email / payments tables so natural-language queries produce better SQL. **Base:** `dev` → **Head:** `ui-fixes-minor` **Scope:** 39 files, ~+6.5k / -2.4k ## Screenshots Captured against the seeded Demo Project on the local dashboard (`admin@example.com` via mock GitHub OAuth). Viewport: **1920×1200** (standard) and **2560×1440** (widescreen). Assets hosted in [this gist](https://gist.github.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9). ### Overview — revamped metrics + line chart | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Users — DataGrid with seeded rows | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Transactions — new DataGridToolbar + sticky chrome | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Teams | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Email Outbox | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Payments — Customers | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Sticky behaviour — scrolled views Grids scrolled down ~600px. The page header is still pinned, and the `DataGrid` toolbar + column header row stay put under it (backdrop-blur + `stickyTop` offset) while the virtualized body rows scroll past. Compare the scrolled view against the top-of-page view above. | Page | Light | Dark | | --- | --- | --- | | Users |  |  | | Teams |  |  | | Transactions |  |  | | Payments Customers |  |  | | Email Outbox |  |  | | Analytics Tables |  |  | ### Other migrated surfaces | Page | Light | Dark | | --- | --- | --- | | Analytics Tables |  |  | | Emails |  |  | | Email Sent |  |  | | Domains |  |  | | Webhooks |  |  | | External DB Sync |  |  | ## What's new ### `DataGrid` in `@stackframe/dashboard-ui-components` A new, fully-typed, fully-controlled grid component under `packages/dashboard-ui-components/src/components/data-grid/`. Single source of truth for tabular UI across the dashboard. Package files: - `data-grid.tsx` — main grid renderer (virtualized rows, sticky toolbar + header) - `data-grid-toolbar.tsx` — built-in toolbar (search, columns, density, export) - `data-grid-sizing.ts` — column width / flex / min-width resolution - `state.ts` — state helpers (`createDefaultDataGridState`, sort / select / paginate utilities, `exportToCsv`, date formatters) - `strings.ts` — i18n string table + `resolveDataGridStrings` - `types.ts` — public types (`DataGridColumnDef`, `DataGridProps`, `DataGridState`, `DataGridDataSource`, etc.) - `use-data-source.ts` — `useDataSource` hook with `client` / `server` / `infinite` modes - `index.ts` — package entrypoint Features: - Controlled state (`state` + `onChange`) covering sorting, pagination, column visibility, column widths, column pinning, selection, date-display mode, and quick search. - Column definitions with `string` / `number` / `date` / `dateTime` / `boolean` / `singleSelect` / `custom` types, custom `renderCell`, custom sort comparators, per-column `parseValue` / `dateFormat`, pinning, align, flex / min / max width. - **Cell overflow control** — new `cellOverflow: "truncate" | "wrap"` per column. `"wrap"` + `rowHeight="auto"` lets rows grow to fit multi-line content. - **Dynamic row heights** — `rowHeight` now accepts `"auto"` with an `estimatedRowHeight` hint for the virtualizer, eliminating scroll-position jank while rows are still being measured. - **Sticky chrome with `stickyTop`** — the toolbar and header stick under a caller-provided offset (matching the page header height) with a proper blur backdrop. See the _Sticky behaviour — scrolled views_ section above for the visual. - Client-side sort + quick-search + pagination via `useDataSource` — consumer never pre-sorts / paginates. - Server-side and async-generator data sources for streaming / cursor pagination. - Paginated and infinite-scroll UI modes. - CSV export + clipboard copy. - Row single / multi selection with shift-range anchor. - Row + cell click / double-click callbacks. - Pluggable toolbar / footer / empty / loading states and i18n strings. ### Dashboard design guide New `apps/dashboard/DESIGN-GUIDE.md`: prescriptive, AI-readable source of truth for dashboard UI. Documents when to use each `design-components` primitive, the `DataGrid` canonical pattern, color / typography / spacing / motion rules, route-specific guidance, and the migration priority. Now also documents the new `cellOverflow` and dynamic-`rowHeight` patterns, and marks `DesignDataTable` as deprecated in favor of `DataGrid` + `useDataSource` + `createDefaultDataGridState`. ### Overview page revamp `apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx` — line chart rewritten on top of the shared `AnalyticsChart` / `DonutChartDisplay` primitives, feeding the revamped Overview. ### Data-table migrations Every shared table under `apps/dashboard/src/components/data-table/` has been rewritten on top of `DataGrid`: - `api-key-table.tsx` - `payment-product-table.tsx` - `permission-table.tsx` - `team-member-search-table.tsx` - `team-member-table.tsx` - `team-search-table.tsx` - `team-table.tsx` - `transaction-table.tsx` — now also wires in `DataGridToolbar` with search / column visibility - `user-search-picker.tsx` - `user-table.tsx` — extracted `USER_TABLE_COLUMNS` for readability / reuse ### Page adoption Page-level tables migrated to `DataGrid` (or the new `useDataSource` + `createDefaultDataGridState` pattern): - `(overview)/line-chart.tsx` - `analytics/tables/query-data-grid.tsx` (now with sticky header) - `domains/page-client.tsx` - `email-drafts/[draftId]/page-client.tsx` - `email-outbox/page-client.tsx` (with `DataGridToolbar`) - `email-sent/page-client.tsx`, `grouped-email-table.tsx`, `sent-emails-view.tsx` - `emails/page-client.tsx` - `external-db-sync/page-client.tsx` - `payments/layout.tsx`, `payments/customers/page-client.tsx`, `payments/products/[productId]/page-client.tsx` - `users/[userId]/page-client.tsx` - `webhooks/page-client.tsx`, `webhooks/[endpointId]/page-client.tsx` - `design-language/page-client.tsx`, `design-language/realistic-demo/page-client.tsx` - `playground/page-client.tsx` ### Backend & supporting changes - `apps/backend/src/lib/ai/prompts.ts` — extends the AI-analytics prompt with detailed schema docs for `contact_channels`, `teams`, `team_member_profiles`, `team_permissions`, `team_invitations`, `email_outboxes`, `project_permissions`, `notification_preferences`, `refresh_tokens`, and `connected_accounts`, so natural-language queries have richer context to compile against. - `apps/backend/src/lib/seed-dummy-data.ts` — additional OAuth providers on seed users, improving dummy-data coverage for the migrated tables (visible on the Users grid). - `apps/dashboard/src/app/globals.css` — adds `--data-grid-sticky-top` token used to derive the grid's sticky offset under the page header. - `packages/template/src/dev-tool/dev-tool-core.ts` — persist the "closed" state when the user closes the dev-tool panel so it doesn't reopen on next load. ## Notes for reviewers - Rebased onto latest `dev`; conflict in `api-key-table.tsx` resolved by keeping the `DataGrid` implementation (consistent with the other migrated tables). - `DesignDataTable` is still in the codebase but marked deprecated in the design guide — new code must use `DataGrid`. - `DataGrid` is fully controlled: consumers must pass state + onChange, must feed `rows` from `useDataSource` (never raw arrays), and must define columns outside the component or via `useMemo`. The guide's §4.12 spells this out. - `rowHeight="auto"` is opt-in; the default fixed-height virtualization path is unchanged and remains the fast path for dense, single-line grids (users, transactions, etc.). - Screenshots are JPEG this round — the local capture tooling's PNG path was producing blank frames, so the new set is `.jpg` end-to-end. Same viewports, same seeded project. ## Test plan - [ ] `pnpm lint` passes - [ ] `pnpm typecheck` passes - [ ] Load the dashboard and verify every migrated surface renders, sorts, searches, paginates, and handles row-click navigation: - [ ] Overview (line chart + donut metrics) - [ ] Users list + user detail (teams, sessions, permissions, API keys) - [ ] Teams list + team detail (members, permissions) - [ ] Domains - [ ] Emails, email-sent, email-outbox, email-drafts - [ ] Webhooks list + endpoint detail - [ ] Payments customers, product detail, transactions (new toolbar) - [ ] External DB sync - [ ] Analytics query table (sticky header) - [ ] Verify infinite-scroll surfaces (domains, etc.) load additional rows on scroll - [ ] Verify sticky header stays below the page header in light and dark themes - [ ] Verify CSV export produces correct output on a representative table - [ ] Verify column resize, visibility toggle, and sort work across themes - [ ] Verify `cellOverflow: "wrap"` rows grow to fit when `rowHeight="auto"` and clip when `rowHeight` is numeric - [ ] Spot-check AI analytics queries against the new schema context (contact_channels, teams, email_outboxes, …) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Unified table components across dashboard with improved infinite pagination and quick search. * **Improvements** * Enhanced table performance with sticky headers and better row height handling. * Improved sorting, filtering, and data loading with consistent state management. * Better visual consistency across all data grids and table layouts. * **UI/Styling** * Refined table styling for better text truncation and content wrapping. * Optimized layout spacing and alignment across dashboard tables. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Developing-Gamer <maxcodes11110@gmail.com> Co-authored-by: Armaan Jain <84474476+Developing-Gamer@users.noreply.github.com> Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
28 KiB
Stack Auth Dashboard Design Guide
This guide defines the source of truth for dashboard UI design and implementation. It is intentionally written for both humans and AI agents.
If this guide conflicts with older examples in the codebase, follow this guide.
1) Core Principle (Non-Negotiable)
Always prefer components from apps/dashboard/src/components/design-components.
- Do not build new ad-hoc visual primitives (for example custom
GlassCard, customChartCard, custom badge pills, custom pill toggles, custom list rows) if a design-components component exists. - If the desired UI can be achieved by tweaking/customizing/extending a design-components component, do that instead of creating a page-local alternative.
- In all cases, default to design-components first; only use a non-design-components approach when there is absolutely no viable way to achieve the result with design-components.
- Use
@/components/ui/*primitives only when no design-components equivalent exists, or when the design-components component intentionally wraps the primitive. - Match existing design-components behavior:
- hover-exit transitions (
transition-* duration-150 hover:transition-none) - glassmorphic surfaces where appropriate
- semantic variants for alerts/badges
- async-safe click handlers via design-components primitives
- hover-exit transitions (
2) Fast Decision Tree
Use this when implementing a new dashboard UI quickly:
- Need a section container/card?
- Use
DesignCard. - For chart-heavy analytics surfaces (especially Recharts tooltips/overflow), use
DesignAnalyticsCard.
- Use
- Need user-facing status/info/warning/error message?
- Use
DesignAlert.
- Use
- Need small semantic label (sent, failed, queued, active)?
- Use
DesignBadge.
- Use
- Need user action button, especially async?
- Use
DesignButton.
- Use
- Need text input/selectors in design-components surfaces?
- Use
DesignInputandDesignSelectorDropdown.
- Use
- Need a segmented/pill switcher?
- Use
DesignPillToggle.
- Use
- Need category tabs with count badges?
- Use
DesignCategoryTabs.
- Use
- Need row/list item with action buttons/menu?
- Use
DesignListItemRow(orDesignUserListfor user rows).
- Use
- Need settings/property grid editor?
- Use
DesignEditableGrid.
- Use
- Need interactive / sortable / searchable data table?
- Use
DataGrid+useDataSource+createDefaultDataGridStatefrom@stackframe/dashboard-ui-components.
- Need dropdown action/selector/toggle menu?
- Use
DesignMenu.
3) Allowed Base UI Usage
@/components/ui/* can still be used for primitives that do not currently have a design-components equivalent:
- dialogs/sheets/popovers (
ActionDialog,FormDialog,Sheet, etc.) - complex layout containers where design-components does not provide one
- highly specialized editor internals
When using a primitive directly:
- keep visual style compatible with design-components surfaces
- do not duplicate a design-components component API locally
- consider creating/extending a design-components component instead of repeating local patterns
3.1) Best Practices (Always Apply)
- Build with design-components primitives first, then add minimal page-level styling.
- Keep components composable: pass data/config via props instead of hardcoding display logic.
- Favor semantic APIs (
variant,color,gradient) over raw class-heavy style forks. - Use accessible defaults:
- clear labels for icon-only controls
- keyboard focus visibility (
focus-visible:*) - semantic roles where applicable
- Keep behavior deterministic:
- one visual language per screen
- one status-color mapping across all routes
- one interaction pattern per control type
3.2) Color System (Light + Dark Theme)
Use semantic tokens and design-components variants first. Avoid ad-hoc hardcoded colors unless there is a documented semantic reason.
Theme token usage priority
- Use component variants (
DesignAlert,DesignBadge,DesignCardgradient, tab/toggle gradients). - Use semantic Tailwind tokens (
bg-background,text-foreground,text-muted-foreground,border-border). - Use opacity layers for subtle surfaces (for example
bg-foreground/[0.03]).
Surface and text rules
- Primary surfaces:
bg-background+ subtle ring/border. - Secondary/muted surfaces: low-opacity foreground overlays.
- Primary text:
text-foreground. - Secondary text:
text-muted-foreground. - Never use pure black/white hardcoded utility values for app UI text/surfaces.
Semantic state colors
- Success: green/emerald (
DesignAlert variant="success",DesignBadge color="green") - Error: red (
DesignAlert variant="error",DesignBadge color="red") - Warning: orange/amber (
DesignAlert variant="warning",DesignBadge color="orange") - Info: blue/cyan (
DesignAlert variant="info",DesignBadge color="blue"or"cyan")
Light vs dark guidance
- Ensure every custom color choice has dark-mode readability.
- In dark mode, reduce high-contrast fills and rely on low-opacity tints + rings.
- Keep contrast high for text and medium for non-critical chrome.
3.3) Typography System
Keep typography concise and consistent. Prefer existing design-components/header patterns.
Recommended scale and usage
- Page title:
text-xl sm:text-2xl font-semibold tracking-tight - Section heading:
text-xs font-semibold uppercase tracking-wider - Body/default control text:
text-sm - Secondary metadata:
text-xs text-muted-foreground - Micro labels/badges:
text-[10px]totext-[11px]
Typography rules
- Use uppercase tracking only for section labels and compact metadata headings.
- Avoid introducing new arbitrary font sizes when an existing size serves the purpose.
- Keep line-length short in cards and alerts for scanability.
- For numeric/stat values, use tabular numerals where needed.
3.4) Spacing and Layout Guidelines
Use a compact, repeatable spacing rhythm.
Spacing rhythm
gap-1(4px): tight icon/text couplinggap-2(8px): compact control spacinggap-3(12px): standard row spacinggap-4(16px): section-internal spacinggap-5(20px): larger section grouping
Padding rhythm
p-2/px-3 py-2: compact controlsp-3: standard compact blocksp-4top-5: card content/major sections
Layout rules
- Use
rounded-2xlfor major containers/cards. - Use
rounded-xlfor controls (inputs, toggles, small cards). - Preserve visual hierarchy:
- page spacing > section spacing > control spacing
- Avoid mixing unrelated spacing scales inside a single component.
3.5) Animation and Micro-Interactions
Motion should feel immediate, subtle, and informative.
Core motion rules
- No hover-enter delay transitions.
- Use hover-exit transitions:
transition-* duration-150 hover:transition-none. - Keep interaction transitions short and subtle.
- Do not use large animated movement in dense admin surfaces.
Duration guidance
50ms-100ms: very small icon feedback150ms: standard hover/focus/press recovery200ms-300ms: layout/state transitions (panel collapse, sheet-like reveals)>300ms: only ambient/non-critical effects
Micro-interaction patterns
- Hover:
- text brightens slightly
- ring/shadow intensifies subtly
- Press:
- instant feedback (no delayed press animation)
- Focus:
- visible
focus-visiblering on all interactive elements
- visible
- Loading:
- use design-components built-in loading states (
DesignButton, tabs/toggles with async) - never freeze the UI without feedback
- use design-components built-in loading states (
Motion accessibility
- Respect
prefers-reduced-motionfor non-essential effects. - Keep micro-interactions understandable without relying on animation alone.
3.6) Best-Practice Checklist (Visual + UX)
- Uses design-components primitives before custom wrappers.
- Uses semantic variants/colors instead of custom status styles.
- Works in both light and dark themes with readable contrast.
- Uses approved typography scale and hierarchy.
- Uses consistent spacing rhythm (
gap-2/3/4,p-3/4/5). - Uses snappy hover-exit transitions and clear focus rings.
- Provides clear loading/disabled/empty/error states.
4) Component-by-Component Contract
This section is prescriptive: use these components with these props for these scenarios.
4.1 DesignCard
File: apps/dashboard/src/components/design-components/card.tsx
Use for:
- page sections
- grouped controls
- analytics panels
- list containers
- glassmorphic blocks used in email/project pages
Props you should use most:
title,icon, optionalsubtitlefor section headersgradient:"blue" | "cyan" | "purple" | "green" | "orange" | "default"glassmorphic(optional explicit override)contentClassNamefor content spacing overrides
Important behavior:
- If
titleis provided,iconis required by type. - Layout is auto-derived:
title + subtitle-> full headertitle only-> compact header- no title -> body-only card
useGlassmorphicDefault()makes nested components default to glassmorphic behavior.
Default recommendation:
- for dashboard sections, use
glassmorphicstyle (either explicit or via nesting context) - use
gradient="default"unless there is semantic reason for colored tint
4.1.1 DesignAnalyticsCard (and chart helpers)
File: apps/dashboard/src/components/design-components/analytics-card.tsx
Use for:
- chart-heavy analytics shells on overview and metrics surfaces
- cards where chart tooltips need to escape clipping/stacking issues
- previously duplicated glass analytics wrappers (
ChartCard,GlassCardclones)
Exports:
DesignAnalyticsCardDesignAnalyticsCardHeaderDesignChartLegenduseInfiniteListWindowDesignInfiniteScrollList
DesignAnalyticsCard props:
gradient:"blue" | "cyan" | "purple" | "green" | "orange" | "slate"className
Rules:
- prefer
DesignAnalyticsCardover local chart wrappers for overview/analytics cards - keep chart implementation local (Recharts config, data transforms), but keep shell/legend/list plumbing shared
- use
DesignChartLegendinstead of hand-rolled dot/label legend rows when layout matches - use
useInfiniteListWindowfor incremental scrolling lists in analytics/list tabs
4.2 DesignAlert
File: apps/dashboard/src/components/design-components/alert.tsx
Use for:
- save success/failure
- warning states (for example SMTP/provider configuration warnings)
- informational notices
Props:
variant:"default" | "success" | "error" | "warning" | "info"titledescriptionglassmorphicwhen rendered on glass surfaces
Rules:
- use semantic variant instead of custom alert class combinations
- keep title short and actionable
- put longer explanation in
description
4.3 DesignBadge
File: apps/dashboard/src/components/design-components/badge.tsx
Use for:
- status chips (sent, failed, queued, draft, active)
- small semantic labels in headers and lists
Props:
labelcolor:"blue" | "cyan" | "purple" | "green" | "orange" | "red"icon(optional)size:"sm" | "md"contentMode:"both" | "text" | "icon"
Rules:
- choose color by meaning, not preference
- use
contentMode="icon"only wheniconis provided - for icon-only badges, accessibility is already handled via
aria-label
4.4 DesignButton
File: apps/dashboard/src/components/design-components/button.tsx
Use for:
- all primary/secondary actions in dashboard surfaces
- async submit/save/delete actions
Props:
variant:"default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "plain"size:"default" | "sm" | "lg" | "icon"onClick(can be async)loading(optional controlled mode)loadingStyle:"spinner" | "disabled"asChildif composition with links/triggers is needed
Rules:
- prefer
DesignButtonover baseButtonfor async behavior and consistent loading semantics - do not hand-roll loading spinners for standard button actions
4.5 DesignInput
File: apps/dashboard/src/components/design-components/input.tsx
Use for:
- text fields inside design-components surfaces
- compact filter fields and inline settings inputs
Props:
size:"sm" | "md" | "lg"prefixItemfor fixed prefix UIleadingIconfor icon-leading input- regular input props (placeholder, value, onChange, disabled, etc.)
Rules:
- use
prefixItemfor prefixed values (domains/paths/currency symbols) - use
leadingIconfor search or query fields
4.6 DesignSelectorDropdown
File: apps/dashboard/src/components/design-components/select.tsx
Use for:
- standard single-select dropdowns in dashboard settings and filters
Props:
valueonValueChangeoptions: { value, label, disabled? }[]placeholdersize:"sm" | "md" | "lg"disabled
Rules:
- prefer this instead of raw
Selectin feature pages unless custom behavior is required
4.7 DesignPillToggle
File: apps/dashboard/src/components/design-components/pill-toggle.tsx
Use for:
- segmented controls
- viewport switches
- compact mode switches
Props:
options: { id, label, icon? }[]selectedonSelectsize:"sm" | "md" | "lg"gradientshowLabels(set false for icon-only controls)glassmorphic
Rules:
- do not create custom inline pill toggle components if this fits
- use
showLabels={false}only with clear icons and tooltip-friendly labels
4.8 DesignCategoryTabs
File: apps/dashboard/src/components/design-components/tabs.tsx
Use for:
- top-level category switching with optional count badges
Props:
categories: { id, label, count?, badgeCount? }[]selectedCategoryonSelectshowBadgesize:"sm" | "md"gradientglassmorphic
Rules:
- use for category-level navigation, not micro toggles
- if there are no category counts and control is small,
DesignPillTogglemay be better
4.9 DesignMenu
File: apps/dashboard/src/components/design-components/menu.tsx
Use for:
- standard row/card action menus
- selector menu (radio group)
- toggles menu (checkbox items)
Variants and required props:
variant="actions"withitemsvariant="selector"withoptions,value,onValueChangevariant="toggles"withoptions,onToggleChange
Common shared props:
trigger:"button" | "icon"triggerLabeltriggerIconalignlabelwithIcons
Rules:
- prefer this over local
DropdownMenuwrappers for common action menus
4.10 DesignListItemRow and DesignUserList
File: apps/dashboard/src/components/design-components/list.tsx
Use for:
- structured list rows with optional per-row actions
- user activity/user list rows
DesignListItemRow props:
title, optionalsubtitleiconsize:"sm" | "lg"buttons(direct actions or menu actions)onClick
DesignUserList props:
users: { name, email, time, color? }[]onUserClickshowAvatargradient:"blue-purple" | "cyan-blue" | "none"
Rules:
- use
size="sm"for dense lists - use
size="lg"for card-like list entries - replace custom row/card list items with this unless layout is truly unique
4.11 DesignEditableGrid
File: apps/dashboard/src/components/design-components/editable-grid.tsx
Use for:
- key/value settings editors
- mixed-type setting controls
- deferred save/discard patterns
Props:
items(typed union:text,boolean,dropdown,custom-dropdown,custom-button,custom)columns:1 | 2deferredSave,hasChanges,onSave,onDiscardexternalModifiedKeys
Rules:
- prefer this for config forms that are row-based and editable inline
- use deferred save mode when many fields should be committed together
4.12 DataGrid + useDataSource + createDefaultDataGridState
Package: @stackframe/dashboard-ui-components
Use for:
- interactive, sortable, searchable data tables
- any table with more than ~20 rows or that needs pagination, column visibility, quick search, or CSV export
Canonical pattern:
import { DataGrid, useDataSource, createDefaultDataGridState, type DataGridColumnDef } from "@stackframe/dashboard-ui-components";
const columns: DataGridColumnDef<MyRow>[] = [
{ id: "name", header: "Name", accessor: "name", width: 200, type: "string" },
{ id: "status", header: "Status", accessor: "status", width: 120, type: "singleSelect",
valueOptions: [{ value: "active", label: "Active" }, { value: "inactive", label: "Inactive" }],
renderCell: ({ value }) => <DesignBadge label={String(value)} color={value === "active" ? "green" : "red"} size="sm" /> },
];
const [gridState, setGridState] = useState(() => createDefaultDataGridState(columns));
const gridData = useDataSource({
data: myRows,
columns,
getRowId: (row) => row.id,
sorting: gridState.sorting,
quickSearch: gridState.quickSearch,
pagination: gridState.pagination,
paginationMode: "client",
});
<DataGrid
columns={columns}
rows={gridData.rows}
getRowId={(row) => row.id}
totalRowCount={gridData.totalRowCount}
isLoading={gridData.isLoading}
state={gridState}
onChange={setGridState}
toolbar={false} // set to false to hide; omit for default toolbar
onRowClick={(row) => handleClick(row)}
maxHeight={400}
/>
Key props:
columns(DataGridColumnDef[]): column definitions withid,header,accessor,type, optionalrenderCell, optionalcellOverflowrows(TRow[]): alwaysgridData.rowsfromuseDataSource, NEVER your raw arraygetRowId((row) => RowId): unique row identifier (RowIdisstring)state/onChange: fully controlled grid state (sorting, pagination, search, visibility)totalRowCount: total rows for pagination displaytoolbar:falseto hide, omit for default, or render function for customonRowClick: optional row click handlermaxHeight: max pixel height before scrollingrowHeight: number (default 44) for fixed height, or"auto"for dynamic row measurementestimatedRowHeight: estimated row height for the virtualizer whenrowHeight="auto"(default 44)
Cell overflow:
cellOverflow: "truncate"(default): single-line with text-overflow ellipsiscellOverflow: "wrap": content wraps naturally; rows grow whenrowHeight="auto"- Use
cellOverflow: "wrap"for badge lists, permission chips, multi-line text - Use default truncate for UUIDs, emails, dates, single-line text
const columns: DataGridColumnDef<MyRow>[] = [
{ id: "userId", header: "User ID", width: 130 }, // truncates (default)
{ id: "auth", header: "Auth methods", width: 150, cellOverflow: "wrap", // badges wrap, row grows
renderCell: ({ row }) => (
<div className="flex flex-wrap gap-1">
{row.authTypes.map((t) => <Badge key={t}>{t}</Badge>)}
</div>
),
},
];
<DataGrid columns={columns} rowHeight="auto" estimatedRowHeight={48} ... />
Rules:
- always initialize state with
createDefaultDataGridState(columns)— never build the state object by hand - always use
useDataSourceto process data — the grid does not sort/filter/paginate on its own - columns must be stable across renders (define outside component or wrap in
useMemo) renderCellmust be a pure function — no React hooks inside it- read the full JSDoc on the
DataGridcomponent for iron rules and advanced usage
4.13 CursorBlastEffect
File: apps/dashboard/src/components/design-components/cursor-blast-effect.tsx
Use for:
- optional high-feedback interactions (playground/internal prototyping)
Props:
blastLifetimeMsmaxActiveBlastsrageClickThresholdrageClickWindowMsrageClickRadiusPxcontainerRef
Rules:
- keep as optional enhancement, not required UX
- avoid distracting overuse in production-critical flows
5) Route-Specific Guidance (Project + Email Surfaces)
Reference surfaces:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/emailsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/email-draftsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/email-outboxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/email-templatesapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/email-themes
Current pattern in these pages often uses custom card/header/pill components. New and refactored code should standardize to design-components primitives as follows.
5.0 /projects/[projectId]/(overview) analytics surfaces
Use:
- chart/list shells:
DesignAnalyticsCard - compact chart headers:
DesignAnalyticsCardHeader - stacked chart legends:
DesignChartLegend - incremental list rendering:
useInfiniteListWindow(orDesignInfiniteScrollListwhere it fits)
Avoid:
- page-local
ChartCardwrappers - duplicated
IntersectionObserverlist window logic per card
5.1 /projects/[projectId]/emails
Use:
- section containers:
DesignCard(title,icon, optionalsubtitle,gradient) - alerts:
DesignAlert(variantby state) - status chips:
DesignBadge(greenfor sent,redfor failed) - actions:
DesignButton - table:
DataGrid+useDataSource+createDefaultDataGridState
Avoid:
- custom
GlassCard - custom status-badge component
- raw
Alertunless special composition is required
5.2 /projects/[projectId]/email-drafts (list)
Use:
- list container:
DesignCard - row items:
DesignListItemRow(size="lg"for card rows orsize="sm"for dense list) - row menus:
DesignMenuwithvariant="actions" - empty state action:
DesignButton
Avoid:
- custom
DraftCardfor standard list row behavior
5.3 /projects/[projectId]/email-drafts/[draftId] (editor)
Use:
- status/sync alerts:
DesignAlert - scope/status chips:
DesignBadge - editor side controls:
DesignButton,DesignSelectorDropdown,DesignInputas needed
Keep:
- specialized editor layout systems if no design-components equivalent exists
5.4 /projects/[projectId]/email-outbox
Use:
- section cards:
DesignCard(preferred for visual consistency with other email screens) - filters:
DesignSelectorDropdown,DesignInput - status badges:
DesignBadge - action buttons/menus:
DesignButton,DesignMenu - data grid/list table:
DataGrid+useDataSource+createDefaultDataGridState
Avoid:
- mixed badge systems (
Badgein some places, custom badges elsewhere)
5.5 /projects/[projectId]/email-templates
Use:
- template item containers:
DesignCard(gradientper semantic section) - alerts/warnings:
DesignAlert - actions:
DesignButton - template row action menu:
DesignMenu
Avoid:
- inline repeated glass class blocks for each template card
5.6 /projects/[projectId]/email-templates/[templateId]
Use:
- save/error notices:
DesignAlert - top actions:
DesignButton - state tags:
DesignBadgewhere needed
5.7 /projects/[projectId]/email-themes
Use:
- section containers:
DesignCard - viewport/device selector:
DesignPillToggle - status messages:
DesignAlert - theme state badges:
DesignBadge - actions:
DesignButton,DesignMenu
Avoid:
- custom
ViewportSelectorifDesignPillTogglesupports the same behavior
5.8 /projects/[projectId]/email-themes/[themeId]
Use:
- state feedback:
DesignAlert - actions:
DesignButton - optional segmented controls:
DesignPillToggle
6) Semantic Mapping Rules
Use consistent semantic color/variant mapping across all pages:
- Success/completed/sent ->
DesignAlert variant="success"andDesignBadge color="green" - Error/failed ->
DesignAlert variant="error"andDesignBadge color="red" - Warning/attention ->
DesignAlert variant="warning"andDesignBadge color="orange" - Info/neutral updates ->
DesignAlert variant="info"andDesignBadge color="blue"or"cyan"
Gradient mapping for cards/tabs/toggles:
- Blue: primary navigation/state
- Cyan: analytics/activity
- Purple: templates/themes or creative tools
- Green: success/completion
- Orange: warnings/caution
- Default: neutral/system sections
7) Interaction and Motion Rules
These rules must be preserved in custom styling and overrides:
- no hover-enter delays
- use hover-exit transitions:
transition-* duration-150 hover:transition-none - keep controls snappy and readable
- avoid heavy animation in dense admin workflows
For async actions:
- prefer design-components primitives that already handle async/loading
- do not swallow async errors; use existing alert-aware async utilities through design-components primitives
8) AI-Readable Implementation Checklist
Use this checklist before opening a dashboard UI PR:
- Replaced ad-hoc cards with
DesignCardwhere possible. - Replaced ad-hoc alerts with
DesignAlert. - Replaced ad-hoc badges/status pills with
DesignBadge. - Replaced ad-hoc segmented controls with
DesignPillToggleorDesignCategoryTabs. - Replaced ad-hoc row/list cards with
DesignListItemRoworDesignUserList. - Used
DesignButtonfor async actions. - Used
DesignSelectorDropdown/DesignInputfor standard field controls. - Used
DataGrid+useDataSource+createDefaultDataGridStatefor interactive tables. - Did not introduce duplicate local wrappers for components already in design-components.
- Kept hover/motion behavior aligned with this guide.
9) Quick Snippets (Canonical)
Section Card
<DesignCard
title="Email Log"
subtitle="View and manage email sending history"
icon={Envelope}
gradient="default"
>
{/* content */}
</DesignCard>
Semantic Alert
<DesignAlert
variant="error"
title="Failed to send email"
description="Please verify provider configuration and try again."
/>
Status Badge
<DesignBadge
label="Sent"
color="green"
icon={CheckCircle}
size="sm"
/>
Viewport Toggle
<DesignPillToggle
options={[
{ id: "desktop", label: "Desktop", icon: Desktop },
{ id: "tablet", label: "Tablet", icon: DeviceTablet },
{ id: "mobile", label: "Mobile", icon: DeviceMobile },
]}
selected={viewport}
onSelect={setViewport}
size="sm"
gradient="default"
/>
Category Tabs
<DesignCategoryTabs
categories={[
{ id: "all", label: "All", count: 42 },
{ id: "failed", label: "Failed", count: 3 },
]}
selectedCategory={category}
onSelect={setCategory}
gradient="blue"
/>
10) Anti-Patterns (Do Not Introduce)
- Creating local
GlassCard/ChartCardcomponents instead ofDesignCardorDesignAnalyticsCard. - Creating local status pills instead of
DesignBadge. - Creating local segmented/pill selectors instead of
DesignPillToggle. - Using raw
Alert/Buttonin standard dashboard surfaces whereDesignAlert/DesignButtonshould be used. - Using
DesignDataTableor rawDataTableinstead ofDataGrid+useDataSource+createDefaultDataGridState.DesignDataTableis deprecated; all new and migrated tables useDataGrid. - Repeating large inline class strings for common design-components patterns.
11) Migration Priority for Existing Email Surfaces
When touching existing email/project pages, migrate in this order:
- Cards/surfaces (
DesignCard/DesignAnalyticsCardfor chart-heavy shells) - Alerts (
DesignAlert) - Badges (
DesignBadge) - Toggles/tabs (
DesignPillToggle/DesignCategoryTabs) - Rows/lists (
DesignListItemRow) - Buttons/menus (
DesignButton/DesignMenu) - Tables/forms (
DataGrid,DesignInput,DesignSelectorDropdown,DesignEditableGrid)
This order yields the biggest consistency win first.
12) Maintenance Rule
Whenever a new reusable visual pattern is introduced in dashboard features:
- add or extend a design-components component first
- then document the component contract and preferred usage here
- avoid introducing permanent page-local UI primitives that duplicate design-components behavior