Skip to main content

max / makeover-webview

0.69.0: a notice fades before it is removed quasicoherent 43bdbff7. makeover-timing has always said Intent::Dismiss is how long a notice lives before it starts to leave, and that the leaving is Motion::Fade. makeover-build wrote --motion-fade into every consumer timing.css and nothing in the tree read it: three renderers removed the node the moment the dismiss was up. Keyed off data-leaving rather than a class, because Emit prefixes every class this crate writes and the party setting it is a script with no prefix to hand. Reduced motion is handled by the token, which timing.css already zeroes. The renderer setting the attribute must still remove the node on a timer: a zero-length transition may fire no event, and a node waiting on one that never comes stays forever.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_0136sbU8F6i9WrcvA3wn4Lgk
Author: Max Johnson <me@maxj.phd> · 2026-08-29 18:44 UTC
Signed with PGP, not checked
Commit: 79938ba46963deb6c4e39c84bb40da0a8ebbf6ef
Parent: a38c05b
2 files changed, +51 insertions, -3 deletions
M Cargo.toml +3 -3
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-webview"
3 - version = "0.68.0"
3 + version = "0.69.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
@@ -17,7 +17,7 @@
17 17 # patch satisfy the requirement and still fail to compile. That happened once
18 18 # with `form::radio_html` calling `FieldKind::Radio`, and makeover-build is where
19 19 # it surfaced, one release later.
20 - makeover-layout = "0.39.0"
20 + makeover-layout = "0.40.0"
21 21 # The capability axis. `makeover-touch` decides whether a hover rule should be
22 22 # gated at all; `makeover-geometry` spells the gate as a media condition. Both
23 23 # answers are owned elsewhere and neither is re-derived here.
@@ -27,7 +27,7 @@
27 27 # satisfies "0.8" and keeps a second makeover-geometry in the graph next to the
28 28 # 0.7 this crate asks for. `Density` is nominally distinct across the two and
29 29 # the build fails on a type that reads as identical.
30 - makeover-touch = "0.29.0"
30 + makeover-touch = "0.30.0"
31 31 makeover-geometry = "0.7"
32 32
33 33 [lints.rust]
M src/lib.rs +48
@@ -2029,6 +2029,54 @@
2029 2029 css.push_str(&form::unit_rules(opts));
2030 2030 css.push_str(&form::option_detail_rules(opts));
2031 2031 css.push_str(&form::note_rules(opts));
2032 + css.push_str(&leaving_rules());
2033 + css
2034 + }
2035 +
2036 + /// How a transient notice goes away.
2037 + ///
2038 + /// quasicoherent `43bdbff7`. `makeover-timing` has said since it shipped that
2039 + /// `Intent::Dismiss` is how long a notice lives *before it starts to leave*,
2040 + /// and that the leaving itself is `Motion::Fade`. `makeover-build` writes
2041 + /// `--motion-fade` into every consumer's `timing.css`, and until this rule
2042 + /// nothing in the tree read it: three renderers removed the node the moment
2043 + /// the dismiss was up, and the crate's own test called the version that waits
2044 + /// the correct one.
2045 + ///
2046 + /// # Why an attribute and not a class
2047 + ///
2048 + /// [`Emit`]'s prefix moves every class this crate writes, so a class here would
2049 + /// have to be resolved through `class()` by whoever sets it -- and the party
2050 + /// setting it is a script, which has no prefix to hand. `data-leaving` is
2051 + /// outside that namespace, so a renderer can set it from JavaScript with no
2052 + /// coordination.
2053 + ///
2054 + /// The transition sits on the notice and the opacity on the leaving state, so
2055 + /// the element is transitionable before the attribute arrives; a transition
2056 + /// declared in the same rule as the value it changes has nothing to animate
2057 + /// from.
2058 + ///
2059 + /// # Reduced motion is handled by the token, not by a second rule here
2060 + ///
2061 + /// `timing.css` already zeroes `--motion-fade` under `prefers-reduced-motion`.
2062 + /// The reader who asked for less motion gets an instant change rather than a
2063 + /// fade, and the renderer that sets the attribute must still remove the node on
2064 + /// a timer rather than on `transitionend` -- a zero-length transition may fire
2065 + /// no event at all, and a node waiting on one that never comes stays forever.
2066 + ///
2067 + /// The fallback is `0ms` and not a guessed duration: a page with no timing
2068 + /// sheet has not opted into this vocabulary, and the honest answer there is the
2069 + /// behaviour it had before, which is the notice going away at once.
2070 + ///
2071 + /// Added 0.69.0, and it is this crate's first `--motion-*` consumer.
2072 + fn leaving_rules() -> String {
2073 + let mut css = String::new();
2074 + let _ = writeln!(
2075 + css,
2076 + "[data-notice] {{\n transition: opacity var(--motion-fade, 0ms) \
2077 + ease-out;\n}}"
2078 + );
2079 + let _ = writeln!(css, "[data-notice][data-leaving] {{\n opacity: 0;\n}}");
2032 2080 css
2033 2081 }
2034 2082