Skip to content

feat(ui): dashboard-pattern blocks (#228) - #233

Merged
Madraka merged 3 commits into
mainfrom
claude/ui-dashboard-blocks
Nov 14, 2026
Merged

Madraka merged 3 commits into
mainfrom
claude/ui-dashboard-blocks

Conversation

@Madraka

@Madraka Madraka commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

What

Adds the five dashboard-pattern pieces that #228 asks for to @cloudhal/ui. Every word is a prop. There is no demo route; tests and DESIGN.md entries cover the new blocks instead.

Piece What it is
ListToolbar (moved from apps/studio/src/components) Fixed slots in a fixed order: search (fills the free width), filters, other actions, sort, then refresh last (Kumo RefreshButton, with its label as a prop). All 17 Studio list screens now import it from @cloudhal/ui. actions is kept, so their markup is unchanged.
StatusIndicator A dot or a thin bar plus the state's word ("Ready", "Failed", "Running") for dense rows. The mark is decorative and aligned with the first line of text. active pulses it, but not under reduced motion.
UsageMeterRow (+ usageState, USAGE_WARN_AT) One usage-rail row: the label with an optional info toggletip, used / limit (formatted by the screen), and a Kumo Meter. The meter turns warning at 80 % (warnAt) and danger at the limit, with the state's word as a badge. Kumo's Meter.label is a plain string, so the visible row is the block's own. The meter keeps its label for screen readers (role="meter", named), and aria-valuetext is the value text plus the state. A test fails if Kumo's label row stops being the meter's first child.
PageLayout docsHref + docsLabel The small outline "View docs" pill after the title (LinkButton size="xs", a 14 px book icon, same tab). It is added to the owned PageHeader copy, marked "Cloudhal".
PageLayout resource (ResourceHeader) Resource mode, like the dashboard's Worker page. The crumbs and an optional environment switcher sit in the top bar. Kumo underline Tabs sit under it, with actions on the right and the one primary action last. There is no large title: title becomes a visually hidden h1. Kumo's tab scroll-button labels are props (tabLabels).

Also:

  • useToggletip. ToggletipBadge's open-on-press logic is now a shared hook, used by ToggletipBadge and by UsageMeterRow's info button.
  • DESIGN.md:
    • a resource template row, and a new §2aj on resource pages, StatusIndicator and UsageMeterRow;
    • the list-toolbar slot order in §1 and §2a;
    • the docs pill on the page-header row;
    • three rows in the §6 matrix.
  • Design lint: a new resource template (PageLayout, crumbs=, title=, resource=, SectionCard).
  • Changeset: a patch for @cloudhal/cli, because Studio ships inside it and every list's search now fills its row. @cloudhal/ui and Studio themselves are private.

Keyboard and focus order (resource mode)

The order follows the DOM, and test/dashboard-blocks.test.tsx checks it:

  1. the crumbs and the switcher, in the top bar;
  2. the tab bar, as a single tab stop on the selected tab;
  3. the actions, with the primary action last;
  4. the content.

Inside the tab bar, ArrowRight moves focus without opening anything (Kumo's default manual activation), and Enter or a click opens the tab. Home and End jump to the first and last tab. The bar keeps one tab stop, which follows the selected tab.

Checks (run locally, one at a time; load stayed under ~4)

  • packages/ui: vitest run passed 257 across 21 files, including the new file's 11. Typecheck is clean.
  • apps/studio: typecheck is clean. design-rules.test.ts passed 609. The full suite passed 1407 across 85 files, run with --no-file-parallelism.
  • pnpm design:lint: all 437 files clean.
  • Biome, size:check: biome shows no errors and size:check is ok.
  • Studio build: pnpm --filter @cloudhal/studio build passed within the bundle budget (initial load 393.8 kB gzip).
  • pnpm design:shots: run once on its own, at 1440 and 390 px, against this branch's Studio. That was this worktree's CLI on a fresh --init-if-empty blog project, with a local test owner account.
    • 14 list routes: /sites, /agents, /agents/skills, /team, and the site's pages, articles, media, redirects, snapshots, activity, collections, taxonomy, translations and comments.
    • All 28 shots passed: no sideways scrolling and no console errors.
    • On a list without filters (Pages) the search spans the row. With filters (Activity) the selects and refresh sit at the end. At 390 px they wrap onto a second row under the search, with refresh still last.

Closes #228

🤖 Generated with Claude Code

Madraka and others added 3 commits November 14, 2026 16:24
- ListToolbar moves from Studio into @cloudhal/ui with fixed slots in a
  fixed order: the search (filling the free width), filters, other
  actions, sort, and refresh last (Kumo RefreshButton). All 17 Studio
  call sites import it from @cloudhal/ui.
- StatusIndicator: a dot or thin bar plus the state's word for dense
  rows (deployments, steps, runs); the word carries the state, the
  pulse stops under reduced motion.
- UsageMeterRow: a usage-rail row with the label, an info toggletip,
  used / limit, and a Kumo Meter that warns at 80 % and is danger at
  the limit, with the state's word as a badge. The meter keeps its label
  for screen readers and reads the value text and the state.
- PageLayout docsHref + docsLabel: the "View docs" pill after the title.
- PageLayout resource mode (ResourceHeader): crumbs plus an optional
  environment switcher in the top bar, underline Tabs under it with the
  actions on the right (primary last), and a visually hidden h1 instead
  of the large title. A new `resource` template is in the design lint
  and in DESIGN.md (§2aj).
- ToggletipBadge's open-on-press logic becomes the shared useToggletip.
- Tests cover the toolbar order, the meter's name, value and state,
  the docs pill, and the resource mode's focus order and arrow,
  Home, End and Enter keys.

Closes #228

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Madraka <[email protected]>
- UsageMeterRow:
  - the info text is inside its button for screen readers (the button's
    name is label + text; the portaled tooltip describes nothing);
  - a fractional limit is the real maximum (0.5 of 0.5 fills the bar);
  - warningLabel and dangerLabel are required, so a state is never shown
    by colour alone.
- ResourceHeader:
  - tabLabels is dropped (underline Tabs render no scroll buttons; a wide
    bar scrolls sideways, arrow keys bring the focused tab into view);
  - one rule under the tabs: the row's hairline, with Kumo's list border
    off, so the active indicator sits on it.
- DESIGN.md §2aj says both.
- Tests:
  - keyboard activation is asserted on its own (Space; happy-dom does not
    turn Enter into a click on a native button), with the click as a
    separate test;
  - the info button's name, the fractional limit and the single rule
    each have a test.

Refs #228

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Madraka <[email protected]>
… (PR #233)

The header row wrapped (flex-wrap), so at 390 px the actions fell onto
a second line with the only rule under them, and the tabs and their
indicator floated above with none.

The row now never wraps:
- Wide (page container >= 48 rem): one line over one hairline. The
  tab list is the min-w-0 part that shrinks and scrolls, and the actions
  keep their size.
- Narrow: the tab line carries the rule across the full width, and the
  actions follow on their own line below. Focus and visual order agree.

Kumo's list border stays off, so the indicator always sits on the one
rule. The test checks both layouts' classes, and DESIGN.md §2aj
describes them.

Checked visually at 1440 and 390 px with a throwaway harness (resource
mode on the site overview, 5 tabs and 2 actions; not committed).

Refs #228

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Madraka <[email protected]>
@Madraka
Madraka merged commit 2f4a1eb into main Nov 14, 2026
9 checks passed
@Madraka
Madraka deleted the claude/ui-dashboard-blocks branch November 14, 2026 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@cloudhal/ui: dashboard-pattern blocks (resource tabs, docs link, list toolbar, status indicator, usage meter row)

1 participant