quality / changes (push) Successful in 7s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 30s
quality / web (push) Successful in 1m3s
quality / api (push) Successful in 1m15s
quality / linux-agent-smoke (push) Failing after 27s
quality / docker-check (push) Failing after 2m28s
CD / quality (push) Failing after 5m52s
CD / publish (push) Skipped
Implement reliable agent apply, telemetry v2, analytics retention, guarded rollouts, enrollment, labels and webhooks. Improve ReUI frontend data safety and bounded list loading, and add Docker smoke, recovery and resource checks. Document local verification and pending native acceptance.
5.6 KiB
5.6 KiB
Layout: house defaults, hierarchy, spacing, surfaces and states
Point of view
Pick an emotional register before you compose - calm, operational, premium, editorial, dense, energetic - and let layout, spacing, surface treatment, and icon behavior all reinforce it. One or two memorable decisions and restraint everywhere else beats ten generic ones. UI with no point of view reads as generated.
The bar: before you finish, ask: would a product team keep this instead of replacing it? Does it still feel strong after swapping in real content? If not, reuse the shipped ReUI design harder - don't restyle it into something new - then run the quality.md gates.
House defaults
- Spacing ladder:
gap-1togap-2in a row,gap-3togap-4between groups,gap-6togap-8between sections,gap-0.5title to description. Deviate when: skip a level if you like, never invert them. - Panel title: no size class on
FrameTitleorCardTitle. Deviate when: never;FrameTitleshipstext-sm font-semibold,CardTitlefollows the installed style. - Meta text:
text-xs. Deviate when: never smaller, except inside illustrative mini-UI artwork. - Frame spacing:
spacing="default", written out. Deviate when:smin a sheet, dialog, drawer or popover, or for a dense data grid in a panel;xsorlgonly with a stated reason. - Leading row icon:
size-4. Deviate when: never in a glyph column;Spinnershipssize-4, so a loading swap keeps the width. - Icon inside a control: no size class; the control sizes it per style and size. Deviate when: never pin
size-*on an svg insideButton,InputGroupButton,InputGroupAddon,DropdownMenuItem,DropdownMenuCheckboxItem,DropdownMenuRadioItem,DropdownMenuSubTrigger,SelectItem,CommandItem,Toggle,ToggleGroupItem,IconTileorMarkerIcon. - Badge size:
default(leavesizeoff). Deviate when:lgorxlonly when the user asks; neversmorxs. - Neutral badge:
variant="outline". Deviate when: neversecondary,defaultorprimary-lightfor an id, tag, version, count or Draft/Beta label; inline after the title, neverml-auto. - State badge:
<tone>-light:success-light,info-light,warning-light,destructive-light;primary-lightfor brand or pending. Deviate when: never the solid tone,secondaryoroutlinefor a state; 1 state badge per row. - Solid badge: never unless asked; always name a variant (an omitted one is the solid primary fill). Deviate when: only when the user asks for a solid fill (
default,secondary,success,info,warning,destructive,focus,invert). - Slot padding: the primitive's own inset; set density with
Frame spacing=orCard size="sm". Deviate when: never a padding class onCardHeader,CardContent,CardFooter,FrameHeader,FramePanel,FrameFooter,SheetHeader,SheetContent,SheetFooter,DialogHeader,DialogContent,DialogFooter,PopoverContent,DropdownMenuContent,TabsContentorAccordionContent. - Inline separator:
<span aria-hidden className="bg-muted-foreground/40 size-1 shrink-0 rounded-full" />. Deviate when: never a typed·between rendered segments; a literal dot only inside 1 data string. - List rows: dense, auto height; a row with no second line stays 1 tight line. Deviate when: never uniform height.
- Current list item: ghost hover fill (
bg-muted,dark:bg-muted/50) plusaria-current. Deviate when: never a dot or a check mark.
Hierarchy
- 1 focal point per card or panel: the dominant metric or task, then its label, then detail. Borders, separators and surfaces make 2 to 3 bands, never equal weight everywhere.
- Overloaded? Remove, in order: decorative summaries, duplicate context, extra badges, long descriptions, secondary actions, mismatched icon treatments.
Spacing and copy
- Siblings take the parent's
gap-*, never their own margins; a heading sits nearer its own content than the section above it. Never pad an operational surface like a marketing page. - Titles 1 to 3 words, a description 1 clause (about 8 words), a context line 2 segments at most, labels and buttons 1 to 2 words, a toast title 4 words at most. Copy that wraps at desktop width is over budget: cut it, never shrink the type.
Surfaces
Framefor data and operational surfaces (tables, lists, tool panels);Cardfor bounded sections and marketing.- Prefer 1 container family per screen. With a KPI row and a table, use FramePanels for both, or Cards for the KPIs and a Frame for the table; never a Card inside a Frame or a Frame inside a Card.
- A
data-gridinside a Frame is the one place for<FramePanel className="p-0">; elsewhere no padding on slots. This is the 1 exception to the Slot padding row above. - 1 archetype per screen (operational ones and the marketing spine): compose.md lists each with its regions.
Type and buttons
- A KPI value is
text-2xl font-semibold tracking-tight tabular-nums. size="sm"on dense surfaces,variant="outline"for secondary actions,ghostfor row actions; 1 primary per region, 1 variant per toolbar.- A toolbar separator is
<Separator orientation="vertical" className="my-auto h-4" />; at a 390px viewport a toolbar stays on 1 row.
States and responsive
- Empty: a short message plus the primary action. Loading: a
Skeletonin the real layout, never a centered spinner. Error: inline with a retry. - No horizontal page scroll at 390px: reflow to 1 column instead of shrinking;
min-w-0on the shrinking parent andtruncateon single-line labels.