fix(homepage): uniform 4-column grid, hold the status gutter, unleak AI Systems

The board's card width changed at every group boundary because `columns:` was
being tuned per group under the 2026-08-18 "columns = member count" rule. That
rule is retired: it sets `lg:grid-cols-N` for one group, so it fixes that
group's CARD WIDTH, not its density. Measured on the live board, Notes rendered
a single 1464px card, News and Media 728px, Eval & Retrieval 286px, everything
else 360px. All 20 groups are now `columns: 4` and every card renders at 360.

`.service-name` reserved a 78px status gutter with `padding-right` and relied
on `overflow: hidden` to hold it, but overflow clips at the PADDING box — the
gutter was spill room the title printed straight through. Six cards on the AI
tab rendered their name underneath their own status pill, measured by testing
the title text node's box against the status cluster's. The intended ellipsis
never fired either: it is painted by whichever block's own line overflows, and
that is the anonymous box around the bare title text node, which does not carry
`overflow`. The gutter holds by wrapping now, and the description opts back out
of it with a negative margin since the pill only ever covers the first line.

Scriberr's `homepage.group=AI Systems` named a group absent from `layout:`, so
it had no `tab:` and rendered on all four tabs — the same defect as UltraSeedbox
in 2026-08-18, arriving this time from a container label. Relabelled to
`AI - Audio Tools`, where the other ASR seats already live. The
`homepage.group=AI Systems` sample in the repo-root CLAUDE.md was the source of
the name and now carries the constraint, plus the fact that a labelled
container is discovered from any of the five configured engines and must not
also be listed in services.yaml.

Also: descriptions clamped to three lines so a five-line outlier stops dragging
its row 50px taller than its neighbours; icon ramp overridden off slate-400 ->
slate-700, which was sinking the bottom half of every glyph into the card fill;
bookmark groups and Jellyfin's trailing stream rows brought into the card and
eyebrow vocabulary; group gap 10px -> 22px now that width no longer separates
them.

The icon override sits on `html[class]`, not `:root`: Homepage sets the same
variables on `.theme-slate`, which is on the <html> element, and a class beats
`:root` on the same element.

Verified with Playwright against the live board — per-group card width, card
height spread, and a geometric title-vs-status collision check, before and
after. Before/after captures: http://10.100.10.50:8090/b/homepage-relayout/
This commit is contained in:
vh
2026-08-24 08:15:02 -07:00
parent 32349b7653
commit f6f2f69649
8 changed files with 623 additions and 77 deletions
+51 -5
View File
@@ -82,14 +82,60 @@ Three fixes, all in this stack's config except where noted:
they had members, so the last row of each was mostly dead space (Notes: 1
card in a 4-wide row). Columns now track member counts; see the rule in
`settings.yaml`. Check with `GET /api/services`, which prints live per-group
counts.
counts. ⚠ **Superseded 2026-08-24** — see below.
## 2026-08-24 layout + theme pass
- **Columns are 4 everywhere now; "columns = member count" is retired.**
`columns:` sets `lg:grid-cols-N` for one group, so tuning it per group fixes
that group's *card width* — and the width then changed at every group
boundary. Measured on the live board: Notes rendered a single 1464px card,
News and Media 728px, the AI tab alternated 360 / 728 / 286 down the page.
The old rule was avoiding dead cells in a short last row and bought a worse
defect. A short last row is what a grid looks like; a card wider than its
neighbours is what a mistake looks like. Rule and reasoning are at the top of
`settings.yaml`'s `layout:` block.
- **Scriberr's `AI Systems` group was on all four tabs** — the same untabbed-
group behaviour as UltraSeedbox in 2026-08-18, arriving this time from a
container label rather than from this file. Relabelled to `AI - Audio Tools`
in `stacks/scriberr/compose.yaml` (its real home, alongside Parakeet and
Speaches) and the container recreated. The `homepage.group=AI Systems` sample
in the repo-root `CLAUDE.md` was the source and now carries the constraint.
- **Long service names printed under their own status pill.** `.service-name`
reserved a 78px right gutter with `padding-right` and relied on
`overflow: hidden` to hold it — but overflow clips at the *padding* box, so
the gutter was spill room, not a guard. Every long name on the AI tab
collided. It holds by wrapping now; see the comment on the rule.
- **Descriptions are clamped to three lines** (floor of two, unchanged). Four-
and five-line descriptions were dragging their whole row 30-50px taller than
the cards beside them.
- **Icons were grey smudges.** Homepage masks every mdi glyph over
`--color-logo-start/stop`, stock slate-400 → slate-700, and the dark stop
sank the bottom of each glyph into the card fill. Overridden to ice → aurora
blue. ⚠ The override must sit on `html[class]`, not `:root` — Homepage sets
the same variables on `.theme-slate`, which is on `<html>`, and a class beats
`:root` on the same element.
- **Bookmark groups and Jellyfin's trailing stream rows** were the last two
un-themed components; both now use the card/eyebrow vocabulary.
Verified with Playwright against the live board — per-group card width, card
height spread, and a geometric title-vs-status collision check. Script pattern
is in the "iteration loop" note below.
## The tab bar goes missing after a recreate, then comes back on its own
**Status: self-healing, cause not pinned. Do not chase it.** After a recreate
the client render comes up with no tab bar, no wallpaper and no i18n (the
search box shows the raw key `search.search`), and groups fall back to
side-by-side columns. It restores itself with no intervention.
**Status: self-healing, cause not pinned, and INTERMITTENT. Do not chase it.**
After a recreate the client render can come up with no tab bar, no wallpaper
and no i18n (the search box shows the raw key `search.search`), with groups
falling back to side-by-side columns. It restores itself with no intervention.
⚠ **It does not happen every time.** The 2026-08-24 recreate (`compose up -d
--force-recreate homepage`, to land a `settings.yaml` change) came up correct
within 10 seconds: the fresh `columns: 4` payload was in the served HTML on the
first poll, and a Playwright pass clicked through all four tabs a minute later
with the bar present. So do not pre-emptively schedule an hour of ugliness
around a config change — recreate, check, and only then walk away if it is in
the broken state.
**Timing, measured rather than assumed:** five minutes is NOT enough — a fresh
container was still tab-less at 4m30s, twice. It was observed healthy again