Cap the long lists, and wrap a wide rank on a phone

Two things the first pass got wrong.

The Changes and "Given up on" lists had no cap, so they ran as long as the
audit trail did — and worse, a truncating flex row still offers its whole
unwrapped line as a min-content contribution, so the card sized itself to the
longest entry and took the page sideways with it. Both now use the same
capped, scrolling box the runs and failures lists already had, which fixes the
length and the width together.

Running downwards, a rank of eight nodes was two thousand pixels across
because a node box is landscape: siblings cost a rank four times as much
across as they do down it. A rank wider than two now wraps onto the ranks
below, settling over a few passes since wrapping one rank pushes what was
under it up into the room that freed. The value chip on an edge no longer
reserves its width there either — that width is spent across the screen rather
than along the flow, and the rank gap it would widen is already wider than the
chip is tall. The same flow that laid out 2040x216 is now 803x722; a chain and
a diamond are unchanged, and so is every desktop layout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDSXaRhvqHYNevgDGmNAto
This commit is contained in:
2026-08-17 18:22:15 +02:00
co-authored by Claude Opus 5
parent bb90a24b90
commit b16408f4d6
3 changed files with 121 additions and 23 deletions
@@ -28,6 +28,18 @@ import {
const RUNS_SHOWN = 15
const FAILURES_SHOWN = 25
/**
* A list of rows in a card: capped and scrolled rather than as long as it
* happens to be.
*
* The cap is the visible reason; the scroll container is the structural one. A
* row is `min-w-0 flex-1 truncate`, and a truncating flex item still offers its
* whole unwrapped line as a min-content contribution — so without this the card
* sizes to the longest entry it holds and takes the page sideways with it. See
* DESIGN-GUIDELINES.md → Responsive.
*/
const LIST = "max-h-96 overflow-y-auto"
/**
* A moment picked off a chart.
*
@@ -275,9 +287,7 @@ export function HealthActivity({ range }: { range: Range }) {
<div
className={cn(
CARD,
// Capped rather than as long as it happens to be: the two lists
// sit side by side, and a filtered one is meant to be scrolled.
"max-h-96 overflow-y-auto",
LIST,
runsAt.pinned !== null && "border-primary",
)}
data-testid="recent-runs"
@@ -334,7 +344,7 @@ export function HealthActivity({ range }: { range: Range }) {
<div
className={cn(
CARD,
"max-h-96 overflow-y-auto",
LIST,
failuresAt.pinned !== null && "border-primary",
)}
data-testid="failures"
@@ -359,7 +369,7 @@ export function HealthActivity({ range }: { range: Range }) {
{dead?.length ? (
<section className="grid gap-3">
<h2 className={PANEL_SECTION}>Given up on</h2>
<div className={CARD}>
<div className={cn(CARD, LIST)}>
{dead.map((item) => (
<div
key={item.id}
@@ -382,7 +392,7 @@ export function HealthActivity({ range }: { range: Range }) {
<section className="grid gap-3">
<h2 className={PANEL_SECTION}>Changes</h2>
<div className={CARD}>
<div className={cn(CARD, LIST)}>
{audit?.length ? (
audit.map((event) => (
<div