Patterns
Web Application
A complete product surface assembled from the package: the Tier 3 application frame, a page header and container, KPI cards, a members table with search, sorting, selection and paging, tabbed content, create and edit forms, and a settings area whose tabs are real routes.
Nothing on this page is a drawing of a component. Every demo is the real export, wired to real state — the sidebar navigates, the table sorts, the filters filter, and the confirmation dialogs actually remove rows.
Application Shell
AppShell is the whole frame: sidebar, header, workspace switcher, product launcher, user menu, mobile menu and the ⌘K palette slot, around a scrolling <main>. You pass data once at the layout; every part reads it back from the shell context.
Overview
Open tasks
248
+12vs last week
Closed this week
96
+18%vs last week
Recent activity
What the team changed today.
Dana Whitfield closed ·
Hà Vũ assigned you ·
Tomas Berg commented on ·
Grace Okafor opened ·
Move around the sidebar and the screen changes. Switch workspace at the top of the sidebar, switch product from the launcher in the header — the two move independently. The search field opens the ⌘K palette; CRM is locked, so choosing it calls onUpsell instead of navigating.
App Header
Three zones — identity, a centred search pill that opens the command palette, and the action cluster. The notification panel is deliberately not in the package: only the bell is, because the payload of a notification differs per product.
Open the bell for the app-owned panel, the grid for the product launcher, the avatar for the user menu, or click the search field to raise the command palette.
Stat Cards
StatCard carries the label, the figure, an icon tone, a delta and an optional footer. Colour comes from direction times polarity, so “overdue is up” reads red while “spend is down” reads green. Flip the switch to see the loading skeleton the package ships.
Open tasks
248
+12vs last week
Closed this week
96
+18%vs last week
Overdue
7
+3vs last week
Spend to date
$89.4K
-4.1%vs budget
Data Table
The list screen every product needs: SearchFilterBar + DataTable + TablePagination, with a column toggle, row selection, a bulk action behind ConfirmDialog and two different empty states. Search, sort, filter, select and remove rows — all of it is live.
| Actions | ||||||
|---|---|---|---|---|---|---|
| Bình Phan[email protected] | Owner | Active | 14 | |||
| Dana Whitfield[email protected] | Admin | Active | 9 | |||
| Hà Vũ[email protected] | Admin | Active | 11 | |||
| Grace Okafor[email protected] | Admin | Active | 8 | |||
| Sara Lindqvist[email protected] | Member | Active | 7 |
Content with Tabs
A page header with a primary action over a tabbed list of resource cards. These panels live in one document, so this is Tabs — the moment a tab becomes its own URL, it is RouteTabs instead.
Projects
Everything the workspace is currently shipping.
Billing rebuild
BIL · 18 open · due 12 Sep 2026
Onboarding v3
ONB · 26 open · due 05 Sep 2026
Design system 1.0
DS · 5 open · due 19 Sep 2026
Create Dialog
A short create flow in a modal, laid out with FormSection and FormField. Spread the control argument into the input and label, description, error text and required state are wired for you.
The submit button stays disabled until name and email are valid.
Edit Drawer
The same fields in a side sheet — better for editing a record you want to keep in view, and it has room for a long form without the scrolling body a small dialog forces on you.
The drawer keeps its draft between opens — edit the role, save, then reopen it.
Settings Page
Four settings sections behind RouteTabs: real links carrying aria-current="page", drawn from the same class table as Tabs so the two are indistinguishable on screen. The header and the strip belong in the settings layout, not in each page.
Settings
Preferences for Acme Corp.
Workspace
Everyone in Acme Corp sees this.
Prefixes every task id.
Current route: /settings
Multi-app Navigation
Comitor navigation has two perpendicular axes: workspace and product. Here the frame runs layout="header-first", so the workspace switcher moves into the header and the launcher sits beside it. A product outside the plan shows a padlock and calls onUpsell rather than landing the reader on a 403.
- Workspace
- Acme Corp · Business
- Product
- Tasks
- Route
- /tasks
The two axes are perpendicular: changing workspace keeps you in the same product, and changing product keeps you in the same workspace. Chat carries its own accent, so the sidebar highlight and the icon tints follow the product you are in.
Open the grid icon in the header for the product launcher, and the workspace name on its left for the workspace list.
Best Practices
Do
- Pass workspaces, apps, nav and user to
AppShellonce, in the layout, and let the parts read the context. - Override every
labelsobject — the defaults are Vietnamese, accessible names included. - Reset to page 1 whenever a filter changes, or the reader lands on an empty page.
- Write two empty states: “nothing here yet” and “nothing matched” need different next actions.
- Put irreversible actions behind
ConfirmDialog, single-row deletes included.
Don't
- Do not rebuild
DataTableout of<table>— you lose sorting, selection, pinned columns, skeletons and the empty state. - Do not use
Tabswhen each tab is a URL: the roles it announces describe a panel swap that is not happening. - Do not omit
LinkComponenton Next — every tab and nav item becomes a full page load. - Do not render a launcher tile for a product the workspace has not bought.
- Do not colour a delta by its arrow alone; state the polarity of the metric.
Accessibility
- The frame supplies the landmarks:
<header>,<aside>with a labelled<nav>, and the scrolling<main>. - Give every table an
aria-label, and every icon-only row action one naming the record it acts on. LetterAvataralready carries the name as screen-reader text — mark itaria-hiddenwhen a visible name sits next to it, or the name is read twice.- A
Progressbar is a mute picture; name it after the thing it measures. - Where a label is both visible text and an accessible name, translate both halves. An English
aria-labelover Vietnamese visible text fails WCAG 2.5.3, Label in Name. - Three components on this page emitted Vietnamese with no prop to reach it through 0.9.1, and every one of the strings is
sr-onlyor anaria-label, which is exactly why they survived a visual review.1.0.0closes all three — two of them need a prop from you, the third fixed itself:- Combobox — the clear button (Xóa lựa chọn), rendered only once
clearablehas something to clear. PassclearLabel, added in 1.0.0 to bothComboboxandMultiCombobox— the multi-select one defaultsclearableto true, so it carried the string without anything being switched on. - CommandPalette — both the dialog title and its description. 1.0.0 adds a
labelsprop carryingtitleanddescription; both are announced every time the palette opens. MobileMenu— the off-canvas sheet’s close button. Through 0.9.1 it fell back to theSheetContentdefault (Đóng), whichMobileMenuPropsdid not forward, so neithermobileMenuPropsnorlabels.closeMenureached it. 1.0.0 wires it tolabels.closeMenu, so this one needs no new prop — an app that already fills inShellLabelsis fixed by the upgrade alone. See AppShell.
- Combobox — the clear button (Xóa lựa chọn), rendered only once
- Wherever the page owns the container, the label is reachable — and easy to miss:
closeLabelonDialogContentandSheetContentis a standalone prop, not part of anylabelsbag, so it is the one that gets forgotten.