Expand description
The adaptation layer of the make-family design system.
makeover answers what colour. makeover-geometry answers how much
space, and owns the two axes an adaptation is stated against:
Density (pointer or touch) and SizeClass (compact, medium,
expanded). makeover-layout answers what the thing is. This crate
answers one question and no other:
Does this affordance exist here?
Like makeover-layout it emits nothing. It is a description, rendered to
CSS by makeover-webview and to whatever the other renderers can express.
§Why this is a crate and not a density preset
Measured across the MNW server’s @media blocks (137) and goingson’s
ui-mode-* blocks (192), bucketed by what the declarations inside actually
change:
| bucket | MNW | GO | retired by |
|---|---|---|---|
| density | 32% | 27% | a makeover-geometry preset |
| type | 23% | 15% | the type scale |
| columns | 20% | 12% | makeover_layout::Column |
| reflow | 16% | 17% | makeover_layout::Arrangement |
| show/hide | 12% | 12% | this crate |
| reposition | 6% | 20% | this crate |
| appearance | 1% | 17% | this crate |
The bottom three are the roughly 43% that no spacing scale can retire, and
trying is the mistake this whole family already made once. display: none
on a keyboard hint says the affordance does not exist on touch. No amount
of gap retuning expresses that, and a scale that tried would be smuggling a
product claim onto a measurement axis — which is exactly what the 2026-07-29
Touch demolition was for.
Two of the eight members are not in that census at all, and the exception is
worth stating rather than leaving to be noticed. Affordance::Gesture and
Affordance::Haptic adapt behaviour, which no stylesheet contains, so
counting @media blocks could never have found them. Their evidence has the
same shape in a different file: goingson frontend/js/touch.js:12 and
Balanced Breakfast frontend/js/touch.js:12 carry the identical line
const isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);and each hangs five gestures off it. Two apps, arrived at independently, hand-rolling the density question this crate exists to answer.
§The two axes are borrowed, never redefined
Boundaries are not this crate’s job. makeover-geometry quotes Material 3’s
window size classes at 600 and 840 and carries Density; this crate names
affordances against those two and adds no third axis, no fourth class and
no breakpoint of its own. If a rule here wants a boundary that does not
exist, that is a conversation with makeover-geometry, not a constant.
§What density is allowed to gate
Density is a claim about the contact patch and nothing else. So it gates affordances that depend on an interaction a fingertip cannot perform — hovering, and the keyboard chrome that documents shortcuts a touch surface has no way to send. It does not gate anything that is really about how much screen there is. A phone is small and touch; a tablet is big and touch.
That separation is asserted, not merely intended, by
density_gates_only_what_the_contact_patch_touches. Putting a screen-budget
claim on the input device is the specific failure that produced this crate,
and re-introducing it should have to come to the test and say so.
§Both densities gain something (0.3.0)
Until 0.3.0 this crate asserted that touch is pointer minus what a fingertip
cannot do — availability only ever subtracted as you moved from
Density::Pointer to Density::Touch, and a test called
touch_never_gains_an_affordance_pointer_lacks said so. That claim is
withdrawn, deliberately, because it left a hole the crate could not name:
Affordance::Hover’s own doc says a fingertip has no hover state and that
something else has to carry the same actions, and nothing here was allowed
to be that something. Affordance::Anchored and Affordance::Overflow
compensate on the size axis; nothing compensated on the density axis.
Affordance::Gesture and Affordance::Haptic are that compensation, and
both are gained by touch rather than lost to it.
The claim is replaced rather than dropped. Affordance::gained_by makes
each member declare which density it belongs to, and
a_density_member_is_available_on_exactly_the_density_it_declares checks the
declaration against the rule. So a new member still cannot quietly invert:
it has to say which way it goes, in code, and the test is where a wrong
answer surfaces.
§Collapsing is allowed, inverting is not
Borrowed verbatim from makeover-geometry, where two gap relationships both
resolve to zero cells on a terminal and stay two members regardless. Two
affordances here may have identical availability today — Affordance::Hover
and Affordance::Hint do — and are still two members, because the call
site names what is being gated, not the rule. What must never happen is
one of them becoming available where the other is not for a reason that is
really the same reason.
§Deliberately absent
A navigation shell fork. goingson currently carries two: 12 forked
selectors and 10 desktop-only rules concentrated in .app-header, .tab,
.tab-navigation, .pill-nav, .saved-views-sidebar and
.modal-container. That is not one shell adapting, it is two shells, and
choosing to build two is a product decision rather than an adaptation. This
crate will not describe it, and goingson’s own restructure is the way it
stops being true. Named here the way makeover-layout names validation
absent, so nobody has to discover it.
Which class applies. The app decides, from a measured width via
SizeClass::at_width and from whatever it already knows about the input.
This crate takes both as arguments and never sniffs.
What a renderer does when an affordance is unavailable. Hiding it,
substituting it, or showing it unconditionally anyway is renderer policy.
makeover-layout already deleted Fill::fallback for being exactly that.
Enums§
- Affordance
- An affordance whose existence depends on the surface it is offered on.
- Density
- Which input the layout is being sized for.
- Priority
- What a member is worth when there is not room for all of them.
- Size
Class - How much screen there is, independent of what is pointing at it.
Functions§
- column_
cutoff - The column-drop cutoff a window of this size class asks for.