Skip to main content

max / goingson

Fail the build when a rule takes a property the generated sheet already sets look-wave-2 F3 specified this guard on 2026-08-09 and it was never built, which is why the sort caret was found by audit rather than by a build. It is on now. What it found is not what the audit predicted, and the difference is worth recording. Nine class-and-property overlaps exist between styles.css and the generated sheet, and every one is the two sheets styling different selector arms of the same class: makeover draws the disabled and tone arms, GO draws the resting one and its own variants. None is an override. The true duplicates were the two carets, and those went on 2026-08-11. So the nine are declared with their reasons rather than deleted, and the check now holds the arrangement rather than the absence of one. `.table-heading { content }` is the entry worth reading: the reserved caret gap belongs on the unsorted arm, and putting it on the sorted arm blanks the generated caret. That trap bit twice, and it now has a name in the build rather than only in a comment. An entry that stops colliding fails too, so the list cannot outlive what it describes. Verified in both directions: a `.card { background }` added to styles.css fails the build and names the file, the class and the property.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-12 01:48 UTC
Commit: b2edf80d84bc7e3a130a44b7743d4bc447f7e77b
Parent: cf1f996
3 files changed, +61 insertions, -7 deletions
M Cargo.lock +4 -4
@@ -3398,9 +3398,9 @@
3398 3398
3399 3399 [[package]]
3400 3400 name = "makeover-build"
3401 - version = "0.21.0"
3401 + version = "0.23.1"
3402 3402 source = "registry+https://github.com/rust-lang/crates.io-index"
3403 - checksum = "1b2484a18b0a0355762c4c5feaaaa8165b38ec33ce55fd2df6092dd0887b4a49"
3403 + checksum = "09326dad8b7fdbf1ae4359f866e8f4432a86c837f5a8bf1576bf0c298afcd41b"
3404 3404 dependencies = [
3405 3405 "makeover",
3406 3406 "makeover-geometry",
@@ -3442,9 +3442,9 @@
3442 3442
3443 3443 [[package]]
3444 3444 name = "makeover-webview"
3445 - version = "0.28.0"
3445 + version = "0.30.1"
3446 3446 source = "registry+https://github.com/rust-lang/crates.io-index"
3447 - checksum = "d2402af6249ca10f0a7ddbad2b5eb81725676ffca84093cb86bc1cadfa5b2272"
3447 + checksum = "d6f86fc082c5fc0d73192a3f3f5f74840848ba2cc815154ab207b1fc707f7c64"
3448 3448 dependencies = [
3449 3449 "makeover-geometry",
3450 3450 "makeover-layout",
@@ -18,11 +18,11 @@
18 18 # Materialises all three generated files: themes/, geometry.css, layout.css,
19 19 # and holds both drift checks this build.rs used to carry itself: touch density
20 20 # as of 0.17, breakpoints as of 0.18.
21 - makeover-build = "0.21.0"
21 + makeover-build = "0.23.1"
22 22 # The table CSS is generated here too: the columns are this app's, so the
23 23 # shared helper cannot know them, but the tracks and the narrowing rules come
24 24 # from the description rather than from hand-written nth-child cuts.
25 - makeover-webview = "0.28.0"
25 + makeover-webview = "0.30.1"
26 26 makeover-layout = "0.16.0"
27 27 # Width. Direct rather than through makeover-webview, because the narrow table
28 28 # pass keys off SizeClass::Compact and a boundary reached transitively is a
@@ -112,7 +112,7 @@
112 112 # The forms emitter and the description it renders. Runtime deps, not build:
113 113 # form markup depends on the value, the error and the options at the moment a
114 114 # modal opens, so it cannot be materialised the way the stylesheet is.
115 - makeover-webview = "0.28.0"
115 + makeover-webview = "0.30.1"
116 116 makeover-layout = "0.16.0"
117 117 # Browser opening
118 118 open = { workspace = true }
@@ -411,6 +411,54 @@
411 411 println!("cargo:rerun-if-changed=frontend/index.html");
412 412 }
413 413
414 + /// Class-and-property overlaps with the generated stylesheet that have been
415 + /// read and kept.
416 + ///
417 + /// Every one of these is the two sheets styling **different selector arms** of
418 + /// the same class, which is a pairing rather than a collision. The check
419 + /// collapses arms, because separating them would need a selector matcher and a
420 + /// checker that guesses wrong about specificity fails correct builds. So the
421 + /// judgement is recorded here instead.
422 + ///
423 + /// The pattern, which holds for six of the eight: makeover styles the state
424 + /// arms of a component -- `:disabled`, `[aria-disabled]`, `[data-tone]` -- and
425 + /// this app styles the base and its own variants. Neither sheet touches the
426 + /// other's arm, and deleting either side would lose a state nothing else
427 + /// draws.
428 + ///
429 + /// - `.badge { color }` and `.tab { color }` -- makeover colours the tone and
430 + /// disabled arms; GO colours the base and its own `[data-color]` palette,
431 + /// which is a per-tag colour rather than a makeover `Tone`.
432 + /// - `.button { color }`, `.button { cursor }`, `.card { cursor }` -- makeover
433 + /// sets the disabled arm (`not-allowed`, muted text); GO sets the resting one
434 + /// (`pointer`, ordinary text) and its `--primary` / `--danger` variants.
435 + /// - `.field { color }` -- same split.
436 + /// - `.field { box-shadow }` -- makeover owns the resting and invalid bevels;
437 + /// GO's are `.field--ghost` (deliberately borderless in toolbars) and the
438 + /// `:focus` ring on an invalid field, which makeover draws no arm for.
439 + /// - `.table-heading { content }` -- the sort caret's reserved gap. Declared on
440 + /// the **unsorted** arm (`:not([aria-sort])::after`) precisely so it cannot
441 + /// blank the generated caret on the sorted one. Declaring it on the sorted
442 + /// arm is the trap that bit twice on 2026-08-11; this entry is what keeps the
443 + /// correct arrangement legible rather than looking like the bug.
444 + /// - `.table-heading { display }` -- different elements, not different arms:
445 + /// makeover sets `table-cell` on the heading, GO sets `inline-block` on the
446 + /// caret pseudo-element inside it.
447 + ///
448 + /// An entry that stops colliding fails the build, so this list cannot outlive
449 + /// what it describes.
450 + const REVIEWED_OVERLAPS: &[(&str, &str)] = &[
451 + ("badge", "color"),
452 + ("button", "color"),
453 + ("button", "cursor"),
454 + ("card", "cursor"),
455 + ("field", "box-shadow"),
456 + ("field", "color"),
457 + ("tab", "color"),
458 + ("table-heading", "content"),
459 + ("table-heading", "display"),
460 + ];
461 +
414 462 /// Widths that are tuning inside the wide shell, not a shell boundary.
415 463 ///
416 464 /// A shell boundary is a [`SizeClass`] edge and belongs to makeover-geometry.
@@ -446,6 +494,12 @@
446 494 // a parameter to get there: TUNING_WIDTHS is this app's own list, where the
447 495 // touch string is makeover-geometry's and no app gets a say.
448 496 makeover_build::check_breakpoints(&frontend, TUNING_WIDTHS);
497 + makeover_build::check_vocabulary(
498 + &frontend,
499 + &makeover_build::Emit::default(),
500 + &["geometry.css", "layout.css", "tables.css"],
501 + REVIEWED_OVERLAPS,
502 + );
449 503 makeover_build::check_touch_density(frontend.join("js"));
450 504 check_sortable_headers(&frontend);
451 505