mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 20:05:46 -06:00
0a8226c3db
* fix(ui): give embedded settings sections the shared section rhythm The agents tab panel hosted settings sections in a bare div, so sections stacked with zero separation; the bespoke .agents-main margin rule in agents.css missed the nested tabpanel entirely. Descriptions also pulled up to 4px under control-height header actions (squeezed Verify/Save rows). Add a .settings-stack primitive to settings.css for embedded surfaces, use it on the agent tab panel, delete the page-local margin fork, and let section descriptions clear action-bearing headers. * test(agents): export getRuntimeConfigSourceSnapshot from runtime-snapshot mock Main's checks-node-compact-large shard is red: #126531 routed provider-model-routes through projectConfigOntoRuntimeSourceSnapshot, which reads getRuntimeConfigSourceSnapshot, and this suite's explicit vi.mock factory did not export it (24 failures). Return null so the projection no-ops and resolvers keep reading the provided config.
3.6 KiB
3.6 KiB
Settings Design Language
Every settings surface (the /settings takeover pages plus the Plugins/Skills hubs) uses one structural pattern. Workspace styles live in ui/src/styles/settings.css, while controls shared with startup surfaces live in ui/src/styles/settings-controls.css; templates are built through the helpers in ui/src/components/settings-ui.ts.
Anatomy
.settings-page ← single column, 760px (or --wide: 1120px)
.settings-section ← repeated per topic
.settings-section__heading ← plain uppercase text label, outside any surface
.settings-group ← the ONLY surface (card bg, border, radius-lg)
.settings-row ← title/description left, one control right
.settings-row ← hairline divider between rows
- Sections are typography, not chrome. Grouping comes from whitespace + a small uppercase heading — never a card header.
- Embedded surfaces keep the rhythm. A container that hosts sections without
.settings-page's centered column (tab panels, split layouts) takes.settings-stack; never re-space sections with page-local margin rules. - Exactly one level of elevation. A group never contains another card, callout, or bordered box. Nested detail uses
.settings-subrows(indented rows), a stacked row, or a drill-in nav row. - Row anatomy: left is title (
--control-ui-text-md, weight 500) over an optional one-line description (muted, sm). Right is exactly one control: toggle, select, segmented, button, plain value, or chevron (nav). Wide editors use thestackedvariant. - Lists are rows too. An entity list (plugin, device, session) is a group whose rows carry an action cluster in the control slot — same anatomy as a toggle row.
Rules
- No status pills. Status is
renderSettingsStatus— a dot + plain text (● Connected). Badges (.settings-count) exist only for genuine counts. - Spacing uses
--space-*tokens (base.css); no hardcoded paddings/gaps. - Motion budget: color/background transitions only. No enter animations, staggered reveals, or hover glows.
- Buttons: default
.btn(quiet).--accentprimary at most once per view. Danger actions live in adanger: truesection at the page bottom. - One control set. Use
renderSettingsToggleRow(preferred: label-wrapped, whole row clickable, accessible name for free) orrenderSettingsTogglewith a requiredariaLabel,renderSettingsSegmented,.settings-select,.settings-input. Do not add another toggle or badge variant. - Every control needs an accessible name. Row titles are plain text, not
<label>s — selects/inputs in a control slot must carryaria-label(usually the row title string). - No new page CSS files for settings surfaces. Page-specific styles belong in
settings.cssonly when a primitive is genuinely missing — extend the system, don't fork it.
Helpers
import {
renderSettingsPage,
renderSettingsSection,
renderSettingsRow,
renderSettingsNavRow,
renderSettingsToggleRow,
renderSettingsSegmented,
renderSettingsStatus,
renderSettingsValue,
renderSettingsEmpty,
} from "../../components/settings-ui.ts";
renderSettingsPage([
renderSettingsSection({ title: t("settings.notifications") }, [
renderSettingsToggleRow({
title: t("settings.systemNotifications"),
description: t("settings.systemNotificationsDesc"),
checked,
onChange,
}),
]),
]);
Custom content inside a group (tables, meters) is allowed as an escape hatch — keep it inside one .settings-group and match row paddings (--space-3 --space-4).