Watch
0
0
Fork
You've already forked hyperhive
0

dashboard.css + docs: migrate topology indent + icon/pending prose (#712 batch 12)

dashboard.css carried many CSS WHY-blocks duplicating prose
already in docs/web-ui.md (after PRs #722/#724/#728/#776).
Substantive new migration: topology indent geometry. Remaining
cookies collapse to docs pointers.

Moved to docs/web-ui.md::Topology tree:
- Indent + lane geometry paragraph (~18 lines new prose):
  per-depth 1.8em ladder rationale + hardcoded six levels +
  CSS Values 5 attr() partial-support caveat (Chromium-only as
  of 2026) + lane center continuation bars + bottom: -0.4em
  prefix extension to merge sibling vertical lines + horizontal
  stub at icon midline.

Collapsed in dashboard.css:
- Topology indent block (#363/#361) → 3-line pointer
- Tree prefix lanes block (#388) → 3-line pointer
- Tree prefix depth-step ladder (~6 lines) → 4-line tighter
  paraphrase (lane-meets-icon rationale stays inline as it's
  immediate context for the values below)
- Live cards icon-left split (#177/#344) → 6-line tighter
  paraphrase + docs pointer
- Icon selection-toggle hover/focus (#443) → docs pointer
- Icon img absolute positioning (#177) → docs pointer
- Icon favicon fallback (#195/#202) → docs pointer
- Pending state queued vs running (#769) → docs pointer
- Icon-only nav strip (#262/#333) → 7-line tighter + docs pointer
- Approval requested-at chip (#272) → 2-line pointer
- In-flight step indicator (#437) → 4-line pointer
- M0V3 affordance (#486) → 3-line pointer
- Cancel-X queued (#575) → 5-line pointer

dashboard.css: 51 → 36 cookies (counting hex-color false positives;
~15 actual issue refs scrubbed). Net ~50 lines of CSS WHY-prose
collapsed to brief docs pointers; ~18 lines of new substantive
prose migrated to docs/web-ui.md::Topology tree.
This commit is contained in:
iris 2026-05-31 14:47:04 +02:00 • committed by mara
commit 9c72fd369a
2 changed files with 72 additions and 70 deletions

View file

@ -538,6 +538,26 @@ needed). The joint at the row's own depth column is `├` (more
siblings below) or `└` (last sibling at this depth — vertical
stops at the row's icon midline).
**Indent + lane geometry.** Each depth level shifts the row right
by `1.8em` (the lane width). The per-depth ladders are hardcoded
for six levels — enough for any plausible hive topology, and the
typed `attr()` function from CSS Values 5 that would collapse
this to one rule is still partial-support (Chromium-only as of
2026). The `.tree-prefix` span sits absolutely positioned with
`left: -<depth>*1.8em` so its right edge meets the row content
(the icon) and its leftmost lane lines up with top-level rows'
icons at `x = 0`. Each `.tree-lane` is `flex: 0 0 1.8em` so all
lanes have equal width. Continuation bars are drawn at lane
center (`left: 0.6em`, `border-left: 1px solid currentColor`,
`top: 0; bottom: 0`) and extend through `.containers { gap: 0.4em }`
into the next sibling's prefix (`bottom: -0.4em` on the prefix
span itself) so adjacent ancestor lines visually merge into one
unbroken vertical line. The horizontal stub at a row's own joint
lands at the icon midline so the L/T meets the icon edge cleanly.
When every container has `parent = null` (pre-topology state) the
`[data-depth]` attribute is absent on every row and these rules
are no-ops — the layout reads exactly like the legacy flat list.
### Selection bar
Per-card action buttons (`R3ST4RT` / `ST0P` / `ST4RT` / `R3BU1LD` /

View file

@ -189,16 +189,9 @@ a:hover {
background: rgba(24, 24, 37, 0.55);
transition: opacity 200ms ease, border-color 200ms ease;
}
/* Topology indent (#363). Each depth level shifts the row right by one
step; the .tree-prefix span (drawn by tabs.js::treePrefix) carries
the ├─ / └─ glyph and any continuation lines that thread through
ancestor columns. When every container has parent=null (pre-#361
state) `[data-depth]` is absent on every row and these rules are
no-ops — the layout reads exactly like the legacy flat list.
Per-depth indent: hardcoded steps for 6 levels (sufficient for any
plausible hive topology) — the typed `attr()` function from CSS
Values 5 would collapse this to one rule, but browser support is
still partial (Chromium-only as of 2026). */
/* Topology indent ladder. See docs/web-ui.md::Topology tree (Indent
+ lane geometry paragraph) for the 1.8em-per-depth-level
rationale + CSS-attr()-not-yet-portable caveat. */
.container-row[data-depth] { position: relative; }
.container-row[data-depth="1"] { margin-left: 1.8em; }
.container-row[data-depth="2"] { margin-left: 3.6em; }
@ -206,16 +199,9 @@ a:hover {
.container-row[data-depth="4"] { margin-left: 7.2em; }
.container-row[data-depth="5"] { margin-left: 9em; }
.container-row[data-depth="6"] { margin-left: 10.8em; }
/* Tree prefix sits in the left margin and paints the connecting
├ / └ / │ lanes as CSS rules rather than text glyphs (#388). Each
ancestor depth gets its own `.tree-lane` so we can paint a
full-row-height vertical bar that extends through the
.containers row gap into the next sibling — text box-drawing
glyphs only fill one text line, which left visible breaks
between rows once cards grew taller than one line of text (5em
square icons + multi-line body). The horizontal stub at the row's
own joint lands at the icon midline so the L/T meets the icon
edge cleanly. */
/* Tree prefix lanes — DOM-painted, not text-glyph-painted. See
docs/web-ui.md::Topology tree for the full-row-height vertical
bar rationale + icon-midline joint alignment. */
.container-row .tree-prefix {
position: absolute;
/* Extend into the `.containers { gap: 0.4em }` below so vertical
@ -228,12 +214,10 @@ a:hover {
user-select: none;
color: var(--purple-dim);
}
/* Each depth step is one 1.8em lane wide — same step as the row's
own margin-left ladder above, so the rightmost lane (the joint)
sits flush against the row content (the icon). The prefix's left
edge is depth*1.8em LEFT of the row's left edge, so its right
edge meets the icon, and the leftmost lane lines up with the
top-level rows' icons at x=0. */
/* Prefix-left-edge ladder. Each depth step is 1.8em (matches the
row indent ladder above) so the prefix's right edge meets the
icon and its leftmost lane lines up with top-level rows' icons
at x = 0. */
.container-row[data-depth="1"] .tree-prefix { left: -1.8em; }
.container-row[data-depth="2"] .tree-prefix { left: -3.6em; }
.container-row[data-depth="3"] .tree-prefix { left: -5.4em; }
@ -277,14 +261,12 @@ a:hover {
width: 2em;
border-top: 1px solid currentColor;
}
/* Live cards get the icon-left / body-right split; tombstone rows keep
the plain stacked block layout. The icon is a background-image div
with no intrinsic size, so its load state can never reflow the row
(issue #177). It used to `align-self: stretch` to fill the body
height, but with state badges / rate-limit pills / etc. wrapping the
head row, the body grew taller and the square icon grew with it —
so two cards with different content showed different-sized icons
(issue #344). Fixed at 5em now; height follows from aspect-ratio. */
/* Live cards get the icon-left / body-right split; tombstone rows
keep the plain stacked block layout. The icon's fixed 5em width
+ aspect-ratio-derived height avoid the align-self: stretch
feedback loop that used to make different-content cards show
different-sized icons. See docs/web-ui.md::Container row (Icon
layout + load strategy) for the load-state reflow rationale. */
.container-row:not(.tombstone) {
display: flex;
align-items: flex-start;
@ -298,9 +280,10 @@ a:hover {
aspect-ratio: 1;
border-radius: 6px;
background-color: rgba(17, 17, 27, 0.6);
/* #443 — icon is the selection toggle. Cursor + hover ring make
that affordable without a chrome change. The :focus-visible ring
covers keyboard activation (Enter / Space). */
/* Icon doubles as the selection toggle — cursor + hover ring
make that affordable without a chrome change. The
:focus-visible ring covers keyboard activation (Enter / Space).
See docs/web-ui.md::Selection bar for the toggle semantics. */
cursor: pointer;
transition: box-shadow 120ms ease, transform 120ms ease;
}
@ -321,9 +304,9 @@ a:hover {
.container-row.selected > .container-icon {
box-shadow: 0 0 0 2px var(--purple), 0 0 12px -4px var(--purple);
}
/* The icon image fills the square wrapper and is taken out of flow
(absolute) so its load state — pending, loaded, broken — can never
contribute intrinsic size or reflow the row. (issue #177) */
/* Icon image — absolutely positioned, fills the square wrapper.
See docs/web-ui.md::Container row (Icon layout + load strategy)
for the load-state-can't-reflow-row rationale. */
.container-row:not(.tombstone) > .container-icon > .container-icon-img {
position: absolute;
inset: 0;
@ -331,8 +314,9 @@ a:hover {
height: 100%;
object-fit: contain;
}
/* When the <img> fails to load it falls back to the dimmed hyperhive
mark, standing in for the unreachable agent icon (issues #195, #202). */
/* When the <img> fails to load it falls back to the dimmed
hyperhive mark — see docs/web-ui.md::Container row for the
fire-and-forget load + favicon fallback chain. */
.container-row:not(.tombstone) > .container-icon.icon-unreachable {
filter: grayscale(1);
opacity: 0.4;
@ -341,11 +325,11 @@ a:hover {
flex: 1;
min-width: 0;
}
/* Pending state splits queued vs running (#769): queued ops show
only the pending-state badge — the row sits unhighlighted so a
long queue doesn't paint half the SW4RM tab amber. Running ops
keep the amber row tint AND get a rotating amber ring on the
agent icon so it's obvious which container is actually moving. */
/* Pending state splits queued vs running — queued ops show only
the pending-state badge (no row tint), running ops keep the
amber row tint AND get a rotating amber ring on the icon. See
docs/web-ui.md::Container row (Pending-state derivation) for
the priority order. */
.container-row.pending .actions { opacity: 0.4; pointer-events: none; }
.container-row.pending-running {
border-color: var(--amber);
@ -385,11 +369,13 @@ a:hover {
font-weight: bold;
}
.container-row .head .meta { margin-left: auto; }
/* Icon-only nav strip in the head row — the per-container backend-
supplied link list (issue #262). Inline-flex + gap so a longer list
(e.g. with `dashboardLinks` extras) doesn't cram (issue #333). Each
link gets a comfortable hit target with a subtle hover so the
icons read as interactive rather than decorative. */
/* Icon-only nav strip in the head row — backend-supplied per-
container link list. Inline-flex + gap so a longer list (e.g.
with `dashboardLinks` extras) doesn't cram. Each link gets a
comfortable hit target with a subtle hover so the icons read
as interactive rather than decorative. See
docs/web-ui.md::Container row Line 1 for the link-list source
of truth. */
.container-row .head .nav-strip {
display: inline-flex;
align-items: center;
@ -605,9 +591,8 @@ code {
flex-wrap: wrap;
gap: 0.3em;
}
/* When the approval was requested — right-aligned in the head row;
goes amber once it has been pending ≥ 1h so a stale request stands
out at a glance (issue #272). */
/* Approval requested-at chip — right-aligned, goes amber after
1h. See docs/web-ui.md::Approval card. */
.approval-ts {
margin-left: auto;
color: var(--muted);
@ -836,11 +821,10 @@ code {
.rqe-source-approval { color: var(--green); border-color: var(--green); }
.rqe-when { color: var(--muted); font-size: 0.85em; }
.rqe-reason { color: var(--muted); font-size: 0.85em; flex: 1 1 auto; }
/* #437: in-flight step indicator on running queue entries — sub-line
below the main row, indented under the state glyph + kind. Cyan
keeps it visually grouped with the running spinner instead of
blending into the muted reason/timing chips. flex-basis: 100% so
it always wraps to its own line. */
/* In-flight step indicator on running queue entries — cyan sub-
line below the main row, wraps to its own line via flex-basis:
100%. See docs/web-ui.md::R3BU1LD QU3U3 for the step-annotation
semantics. */
.rqe-step {
flex-basis: 100%;
margin: 0.1em 0 0 1.8em;
@ -858,12 +842,11 @@ code {
white-space: pre-wrap;
}
/* #575: cancel-X for queued rebuild_queue entries. Quiet by default
(sits at the far right via margin-left: auto), lights red on hover.
Renders only when `state === 'queued'` per renderQueueEntry's
guard — Running / terminal rows don't get the affordance. Mirrors
the side-panel-close glyph shape (✗) without the full B6N-style
`btn-deny` width so it stays unobtrusive in a list row. */
/* Cancel-X for queued rebuild_queue entries — sits at far right
via margin-left: auto, lights red on hover. Mirrors the side-
panel-close glyph shape (✗) so it stays unobtrusive in a list
row. See docs/web-ui.md::R3BU1LD QU3U3 for the queued-only
gating. */
.rqe-cancel {
margin-left: auto;
}
@ -942,10 +925,9 @@ ul form.inline { display: inline-block; }
.btn-restart { color: var(--cyan); border-color: var(--cyan); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
.btn-stop { color: var(--pink); border-color: var(--pink); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
.btn-start { color: var(--green); border-color: var(--green); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
/* #486 — M0V3 affordance (selection bar). Mauve picks up the same
accent the question-override / mid-status surfaces use; reads as
"structural change" rather than the destructive red / amber chrome
of destroy / rebuild. */
/* M0V3 affordance (selection bar) — mauve reads as "structural
change" rather than the destructive red / amber chrome of
destroy / rebuild. See docs/web-ui.md::Selection bar. */
.btn-move { color: var(--mauve); border-color: var(--mauve); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
.btn-talk { color: var(--cyan); border-color: var(--cyan); }
.btn-spawn { color: var(--amber); border-color: var(--amber); }