Skip to main content
A new screen is assembled from shared parts. You write the data and the text, the parts carry the look.

Settings and admin pages

Everything under /settings and /admin is composed from SettingsPage, SettingsSection, SettingsRows and SettingsRow. Tabs on these pages are underlined and carry a count badge. Edit and configuration forms open as a popup, never as a sheet.

Popups

Build a popup from the SettingsDialog parts. A dialog is for a decision with consequences. Change a single value in place instead.

Lists of records

A list of registry objects, work items, projects or time entries is a RecordTable from src/shared/components/record-table/. It brings the header with field icons, the rows, the faint grid and the count under the list. Import its parts, not its class constants. The check record-table reports a hand-built <Table>. A table that is not a record list says so with design-ok: record-table (reason).

Detail of one object

Open an object in a DetailDialog with DetailMain for the content and DetailAside for the properties. A full page uses the same two columns. The dialog gets title, onClose, optional browse controls and actions from the feature.

Empty states

An empty area and an empty result
Pass the action only where the person may use it today. The shell holds no rights check.

Checklist for a new page

1

Pick the template

Settings, list, board, detail or dashboard. Reuse the part from the table above.
2

Show the three states

Add a skeleton of the final shape, an empty state and an error state.
3

Check light, dark and a second preset

Open the page in light and dark and in the default preset and one other.
4

Run the design check

Run npm run lint:ui:changed and fix every finding. See Lint ratchet.
npm run list:pages lists every page route with the template it most likely uses, for acceptance checklists.
Last modified on November 4, 2026