From c3de7dbd58be3481bde4f93e387e26c6c4cf4427 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Tue, 18 Aug 2026 22:38:21 -0700 Subject: [PATCH] feat(homepage): add Arbo 'Raven' theme to custom.css; correct the tab-bar note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports Arbo's design tokens (irv-ml1:8201) into conf/custom.css — flat raven ink #021425, card surface #112333 on #1B2E3D borders, Manrope, and mint #2FFC89 reserved for signal so a green pill means the service is actually serving. Values read off Arbo's running :root custom properties rather than sampled from a screenshot. CSS rather than settings.yaml because Homepage's color: setting only accepts built-in Tailwind ramps. NOT YET DEPLOYED — live still runs the stock theme pending an A/B decision. Prototype is at 10.0.50.45:5199; shots in ~/booth-data/homepage-cleanup/. Promoting it also means dropping the background: block from settings.yaml. Also corrects the previous commit's tab-bar claim. It is not a fixed few minutes of warm-up: a fresh container was still tab-less at 4m30s twice, and recovered on its own about an hour later. Cause remains unpinned; the README now records the measured timing and the four ruled-out causes. --- stacks/homepage/README.md | 73 ++++++++++---- stacks/homepage/conf/custom.css | 173 +++++++++++++++++++++++++++++++- 2 files changed, 224 insertions(+), 22 deletions(-) diff --git a/stacks/homepage/README.md b/stacks/homepage/README.md index 33c029c..fd57e61 100644 --- a/stacks/homepage/README.md +++ b/stacks/homepage/README.md @@ -84,21 +84,39 @@ Three fixes, all in this stack's config except where noted: `settings.yaml`. Check with `GET /api/services`, which prints live per-group counts. -## RESOLVED — the tab bar takes several minutes to appear after a recreate +## The tab bar goes missing after a recreate, then comes back on its own -**Status: closed, and the answer is "wait".** After the 2026-08-18 recreate the -client render came up with no tab bar, no wallpaper and no i18n (the search box -read the raw key `search.search`); groups fell back to side-by-side columns. -It restored itself with no further intervention. **A freshly recreated homepage -container needs a few minutes before the client render is whole** — far longer -than the healthcheck takes to report `healthy`, which is the trap: `docker ps` -says the service is up while the page is still visibly wrong. +**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. -**So: after any `docker compose up -d --force-recreate` here, do not judge the -dashboard for at least ~5 minutes, and do not start changing config to chase -it.** Everything below is the evidence trail from doing exactly that, kept -because it rules out four plausible causes and will save the next session the -same hour. +**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 +after a gap of roughly an hour. So the window is somewhere between those, and +`docker ps` reporting `healthy` says nothing about it: the container is +serving, the page is just wrong. + +**Practical rule: recreate, then walk away and re-check later.** Do not start +editing config to fix it — that is how an hour got spent ruling out four +causes that were never the cause: + +- **Not the config.** Restoring `settings.yaml` *and* `services.yaml` to their + committed versions reproduces it, as does the pre-adoption backup config in + `/opt/docker-bu/conf/homepage/`. +- **Not the v2.0.0 release.** A throwaway container on `v1.13.2` (the last v1) + shows identical symptoms, and the image never changed anyway — working and + broken containers both report `v2.0.0` / rev `17456f2`. +- **Not `PUID`/`PGID`**, and not Docker discovery — tested with both, and with + the socket unmounted entirely. +- **Not a server-side failure.** The server-rendered HTML still contains the + tab markup, the background URL and `useEqualHeights` throughout, and + `GET /api/validate` returns `[]`. Whatever is lost, is lost client-side, and + with no page error, no failed chunk and no non-200. + +If someone does want to pin it: the shape (recovers on a long timescale, no +client exception) points at a cache or revalidation window rather than a +config fault. Start there, not in this directory. **What it is not** — both obvious suspects were tested and cleared: @@ -119,17 +137,32 @@ widget 403s. `GET /api/validate` returns `[]`. A fresh container never renders tabs here regardless of image version, config version, `PUID`/`PGID`, or whether Docker discovery is mounted at all. -**What it actually was: warm-up time.** Every throwaway container in the list -above was judged within ~30 seconds of starting, which is why they all looked -broken — they were all in the same warm-up window, and that consistency read as -a reproduction when it was really the same mistake five times. The live -container recovered on its own once left alone. The lesson is a measurement -discipline, not a config one, and it sits alongside the existing warning that -`docker ps` health and a correct render are different questions. +Every throwaway container in that list was judged within ~30s of starting, so +they were all inside the same window — and that consistency read as a +reproduction when it was really the same measurement mistake five times over. +This sits alongside the existing warning that `docker ps` health and a correct +render are different questions. Before/after evidence: `~/booth-data/homepage-cleanup/` on nh3-dev → `http://10.100.10.50:8090/b/homepage-cleanup/` (24h TTL). +## Theme — the Arbo "Raven" palette (`conf/custom.css`) + +`conf/custom.css` carries a port of Arbo's design tokens (irv-ml1:8201): flat +raven ink `#021425`, card surface `#112333` on `#1B2E3D` borders, Manrope, and +mint `#2FFC89` reserved for signal — a status pill is green only when the +service is actually serving. Values were read off Arbo's running `:root` +custom properties, not sampled from a screenshot, so drift checks are a diff. + +It lives in CSS rather than `settings.yaml` because Homepage's `color:` setting +only accepts built-in Tailwind ramps and has no separate surface/border/accent +knobs. `color: slate` stays as the base ramp. The `!important` flags are +load-bearing against Tailwind utility specificity. + +Pairs with removing the `background:` block from `settings.yaml` — Arbo has no +background image, and the wallpaper fought the card surfaces, which sit only a +little lighter than the page. + ## Open question `ESH-FileBot` (`10.0.50.70`) is still described as "role TBC" — it responds to diff --git a/stacks/homepage/conf/custom.css b/stacks/homepage/conf/custom.css index fcc7889..d278468 100644 --- a/stacks/homepage/conf/custom.css +++ b/stacks/homepage/conf/custom.css @@ -1,2 +1,171 @@ -/* Custom styling for homepage. Empty placeholder — keep file present so - * homepage doesn't 404 on the asset. */ +/* Homepage — Corviduo "Raven" theme, ported from Arbo (irv-ml1:8201). + * + * PROVENANCE: every value below is lifted verbatim from Arbo's own :root + * custom properties, read off the running app rather than eyeballed from a + * screenshot. Keep the --raven-* / --eye-* / --surface* names identical to + * Arbo's so a future drift check is a diff, not an interpretation. + * + * WHY CSS AND NOT settings.yaml: Homepage's `color:` setting only accepts + * built-in Tailwind ramps (slate, zinc, ...). None of them is the raven mint, + * and there is no setting for surface/border/accent separately. `color: slate` + * stays as the base ramp because it is the closest neutral; everything that + * makes this look like Arbo is here. + * + * SPECIFICITY: Homepage is Tailwind-generated, so utility classes win on + * equal specificity. `!important` below is deliberate and load-bearing — + * removing it silently reverts individual rules to the stock theme. + */ + +@import url("https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&display=swap"); + +:root { + /* --- Arbo brand ramp (verbatim) --- */ + --raven-ink: #021425; + --raven-black: #0f2131; + --raven-eye: #2ffc89; + --raven-bone: #e5f4f4; + + --black-700: #1b2e3d; + --black-600: #283b4a; + --slate-400: #6a7f88; + --slate-300: #8499a0; + + --eye-500: #2ffc89; + --eye-400: #77ffa3; + --eye-600: #00cf67; + + --info-500: #00a5fb; + --success-500: #30ab60; + --warning-500: #ecb230; + --error-500: #f5686c; + + /* --- semantic surfaces (Arbo's own mapping) --- */ + --bg: var(--raven-ink); + --bg-sunken: #010e1b; + --surface: #112333; + --surface-2: #1b2e3d; + --surface-3: #283b4a; + --fg: #dfeeee; + --fg-muted: var(--slate-300); + --fg-subtle: var(--slate-400); + --border: #1b2e3d; + --border-strong: #283b4a; + --accent: var(--eye-500); +} + +/* ---------- page ---------- */ +/* Flat ink, no photo. Arbo has no background image and the wallpaper was + * fighting the card surfaces for contrast — the cards are only ~0x10 lighter + * than the page, which reads as depth on flat ink and as mud over a photo. */ +body, +#page_wrapper, +#page_container { + background: var(--bg) !important; + color: var(--fg) !important; + font-family: Manrope, ui-sans-serif, system-ui, -apple-system, "Segoe UI", + Roboto, "Helvetica Neue", Arial, sans-serif !important; +} + +/* ---------- header widgets ---------- */ +#information-widgets .widget-container, +#information-widgets .information-widget-search { + background: var(--surface) !important; + border: 1px solid var(--border) !important; + border-radius: 0.5rem !important; +} + +#information-widgets .information-widget-search input { + background: transparent !important; + color: var(--fg) !important; +} + +/* ---------- tabs ---------- */ +/* Arbo marks the active nav item with a mint rule and a raised surface. */ +#myTab li button, +#services + div li button { + color: var(--fg-muted) !important; + border-radius: 0.5rem !important; +} + +#myTab li button[aria-selected="true"], +#myTab li button.active { + background: var(--surface-2) !important; + color: var(--fg) !important; + box-shadow: inset 0 -2px 0 0 var(--accent) !important; +} + +/* ---------- group headers ---------- */ +.service-group-name, +.bookmark-group-name { + color: var(--fg-subtle) !important; + font-weight: 600 !important; + letter-spacing: 0.04em !important; + text-transform: uppercase !important; + font-size: 0.78rem !important; +} + +/* ---------- service cards ---------- */ +.service-card, +.bookmark li > a, +.bookmark-list li > a { + background: var(--surface) !important; + border: 1px solid var(--border) !important; + border-radius: 0.5rem !important; + transition: background 120ms ease, border-color 120ms ease !important; +} + +.service-card:hover, +.bookmark li > a:hover, +.bookmark-list li > a:hover { + background: var(--surface-2) !important; + border-color: var(--border-strong) !important; +} + +.service-name, +.bookmark-name { + color: var(--fg) !important; + font-weight: 600 !important; +} + +.service-description, +.bookmark-description { + color: var(--fg-muted) !important; +} + +/* ---------- status pills + widget stat blocks ---------- */ +/* Stock Homepage renders every pill in the same slate; Arbo reserves colour + * for signal. Green only means "this is actually serving". */ +.service-tag, +.service-block, +.service-stats { + background: var(--surface-2) !important; + border: 1px solid var(--border) !important; + color: var(--fg-muted) !important; + border-radius: 0.375rem !important; +} + +.status-online, +.status-healthy, +.status-running { + color: var(--eye-500) !important; +} + +.status-offline, +.status-unhealthy, +.status-error { + color: var(--error-500) !important; +} + +.status-unknown, +.status-exited, +.status-partial { + color: var(--warning-500) !important; +} + +/* ---------- focus ---------- */ +a:focus-visible, +button:focus-visible, +input:focus-visible { + outline: 2px solid var(--accent) !important; + outline-offset: 2px !important; +}