fix(web): an error cell must not lose its colour to the dimming or the selection

Found by reviewing the restyling before pushing it, and demonstrated before
it was fixed.

`td.error` and `td:not(.editable)` used to set DIFFERENT properties — colour
and opacity — so both applied and a dimmed error cell was a dimmed red. The
restyling made the dimming a colour too, at the same specificity and below
`td.error` in the file, so an error in an aggregate cell rendered plain grey:
rgb(107, 103, 95) where the error colour is rgb(168, 50, 45). `td.selected`
and `td.typing` did the same by setting `color: var(--fg)`.

`#grid td.error` is the only place on this page where colour carries MEANING
rather than emphasis, and it is the one a reader takes at face value: a
circular reference or a `mixed` aggregate reading as an ordinary number is
worse than it reading as nothing.

So the dimming moves ABOVE the semantic colours, and a selection is a ring
and a tint and never a colour. That is the class of defect a stylesheet has
and a program does not — two rules that were independent become one property,
and nothing in any language fails.

Also: an expanded notice now caps at 40vh and scrolls. `main` is free to
shrink to nothing, so on a short viewport opening it could have pushed the
grid out entirely.

The test is aimed at the STYLESHEET rather than at a document, because the
question is which rule wins and a fixture per combination would be three
documents testing three documents. It appends a <td> carrying each
combination to the real page's real table and compares two controls taken the
same way, rather than pinning absolute colours.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
This commit is contained in:
Edward Langley
2026-09-21 22:33:02 -07:00
parent 8ad5c0806d
commit 52f615d14d
4 changed files with 103 additions and 8 deletions
+12 -8
View File
@@ -203,6 +203,10 @@ header h1 {
}
.notice-body {
/* Expanded, this must not take the whole window: `main` is free to shrink
to nothing, so a short viewport would lose the grid entirely. */
max-height: 40vh;
overflow: auto;
padding: 0 0.9rem 0.6rem;
max-width: 100ch;
color: var(--dim);
@@ -391,6 +395,14 @@ td.numeric {
text-align: right;
}
/* Everything else on the grid is an aggregate, and dimming it is what makes
the settable cells read as the surface you can act on. */
td:not(.editable) {
color: var(--dim);
}
/* …and the semantic colours come AFTER it, because they are now the same
property at the same specificity and only the order separates them. */
td.error {
color: var(--error);
font-weight: 600;
@@ -405,12 +417,6 @@ td.editable:hover {
background: color-mix(in srgb, var(--accent) 8%, transparent);
}
/* Everything else on the grid is an aggregate, and dimming it is what makes
the settable cells read as the surface you can act on. */
td:not(.editable) {
color: var(--dim);
}
/* The selected cell, and the row header that names it. `:has()` is what lets
the header light up without the page deriving which row is selected — the
selection is already on the cell, decided in Rust. */
@@ -418,7 +424,6 @@ td.selected {
background: var(--accent-soft);
outline: 2px solid var(--accent);
outline-offset: -2px;
color: var(--fg);
font-weight: 700;
}
@@ -434,7 +439,6 @@ td.typing {
outline: 2px solid var(--accent);
outline-offset: -2px;
background: var(--accent-soft);
color: var(--fg);
font-weight: 700;
}