Skip to main content

max / makenotwork

Hand surface, bevel and tone back to makeover on a described act The bare `button` element rule in style.css sets background, box-shadow and color, the same three the generated `.button` sets. style.css is in @layer components, which the order statement puts after `makeover`, so the element rule won whatever the specificity: a described act rendered in --surface-overlay with --shadow-raised and, worse, in --content rather than its tone. A destructive act was indistinguishable from an ordinary one. Four handoff rules, the same `revert-layer` deferral `.tab.chosen` already uses. The app keeps padding, border and font from the element rule; makeover keeps surface, bevel and tone. Tone is handed back on `[data-tone]` rather than on `.button`, because an untoned button has no colour in the layer to roll back to and would land on the UA default instead of --content. Verified by rendering, not by reading the sheets: a probe page with the four sheets and the layer statement, under a theme setting --surface-raised and --surface-overlay apart. Before, all three buttons took --surface-overlay and --content; after, a described act takes --surface-raised and the danger act takes --danger, with padding and border unchanged on all three. Three REVIEWED_OVERLAPS entries, because the drift check reads property names and not values and cannot see that a `revert-layer` declaration is a deferral. Also corrects four comments that said this file is unlayered. It has been in @layer components since b3dca353; the conclusions they drew were right and the mechanism they named was not.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-22 03:51 UTC
Signed with PGP, not checked
Commit: fc73366091d1e0e6546174b1cede8c1236faec4e
Parent: 7c67064
2 files changed, +71 insertions, -14 deletions
M server/build.rs +22 -5
@@ -631,11 +631,25 @@
631 631 /// `.tab { box-shadow }` and `.chosen { box-shadow }` are the third kind and the
632 632 /// awkward one -- one rule, two entries, because the check reads every class in
633 633 /// a selector and `.tab.chosen` is both. This file zeroes the base `button`
634 - /// shadow on a tab, and unlayered CSS outranks `@layer makeover` whatever the
635 - /// specificity, so the generated `.tab.chosen` bevel cannot apply on its own.
636 - /// The chosen arm takes `box-shadow: revert-layer`, which hands the property
637 - /// back to the layer rather than naming a value. Two arms, one of which is a
638 - /// deferral rather than an override, so both entries stay.
634 + /// shadow on a tab, and it is in `@layer components`, which the order statement
635 + /// puts after `makeover`, so it outranks the layer whatever the specificity and
636 + /// the generated `.tab.chosen` bevel cannot apply on its own. The chosen arm
637 + /// takes `box-shadow: revert-layer`, which hands the property back to the layer
638 + /// rather than naming a value. Two arms, one of which is a deferral rather than
639 + /// an override, so both entries stay.
640 + ///
641 + /// **A deferral, again.** `.button { background }`, `{ box-shadow }` and
642 + /// `{ color }` are the same shape as the chosen tab and were added for the same
643 + /// reason (2026-08-21). The bare `button` element rule in `style.css` sets all
644 + /// three, and a described act carries the generated `.button` class, so before
645 + /// the handoff an act rendered in `--surface-overlay` with `--shadow-raised`
646 + /// and in `--content` rather than its tone -- a destructive act indistinguishable
647 + /// from an ordinary one. The three declarations here are all `revert-layer`, so
648 + /// each hands the property back rather than taking it; the app keeps padding,
649 + /// border and font from the element rule. The check reads property names and
650 + /// not values, which is why a deferral has to be recorded here at all: an entry
651 + /// buys silence for a real override on the same pair later, and that is the
652 + /// cost of every line in this list.
639 653 ///
640 654 /// **A divergence taken on purpose.** `.table-row { display }` is `grid` here
641 655 /// against makeover's `table-row`, which is the same build-time grid story
@@ -669,6 +683,9 @@
669 683 // a generated stylesheet has no way to reach <noscript>.
670 684 ("showing-frame", "display"),
671 685 ("current", "display"),
686 + ("button", "background"),
687 + ("button", "box-shadow"),
688 + ("button", "color"),
672 689 ("card", "color"),
673 690 ("chosen", "box-shadow"),
674 691 ("progress-fill", "background"),
@@ -453,6 +453,45 @@
453 453 box-shadow: var(--shadow-inset);
454 454 }
455 455
456 + /* A described act carries the generated `.button` class, and the element rule
457 + above sets the same surface, bevel and tone the generated class does. This
458 + file is in @layer components, which the order statement puts after
459 + `makeover`, so the element rule wins whatever the specificity. Without the
460 + handoff below a described act renders in --surface-overlay with
461 + --shadow-raised and, worse, in --content rather than its tone: a
462 + destructive act looks identical to an ordinary one. Same mechanism and same
463 + remedy as `.tab.chosen` further down; read the comment there.
464 +
465 + The split is deliberate. The app keeps padding, border and font from the
466 + element rule, makeover keeps surface, bevel and tone. That is the
467 + arrangement that already makes `.field` work, where the app's input rule
468 + happens to set only geometry and typography.
469 +
470 + Tone is handed back on `[data-tone]` rather than on `.button`, because an
471 + untoned button has no colour in the layer to roll back to and would land on
472 + the UA default instead of --content. */
473 + .button {
474 + /* respec-ok: a layer handoff, not a value. */
475 + background: revert-layer;
476 + box-shadow: revert-layer;
477 + }
478 +
479 + .button:hover {
480 + /* respec-ok: a layer handoff, not a value. */
481 + background: revert-layer;
482 + }
483 +
484 + .button:active {
485 + /* respec-ok: a layer handoff, not a value. */
486 + background: revert-layer;
487 + box-shadow: revert-layer;
488 + }
489 +
490 + .button[data-tone] {
491 + /* respec-ok: a layer handoff, not a value. */
492 + color: revert-layer;
493 + }
494 +
456 495 .btn-primary {
457 496 background: var(--primary-dark);
458 497 color: var(--primary-light);
@@ -819,9 +858,10 @@
819 858 for a git nav link; the tab has one.
820 859
821 860 The chosen arm's box-shadow comes back through the layer rather than being
822 - restated. `.tab` below zeroes the base `button` shadow, and this file is
823 - unlayered, so it outranks @layer makeover whatever the specificity: without
824 - the revert, the generated bevel never applies and a chosen tab reads flat.
861 + restated. `.tab` below zeroes the base `button` shadow, and this file is in
862 + @layer components, which the order statement puts after `makeover`, so it
863 + outranks it whatever the specificity: without the revert, the generated
864 + bevel never applies and a chosen tab reads flat.
825 865 `revert-layer` hands the property back instead of naming a value, which is
826 866 the difference between deferring to the design system and copying it. */
827 867 .tab.chosen {
@@ -1156,8 +1196,8 @@
1156 1196 /* Fill and depth come from layout.css, generated by makeover-webview: the card
1157 1197 is `--surface-raised` plus `--bevel-raised`, and `:hover` goes to
1158 1198 `--hover-surface` on a fine pointer only. Declaring either here would take
1159 - them back, since style.css is unlayered and unlayered beats the `makeover`
1160 - layer whatever the specificity. The box-shadow this used to carry was
1199 + them back, since style.css is in @layer components, which the order
1200 + statement puts after `makeover`, whatever the specificity. The box-shadow this used to carry was
1161 1201 `--shadow-card`, which already aliases `--bevel-raised`, so only the fill
1162 1202 actually moved: `--surface-overlay` to `--surface-raised`. Those are equal on
1163 1203 the platform default and a theme may set them apart. */
@@ -11614,8 +11654,8 @@
11614 11654
11615 11655 /* Only the corner. `display`, `width`, `height` and the raised fill and bevel
11616 11656 all come from makeover's own `.picture-img` rule, which layout.css puts on
11617 - every page through crate::shell::head. Restating any of them here is an
11618 - unlayered declaration quietly beating the design system, which is what the
11657 + every page through crate::shell::head. Restating any of them here is a
11658 + components-layer declaration quietly beating the design system, which is what the
11619 11659 drift check in build.rs exists to catch -- and did, on the first attempt at
11620 11660 this file. */
11621 11661 [data-widget="carousel"] .picture-img {
@@ -11643,8 +11683,8 @@
11643 11683 `box-shadow: 0 4px 10px var(--elevation), 0 14px 36px var(--elevation)`,
11644 11684 which is the defect `c0b63ea9` names: in-flow depth is a bevel and elevation
11645 11685 is for what sits *over* the page. It survived this long because it was
11646 - invisible -- an unlayered declaration on the same class makeover sets, so it
11647 - beat the design system's bevel silently. The drift check in build.rs found it
11686 + invisible -- a declaration in a later layer on the same class makeover
11687 + sets, so it beat the design system's bevel silently. The drift check in build.rs found it
11648 11688 the moment the frame became a described `.picture-img`.
11649 11689
11650 11690 So the frame is raised by makeover's bevel now, from `Depth::Raised`, and