Skip to main content

max / makeover-webview

The rules a derived showing-chrome needs makeover-layout 0.23.0 lets a description say a region shows some of its children. A renderer derives its own chrome from that; these are the rules that chrome needs, and they run in the direction the enhancement does. Nothing here hides a child or shows a control. Whatever binds the region sets data-ready, and that is what collapses the stack and reveals the row -- so a reader with no script gets every child and no controls, which is more content rather than less, and a reader with script gets a settled page rather than a stack that jumps after load. MNW proved the shape by hand with a <noscript> stylesheet; the property is the description's, not one app's. No flex and no gap: how the three controls sit is spacing, and row_rules refuses spacing for the same reason. current joins chosen and latched as a bare state.
Author: Max Johnson <me@maxj.phd> · 2026-08-14 22:28 UTC
Signed with PGP, not checked
Commit: a7b85229408adca95eed68006e452db1d3649eaa
Parent: 4510ddd
2 files changed, +68 insertions, -1 deletion
M src/lib.rs +65
@@ -1084,6 +1084,70 @@
1084 1084 css
1085 1085 }
1086 1086
1087 + /// A region showing one child at a time, and the chrome that moves between them.
1088 + ///
1089 + /// [`makeover_layout::Showing`] lets a description say that a region holds
1090 + /// several children and shows some of them. A renderer derives its own chrome
1091 + /// from that, which is what stops every renderer growing a `match` on a widget
1092 + /// name; these are the rules the derived chrome needs.
1093 + ///
1094 + /// # Why the default is every child, and the enhancement takes them away
1095 + ///
1096 + /// The controls are a lie until something binds them. A prev button rendered
1097 + /// into a document with no script is a control that looks live and answers
1098 + /// nothing, and the reader it lies to is exactly the one who cannot see the
1099 + /// other children either — the collapsing and the moving are the same half.
1100 + ///
1101 + /// So the rules run in the direction the enhancement does. Nothing here hides a
1102 + /// child and nothing here shows a control. Whatever binds the region sets
1103 + /// `data-ready` on it, and that is what collapses the stack to one and reveals
1104 + /// the row that moves it. A reader with no script gets every child in order and
1105 + /// no controls, which is more content rather than less, and a reader with
1106 + /// script gets a settled page rather than a stack that jumps to one frame after
1107 + /// load.
1108 + ///
1109 + /// MNW proved this shape by hand — a `<noscript>` stylesheet opening its
1110 + /// carousel back out — and it is here rather than there because the property is
1111 + /// the description's, not one app's.
1112 + ///
1113 + /// # Not spacing
1114 + ///
1115 + /// The row's gaps are `makeover-geometry`'s question and are absent for
1116 + /// [`row_rules`]'s reason. What is here is `display`, which carries no
1117 + /// magnitude, and the muted readout, which is the same claim `picture-caption`
1118 + /// makes: it says where you are among the children and it is not one of them.
1119 + fn showing_rules(opts: &Emit) -> String {
1120 + let controls = class("showing", opts);
1121 + let position = class("showing-position", opts);
1122 + let frame = class("showing-frame", opts);
1123 + let mut css = String::new();
1124 +
1125 + // Hidden until something binds it, which is the whole argument above.
1126 + let _ = writeln!(css, ".{controls} {{\n display: none;\n}}");
1127 + // Block, and nothing about how the three sit in it. A button and a span are
1128 + // inline already, so they make a row without this crate saying so, and
1129 + // saying so is where `align-items` and a gap would follow -- both spacing,
1130 + // both `makeover-geometry`'s, and `row_rules` refuses them for the same
1131 + // reason.
1132 + let _ = writeln!(
1133 + css,
1134 + "[data-ready] > .{controls} {{\n display: block;\n}}"
1135 + );
1136 +
1137 + // A child is in flow until the region is bound, and then only the current
1138 + // one is. `.current` is a modifier for the reason `.chosen` and `.latched`
1139 + // are: one name for the state, set by whoever knows it.
1140 + let _ = writeln!(
1141 + css,
1142 + "[data-ready] > .{frame}:not(.current) {{\n display: none;\n}}"
1143 + );
1144 +
1145 + // Reads back one step. `picture-caption`'s rule and its reason.
1146 + let _ = writeln!(css, ".{position} {{\n color: var(--content-muted);\n}}");
1147 +
1148 + css
1149 + }
1150 +
1087 1151 /// A region's stand-in, and the header of a table that can be reordered.
1088 1152 ///
1089 1153 /// Both are 0.12.0 members and both are colour and affordance only, which is
@@ -1287,6 +1351,7 @@
1287 1351 css.push_str(&progress_rules(opts));
1288 1352 css.push_str(&figure_rules(opts));
1289 1353 css.push_str(&picture_rules(opts));
1354 + css.push_str(&showing_rules(opts));
1290 1355 css.push_str(&state_rules(opts));
1291 1356 css.push_str(&table_rules(opts));
1292 1357 css
@@ -377,7 +377,9 @@
377 377 );
378 378 // `chosen` and `latched` never stand alone: the sheet writes
379 379 // `.mo-tab.chosen`, so the state stays bare while the thing moves.
380 - let states = ["chosen", "latched"];
380 + // `current` is the third, and it is the same shape: which child of a
381 + // region showing one at a time is the one showing.
382 + let states = ["chosen", "latched", "current"];
381 383 for name in &plain {
382 384 let expected = if states.contains(&name.as_str()) {
383 385 name.clone()