From 92b9e6732785758e2900f7124494fe9edfeb821d Mon Sep 17 00:00:00 2001 From: iris Date: Wed, 12 Aug 2026 17:25:38 +0200 Subject: [PATCH 1/2] frontend: unify pill/chip/badge shape into shared CSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per mara's steer on #3053 ("chip/pill/badge is the same if you squint ... theme unification is part of the goal ... make it common css instead of component, thats fine. but make them look unified"): shared CSS, not a JS component. New @hive/shared/pill.css defines the shape (border/border-radius/ padding/font-size/letter-spacing — color stays per-site, that's the meaningful semantic part) in two tiers matching what was already organically in use: `.hive-pill` (primary state chips) and `.hive-pill-sm` (secondary meta chips). Every pre-existing classname that drew its own copy of this shape (dashboard's `.badge` family, agent's `.status-badge`/`.state-badge`/`.header-pill`/`.ctx-badge`/ `.model-chip`/`.effort-chip`) is folded straight into the same selector groups, so no markup or JS changes were needed anywhere — every render call site keeps constructing the exact same classnames it always did. Most visible consequence: dashboard's `.badge` family moves from its own shape (2px square corners, uppercase, tighter padding) onto the shared rounded-pill shape + agent's "sm" tier sizing, matching the "look unified" ask directly. `npm run build` clean across all three packages. --- frontend/packages/agent/src/agent.css | 37 +++++---------- frontend/packages/dashboard/src/common.css | 14 +++--- frontend/packages/shared/package.json | 1 + frontend/packages/shared/src/pill.css | 53 ++++++++++++++++++++++ 4 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 frontend/packages/shared/src/pill.css diff --git a/frontend/packages/agent/src/agent.css b/frontend/packages/agent/src/agent.css index 411ed31a..c9770ec5 100644 --- a/frontend/packages/agent/src/agent.css +++ b/frontend/packages/agent/src/agent.css @@ -6,6 +6,10 @@ agent SUB-pages (stats, screen) the same back-link nav the dashboard's standalone pages use. The live terminal page keeps its own header. */ @import "@hive/shared/chrome.css"; +/* Shared pill/chip/badge shape (the "extract pill component" issue, + resolved as shared CSS rather than a JS component) — see pill.css + itself for the classnames it covers. */ +@import "@hive/shared/pill.css"; /* Themed dialog/toast component CSS (modal.js) now lives in each component's own shadow root (adoptedStyleSheets), not a global stylesheet — nothing to @import here any more. */ @@ -302,14 +306,11 @@ h2, h3 { /* Header pill — inbox / loose-ends triggers. Compact, count-prominent. */ .header-pill { + /* border/radius/padding/font-size/letter-spacing: shared pill.css */ background: transparent; - border: 1px solid var(--purple-dim); + border-color: var(--purple-dim); color: var(--fg); font-family: inherit; - font-size: 0.85em; - letter-spacing: 0.04em; - border-radius: 999px; - padding: 0.25em 0.7em; display: inline-flex; align-items: center; gap: 0.4em; @@ -604,12 +605,9 @@ pre.diff { } .model-chip, .effort-chip { + /* border/radius/padding/font-size/letter-spacing: shared pill.css */ display: inline-block; - padding: 0.1em 0.6em; - border: 1px solid var(--purple-dim); - border-radius: 999px; - font-size: 0.78em; - letter-spacing: 0.04em; + border-color: var(--purple-dim); } .model-chip { color: var(--cyan); } .effort-chip { color: var(--amber); } @@ -618,13 +616,10 @@ pre.diff { breakdown on hover. Sized/coloured like a peer of model-chip so the state row reads as one row of chrome. */ .ctx-badge { + /* border/radius/padding/font-size/letter-spacing: shared pill.css */ display: inline-block; - padding: 0.1em 0.6em; - border: 1px solid var(--purple-dim); - border-radius: 999px; + border-color: var(--purple-dim); color: var(--green); - font-size: 0.78em; - letter-spacing: 0.04em; cursor: default; white-space: pre-line; } @@ -633,12 +628,8 @@ pre.diff { `.state-badge` / `.model-chip` so the state row stays visually uniform; colour communicates the actual reachability state. */ .status-badge { + /* border/radius/padding/font-size/letter-spacing: shared pill.css */ display: inline-block; - padding: 0.25em 0.8em; - border: 1px solid; - border-radius: 999px; - font-size: 0.85em; - letter-spacing: 0.05em; } .status-badge.status-loading { color: var(--muted); border-color: var(--purple-dim); } .status-badge.status-online { color: var(--green); border-color: var(--green); @@ -672,12 +663,8 @@ pre.diff { `:disabled` opacity treatment lives on the shared `.overflow-item:disabled` rule). */ .state-badge { + /* border/radius/padding/font-size/letter-spacing: shared pill.css */ display: inline-block; - padding: 0.25em 0.8em; - border: 1px solid; - border-radius: 999px; - font-size: 0.85em; - letter-spacing: 0.05em; transition: color 280ms ease, border-color 280ms ease, box-shadow 280ms ease, background 280ms ease; } diff --git a/frontend/packages/dashboard/src/common.css b/frontend/packages/dashboard/src/common.css index d8746d73..4d026641 100644 --- a/frontend/packages/dashboard/src/common.css +++ b/frontend/packages/dashboard/src/common.css @@ -4,6 +4,10 @@ @import "@hive/shared/terminal.css"; @import "@hive/shared/tabs.css"; @import "@hive/shared/chrome.css"; +/* Shared pill/chip/badge shape (the "extract pill component" issue, + resolved as shared CSS rather than a JS component) — see pill.css + itself for the classnames it covers. */ +@import "@hive/shared/pill.css"; /* ─── global typography ───────────────────────────────────────────── Element-level rules shared across all three pages (index, flow, @@ -54,15 +58,11 @@ code { /* ─── status badges ───────────────────────────────────────────────── .badge base + semantic colour variants. Used on container rows - (tabs.js) and build-log rows (logs.js). */ + (tabs.js) and build-log rows (logs.js). Shape (border/radius/padding/ + font-size/letter-spacing) is shared with the agent UI's pill/chip + family — see @hive/shared/pill.css. */ .badge { display: inline-block; - padding: 0.05em 0.5em; - border: 1px solid; - border-radius: 2px; - font-size: 0.75em; - letter-spacing: 0.08em; - text-transform: uppercase; } .badge-warn { color: var(--amber); border-color: var(--amber); diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index 478a1cf2..5dc89c8d 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -16,6 +16,7 @@ "./base.css": "./src/base.css", "./terminal.css": "./src/terminal/terminal.css", "./chrome.css": "./src/chrome.css", + "./pill.css": "./src/pill.css", "./forms.js": "./src/forms.js", "./prefs.js": "./src/prefs.js", "./dom.js": "./src/dom.js", diff --git a/frontend/packages/shared/src/pill.css b/frontend/packages/shared/src/pill.css new file mode 100644 index 00000000..de1254d7 --- /dev/null +++ b/frontend/packages/shared/src/pill.css @@ -0,0 +1,53 @@ +/* Shared pill/chip/badge shape — the "extract pill component" issue, + resolved as shared CSS rather than a JS component. Every inline + status/count chip across the dashboard + per-agent UI shares its + structural shape (border, radius, padding, font-size, letter- + spacing) from here instead of re-declaring the same values per + site; only the per-semantic COLOR (ok/fail/warn/…) stays local to + each page's own CSS — this file is shape only. + + Two size tiers, matching what was already organically in use rather + than inventing a third: + .hive-pill — primary state chips (agent status/state/header pills) + .hive-pill-sm — secondary meta chips (agent model/effort/ctx chips, + the dashboard's whole `.badge` family) + + Every pre-existing classname that drew its own copy of this shape + (`.status-badge`/`.state-badge`/`.header-pill`/`.ctx-badge`/ + `.model-chip`/`.effort-chip`/`.badge`) is folded into the same + selector groups below, so no markup changes were needed anywhere to + pick this up — `display` stays per-site (inline-block for plain + text, inline-flex for header-pill's icon+label+count layout), since + that's a layout concern, not part of the pill's shape identity. */ + +.hive-pill, +.hive-pill-sm, +.status-badge, +.state-badge, +.header-pill, +.ctx-badge, +.model-chip, +.effort-chip, +.badge { + border: 1px solid; + border-radius: 999px; +} + +.hive-pill, +.status-badge, +.state-badge, +.header-pill { + padding: 0.25em 0.8em; + font-size: 0.85em; + letter-spacing: 0.05em; +} + +.hive-pill-sm, +.ctx-badge, +.model-chip, +.effort-chip, +.badge { + padding: 0.1em 0.6em; + font-size: 0.78em; + letter-spacing: 0.04em; +} From 8dd6d05d6cc09aa47d6a83684a8aa8c999423f96 Mon Sep 17 00:00:00 2001 From: iris Date: Wed, 12 Aug 2026 17:38:07 +0200 Subject: [PATCH 2/2] frontend: unify pill/chip/badge shape into shared CSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per mara's steer on #3053 ("chip/pill/badge is the same if you squint ... theme unification is part of the goal" then "make it common css instead of component, thats fine. but make them look unified (not as much per usage css)"): shared CSS, not a JS component. New @hive/shared/pill.css defines two classes, `.hive-pill` (primary state chips) and `.hive-pill-sm` (secondary meta chips) — border/ border-radius/padding/font-size/letter-spacing (colour stays per-site, the meaningful semantic part). Every render call site across dashboard (swarm.js/core.js/builds.js) and agent (index.html/app.js) now carries one of the two shared classes directly, alongside its own existing semantic-colour modifier class. Second cut of this PR, per argus's approve + mara's follow-up review comment on the first cut: the first version instead enumerated every legacy classname (`.badge`, `.status-badge`, `.header-pill`, etc.) straight into pill.css's own selector groups so no call sites needed touching. Mara's correction: that just relocates the duplication rather than removing it, and the shared CSS shouldn't have to keep naming every consumer. This version does the real rename instead. Most visible consequence, unchanged from the first cut: dashboard's `.badge` family moves off its own shape (2px square corners, uppercase, tighter padding) onto the shared rounded-pill shape + agent's "sm" tier sizing. `npm run build` clean across all three packages; verified the compiled bundles carry the new classnames at every call site (dashboard JS, agent index.html + app.js), not just the source tree. Fixes #3053 --- frontend/packages/agent/src/agent.css | 28 +++++------ frontend/packages/agent/src/app.js | 4 +- frontend/packages/agent/src/index.html | 16 +++---- frontend/packages/dashboard/src/builds.js | 8 ++-- frontend/packages/dashboard/src/common.css | 12 ++--- frontend/packages/dashboard/src/core.js | 8 ++-- frontend/packages/dashboard/src/swarm.js | 18 +++---- frontend/packages/shared/src/pill.css | 55 +++++++++------------- 8 files changed, 66 insertions(+), 83 deletions(-) diff --git a/frontend/packages/agent/src/agent.css b/frontend/packages/agent/src/agent.css index c9770ec5..9b96b530 100644 --- a/frontend/packages/agent/src/agent.css +++ b/frontend/packages/agent/src/agent.css @@ -304,9 +304,12 @@ h2, h3 { background: color-mix(in srgb, var(--purple) 6%, transparent); } -/* Header pill — inbox / loose-ends triggers. Compact, count-prominent. */ +/* Header pill — inbox / loose-ends triggers. Compact, count-prominent. + Markup carries `hive-pill` alongside `header-pill` (shared pill.css + supplies border/radius/padding/font-size/letter-spacing; `display` + is the one thing overridden here, to `inline-flex`, for the icon+ + label+count row layout — see pill.css's own note on that). */ .header-pill { - /* border/radius/padding/font-size/letter-spacing: shared pill.css */ background: transparent; border-color: var(--purple-dim); color: var(--fg); @@ -603,10 +606,10 @@ pre.diff { font-size: 0.8em; letter-spacing: 0.05em; } +/* Markup carries `hive-pill-sm` alongside each of these (shared + pill.css supplies the shape); rules below are colour only. */ .model-chip, .effort-chip { - /* border/radius/padding/font-size/letter-spacing: shared pill.css */ - display: inline-block; border-color: var(--purple-dim); } .model-chip { color: var(--cyan); } @@ -616,21 +619,15 @@ pre.diff { breakdown on hover. Sized/coloured like a peer of model-chip so the state row reads as one row of chrome. */ .ctx-badge { - /* border/radius/padding/font-size/letter-spacing: shared pill.css */ - display: inline-block; border-color: var(--purple-dim); color: var(--green); cursor: default; white-space: pre-line; } -/* Harness reachability badge. Same chip shape + sizing as - `.state-badge` / `.model-chip` so the state row stays visually - uniform; colour communicates the actual reachability state. */ -.status-badge { - /* border/radius/padding/font-size/letter-spacing: shared pill.css */ - display: inline-block; -} +/* Harness reachability badge. Markup carries `hive-pill` alongside + `status-badge` (shared pill.css supplies the shape); colour + communicates the actual reachability state. */ .status-badge.status-loading { color: var(--muted); border-color: var(--purple-dim); } .status-badge.status-online { color: var(--green); border-color: var(--green); text-shadow: 0 0 6px color-mix(in srgb, var(--green) 55%, transparent); } @@ -662,9 +659,10 @@ pre.diff { the overflow menu (`.overflow-item-new-session` covers it; the `:disabled` opacity treatment lives on the shared `.overflow-item:disabled` rule). */ +/* Markup carries `hive-pill` alongside `state-badge` (shared pill.css + supplies the shape); this rule is only the transition + state- + specific colour below. */ .state-badge { - /* border/radius/padding/font-size/letter-spacing: shared pill.css */ - display: inline-block; transition: color 280ms ease, border-color 280ms ease, box-shadow 280ms ease, background 280ms ease; } diff --git a/frontend/packages/agent/src/app.js b/frontend/packages/agent/src/app.js index c80b7999..c8471a1b 100644 --- a/frontend/packages/agent/src/app.js +++ b/frontend/packages/agent/src/app.js @@ -793,7 +793,7 @@ window.marked = marked; const def = STATE_LABELS[stateName] || STATE_LABELS.loading; const age = fmtAge(Date.now() - stateSince); badge.textContent = def.glyph + ' ' + def.text + ' · ' + age; - badge.className = 'state-badge state-' + stateName; + badge.className = 'hive-pill state-badge state-' + stateName; badge.title = (STATE_TOOLTIPS[stateName] || '') + '\nin this state for ' + age; const cancelBtn = $('cancel-btn'); if (cancelBtn) cancelBtn.hidden = stateName !== 'thinking'; @@ -1057,7 +1057,7 @@ window.marked = marked; if (!el_) return; const def = ALIVE_LABELS[status] || ALIVE_LABELS.loading; el_.textContent = def.glyph + ' ' + def.text; - el_.className = 'status-badge ' + def.cls; + el_.className = 'hive-pill status-badge ' + def.cls; } function renderModelChip(model) { diff --git a/frontend/packages/agent/src/index.html b/frontend/packages/agent/src/index.html index 86686717..3bb75ad3 100644 --- a/frontend/packages/agent/src/index.html +++ b/frontend/packages/agent/src/index.html @@ -35,12 +35,12 @@
- - … booting - - - - + + … booting + + + +
@@ -52,13 +52,13 @@ it — see docs/web-ui.md::Per-agent page (Overflow button) for the rare-destructive-extra-click rationale. -->
- -