Skip to content

SPA: elevation hierarchy — stop using one card treatment for stat cards, panels, toolbars and entry panes #651

Closed

Description

@mforce

Part of the SPA look-and-feel pass (design review 2026-09-02). Sibling slices: numeric typesetting, all-caps labels, table layout, dashboard rework, empty states, visual identity.

Problem

Every surface gets the same treatment: .stat, .panel, .toolbar, .entry-pane, .card, .order-panel all carry --r-card: 16px + --shadow-card + hairline (web/src/styles.css ~1052, ~1496, ~1541). A filter bar has the same visual weight as a data panel, and a data panel the same as a floating dialog. The result reads as a generic SaaS card kit and gives the eye no hierarchy.

Proposal

Elevation encodes what floats; radius encodes nesting depth.

  • Floats (dialogs, the Daily entry sticky action bar, the mobile tab bar): keep shadow.
  • Panels / stat cards / entry panes: border only, no shadow.
  • Toolbars (filter bars): inset — --surface-2 fill, no border or a hairline, no shadow, smaller radius.
  • Radius scale of three steps (e.g. 6 / 10 / 16) by depth: page-level container 16, nested panel 10, controls/chips 6. --r-card stays for the top level; add --r-panel and reuse --r-input.
  • Both themes and all four farm palettes (Per-farm theme — curated accent palettes in farm settings (API + SPA) #149) — the existing styles.test.ts required-set test covers any new token.

Acceptance

  • No box-shadow on .panel, .stat, .entry-pane, .toolbar, .order-panel; shadow remains on .dialog, the entry footer bar and .tabbar.
  • Toolbar reads as inset (screenshot before/after in the PR).
  • Three-step radius scale declared as tokens; no literal radius values added.
  • Light + night + all farm palettes checked (screenshots).
  • Styles unit test pins the shadow allow-list (frontend tests ship in the same PR).

Siblings: #650 #652 #653 #654 #655 #656 #657

Activity

  1. mforce commented on Sep 2, 2026

    @mforce
    OwnerAuthor

    Heads-up from #654, merged as 396ba233: the stat-card CSS is gone.

    .stat-grid, .stat, .stat-value and .stat-label were deleted from web/src/styles.css, because the dashboard was their only renderer and #654 replaced that row with capture-status tiles. Both of your scopes list those selectors, so that part of your work is already done — please re-read your issue body against main before picking it up rather than against the version written on 2026-09-02.

    What #654 deliberately did not touch, so it is still yours:

    One thing worth knowing before you edit those files: web/src/styles.test.ts gained a guard that walks every rule whose selector reaches a dashboard surface — including a selector list like .unrelated, .capture-tile:hover — and fails on box-shadow, text-transform, transition, animation, or any colour that does not resolve through a var(--token). It is deliberately strict; if your slice needs one of those on a dashboard surface, change the guard in the same commit and say why.

  2. mforce commented on Sep 2, 2026

    @mforce
    OwnerAuthor

    Shipped in #661, merged as 28db4c75.

    Delivered: box-shadow removed from .card, .panel, .order-panel and .toolbar; .named-picker-listbox re-pointed at --shadow-dialog; --shadow-card retired entirely (no consumer left, both :root declarations deleted); three-step radius scale as tokens (--r-input: 6px, new --r-panel: 10px, --r-card: 16px), no literal radius added; web/src/styles.elevation.test.ts walks the whole stylesheet and pins the shadow allow-list, with the radius scale pinned beside it.

    Two things this issue asked for that it did not get, recorded rather than dropped:

    1. "Toolbar reads as inset (screenshot before/after in the PR)" — not demonstrable. .toolbar has zero call sites in the SPA; nothing applies the class. The rule is styled correctly but renders nowhere, so no before/after screenshot of it exists or can. It is kept deliberately as groundwork for SPA: table layout — provenance column to one line, date-range filters into a bounded toolbar #653, which introduces the filter-bar consumers — see the note on that issue and the comment now carried on the rule itself. Not a follow-up: SPA: table layout — provenance column to one line, date-range filters into a bounded toolbar #653 owns it.

    2. "Light + night + all farm palettes checked (screenshots)" — partially met. Before/after screenshots were captured from real rebuilt sim stacks and posted on feat(web): elevation hierarchy and sentence-case labels (#651, #652) #661, but only in light, default palette. Night and the forest / slate / terracotta palettes were not visually checked. What was checked across all four palettes and both modes is the contrast maths, asserted in styles.caps.test.ts via the repo's own contrast() helper, and the --canvas/--surface-2 separation (1.05:1–1.21:1 everywhere), which is what drove the decision to keep the toolbar's hairline border. Filed as SPA: visual check of #651/#652 in night mode and the three non-default farm palettes #664 rather than left as a checked box that nobody ticked.

    Also worth recording, because it changes how this issue reads in hindsight: two of the five selectors this issue named did not carry the defect. .stat/.stat-label no longer exist — #654 replaced them with .capture-tile*, which already shipped border-only and sentence-case. .entry-pane already had no shadow. And --shadow-card was rgba(29,21,33,0.04) / rgba(29,21,33,0.05) — 4% and 5% alpha — so removing it is close to imperceptible. The honest summary is that this slice was token hygiene plus groundwork, not a visible change. The verified inventory is in docs/designs/651-652-spa-elevation-and-caps.md.

  3. added a commit that references this issue on Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions