# 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. It is also
why the webview is the wrong renderer to derive a vocabulary from: it can
express anything, so it never pushes back.
## The stylesheet
`stylesheet(&Emit)` writes the component CSS: depths, controls, tokens,
selectors, rows, tables, facets, meters, charts, figures and placeholders. An
app that builds its own markup keeps every line of it and gains 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); }
.button:active { background: var(--surface-well, var(--surface-page)); box-shadow: var(--bevel-inset); }
```
The depth classes are surfaces and nothing more, and the states belong to the
named surfaces. `.button` is that same depth *and* a control, so it carries
hover, press, focus and disabled. `.card` is a raised object, and carries those
states only when it is written on a control: `a.card`, `button.card`,
`label.card`, or a card with `data-act`. A card that is only read has no hover
to cancel.
Hover rules are gated on the pointer density `makeover-touch` and
`makeover-geometry` answer for, never decided here, and every focus ring is the
one `focus_rule` writes.
## The markup
Each module renders one description member to markup, and each has a
`_into` form that writes into a buffer the caller already has:
- `form::field_html` renders a `makeover_layout::Field`: label, control, hint,
error and note, marked `aria-invalid` and `aria-describedby` from the
description itself.
- `list::cells_html` renders a row's cells against its columns, and `table`
holds the rules that narrow a table by column priority.
- `facet`, `meter`, `chart`, `figure` and `placeholder` render the members of
the same names.
```rust
use makeover_layout::{Field, FieldKind, Value};
use makeover_webview::{Emit, form::{Filling, field_html}};
let field = Field::new(FieldKind::Text, "title", "Title");
let html = field_html(&field, &Filling::of(Value::Text("Ship it")), &Emit::default());
```
Everything is escaped here. One escaper covers both text and attribute sinks,
and the single hole is `form::Markup`, which a caller has to name. The copy the
markup carries (the required marker, the exclude button's name, the markdown
editor's modes) comes from `Emit`, English by default.
Numbers the rules read, such as a bar's value, ride in `data-vars` rather than
`style`, so a page under a `style-src` without `'unsafe-inline'` still draws
them. Serve `VARS_JS` beside the stylesheet to apply them.
The `_placed` forms write the same bytes and also report where each part
landed, for a caller compiling this markup into a template.
## Substitution, three ways
A theme with no `surface-well` is answered differently by each of the three
renderers, which is why substitution belongs to a renderer and not to the
description:
- `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. Consumers take it directly or through `makeover-build`, which
writes the stylesheet into an app's frontend at build time.
## Licence
MIT.