Skip to main content

max / makeover-webview

The geometry a folded menu run needs, and the mark that turns wrapping off Fallback::Menu has been honoured as wrap since 0.49.0, and run_rules says exactly why: knowing a run is out of room is a measurement, @container compares against a length, and every honest CSS spelling needs the authored breakpoint the room ruling forbids. quasi-webview's menu.js is the measuring; this is the half that has to be here. Two classes for the control the shed members move into, and one override: a run a script has taken over carries data-menu and goes back to nowrap, because a wrapped run always fits and cannot be measured. The unmarked rule is untouched, so a page shipping no script still wraps and keeps every member reachable. No width, no padding, no border on the panel. All three are sizes and sizes are makeover-geometry's; the shadow is what an overlaid surface takes instead of an edge.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-28 15:35 UTC
Signed with PGP, not checked
Commit: d8397220164fcae81363946653e64c048b1ab7c1
Parent: 18c1e2d
2 files changed, +85 insertions, -4 deletions
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-webview"
3 - version = "0.63.0"
3 + version = "0.64.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/lib.rs +84 -3
@@ -1148,8 +1148,22 @@
1148 1148 ///
1149 1149 /// The real mechanism needs the shed members to have somewhere to go, which is
1150 1150 /// markup and belongs to quasi-webview: an overflow control is a member of the
1151 - /// run, and the description does not yet say that a member *is* one. Filed
1152 - /// rather than guessed at.
1151 + /// run, and the description does not yet say that a member *is* one.
1152 + ///
1153 + /// # Menu, once a script is measuring
1154 + ///
1155 + /// That is now built, in `quasi-webview`'s `menu.js`, and this crate's half of
1156 + /// it is two classes and one override. A script that has taken a menu run over
1157 + /// marks it `data-menu`, and a marked run goes back to `nowrap`: wrapping is
1158 + /// what hides the overflow condition the script is trying to measure. The
1159 + /// unmarked rule above is untouched, so a page that ships no script still
1160 + /// wraps, which is rule 1 — every member reachable — rather than a strip with
1161 + /// tabs squeezed off the end.
1162 + ///
1163 + /// `.run-overflow` is the control the shed members move into and
1164 + /// `.run-overflow-items` is where they land. The geometry is this crate's the
1165 + /// way every other surface's is; what is *in* it is the script's, because which
1166 + /// members no longer fit is a measurement and not a description.
1153 1167 fn run_rules(opts: &Emit) -> String {
1154 1168 let run = class("run", opts);
1155 1169 let mut css = String::new();
@@ -1187,6 +1201,49 @@
1187 1201 }
1188 1202 }
1189 1203
1204 + // A menu run a script has taken over. The mark is the script's and this is
1205 + // the only rule that reads it: wrapping is what a run does when nothing is
1206 + // measuring, and it is also what makes the overflow unmeasurable, since a
1207 + // wrapped run always fits. The two cannot both be on.
1208 + let menu = class(fallback_class(Fallback::Menu), opts);
1209 + let _ = writeln!(css, ".{menu}[data-menu] {{\n flex-wrap: nowrap;\n}}");
1210 +
1211 + // The overflow control, and it is a member of the run like any other: in
1212 + // flow, at the end, taking the width of what is in it. `relative` is what
1213 + // the items hang off.
1214 + let overflow = class("run-overflow", opts);
1215 + let items = class("run-overflow-items", opts);
1216 + let _ = writeln!(css, ".{overflow} {{\n position: relative;\n}}");
1217 +
1218 + // Overlaid rather than in flow, for the reason every menu is: a control
1219 + // that pushed the page down when it opened would change the layout it was
1220 + // opened to escape. `inset-inline-end: 0` rather than a left, so the panel
1221 + // stays on the page in both writing directions.
1222 + //
1223 + // No width, no padding and no border. All three are sizes and sizes are
1224 + // makeover-geometry's; what is stated here is placement, the surface and
1225 + // the shadow that separates it from the page, which is the same division
1226 + // `figure_rules` and the timeline entry make. The elevation shadow is what
1227 + // an overlaid surface takes instead of an edge -- see `ELEVATION_PROPERTY`.
1228 + let _ = writeln!(
1229 + css,
1230 + ".{items} {{\n \
1231 + position: absolute;\n \
1232 + inset-block-start: 100%;\n \
1233 + inset-inline-end: 0;\n \
1234 + z-index: 1;\n \
1235 + display: flex;\n \
1236 + flex-direction: column;\n \
1237 + align-items: stretch;\n \
1238 + background: var(--surface-raised);\n \
1239 + box-shadow: var(--elevation-overlay);\n\
1240 + }}"
1241 + );
1242 +
1243 + // `hidden` is how the script closes it, and a flex display would otherwise
1244 + // beat the attribute's own `display: none`.
1245 + let _ = writeln!(css, ".{items}[hidden] {{\n display: none;\n}}");
1246 +
1190 1247 css
1191 1248 }
1192 1249
@@ -1197,7 +1254,19 @@
1197 1254 /// from outside, so the list sits beside it and a test holds the two together.
1198 1255 /// `run` is in it because it is emitted in its own right rather than only as a
1199 1256 /// fallback's fallback.
1200 - pub const RUN_CLASSES: &[&str] = &["run", "run-wrap", "run-stack", "run-shed", "run-menu"];
1257 + pub const RUN_CLASSES: &[&str] = &[
1258 + "run",
1259 + "run-wrap",
1260 + "run-stack",
1261 + "run-shed",
1262 + "run-menu",
1263 + // Not returned by `fallback_class`: these two are the overflow control a
1264 + // measuring renderer builds, and they are in the list because the list is
1265 + // what a host seals its vocabulary against. A class emitted by a script and
1266 + // missing from here is a control with no surface and no edge.
1267 + "run-overflow",
1268 + "run-overflow-items",
1269 + ];
1201 1270
1202 1271 /// The class a run carries for what it does when it is tight.
1203 1272 ///
@@ -3079,6 +3148,18 @@
3079 3148 | "center"
3080 3149 | "min-content"
3081 3150 | "1 1 max-content"
3151 + // A menu run's overflow control, 0.64.0.
3152 + // `column` and `stretch` are the same reading
3153 + // `flex` and `center` get one line up: which way
3154 + // the shed members stack inside the panel and how
3155 + // they line up across it. Neither is a magnitude.
3156 + //
3157 + // `100%` is already above and reused here as the
3158 + // panel's `inset-block-start`, which is "the whole
3159 + // of the control it hangs from" rather than a
3160 + // distance anybody picked.
3161 + | "column"
3162 + | "stretch"
3082 3163 // A picture's three, 0.36.0. `block` is structure
3083 3164 // for the reason `table` is: an inline image sits
3084 3165 // on the baseline and carries a descender's worth