Files
openclaw/ui/src/styles/config-quick.css
Peter Steinberger 2ac723282c refactor(ui): unified settings design language across all Control UI settings surfaces (#106470)
* feat(ui): add settings design-language foundation (tokens, settings.css, helpers)

* refactor(ui): move settings workspace shell styles into settings.css

* refactor(ui): migrate all settings surfaces to the unified settings design language

Every settings surface (General quick+advanced schema form, Appearance,
Communications, Automation, MCP, Infrastructure, AI Agents, Channels,
Connection, About, Profile, Model Providers, Plugins, Skills, Skill
Workshop chrome, Devices, Worktrees, Debug, Logs, Activity, Automations,
Sessions, Usage chrome) now renders through the settings-ui helpers and
settings.css: sections as plain uppercase headings outside one flat
group surface, hairline-divided rows, a single toggle/segmented/status
vocabulary, status dots instead of pills, and no cards-in-cards or enter
animations. Deletes the qs-*/cfg-*/config-section-card/account-card/
plugins-row/cron-editor-card/session-badge/agent-pill families and
orphaned shared rules.

* style(ui-docs): format design-system docs

* fix(ui): make activity statusKind lookup exhaustive for consistent-return lint

* fix(ui): address settings design-language review findings

Label-wrapped toggle rows with required accessible names, named selects
and cron form controls, busy-disabled segmented controls with restored
theme-transition anchors, collapsible skill groups, semantic plugin
headings, gateway connected status, paired-devices empty state, logs
subtitle, cron prompt help, and keyboard-accessible session goal
objectives.

* fix(ui): harden settings primitives per review round two

Wrap row action clusters, fully-inset nav focus ring, restore number
steppers in the schema form, cap node capability lines, keep the named
Activity region landmark, and associate cron help text via the row
label.

* fix(ui): skip empty section headings and fix the toggle doc example

* docs(ui): import renderSettingsToggleRow in the settings example

* fix(ui): rename number stepper delta to avoid shadowing
2026-07-13 08:40:58 -07:00

422 lines
9.1 KiB
CSS

/* ── Config page leftovers ─────────────────────────────────────────────────
*
* Quick Settings now renders through the shared settings design language
* (ui/src/styles/settings.css). This file keeps only genuinely page-specific
* rules: the logs-page fill layout, the Simple/Advanced view toggle, the
* advanced-view accordion nav (used by view.ts), and the two documented
* escape hatches (identity blocks, gateway-host stats grid).
*/
.content:has(openclaw-logs-page) {
overflow: hidden;
}
.content:has(openclaw-logs-page) > openclaw-router-outlet {
display: flex;
flex-direction: column;
margin-top: 0;
min-height: 0;
height: 100%;
width: 100%;
}
openclaw-logs-page {
display: contents;
}
.content:has(openclaw-logs-page) .content-header {
flex: 0 0 auto;
}
.settings-workspace--fill-height .logs-card {
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
.settings-workspace--fill-height .log-stream {
flex: 1 1 auto;
min-height: 0;
max-height: none;
}
/* Persistent Simple/Advanced switch in the page header; keeps the way back
from advanced settings visible at all times. */
.config-view-toggle {
flex: 0 0 auto;
align-self: center;
}
/* Mobile fallback: layout.mobile.css hides .content-header at small widths,
which would strand users in advanced mode with no way back. Mirror the
switch in an in-body row that only shows when the header is hidden. */
.config-view-toggle-row {
display: none;
}
@media (max-width: 768px), (max-width: 932px) and (max-height: 500px) and (orientation: landscape) {
.config-view-toggle-row {
display: flex;
justify-content: flex-end;
width: 100%;
max-width: 760px;
margin: 0 auto;
padding: var(--space-3) var(--space-4) 0;
}
}
/* ── Config Accordion Nav (Advanced Settings, rendered by view.ts) ── */
.config-accordion-nav {
margin-bottom: 20px;
}
.config-accordion-group {
border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
border-radius: var(--radius-md);
margin-bottom: 4px;
overflow: hidden;
transition: border-color var(--duration-fast) var(--ease-out);
}
.config-accordion-group:hover {
border-color: var(--border);
}
.config-accordion-group__header {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 12px 16px;
font-size: 0.8125rem;
font-weight: 650;
letter-spacing: -0.01em;
color: var(--text-strong);
background: color-mix(in srgb, var(--bg-elevated) 50%, var(--card) 50%);
border: none;
transition: background var(--duration-fast) var(--ease-out);
}
.config-accordion-group__header:hover {
background: var(--bg-hover);
}
.config-accordion-group__header--active {
background: var(--bg-hover);
}
.config-accordion-group__icon {
display: flex;
align-items: center;
color: var(--accent);
opacity: 0.7;
}
.config-accordion-group__icon svg {
width: 16px;
height: 16px;
}
.config-accordion-group__chevron {
margin-left: auto;
opacity: 0.35;
transition: transform var(--duration-normal) var(--ease-out);
}
.config-accordion-group__chevron--open {
transform: rotate(180deg);
}
.config-accordion-group__items {
border-top: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
padding: 4px 0;
background: var(--card);
}
.config-accordion-group__item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 9px 16px 9px 40px;
font-size: 0.8125rem;
font-weight: 450;
color: var(--muted);
background: none;
border: none;
transition:
background var(--duration-fast) var(--ease-out),
color var(--duration-fast) var(--ease-out);
}
.config-accordion-group__item:hover {
background: var(--bg-hover);
color: var(--text);
}
.config-accordion-group__item--active {
color: var(--accent);
font-weight: 600;
}
.config-accordion-group__item-icon {
display: flex;
align-items: center;
opacity: 0.45;
}
.config-accordion-group__item-icon svg {
width: 14px;
height: 14px;
}
/* ── Escape hatch: Personal identity blocks (avatar-led rows) ──
The standard settings row (text left, one control right) cannot lead with
an avatar preview; these blocks stay inside one .settings-group and match
the row paddings. */
.config-identity {
display: flex;
align-items: flex-start;
gap: var(--space-3);
padding: var(--space-3) var(--space-4);
min-width: 0;
}
.config-identity + .config-identity {
border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
}
.config-identity__avatar {
width: 48px;
height: 48px;
flex: 0 0 auto;
border-radius: var(--radius-md);
border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
object-fit: cover;
object-position: center;
}
.config-identity__avatar--text,
.config-identity__avatar--default {
display: grid;
place-items: center;
background: var(--accent-subtle);
color: var(--accent);
font-size: var(--control-ui-text-md);
font-weight: 650;
}
.config-identity__avatar--fallback {
padding: var(--space-2);
background: color-mix(in srgb, var(--bg) 84%, var(--bg-elevated) 16%);
object-fit: contain;
}
.config-identity__avatar--default svg {
width: 18px;
height: 18px;
fill: currentColor;
}
.config-identity__copy {
min-width: 0;
flex: 1 1 auto;
}
.config-identity__eyebrow {
font-size: var(--control-ui-text-xs);
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
}
.config-identity__title {
font-size: var(--control-ui-text-md);
font-weight: 500;
color: var(--text-strong);
line-height: 1.35;
overflow-wrap: anywhere;
}
.config-identity__sub {
margin-top: 2px;
font-size: var(--control-ui-text-sm);
color: var(--muted);
line-height: 1.45;
}
.config-identity__source {
display: grid;
gap: 2px;
margin-top: var(--space-2);
font-size: var(--control-ui-text-xs);
color: var(--muted);
}
.config-identity__source code {
color: var(--text);
font-family: var(--mono);
font-size: var(--control-ui-text-xs);
overflow-wrap: anywhere;
}
.config-identity__issue {
margin-top: var(--space-2);
}
.config-identity__error {
margin-top: var(--space-2);
color: var(--danger);
font-size: var(--control-ui-text-sm);
line-height: 1.35;
}
.config-identity__repair {
display: grid;
gap: var(--space-2);
margin-top: var(--space-2);
}
.config-identity__field {
display: grid;
gap: var(--space-1);
max-width: 320px;
}
.config-identity__field-label {
font-size: var(--control-ui-text-sm);
color: var(--muted);
}
.config-identity__actions {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
}
.config-identity__actions .btn {
width: fit-content;
}
.config-identity__hint {
font-size: var(--control-ui-text-sm);
line-height: 1.45;
}
/* ── Escape hatch: Gateway host identity + metered stats grid ──
A genuine two-column stat grid with usage meters; stays flat (no nested
borders/surfaces) inside one .settings-group with row-matched paddings. */
.config-host {
display: grid;
grid-template-columns: minmax(180px, 240px) minmax(0, 1fr);
gap: var(--space-4) var(--space-5);
align-items: start;
padding: var(--space-3) var(--space-4);
}
.config-host__identity {
display: grid;
align-content: start;
gap: var(--space-1);
min-width: 0;
}
.config-host__name {
font-size: var(--control-ui-text-md);
font-weight: 600;
line-height: 1.3;
color: var(--text-strong);
overflow-wrap: anywhere;
}
.config-host__meta {
font-size: var(--control-ui-text-sm);
line-height: 1.4;
color: var(--muted);
}
.config-host__address {
font-family: var(--mono);
font-size: var(--control-ui-text-xs);
color: var(--text);
overflow-wrap: anywhere;
}
.config-host__stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr));
gap: var(--space-3);
}
.config-host__stat {
display: grid;
align-content: start;
gap: var(--space-1);
min-width: 0;
}
.config-host__stat-label {
font-size: var(--control-ui-text-xs);
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
}
.config-host__stat-value {
display: flex;
align-items: baseline;
gap: var(--space-1);
font-size: var(--control-ui-text-lg);
font-weight: 600;
line-height: 1.2;
color: var(--text-strong);
font-variant-numeric: tabular-nums;
}
.config-host__stat-unit {
font-size: var(--control-ui-text-xs);
font-weight: 500;
color: var(--muted);
}
.config-host__stat-detail {
font-size: var(--control-ui-text-xs);
line-height: 1.35;
color: var(--muted);
overflow-wrap: anywhere;
}
.config-host__meter {
height: 4px;
border-radius: var(--radius-full);
background: color-mix(in srgb, var(--border) 55%, transparent);
overflow: hidden;
}
.config-host__meter-fill {
width: var(--config-host-meter-fill, 0%);
height: 100%;
border-radius: inherit;
background: var(--ok);
}
.config-host__meter-fill--warn {
background: var(--warn);
}
.config-host__meter-fill--critical {
background: var(--danger);
}
@media (max-width: 640px) {
.config-host {
grid-template-columns: 1fr;
}
}