Give the sidebar groups, and tidy the tables, disclosures and tab bar

The desktop sidebar is now grouped: Queue, On-call, Alerts and Stats; Team
and Admin; then Account at the foot, shown as the signed-in person with an
avatar and their name. The active item has a bar as well as a tint. The
queue's row of team chips is gone: the team selector is the one place the
team is chosen, and the chips offered the same choice a second time.

Tables get 16px between columns, so a right-aligned count no longer touches
the text beside it. An empty SSO group reads "Not configured" instead of a
dash, and the main action on each of those pages (New source, New switch,
Add member, Assign, and the admin Add, Create invite and Create) is a filled
button, with Edit and Cancel staying secondary. The bare triangles on
"Grouped by" and the label lists are real disclosure buttons: a chevron
that turns and a 44px target on a touch screen.

On a phone the More tab was a <button> that kept the browser's grey box, so
it looked highlighted next to four plain links. It now matches them, the
labels are 12px and the open tab's icon is filled.

Web UI only: no endpoint or JSON shape changed, so nothing to mirror in
terdut-tui.
This commit is contained in:
Niklas Ye
2026-10-08 14:51:50 +02:00
parent 3a96b20cbe
commit bcf1a3e99b
8 changed files with 94 additions and 56 deletions
+45 -8
View File
@@ -296,6 +296,7 @@ input:focus, textarea:focus { outline: none; border-color: var(--accent); box-sh
border-top: 1px solid var(--border);
}
.nav-brand { display: none; }
.nav-sep, .nav-avatar { display: none; }
/* Hidden here (shown from 900px below): on the phone bar the team switcher
lives in the topbar instead, as #team-selector-mobile. */
.nav-team-selector { display: none; }
@@ -305,7 +306,10 @@ input:focus, textarea:focus { outline: none; border-color: var(--accent); box-sh
block below cancels it back to a natural-width row item. */
flex: 1 1 0;
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
color: var(--faint); font-size: 11px; font-weight: 600;
color: var(--muted); font-size: 12px; font-weight: 600;
/* The More tab is a <button>; without this it keeps the browser's grey box
and reads as a highlighted tab beside four plain links. */
background: none; border: 0; font-family: inherit; cursor: pointer;
/* min-width lets a column shrink below its label's natural width, which is
what stops six tabs widening the bar past the screen. */
min-width: 0; padding: 0 2px;
@@ -320,6 +324,8 @@ input:focus, textarea:focus { outline: none; border-color: var(--accent); box-sh
.nav-link svg { width: 24px; height: 24px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nav-link[aria-current="page"] { color: var(--accent); }
/* The open tab's icon is filled, so it is not told apart by colour alone. */
.nav-link[aria-current="page"] svg { fill: currentColor; fill-opacity: 0.16; }
.nav-badge {
position: absolute; top: 6px; left: calc(50% + 6px);
min-width: 18px; height: 18px; padding: 0 5px;
@@ -440,7 +446,6 @@ input:focus, textarea:focus { outline: none; border-color: var(--accent); box-sh
/* Which team's queue a row came from. Only rendered for somebody in more than
one team, so it never repeats the same word down the whole list. */
/* Separates the status chips from the team chips in the queue's filter row. */
.chip-sep { width: 1px; align-self: stretch; background: var(--border); margin: 0 2px; }
.row-team {
padding: 1px 6px; border-radius: 4px;
@@ -564,11 +569,25 @@ input:focus, textarea:focus { outline: none; border-color: var(--accent); box-sh
.alert-item-summary { color: var(--muted); font-size: 14px; overflow-wrap: anywhere; }
.alert-item-foot { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 13px; color: var(--faint); }
.alert-item-foot a { color: var(--accent); font-weight: 600; }
details > summary { cursor: pointer; color: var(--muted); font-size: 13px; font-weight: 600; list-style: none; }
/* A disclosure is a button: a chevron that turns, and a 44px target on a
touch screen (the bare triangle it replaces was a few pixels wide). */
details > summary {
display: flex; align-items: center; gap: 8px;
min-height: 44px; margin: 0 -6px; padding: 0 6px; border-radius: var(--radius-sm);
cursor: pointer; color: var(--muted); font-size: 14px; font-weight: 600; list-style: none;
}
details > summary::-webkit-details-marker { display: none; }
details > summary::before { content: "▸ "; }
details[open] > summary::before { content: "▾ "; }
details > summary::before {
content: ""; flex: none; width: 7px; height: 7px; margin: 0 5px 0 3px;
border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
transform: rotate(-45deg); transition: transform 0.12s;
}
details[open] > summary::before { transform: rotate(45deg); }
details > summary:hover { background: var(--surface-2); color: var(--text); }
details > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
details[open] > summary { margin-bottom: 8px; }
@media (hover: hover) and (pointer: fine) { details > summary { min-height: 32px; } }
@media (prefers-reduced-motion: reduce) { details > summary::before { transition: none; } }
/* One .tl-phase per status the incident has been through (see
timelinePhases() in incident.js) — each with its own .timeline <ol>, so
@@ -827,6 +846,17 @@ kbd {
.nav-team-selector { display: inline-flex; margin: -8px 10px 14px; width: calc(100% - 20px); }
.nav-link-secondary { display: flex; }
.nav-more-btn { display: none; }
.nav-sep { display: block; height: 1px; margin: 8px 10px; background: var(--border); }
/* The account link sits at the foot of the sidebar, as the signed-in person. */
.nav-sep-account { margin-top: auto; }
.nav-account .nav-avatar {
display: grid; place-items: center; flex: none;
width: 28px; height: 28px; margin: -4px 0 -4px -4px; border-radius: 50%;
background: var(--accent-soft); color: var(--accent);
font-size: 13px; font-weight: 750; text-transform: uppercase;
}
.nav-account .nav-avatar:not([hidden]) ~ .nav-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-account svg:has(~ .nav-avatar:not([hidden])) { display: none; }
.nav-link {
flex: none; flex-direction: row; justify-content: flex-start; gap: 12px;
min-height: 40px; padding: 0 10px; border-radius: var(--radius-sm);
@@ -834,6 +864,11 @@ kbd {
}
.nav-link:hover { background: var(--surface-2); }
.nav-link[aria-current="page"] { background: var(--accent-soft); color: var(--accent); }
/* A 3px bar on the active item, as well as the tint. */
.nav-link[aria-current="page"]::before {
content: ""; position: absolute; left: -12px; top: 8px; bottom: 8px; width: 3px;
border-radius: 0 3px 3px 0; background: var(--accent);
}
.nav-link svg { width: 20px; height: 20px; }
.nav-badge { position: static; margin-left: auto; }
@@ -851,7 +886,6 @@ kbd {
/* The pane is 340-420px wide and a mouse cannot scroll a row whose scrollbar
is hidden, so the chips wrap here instead: Archived stays reachable. */
.pane-list .chips { flex-wrap: wrap; overflow-x: visible; }
.pane-list .chip-sep { display: none; }
.chips-fade { display: none; }
/* With nothing selected there is no detail to show next to, so the list
takes the whole row instead of leaving the second column as dead space
@@ -901,9 +935,12 @@ kbd {
.admin-table th {
text-align: left; font-weight: 600; color: var(--muted); font-size: 12px;
text-transform: uppercase; letter-spacing: 0.04em;
padding: 4px 8px 4px 0; border-bottom: 1px solid var(--border);
padding: 4px 16px 4px 0; border-bottom: 1px solid var(--border);
}
.admin-table td { padding: 8px 8px 8px 0; border-bottom: 1px solid var(--border); vertical-align: middle; }
/* 16px between columns, so a right-aligned count never touches the
left-aligned text beside it ("5" against "16d ago"). */
.admin-table td { padding: 10px 16px 10px 0; border-bottom: 1px solid var(--border); vertical-align: middle; }
.admin-table th:last-child, .admin-table td:last-child { padding-right: 0; }
.admin-table tr:last-child td { border-bottom: none; }
.admin-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.admin-table td .btn-sm + .btn-sm { margin-left: 6px; }