# makeover-webview The webview renderer for [`makeover-layout`](https://makenot.work/git/max/makeover-layout). Emits CSS. ## The renderer that needs no palette `makeover-immediate` and `makeover-tui` both take a `Palette`, because egui and a terminal need an actual colour before they can put anything on screen. A webview does not. `var(--surface-raised)` *is* the late binding, and the browser resolves it against whatever the theme layer last wrote onto `:root`. So this crate emits text naming intents and never learns a colour, which is the deferral rule with no adapter in the way. It is also why the webview was always the wrong renderer to derive a vocabulary from: it can express anything, so it never pushes back. ## Phase A: the stylesheet No markup here, deliberately. GoingsOn has 145 `innerHTML` sites and Balanced Breakfast 175 `createElement` sites, so moving markup is a migration while adopting a generated stylesheet is a deletion. The apps keep every line of their markup and gain the classes. ```css :root { --bevel-raised: inset 1px 1px 0 var(--bevel-light), inset -1px -1px 0 var(--bevel-dark); --bevel-inset: inset 1px 1px 0 var(--bevel-dark), inset -1px -1px 0 var(--bevel-light); } .raised { background: var(--surface-raised); box-shadow: var(--bevel-raised); } .well { background: var(--surface-well, var(--surface-page)); box-shadow: var(--bevel-inset); } .raised:active { box-shadow: var(--bevel-inset); } ``` Those two custom properties are byte-identical to what both apps already hand-write, which is asserted in the tests. Adoption removes duplicated lines rather than changing a pixel. `.raised:active` is the one thing this renderer gets for free: the cascade carries a pressed state that an immediate-mode renderer resolves per call site, eighteen of them in audiofiles. ## Phase B: form markup `form::field_html` renders a `makeover_layout::Field` to the group both apps build by hand today: label, control, hint, error, in goingson's shape and class names, so adopting it deletes `renderFormField` rather than restyling anything. ```rust use makeover_layout::{Field, FieldKind}; use makeover_webview::{Emit, form::{Filling, Value, field_html}}; let field = Field::new(FieldKind::Text, "title", "Title"); let html = field_html(&field, &Filling::of(Value::Text("Ship it")), &Emit::default()); ``` It emits strings because both apps interpolate fields into larger string-built forms, and it escapes them itself. One escaper covers both sinks: goingson needs four and a correct choice at 543 call sites only because `textContent` serialization will not encode a double quote, which is not a constraint Rust has. The single hole is `form::Markup`, which a caller has to name. Three things the emitter does that neither app does at initial render: - `aria-invalid="true"` on an invalid control, which is what the generated stylesheet keys its danger ring on. goingson sets it from its runtime validation path and not from its renderer, so a field rendered already-invalid is styled as though nothing were wrong. - `aria-describedby` naming the hint as well as the error, so standing help survives an error appearing. - A secret never carries its value into the markup, on `FieldKind::confidential`. Rows and tables are the other half of phase B and are not written yet. ## Substitution, three ways `Fill::Well` has no colour on makeover before 2.3.0, and the three renderers answer that differently. That is the evidence that taking `Fill::fallback` out of the description was right: - `makeover-immediate` substitutes the page in Rust. - `makeover-tui` refuses to substitute and draws an edge, because a terminal quantises the two together. - here, CSS already has the mechanism. `var(--surface-well, var(--surface-page))` falls back in the browser and nothing in Rust decides anything. ## Status On crates.io at 0.5.1, which is the CSS half only; the form markup is unreleased. Consumers are GoingsOn and Balanced Breakfast, both through makeover-build. `cargo run --example dump` prints the stylesheet. ## Licence MIT.