Skip to main content

max / makeover-webview

0.57.0: a suggestion's second line, in place of a reason it cannot be picked `1fcf2e9b`, ruled by Max 2026-08-21. A suggestion list now carries `Candidate` rather than `Choice`, and a candidate has no `unavailable` -- a suggestion that cannot be picked is a row a route should not have offered. What it carries instead is the line that tells it from a row reading the same. `.form-suggestion-why` goes and `.form-suggestion-detail` arrives. Not only a rename: `why` was drawn in the disabled token, which said the row was refusing. A detail orients rather than refuses, so it reads as `--content-muted`, which is what every other secondary line here reads as. The interactive rules lose the comment claiming the disabled state draws the unpickable candidate. Nothing is unpickable now, and a comment explaining a state that cannot happen is how the next session re-derives it.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-22 03:11 UTC
Signed with PGP, not checked
Commit: 056b8ce32ffea4bc2104ffa9442e96968cff6de6
Parent: c4e5d79
3 files changed, +27 insertions, -11 deletions
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-webview"
3 - version = "0.56.0"
3 + version = "0.57.0"
4 4 edition = "2024"
5 5 # One copy of this renderer per dependency graph, enforced by cargo rather than
6 6 # by remembering. Two versions means the generated stylesheet and the emitted
M src/form.rs +9 -10
@@ -41,7 +41,7 @@
41 41 //! write it back, at which point it is a form model.
42 42
43 43 use crate::{Emit, class, push_class};
44 - use makeover_layout::{Choice, Depth, Field, FieldKind, Intent as _, Selector, State};
44 + use makeover_layout::{Choice, Depth, Field, FieldKind, Selector};
45 45 use std::fmt::Write as _;
46 46
47 47 /// A string that is already markup, and is emitted without escaping.
@@ -987,16 +987,14 @@
987 987 pub(crate) fn suggestion_rules(opts: &Emit) -> String {
988 988 let list = class("form-suggestions", opts);
989 989 let entry = class("form-suggestion", opts);
990 - let why = class("form-suggestion-why", opts);
990 + let detail = class("form-suggestion-detail", opts);
991 991 let mut css = String::new();
992 992
993 993 let _ = writeln!(css, ".{list}:empty {{\n display: none;\n}}");
994 994 // Over what it covers, which is what a list of candidates is even in flow:
995 995 // it is answering the box above it and goes away when the answer is taken.
996 996 css.push_str(&crate::depth_rule(&list, Depth::Overlay));
997 - // An entry answers a click, so it gets every state one implies -- including
998 - // the disabled rule, which is what draws the candidate that cannot be
999 - // picked and is why `aria-disabled` is the mark rather than a class.
997 + // An entry answers a click, so it gets every state one implies.
1000 998 css.push_str(&crate::interactive_rules(&entry, Depth::Flat, opts));
1001 999 // The keyboard's highlight and the pointer's are the same surface. They are
1002 1000 // the same fact told two ways, and a list where arrowing and hovering look
@@ -1012,11 +1010,12 @@
1012 1010 css,
1013 1011 ".{entry}[aria-selected=\"true\"] {{\n background: var(--hover-surface);\n}}"
1014 1012 );
1015 - let _ = writeln!(
1016 - css,
1017 - ".{why} {{\n color: var(--{});\n}}",
1018 - State::Disabled.token()
1019 - );
1013 + // The second line, muted rather than disabled. `1fcf2e9b` replaced the
1014 + // unavailable reason this rule used to draw: a candidate carries no
1015 + // `unavailable`, and what sits beside the label now is what tells one row
1016 + // from another that reads the same. Disabled would say the row cannot be
1017 + // picked, which is the opposite of what the detail is for.
1018 + let _ = writeln!(css, ".{detail} {{\n color: var(--content-muted);\n}}");
1020 1019
1021 1020 css
1022 1021 }
M src/lib.rs +17
@@ -166,6 +166,23 @@
166 166 //! the same magnitude with its sign off the depth. Both values are the measured
167 167 //! consensus rather than a new opinion.
168 168 //!
169 + //! # 0.57.0: a suggestion's second line, in place of a reason it cannot be picked
170 + //!
171 + //! `1fcf2e9b`, ruled by Max 2026-08-21. `Outcome::Suggestions` now carries
172 + //! `Candidate` rather than `Choice`, and a candidate has no `unavailable`: a
173 + //! suggestion that cannot be picked is a row a route should not have offered.
174 + //! What it has instead is a `detail`, the line that tells it from a row reading
175 + //! the same -- "Format" under audio, software, writing and video.
176 + //!
177 + //! So `.form-suggestion-why` is gone and `.form-suggestion-detail` is here, and
178 + //! the difference is not only the name. `why` was drawn in the disabled token,
179 + //! which said the row was refusing. A detail orients rather than refuses, so it
180 + //! is `--content-muted`, which is what every other secondary line in this crate
181 + //! reads as.
182 + //!
183 + //! No consumer is affected: the only emitter is `quasi-webview`, which moves in
184 + //! the same cascade.
185 + //!
169 186 //! # 0.56.0: an interval, as the group MNW already wrote by hand
170 187 //!
171 188 //! [`makeover_layout::FieldKind::Interval`] emits a `role="group"` named by the