| 1 |
# makeover-layout |
| 2 |
|
| 3 |
The renderer-agnostic half of the make-family design system. |
| 4 |
|
| 5 |
`makeover` answers *what colour*, and varies by theme. `makeover-geometry` |
| 6 |
answers *how much space*, and varies by density and surface. This crate answers |
| 7 |
*what the thing is*, and varies by nothing. |
| 8 |
|
| 9 |
## The deferral rule |
| 10 |
|
| 11 |
A description names intents and relationships, never values. Say `Fill::Raised`, |
| 12 |
never `#D9DDF4`. Say `Gap::Peer`, never `6px`. What is left once colour and |
| 13 |
spacing are deferred is composition: which edges are lit, what inverts on press, |
| 14 |
what nests in what. |
| 15 |
|
| 16 |
The constraint that shapes it: a renderer that can only paint rectangles has to |
| 17 |
be able to express the result. egui has no `box-shadow: inset` and one stroke per |
| 18 |
widget with no per-side control. A terminal has box-drawing characters and one |
| 19 |
cell of resolution, and cannot draw a two-tone lit edge at all. So this crate |
| 20 |
names the intent, and each renderer picks an expression it can produce, including |
| 21 |
dropping half of one. |
| 22 |
|
| 23 |
## What is here |
| 24 |
|
| 25 |
**Depth**: `Bevel`, `Edge`, `Fill`, `Depth`. Fill and bevel are named together, |
| 26 |
because naming them apart is what let them disagree. Every consumer had at least |
| 27 |
one region carrying a raised bevel over a recessed fill; audiofiles fixed it and |
| 28 |
wrote the bug down, Balanced Breakfast still had twelve of them a year later. |
| 29 |
`Depth` makes that unrepresentable. |
| 30 |
|
| 31 |
**Components**: `Token` (a badge does not answer a click, a chip does), `Notice` |
| 32 |
(a toast is transient, a banner is persistent), `RowPart`, `Heading`, `Selector`, |
| 33 |
`Readiness`, and `Tone`, the one intent family they share. |
| 34 |
|
| 35 |
**Schemas**: `Field` for forms, `Column` for lists and tables. A column carries a |
| 36 |
drop priority, which is what replaces addressing columns by position. Both |
| 37 |
webview apps hide narrow-screen columns with `nth-child`, so inserting a column |
| 38 |
silently hides the wrong one. |
| 39 |
|
| 40 |
**Structure**: `Region` for the parts of a screen, `Arrangement` for how a screen |
| 41 |
is put together. Two arrangements, because the two apps have two. `Showing` says |
| 42 |
how many of a region's children are visible at once, which is what lets a |
| 43 |
renderer derive a tab strip or a prev/next row for itself instead of recognising |
| 44 |
a widget by name. |
| 45 |
|
| 46 |
The vocabulary was read off audiofiles, the only implementation written by a |
| 47 |
consumer with no CSS, then checked against both webview apps. |
| 48 |
|
| 49 |
## What is not here, on purpose |
| 50 |
|
| 51 |
Validation. `Field` describes fields, not constraints. Neither app has a shared |
| 52 |
story, and a schema that describes one without the other acquires a constraint |
| 53 |
layer per app, which is how the divergence this crate exists to end got started. |
| 54 |
|
| 55 |
## Where the description stops |
| 56 |
|
| 57 |
The bespoke widgets: a day-plan timeline, a kanban board, a calendar. A |
| 58 |
description expressive enough to produce those is not a description any more, it |
| 59 |
is a widget library with one app on top. Generate the boring 80% so the bespoke |
| 60 |
20% gets the attention. |
| 61 |
|
| 62 |
`Region::Bespoke` states that limit instead of hiding it. The description names |
| 63 |
the place, the app owns the contents, and a screen with a timeline in it is still |
| 64 |
a whole screen. Without that member the description would cover only the boring |
| 65 |
screens, and the interesting ones would need a second path beside the router. |
| 66 |
One opaque member, and `Region::described()` is how a renderer tells it from the |
| 67 |
rest. |
| 68 |
|
| 69 |
## Status |
| 70 |
|
| 71 |
On crates.io at 0.2.0, published 2026-07-29. 0.1.0 went out the day before, and |
| 72 |
the sequencing decision behind it was not to publish until a second renderer had |
| 73 |
exercised the vocabulary; three had, and it cost the description exactly one |
| 74 |
deletion (`Fill::fallback`, which turned out to be renderer policy). |
| 75 |
|
| 76 |
0.2.0 went out on a different basis: no renderer has been through the components, |
| 77 |
schemas or structure yet. So expect the first renderer to force a correction, and |
| 78 |
expect that correction to be a 0.3.0 with a migration rather than an edit. |
| 79 |
|
| 80 |
Design lives in the wiki note `makeover-layout`; the backlog is in GoingsOn under |
| 81 |
the project of the same name. |
| 82 |
|
| 83 |
## Licence |
| 84 |
|
| 85 |
MIT. |
| 86 |
|