Skip to main content

max / makenotwork

Hand the described components back their chrome, and stop paying for it in the allowlist Every bare `button`, `a`, `input`, `select`, `textarea`, `label` and `p` rule in style.css reaches the described screens, and style.css is in @layer components, which the order statement puts after `makeover`. So this file was taking sixty-four properties from the design system on components it does not draw: a described chip's disabled colour, every focus ring makeover insets, a field's well, the surface and bevel of a card, a tab and both facet controls. The new section at the end of the file hands them back. It is at the end because a handoff has to outrank the rule it defers past and some of those rules carry an attribute, so `.field` does not beat `input[type="text"]` and the field handoffs are element-qualified. Each one is on the arms makeover actually paints, not on the plain class: makeover gives a chip a colour only when it is disabled, and a blanket handoff would roll a live chip back to the UA default instead. The nineteen `revert-layer` entries in REVIEWED_OVERLAPS are gone. makeover-build 0.50.0 reads the value, so a handoff is no longer indistinguishable from an override -- which is what had made the list of remedies and the list of licences the same list, each entry buying silence for a real override on the same pair afterwards.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-22 16:59 UTC
Signed with PGP, not checked
Commit: 3531fca0c174c407779f2983cf86d40eec608329
Parent: fe7d20c
4 files changed, +241 insertions, -80 deletions
M server/Cargo.lock +24 -24
@@ -2865,7 +2865,7 @@
2865 2865 checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
2866 2866 dependencies = [
2867 2867 "libc",
2868 - "windows-sys 0.52.0",
2868 + "windows-sys 0.61.2",
2869 2869 ]
2870 2870
2871 2871 [[package]]
@@ -3650,7 +3650,7 @@
3650 3650 checksum = "1a791e6620676a875f362f3156ed213e73ca099a09bf992c18812abe65cc37b1"
3651 3651 dependencies = [
3652 3652 "bstr",
3653 - "hashbrown 0.15.5",
3653 + "hashbrown 0.17.1",
3654 3654 ]
3655 3655
3656 3656 [[package]]
@@ -4801,7 +4801,7 @@
4801 4801 source = "registry+https://github.com/rust-lang/crates.io-index"
4802 4802 checksum = "82cb6a9f675da968c63b6208c641b9dca58fc0133ae53375736b1767b0cab8bd"
4803 4803 dependencies = [
4804 - "windows-sys 0.59.0",
4804 + "windows-sys 0.61.2",
4805 4805 ]
4806 4806
4807 4807 [[package]]
@@ -5381,9 +5381,9 @@
5381 5381
5382 5382 [[package]]
5383 5383 name = "makeover-build"
5384 - version = "0.49.1"
5384 + version = "0.50.0"
5385 5385 source = "registry+https://github.com/rust-lang/crates.io-index"
5386 - checksum = "b66a88fb6bd30611d1b30c28d1ede1742c58236148f8e825996dd23fc995966b"
5386 + checksum = "ea4372039ae6e2b306a5fa084f813ef9fef5f7ed6221a17cd7b59fab712fa70a"
5387 5387 dependencies = [
5388 5388 "makeover",
5389 5389 "makeover-geometry",
@@ -5414,9 +5414,9 @@
5414 5414
5415 5415 [[package]]
5416 5416 name = "makeover-webview"
5417 - version = "0.57.0"
5417 + version = "0.58.0"
5418 5418 source = "registry+https://github.com/rust-lang/crates.io-index"
5419 - checksum = "de975123768cac0c56ae40479cac721fe8498e0fd50e643e6884799d8eb12d75"
5419 + checksum = "6ec0cf871810510f38e24230d1781cb572437a0c4550b638b3d45e3ba1474cb4"
5420 5420 dependencies = [
5421 5421 "makeover-geometry",
5422 5422 "makeover-layout",
@@ -5703,7 +5703,7 @@
5703 5703 source = "registry+https://github.com/rust-lang/crates.io-index"
5704 5704 checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
5705 5705 dependencies = [
5706 - "windows-sys 0.59.0",
5706 + "windows-sys 0.61.2",
5707 5707 ]
5708 5708
5709 5709 [[package]]
@@ -7320,7 +7320,7 @@
7320 7320 "errno",
7321 7321 "libc",
7322 7322 "linux-raw-sys 0.12.1",
7323 - "windows-sys 0.52.0",
7323 + "windows-sys 0.61.2",
7324 7324 ]
7325 7325
7326 7326 [[package]]
@@ -7400,7 +7400,7 @@
7400 7400 "security-framework 3.7.0",
7401 7401 "security-framework-sys",
7402 7402 "webpki-root-certs",
7403 - "windows-sys 0.52.0",
7403 + "windows-sys 0.61.2",
7404 7404 ]
7405 7405
7406 7406 [[package]]
@@ -8526,7 +8526,7 @@
8526 8526 "getrandom 0.4.3",
8527 8527 "once_cell",
8528 8528 "rustix 1.1.4",
8529 - "windows-sys 0.52.0",
8529 + "windows-sys 0.61.2",
8530 8530 ]
8531 8531
8532 8532 [[package]]
@@ -9952,7 +9952,7 @@
9952 9952 source = "registry+https://github.com/rust-lang/crates.io-index"
9953 9953 checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
9954 9954 dependencies = [
9955 - "windows-sys 0.48.0",
9955 + "windows-sys 0.61.2",
9956 9956 ]
9957 9957
9958 9958 [[package]]
@@ -10740,6 +10740,18 @@
10740 10740 "pkg-config",
10741 10741 ]
10742 10742
10743 + [[patch.unused]]
10744 + name = "kberg"
10745 + version = "0.1.0"
10746 +
10747 + [[patch.unused]]
10748 + name = "ops-status"
10749 + version = "0.1.0"
10750 +
10751 + [[patch.unused]]
10752 + name = "painhours"
10753 + version = "0.1.0"
10754 +
10743 10755 [[patch.unused]]
10744 10756 name = "quasi-immediate"
10745 10757 version = "0.53.0"
@@ -10756,18 +10768,6 @@
10756 10768 name = "quasi-tauri"
10757 10769 version = "0.53.0"
10758 10770
10759 - [[patch.unused]]
10760 - name = "kberg"
10761 - version = "0.1.0"
10762 -
10763 - [[patch.unused]]
10764 - name = "ops-status"
10765 - version = "0.1.0"
10766 -
10767 - [[patch.unused]]
10768 - name = "painhours"
10769 - version = "0.1.0"
10770 -
10771 10771 [[patch.unused]]
10772 10772 name = "synckit-client"
10773 10773 version = "0.8.1"
@@ -169,7 +169,7 @@
169 169 # through quasi-webview for makeover-layout's reason: two `form::` paths from
170 170 # two resolutions are two crates, and the `data-format="markdown"` rules in
171 171 # `static/layout.css` are generated from this one.
172 - makeover-webview = "0.57.0"
172 + makeover-webview = "0.58.0"
173 173 # For the request head the per-viewer state factory reads. axum re-exports it,
174 174 # but the factory's signature is quasi-axum's and names `http::request::Parts`.
175 175 http = "1.3.1"
@@ -250,7 +250,7 @@
250 250 # Emits static/geometry.css (makeover-geometry) and static/layout.css
251 251 # (makeover-webview). The same generator GO and BB run; only the output paths
252 252 # differ, since the server serves its stylesheets rather than bundling them.
253 - makeover-build = "0.49.0"
253 + makeover-build = "0.50.0"
254 254 # Read directly for the embeds' own copy of the spacing layer, which they need
255 255 # because an iframe cannot link a stylesheet. makeover-build does not re-export
256 256 # it, so the pin lives here and has to track the one makeover-build resolves.
M server/build.rs +36 -54
@@ -127,6 +127,7 @@
127 127 &HAND_WRITTEN_CSS,
128 128 &makeover_build::Emit::default(),
129 129 REVIEWED_OVERLAPS,
130 + REVIEWED_ELEMENT_OVERLAPS,
130 131 );
131 132 makeover_build::check_vocabulary_use(
132 133 &markup_files(),
@@ -620,7 +621,7 @@
620 621 /// Class-and-property overlaps with the generated stylesheet that have been
621 622 /// read and kept.
622 623 ///
623 - /// Three kinds, and only the first is what the check is really for.
624 + /// Two kinds, and only the first is what the check is really for.
624 625 ///
625 626 /// **Different selector arms.** `.badge { color }` and `.card { color }`:
626 627 /// makeover colours the tone and disabled arms, the server colours the base and
@@ -635,43 +636,30 @@
635 636 /// `.progress > .progress-fill`. It already carried a `respec-ok` comment
636 637 /// saying so.
637 638 ///
638 - /// `.tab { box-shadow }` and `.chosen { box-shadow }` are the third kind and the
639 - /// awkward one -- one rule, two entries, because the check reads every class in
640 - /// a selector and `.tab.chosen` is both. This file zeroes the base `button`
641 - /// shadow on a tab, and it is in `@layer components`, which the order statement
642 - /// puts after `makeover`, so it outranks the layer whatever the specificity and
643 - /// the generated `.tab.chosen` bevel cannot apply on its own. The chosen arm
644 - /// takes `box-shadow: revert-layer`, which hands the property back to the layer
645 - /// rather than naming a value. Two arms, one of which is a deferral rather than
646 - /// an override, so both entries stay.
639 + /// `.tab { box-shadow }`, `{ color }` and `{ cursor }` are this file taking the
640 + /// property outright: it zeroes the base `button` shadow on a tab and dresses
641 + /// the tab's own text, and a described tab gets that rather than makeover's.
642 + /// The chosen arm is the opposite and is not here any more -- it is a
643 + /// `revert-layer`, and since makeover-build 0.50.0 a handoff is not read as a
644 + /// taking, so it needs no entry.
647 645 ///
648 - /// **A deferral, again.** `.button { background }`, `{ box-shadow }` and
649 - /// `{ color }` are the same shape as the chosen tab and were added for the same
650 - /// reason (2026-08-21). The bare `button` element rule in `style.css` sets all
651 - /// three, and a described act carries the generated `.button` class, so before
652 - /// the handoff an act rendered in `--surface-overlay` with `--shadow-raised`
653 - /// and in `--content` rather than its tone -- a destructive act indistinguishable
654 - /// from an ordinary one. The three declarations here are all `revert-layer`, so
655 - /// each hands the property back rather than taking it; the app keeps padding,
656 - /// border and font from the element rule. The check reads property names and
657 - /// not values, which is why a deferral has to be recorded here at all: an entry
658 - /// buys silence for a real override on the same pair later, and that is the
659 - /// cost of every line in this list.
646 + /// **What is no longer in this list, and why that is the point.** Nineteen
647 + /// entries came out on 2026-08-22, every one of them a `revert-layer`. The
648 + /// check used to read property names without their values, so a later layer
649 + /// handing a property back looked identical to a later layer taking it, and
650 + /// each of those entries had to be written to buy silence for a remedy. The
651 + /// cost was not the noise: an entry permits a real override on the same pair
652 + /// for good, so the list of remedies and the list of licences were the same
653 + /// list. makeover-build 0.50.0 reads the value, and the handoffs at the end of
654 + /// `style.css` now speak for themselves.
660 655 ///
661 - /// The same pass measured the rest of the element-vs-class overlaps and handed
662 - /// four more classes back the same way, which is where the `chip`, `segment`,
663 - /// `toggle`, `link` and `field` entries come from. `button` takes surface and
664 - /// bevel from a chip, a segment and a toggle; `a` renders a described link as
665 - /// body text; `select` and `textarea` set a background and a bevel where the
666 - /// `input` rule sets neither, which is the accident the shipped `.field`
667 - /// precedents survived on. Every one of those entries is a `revert-layer`.
656 + /// The element rules those handoffs defer past are the other half of the same
657 + /// release: `REVIEWED_ELEMENT_OVERLAPS` below, and the section at the end of
658 + /// `style.css` that keeps it empty.
668 659 ///
669 - /// Not handed back, and still overlapping: `.facet-prune` and `.facet-take`
670 - /// (makeover gives them no base surface, so there is nothing in the layer to
671 - /// roll back to and the app's chrome has to be withdrawn by hand), `.badge` and
672 - /// `.card` on their own arms, and the disabled treatment, where this file's
673 - /// opacity rule is a charter decision rather than an accident. See the
674 - /// mnw-server task filed 2026-08-21.
660 + /// Still overlapping and still this file's: `.badge` and `.card` on their own
661 + /// arms, and the disabled treatment's `opacity`, which is a charter decision
662 + /// and a property makeover does not set.
675 663 ///
676 664 /// **A divergence taken on purpose.** `.table-row { display }` is `grid` here
677 665 /// against makeover's `table-row`, which is the same build-time grid story
@@ -705,26 +693,7 @@
705 693 // a generated stylesheet has no way to reach <noscript>.
706 694 ("showing-frame", "display"),
707 695 ("current", "display"),
708 - ("button", "background"),
709 - ("button", "box-shadow"),
710 - ("button", "color"),
711 696 ("card", "color"),
712 - ("chip", "background"),
713 - ("chip", "box-shadow"),
714 - ("field", "background"),
715 - ("field", "box-shadow"),
716 - ("field", "outline-offset"),
717 - ("link", "background"),
718 - ("link", "border"),
719 - ("link", "color"),
720 - ("link", "font"),
721 - ("link", "padding"),
722 - ("link", "text-decoration"),
723 - ("segment", "background"),
724 - ("segment", "box-shadow"),
725 - ("toggle", "background"),
726 - ("toggle", "box-shadow"),
727 - ("chosen", "box-shadow"),
728 697 ("progress-fill", "background"),
729 698 ("tab", "box-shadow"),
730 699 ("tab", "color"),
@@ -732,6 +701,19 @@
732 701 ("table-row", "display"),
733 702 ];
734 703
704 + /// Bare element rules that reach a generated class and have been read and kept.
705 + ///
706 + /// The second pass of the same check, and the one this file had no answer to
707 + /// until makeover-build 0.50.0: a rule with no class in it is invisible to the
708 + /// list above, and `button { color: var(--content) }` in @layer components beat
709 + /// the generated `.button[data-tone]` on every described act on the site. A
710 + /// destructive act rendered identically to an ordinary one for months.
711 + ///
712 + /// Empty, and meant to stay that way. The remedy for every one of them is a
713 + /// `revert-layer` handoff in style.css, which says which arms makeover keeps in
714 + /// the file the browser reads rather than in a build script.
715 + const REVIEWED_ELEMENT_OVERLAPS: &[(&str, &str, &str)] = &[];
716 +
735 717 /// The short commit sha to stamp into `GIT_HASH`, and the watches that decide
736 718 /// when this build script has to run again.
737 719 ///
@@ -526,6 +526,7 @@
526 526 padding: revert-layer;
527 527 font: revert-layer;
528 528 box-shadow: revert-layer;
529 + cursor: revert-layer;
529 530 }
530 531
531 532 /* A described field on a `select` or a `textarea`. The input rule further down
@@ -11877,4 +11878,182 @@
11877 11878 narrow viewport is this page's call, not the design system's. */ display: none; }
11878 11879 }
11879 11880
11881 +
11882 + /* ============================================================================
11883 + LAYER HANDOFFS
11884 +
11885 + Everything below gives a property back to @layer makeover rather than
11886 + setting one. This file is in @layer components and the order statement puts
11887 + it after `makeover`, so every rule here wins over the design system whatever
11888 + the specificity -- including the bare `button`, `a` and `input` rules further
11889 + up, which carry no class and so reach every described component the site
11890 + renders. That is the whole reason this section exists: without it a described
11891 + act takes this file's chrome instead of the one makeover drew for it, and the
11892 + difference is invisible in the source of either file.
11893 +
11894 + `revert-layer` is the deferral. It names no value, so the design system can
11895 + change what a chip looks like without this file learning about it.
11896 +
11897 + Two rules govern how these are written, and both are load-bearing:
11898 +
11899 + - Hand a property back on the arms makeover actually paints, not on the
11900 + plain class. Makeover gives a chip a colour only when it is disabled, so
11901 + `.chip { color: revert-layer }` would roll a live chip back past this
11902 + file's `button { color }` to the UA default rather than to anything
11903 + makeover chose.
11904 + - Outrank the rule being deferred past. Both rules are this file's and both
11905 + are in the same layer, so that contest is decided by specificity and then
11906 + by source order. `.field` does not beat `input[type="text"]`, which is
11907 + why the field handoffs are element-qualified, and this section sits at
11908 + the end of the file so an equal-specificity handoff still wins.
11909 +
11910 + makeover-build's check_vocabulary reads both halves and fails the build on a
11911 + pair that has neither a handoff nor a reviewed exception, so this section
11912 + cannot fall behind the design system without somebody being told.
11913 + ============================================================================ */
11914 +
11915 + /* The surfaces a bare `button` was taking. `.card`, `.tab` and the two facet
11916 + controls are all rendered as buttons by the described screens. */
11917 + .card,
11918 + .tab,
11919 + .facet-take,
11920 + .facet-prune {
11921 + /* respec-ok: a layer handoff, not a value. */
11922 + background: revert-layer;
11923 + box-shadow: revert-layer;
11924 + }
11925 +
11926 + .card:hover,
11927 + .tab:hover,
11928 + .facet-take:hover,
11929 + .facet-prune:hover {
11930 + /* respec-ok: a layer handoff, not a value. */
11931 + background: revert-layer;
11932 + }
11933 +
11934 + .card:active,
11935 + .tab:active,
11936 + .facet-take:active,
11937 + .facet-prune:active {
11938 + /* respec-ok: a layer handoff, not a value. */
11939 + background: revert-layer;
11940 + box-shadow: revert-layer;
11941 + }
11942 +
11943 + /* A facet control's colour is makeover's in every state: muted when disabled,
11944 + secondary when the value it names is not standing, inherited otherwise.
11945 + There is no arm of it this file has an opinion about. */
11946 + .facet-take,
11947 + .facet-prune {
11948 + /* respec-ok: a layer handoff, not a value. */
11949 + color: revert-layer;
11950 + }
11951 +
11952 + /* The disabled arm. Makeover paints all four properties here -- muted text, the
11953 + not-allowed cursor, and the surface it keeps rather than dropping -- and this
11954 + file's `button:disabled` was taking every one of them. */
11955 + .button:disabled,
11956 + .button[aria-disabled="true"],
11957 + .card:disabled,
11958 + .card[aria-disabled="true"],
11959 + .chip:disabled,
11960 + .chip[aria-disabled="true"],
11961 + .tab:disabled,
11962 + .tab[aria-disabled="true"],
11963 + .segment:disabled,
11964 + .segment[aria-disabled="true"],
11965 + .toggle:disabled,
11966 + .toggle[aria-disabled="true"],
11967 + .field:disabled,
11968 + .field[aria-disabled="true"],
11969 + .facet-take:disabled,
11970 + .facet-take[aria-disabled="true"],
11971 + .facet-prune:disabled,
11972 + .facet-prune[aria-disabled="true"] {
11973 + /* respec-ok: a layer handoff, not a value. */
11974 + background: revert-layer;
11975 + box-shadow: revert-layer;
11976 + color: revert-layer;
11977 + cursor: revert-layer;
11978 + }
11979 +
11980 + /* The focus ring. Makeover insets a field's so it does not collide with the
11981 + bevel and leaves every other one outside, and this file's `:focus-visible`
11982 + rules were flattening the distinction on every described control. */
11983 + .button:focus-visible,
11984 + .card:focus-visible,
11985 + .chip:focus-visible,
11986 + .link:focus-visible,
11987 + .tab:focus-visible,
11988 + .segment:focus-visible,
11989 + .toggle:focus-visible,
11990 + .facet-take:focus-visible,
11991 + .facet-prune:focus-visible {
11992 + /* respec-ok: a layer handoff, not a value. */
11993 + outline: revert-layer;
11994 + outline-offset: revert-layer;
11995 + }
11996 +
11997 + /* A described field is an `<input>`, a `<select>` or a `<textarea>`, and the
11998 + rules for those three carry an attribute, so the handoff has to as well: a
11999 + bare `.field` is the weaker selector and loses inside this layer. The `:focus`
12000 + arm is here because this file sets `outline: none` on it, which removes the
12001 + ring makeover draws on `:focus-visible` rather than replacing it. */
12002 + input.field,
12003 + select.field,
12004 + textarea.field,
12005 + input.field:hover,
12006 + select.field:hover,
12007 + textarea.field:hover,
12008 + input.field:active,
12009 + select.field:active,
12010 + textarea.field:active {
12011 + /* respec-ok: a layer handoff, not a value. */
12012 + background: revert-layer;
12013 + box-shadow: revert-layer;
12014 + }
12015 +
12016 + input.field:focus,
12017 + select.field:focus,
12018 + textarea.field:focus,
12019 + input.field:focus-visible,
12020 + select.field:focus-visible,
12021 + textarea.field:focus-visible {
12022 + /* respec-ok: a layer handoff, not a value. */
12023 + outline: revert-layer;
12024 + outline-offset: revert-layer;
12025 + }
12026 +
12027 + /* A described toggle is an `<input>` too. The hover and active arms are here
12028 + for the same reason as the field's: this file dresses `input[type="submit"]`
12029 + as a raised button in every state, and a toggle is an input. */
12030 + input.toggle,
12031 + input.toggle:hover,
12032 + input.toggle:active,
12033 + input.toggle:focus,
12034 + input.toggle:focus-visible {
12035 + /* respec-ok: a layer handoff, not a value. */
12036 + background: revert-layer;
12037 + box-shadow: revert-layer;
12038 + outline: revert-layer;
12039 + outline-offset: revert-layer;
12040 + }
12041 +
12042 + /* A described link on hover. `.link` above hands the resting colour back; this
12043 + file's `a:hover` is the more specific of the two and was taking the hover
12044 + colour and the underline with it. */
12045 + .link:hover {
12046 + /* respec-ok: a layer handoff, not a value. */
12047 + color: revert-layer;
12048 + text-decoration: revert-layer;
12049 + }
12050 +
12051 + /* Text makeover mutes: a facet's name, and the line a placeholder shows when
12052 + there is nothing to list. This file's bare `p` rule was giving both the body
12053 + colour. */
12054 + .facet-name,
12055 + .placeholder-text {
12056 + /* respec-ok: a layer handoff, not a value. */
12057 + color: revert-layer;
12058 + }
11880 12059 }