Files
ci/dashboard.css
zooqueen 7253cc21ea the board answers on a phone, and a pinned-back sidecar is not the service
The table is wider than a phone and had no box to scroll in, so the page itself
scrolled sideways and carried the header and nav with it: a reader could see
that something was wrong but not what, which is the one thing the four columns
exist to say.

And two containers can share a repository at different versions on purpose — a
sidecar held back while the app moves on. Reading whichever came last reported
the pin unapplied on a service whose app container was carrying it, so the board
named drift where there was none. A workload runs the declared version if any of
its containers does.
2026-08-17 09:17:59 -07:00

103 lines
6.1 KiB
CSS

/* dashboard.css — this page's own rules: what is a row, what sticks, what
collapses on a phone. It names no colour, radius or type size of its own;
every such value is a var() into @hanzo/brand (see brand.go). That is the
difference between consuming the design system and being a second copy of it,
and it is asserted, not merely intended — see TestDashboardCSSNamesNoColours. */
*{box-sizing:border-box}
body{margin:0;background:var(--surface-0);color:var(--text-primary);
font-family:var(--font-sans);font-size:var(--font-size-base);line-height:1.5}
header{display:flex;align-items:center;gap:var(--space-4);
padding:var(--space-4) var(--space-6);background:var(--surface-1);
border-bottom:1px solid var(--border-hairline)}
h1{margin:0;font-size:var(--font-size-lg);font-weight:600;letter-spacing:-.01em}
h1 span{color:var(--hanzo-accent-muted)}
.meta{margin-left:auto;color:var(--text-secondary);font-size:var(--font-size-sm);text-align:right}
.strip{display:flex;gap:var(--space-2);padding:var(--space-4) var(--space-6);flex-wrap:wrap}
.chip{padding:var(--space-2) var(--space-3);background:var(--surface-1);
border:1px solid var(--border-hairline);border-radius:var(--radius-card);
font-size:var(--font-size-sm);color:var(--text-secondary)}
.chip b{color:var(--text-primary);font-weight:600}
.chip.ok b{color:var(--success)} .chip.fail b{color:var(--error)}
.chip.run b{color:var(--warning)} .chip.cancel b{color:var(--text-muted)}
nav{display:flex;gap:var(--space-2);padding:0 var(--space-6) var(--space-4);flex-wrap:wrap}
nav a{padding:var(--space-1) var(--space-3);background:var(--surface-1);
border:1px solid var(--border-hairline);border-radius:var(--radius-full);
color:var(--text-secondary);text-decoration:none;font-size:var(--font-size-sm)}
/* --hanzo-accent-soft is the house "selected row" fill; the active tab is the
one place on this page that is a selection, so it is the one place it is used. */
nav a.on{border-color:var(--hanzo-accent);background:var(--hanzo-accent-soft);color:var(--text-primary)}
nav .who{margin-left:auto;align-self:center;color:var(--text-muted);font-size:var(--font-size-sm)}
/* The stale banner tints its own border colour rather than introducing an amber
of its own — @hanzo/brand ships no warning-surface token, and inventing one
here is exactly the drift this file exists to stop. */
.warn{margin:0 var(--space-6) var(--space-4);padding:var(--space-3) var(--space-4);
border:1px solid var(--warning);border-radius:var(--radius-card);
background:color-mix(in srgb, var(--warning) 10%, transparent);
color:var(--warning-light);font-size:var(--font-size-sm)}
/* The four versions are the answer, so they must survive a narrow screen. The
table scrolls inside its own box; without one the page scrolls sideways and
takes the header and nav with it, leaving a reader able to see that something
is wrong but not what. */
.scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
table{width:100%;border-collapse:collapse}
th{position:sticky;top:0;background:var(--surface-1);text-align:left;
font-size:var(--font-size-xs);text-transform:uppercase;letter-spacing:.06em;
font-weight:600;color:var(--text-muted);padding:var(--space-2) var(--space-3);
border-bottom:1px solid var(--border-hairline-strong)}
td{padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--border-hairline);
vertical-align:top}
/* The table is full-bleed but its text has to sit on the same gutter as the
header, chips and nav above it, which are all --space-6 in. */
th:first-child,td:first-child{padding-left:var(--space-6)}
th:last-child,td:last-child{padding-right:var(--space-6)}
tr:hover td{background:var(--surface-2)}
a{color:inherit}
.dot{display:inline-block;width:8px;height:8px;border-radius:var(--radius-full);
margin-right:var(--space-2)}
.dot.success{background:var(--success)} .dot.failure{background:var(--error)}
.dot.running{background:var(--warning);animation:p 1.4s ease-in-out infinite}
.dot.cancelled{background:var(--text-disabled)}
@keyframes p{50%{opacity:.35}}
/* A build that failed and a build that never ran are different problems, and a
colour alone cannot carry that: the second has no log to open. So `absent` is
drawn as a different SHAPE — a hollow ring rather than a filled dot — and
reads as such before any colour is perceived. */
.dot.absent{background:transparent;border:2px solid var(--text-muted)}
.dot.warning{background:var(--warning)}
/* The four values are one line, and `gap` marks the ends of the arrow that did
not happen — so the eye lands on the pair that disagrees, not on the row. */
.gap{border-left:2px solid var(--warning);color:var(--text-primary)}
.job{font-family:var(--font-mono);font-size:var(--font-size-xs);color:var(--text-muted);
padding:0 var(--space-1);border:1px solid var(--border-hairline);
border-radius:var(--radius-sm);margin-left:var(--space-1)}
.note{font-size:var(--font-size-xs);color:var(--text-muted);white-space:nowrap}
.tag{display:inline-block;font-size:var(--font-size-xs);padding:0 var(--space-2);
border-radius:var(--radius-full);margin-right:var(--space-1);white-space:nowrap}
/* Whatever stops what we wrote from running reads the same, because it is the
same fact from outside; what merely went unproved reads as a caution. */
.tag.unbuilt,.tag.unsynced{color:var(--error);
background:color-mix(in srgb, var(--error) 12%, transparent)}
.tag.unshipped,.tag.untested{color:var(--warning);
background:color-mix(in srgb, var(--warning) 12%, transparent)}
.ok{color:var(--success);font-size:var(--font-size-sm)}
.repo{font-weight:600}
.org{color:var(--text-muted)}
.title{color:var(--text-secondary);max-width:42ch;
overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mono{font-family:var(--font-mono);font-size:var(--font-size-sm);color:var(--text-secondary)}
.empty{padding:var(--space-12) var(--space-6);text-align:center;color:var(--text-secondary)}
footer{padding:var(--space-4) var(--space-6);color:var(--text-muted);
font-size:var(--font-size-sm);border-top:1px solid var(--border-hairline)}
@media(max-width:760px){.hide-sm{display:none}}