Fix the finish review's 8 material findings on the teletext redesign

Applies the fix round from the finish-reviewer's disposition on commit
4b19cbd, in one batch:

- System's disk table is unreadable below 560px: horizontal-scrolling a
  6-column table hid the usage bar and the temperature, the two
  readings that matter most (discipline 3 — one shared ruler — was
  unauditable as a result). Added a disk_row() macro and a phone-only
  .tt-disk-rows reflow (name + full 20-cell bar + temp on one line,
  device/free/total beneath), CSS-toggled against the existing table
  at the same breakpoint.
- Contrast failure on the highest-severity state: white text on the
  red .tt-field.crit/.tt-toast.error/.tt-btn.armed reversals measured
  4.0:1, under the 4.5:1 floor. Switched to black text (5.25:1),
  matching the black-on-yellow .warn pattern already in use — and
  fixed a second instance of the same bug: .tt-field.crit had no
  .tt-field-meta override, unlike .warn.
- Two type families where the direction contract names one blocky
  monospace: unified on a single self-hosted face (Space Mono, one
  weight, no bold) for both body and the double-height row.
- FIRST VIEWPORT proportion: an unpromised 6-item Just Added grid was
  competing with NOW SHOWING for the fold instead of the contract's
  "lower two-thirds in double-height cyan." Cut to a fixed 3-item,
  fixed-3-column row, which also fixes the orphaned-tile grid defect
  as a side effect (a fixed count can't leave a lone tile beside empty
  tracks).
- Three flat mid-grey fills contradicted discipline 1 (no in-between
  tone): .tt-tile-art, .tt-btn:disabled, and .tt-err pre all painted a
  solid --ink-dimmer background. Centralized the dither .tt-tile-art
  already used correctly into a shared --dither-mid generator and
  applied it to all three.
- Google Fonts dependency on a LAN-only home server: a dropped webfont
  request was a silent identity change, not a graceful fallback.
  Self-hosted Space Mono under static/fonts/ via @font-face; dropped
  the <link> tags from _shell.html and login.html.
- Deliberately deferred: "pages differ in stock" (discipline 5) still
  fires on only one band service-wide. Every other finding was a
  bounded fix to existing code; this one asks for a new mechanism (an
  aggregate status band on P100) rather than a correction, so it's
  flagged for a follow-up rather than rushed into this batch.

Recaptured desktop.png, mobile.png, desktop-system.png and
mobile-system.png (the four files the reviewer's findings cited)
through the same true-390px iframe method as the prior round, and
returned them for the fix round's verdict pass.

Known follow-up: DESIGN.md's typography section (written by the
documenter before this batch) still names the old two-family pairing
and needs a re-run now that the font system changed under it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBw1QodG8ZMYHrGeQfqyDS
This commit is contained in:
Barely Removable 2026-08-29 21:18:11 -07:00
parent 4b19cbdfb2
commit bc7779e590
9 changed files with 155 additions and 28 deletions

View file

@ -29,6 +29,23 @@
pages is a failure.
═══════════════════════════════════════════════════════ */
/* Self-hosted, deliberately: this app runs on a LAN-only home server
behind a filtered/offline-capable network, where a Google Fonts
request can simply fail. A dropped @font-face request means every
character on the page silently falls back to whatever monospace the
OS ships — a different world by accident, not a variation of this
one. One weight only (400): there is no bold in this system. */
@font-face {
font-family: 'Space Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('../fonts/SpaceMono-Regular.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
:root {
/* ── The locked eight ──────────────────────────────────
Teletext had exactly these, at full saturation, and the
@ -67,18 +84,28 @@
--ink-dim: color-mix(in srgb, var(--tt-white) 58%, var(--tt-black));
--ink-dimmer: color-mix(in srgb, var(--tt-white) 30%, var(--tt-black));
/* The one legal way to fill an area with a mid-tone: every "flat
grey panel" in the codebase (a disabled button, an unloaded poster,
a code block behind an error detail) draws from this generator
rather than declaring --ink-dimmer as a solid background. A solid
fill of a dimmed token is still a ninth colour by another name. */
--dither-mid: repeating-linear-gradient(45deg,
var(--ink-dimmer) 0 1px, var(--tt-black) 1px calc(var(--dither-size) * 2));
/* ── Type: one face, exactly two heights ───────────────
Teletext's entire scale. Emphasis comes from colour and
from doubling the height, never from a weight ramp and
never from a third size. */
Teletext's entire scale — ONE family, not one for body and a
different one for the double-height row. Emphasis comes from
colour and from doubling the height, never from a weight ramp
(this face loads a single weight, 400, on purpose — there is
no bold anywhere to reach for) and never from a third size. */
--fs: 0.9375rem; /* 15px — the single body size */
--fs2: 1.875rem; /* 30px — double height, the only other size */
--lh: 1.45;
--row: calc(var(--fs) * var(--lh)); /* one character row */
--cell: 1ch;
--font: 'Chivo Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--font-dh: 'DotGothic16', 'Chivo Mono', ui-monospace, Menlo, monospace;
--font: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--font-dh: var(--font);
/* ── Measure ───────────────────────────────────────────
A teletext page was 40 columns. A screen this wide gets

View file

@ -95,7 +95,15 @@
}
/* A page carrying critical state gets a heavier field than a quiet
one (discipline 5) — the band goes red and doubles in height. */
.tt-field.crit { background: var(--alert); color: var(--tt-white); }
.tt-field.crit {
background: var(--alert);
/* Black, not white: white-on-red measures 4.0:1 here, under the
4.5:1 floor for 15px uppercase text. Black-on-red clears 5.25:1
and is the correct teletext reversal — every other coloured band
(.warn, a hover row) already reverses to black. */
color: var(--tt-black);
}
.tt-field.crit .tt-field-meta { color: var(--tt-black); }
.tt-field.warn { background: var(--voice); color: var(--tt-black); }
.tt-field.warn .tt-field-meta { color: var(--tt-black); }
@ -273,6 +281,41 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); }
.tt-table tbody tr:hover th,
.tt-table tbody tr:hover td { background: var(--field); color: var(--tt-white); }
/* ── Disk rows (System, phone reflow of the table above) ──
Below 560px a six-column table stops fitting; scrolling it
horizontally hides the bar and the temperature, the two
readings that matter most here. This is the other rendering
of the same disk data, shown only below that width — see the
media query at the bottom of this file for the swap. */
.tt-disk-rows { display: none; flex-direction: column; }
.tt-disk-row { padding: 0.4em 1ch; }
.tt-disk-row-top {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.4em 1ch;
}
.tt-disk-name {
flex: 0 0 auto;
min-width: 5ch;
color: var(--ink);
letter-spacing: 0.16em;
text-transform: uppercase;
}
.tt-disk-pct {
flex: 0 0 auto;
color: var(--live);
font-variant-numeric: tabular-nums;
}
.tt-disk-row-sub {
margin-top: 0.15em;
color: var(--ink-dim);
}
.tt-disk-row:hover { background: var(--field); }
.tt-disk-row:hover .tt-disk-name,
.tt-disk-row:hover .tt-disk-pct,
.tt-disk-row:hover .tt-disk-row-sub { color: var(--tt-white); }
/* ── Tiles (library / discover) ─────────────────────────
Artwork is the one thing in this world that is not made of
characters, so it is treated as an inserted transmission: a
@ -284,21 +327,28 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); }
grid-template-columns: repeat(auto-fill, minmax(min(12.5ch, 100%), 1fr));
padding: 0 1ch;
}
/* P100's Just Added: a fixed row, not a viewport-dependent auto-fill.
Paired with a fixed item count (see home()), this guarantees a full
row every time rather than leaving a lone tile beside empty tracks —
and it keeps this block compact, so NOW SHOWING above it can hold the
lower two-thirds of the first viewport the direction contract names. */
.tt-tiles-row {
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 560px) {
.tt-tiles-row { grid-template-columns: repeat(1, 1fr); }
}
.tt-tile { display: flex; flex-direction: column; min-width: 0; }
.tt-tile-art {
display: block;
width: 100%;
aspect-ratio: 2 / 3;
object-fit: cover;
background: var(--ink-dimmer);
}
/* No artwork came back. A dithered field, not a fake poster. */
.tt-tile-art-empty {
background-image: repeating-linear-gradient(
45deg,
var(--ink-dimmer) 0 1px,
var(--tt-black) 1px calc(var(--dither-size) * 2)
);
/* Covers the box while a real poster is still loading, and IS the
fallback for a poster that never arrives — dithered, per
discipline 1, never a flat mid-grey panel. */
background-color: var(--tt-black);
background-image: var(--dither-mid);
}
.tt-tile-t {
margin-top: 0.4em;
@ -330,8 +380,13 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); }
cursor: pointer;
}
.tt-btn:hover:not(:disabled) { background: var(--live); }
.tt-btn:disabled { background: var(--ink-dimmer); color: var(--ink-dim); cursor: default; }
.tt-btn.armed { background: var(--alert); color: var(--tt-white); }
.tt-btn:disabled {
background-color: var(--tt-black);
background-image: var(--dither-mid);
color: var(--ink-dim);
cursor: default;
}
.tt-btn.armed { background: var(--alert); color: var(--tt-black); }
.tt-btn.done { background: transparent; color: var(--ok); cursor: default; }
.tt-btn-quiet {
background: transparent;
@ -398,7 +453,8 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); }
.tt-err pre {
margin: 0.3em 0 0;
padding: 0.5em 1ch;
background: var(--ink-dimmer);
background-color: var(--tt-black);
background-image: var(--dither-mid);
color: var(--ink);
font-family: inherit;
font-size: inherit;
@ -539,6 +595,11 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); }
.tt-row-val { margin-left: auto; white-space: normal; }
.tt-head-name { letter-spacing: 0.12em; }
.tt-head-clock { margin-left: 0; flex-basis: 100%; }
/* The System disk table swaps for its row reflow here — see the
"Disk rows" block above. */
.tt-table-scroll { display: none; }
.tt-disk-rows { display: flex; }
}
/* ── Search ─────────────────────────────────────────────
@ -615,7 +676,10 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); }
pointer-events: none;
}
.tt-toast.show { opacity: 1; transform: translateY(0); }
.tt-toast.error { background: var(--alert); color: var(--tt-white); }
.tt-toast.error {
background: var(--alert);
color: var(--tt-black); /* same contrast fix as .tt-field.crit */
}
/* ── Sign-in ────────────────────────────────────────────
The one page with no navigation, so it centres its single

Binary file not shown.