# UI patterns Use the shared components in `src/lib/ui/components` to keep pages consistent. Primitives live in its `primitives` directory. ## Page structure and controls - Use `SectionHeader` for page titles and primary actions. The page owns spacing between sections. - Use `Field` with `Input`, `Select` or `Textarea` for labels, help and errors. Give controls distinct `ariaLabel` values when several share one field. - Pair `Tabs` with a labeled panel. Use `ListFooter` for long lists. - Use `StatusBadge` for workflow states and `ServerIdentity` for server names. Keep raw IDs available for copying and troubleshooting. ## Dialogs and feedback Use `Dialog` for modal workflows and `confirmAction` from `src/lib/ui/confirm.ts` for ordinary confirmations. Browser unload protection remains native. Keep loaded data visible when a refresh fails, show the error and provide a retry action. Distinguish loading, empty and failed states so operators know whether to wait or act. ## Live data Use `pollVisible` from `src/lib/ui/poll.ts` for scheduled page refreshes. It avoids overlapping requests, pauses while the page is hidden and returns a cleanup function for use when the component unmounts. Read live server data through `src/lib/ui/stores/fleet.ts` instead of adding another `/api/servers` polling loop. Server detail views use a scoped Fleet store so their snapshots do not replace the shared fleet. ## Accessibility and small screens Preserve visible keyboard focus, associated field labels and separate navigation and action targets. Avoid nested buttons or placing a button inside a link. Allow long server identities to wrap and tab/filter rows to scroll on narrow screens. ## Navigation Use the shared navigation metadata for Fleet, Releases, Activity, Players, and Admin. Campaigns stays under Releases beside Scripts, Configs, Plugins, Game files, and Betas. The Servers page is the Fleet landing page. Desktop navigation uses a collapsible sidebar; mobile navigation uses a modal drawer. Use Cmd/Ctrl K to search pages, servers, hosts, players, and recorded matches. Common actions open their existing forms and retain role checks and confirmations. Keyboard shortcut help is available from the account menu and with `?`. Detail pages use `Breadcrumbs`. Its parent link restores the validated `returnTo` URL, including filters, sorting, pagination, and anchors. Supply `fallback` when the default parent also needs filters. Do not add a duplicate Back link. ### Activity history Use `ActivityLog` for the fleet timeline and scoped history. The server History tab fixes the server filter; Maintenance fixes the maintenance/recovery source. The API counts and pages retained records together, with stable ordering. Parent operations represent their host receipts and maintenance verification so a single user action does not appear multiple times. Routine successful host checks stay in the technical Host operation history. Keep detail links contextual so their parent breadcrumb restores filters and pagination. ### Type and numbers Use the shared `--text-*` scale, with 12 px as the minimum. Mobile inputs use `--text-input` (16 px) to avoid browser zoom. `npm run check:styles`, also included in `npm run check`, rejects raw pixel font sizes. Use Inter with tabular figures for counts, scores, durations and charts; reserve mono for code, IDs, paths and logs. Static values use text or state colors; the accent marks links and controls. ### Product language | Concept | Visible label | | --- | --- | | A managed game instance | Server; use the friendly map and lobby name | | Creating a server | Create server | | Removing a server | Remove server | | Favourite server | Pin | | Game does not answer | Not responding | | Games | Black Ops I / Black Ops II; BO1 / BO2 in compact labels | | Releasing file changes | Campaigns | | Player completion evidence | Present in final report / Not present in final report | Keep internal values such as `t5`, `t6`, `destroy`, instance IDs, and filenames in API fields and technical details. User-facing actions use the labels above. Player presence in the final report does not establish a win or continuous survival. A missing timestamp is **Not recorded**, not **Never**. Use `RelativeTime` for observed history: seconds, minutes or hours for the last 24 hours, Yesterday for the preceding calendar day, then the shared absolute format. It includes the full timestamp in a tooltip and follows the account's timezone. Scheduled future actions use `timeFormat.dateTime` directly. Database timestamps without an offset are UTC; never parse them as browser-local time. Receipt freshness uses the browser clock and shows Paused or a retry reason when updates are unavailable. These clocks are not live regions. Provisioning detail pages show absolute start and finish times once in the summary. Use `OperationTime` for their steps, checks and activity: offsets from the operation start (`+m:ss` or `+h:mm:ss`), with negative offsets for earlier evidence. Missing timestamps stay **Not recorded**. Use `plural` or `countLabel` from `src/lib/ui/plural.ts` for count-dependent language, including verbs: `plural(count, 'needs', 'need')`. The helper uses `Intl.PluralRules`; supply irregular plural forms explicitly. Keep separators inside string expressions when a Svelte conditional would trim their leading space, for example `{offline ? ' ยท Offline' : ''}`. Recommend an agent upgrade only when its reported version is known to lack a needed capability. Missing calibration samples can simply mean the host is still collecting measurements.