Public: anyone can read this; the crew can change it.

Design: the home KPIs are §5 stat tiles, and the kpi* classes leave §5d #4048

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The home page's nine KPI figures are now built with the shared design-system tile, `stat()`, inside a `.stats mb` section. Every link, value, `title`, note and `aria-label` is still there, and the overdue-backup tile still carries `data-tone="attn"`. The type check passes, 87 tests across the ten home/KPI/stat suites pass, and `bun scripts/design-lint.ts` passes; `--update` kept the baseline as it…

shipped production is serving it deploy c564139

opened this

The home page's nine KPI figures are now built with the shared design-system tile, stat(), inside a .stats mb section. Every link, value, title, note and aria-label is still there, and the overdue-backup tile still carries data-tone="attn". The type check passes, 87 tests across the ten home/KPI/stat suites pass, and bun scripts/design-lint.ts passes; --update kept the baseline as it was. No before/after screenshots are included: this sandbox has no browser, so they still need taking from the preview, light and dark, desktop and 390 px.

stat() (src/web/ui.ts): to carry the old tiles' content without loss, it now also takes:

  • markup for value and note, for the commit hash and "live 5m ago";
  • noteTitle and aria;
  • tone: "attn", with a matching .stat[data-tone="attn"] border rule in components.ts (it mirrors the existing card rule).

The sparkline slot is no longer wrapped in aria-hidden, so each sparkline keeps its role=img and aria-label; an unlabelled one stays decorative. The §5 stat row in docs/DESIGN.md describes these options.

Classes removed: the .kpis, .kpi, .kpi-value and .kpi-note rules are gone from src/web/studio/feed_style.ts and from its §5d bullet.

.kpi-spark stays, unlike the issue asked. A search for the kpi names showed the app settings page's visitors card (appVisitorsView → daySpark) also uses it, and that sparkline is not inside a stat tile. So daySpark still emits class="kpi-spark", while the home sparklines now have no class and take the .stat-spark slot's styling. One visible effect: in that slot (56 × 24 px, top right) the home sparklines draw smaller than before.

Tests:

  • The home KPI page test now asserts that none of kpis, kpi, kpi-value, kpi-note or kpi-spark appears in a class on the home page. It also asserts the KPI links (now including /settings/backups) and the sparkline's role=img / aria-label are still there.
  • Some existing tests had to change, because they pinned the old class names: home-kpi-row, home-kpi-page, home-kpis, home-cache, client-home and kpi-spark. They now look for the stat names instead. The CSS assertion in home-kpi-row now checks the .stat / .stat-note rules: fixed height, note clamped to two lines, no nowrap.

To try it, sign in as an admin on the preview and open /: the figures row should look like the stat tiles elsewhere. Then compare the app settings page's visitors sparkline, which should look unchanged.

🤖 Generated with Claude Code

Closes #3982

commented

The home page's nine KPI figures are now built with the shared design-system tile, stat(), inside a .stats mb section. Every link, value, title, note and aria-label is still there, and the overdue-backup tile still carries data-tone="attn". The type check passes, 87 tests across the ten home/KPI/stat suites pass, and bun scripts/design-lint.ts passes; --update kept the baseline as it was. No before/after screenshots are included: this sandbox has no browser, so they still need taking from the preview, light and dark, desktop and 390 px.

stat() (src/web/ui.ts): to carry the old tiles' content without loss, it now also takes:

  • markup for value and note, for the commit hash and "live 5m ago";
  • noteTitle and aria;
  • tone: "attn", with a matching .stat[data-tone="attn"] border rule in components.ts (it mirrors the existing card rule).

The sparkline slot is no longer wrapped in aria-hidden, so each sparkline keeps its role=img and aria-label; an unlabelled one stays decorative. The §5 stat row in docs/DESIGN.md describes these options.

Classes removed: the .kpis, .kpi, .kpi-value and .kpi-note rules are gone from src/web/studio/feed_style.ts and from its §5d bullet.

.kpi-spark stays, unlike the issue asked. A search for the kpi names showed the app settings page's visitors card (appVisitorsView → daySpark) also uses it, and that sparkline is not inside a stat tile. So daySpark still emits class="kpi-spark", while the home sparklines now have no class and take the .stat-spark slot's styling. One visible effect: in that slot (56 × 24 px, top right) the home sparklines draw smaller than before.

Tests:

  • The home KPI page test now asserts that none of kpis, kpi, kpi-value, kpi-note or kpi-spark appears in a class on the home page. It also asserts the KPI links (now including /settings/backups) and the sparkline's role=img / aria-label are still there.
  • Some existing tests had to change, because they pinned the old class names: home-kpi-row, home-kpi-page, home-kpis, home-cache, client-home and kpi-spark. They now look for the stat names instead. The CSS assertion in home-kpi-row now checks the .stat / .stat-note rules: fixed height, note clamped to two lines, no nowrap.

To try it, sign in as an admin on the preview and open /: the figures row should look like the stat tiles elsewhere. Then compare the app settings page's visitors sparkline, which should look unchanged.

🤖 Generated with Claude Code

Approved

Approved. The nine home tiles now go through stat() in a .stats grid, as docs/DESIGN.md §5 asks. Every link, title, aria-label and the attn tone survive. The kpis, kpi, kpi-value and kpi-note rules are gone from §5d. Tests pin the new markup and the absence of the old classes. Keeping .kpi-spark for the settings-page daySpark is right, and the pull says so. The tiles are plain <a> links and the page is server-rendered, so it works with JavaScript off. I could not see the admin-only row in the preview without an admin session, so the layout is read from the CSS, not from a screenshot.

Two things I would do next:

  1. Check the 160px tiles at 360px and in dark mode. .stat-spark is absolutely positioned at top right (56px wide) over the label row, and the label is only ellipsised at the tile edge. A label like "Shipped per hour" can run under the sparkline. Reserve the spark width on .stat-label when a spark is present, e.g. padding-right on .stat:has(.stat-spark) .stat-label.
  2. Attach the before/after screenshots (light and dark, desktop and 390px) that the pull says are still missing.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs