355. A floating button leaves, a field’s room is both paddings, and a floor starts on its first frame
Date: 2026-09-17
Status
Accepted. Closes the three items ADR-0350, ADR-0352 and ADR-0354 left open. Changes one consequence of each of the last two.
Context
The batch that closed docs/gaps.md G39 to G43 and built three kinds of motion
left three things written down as not done:
text-inputcomputed its room aswidth - 2 × left padding, the same arithmetic that wrappedtext-areawrong in G43. A field does not wrap, so underpadding: 0 16px 0 4pxthe error was a caret scrolled into view 12px late, with the end of the value under the right padding where the clip cut it.- A floating button had no way out. §3.1 says “out: reverse, fast”, and
Overlay.remove()took the button down on the frame it was called, so it vanished. - The Motion screen’s floor was blank in any offscreen render, so the screen had no golden. The settle started on the canvas’s first paint. An offscreen render renders twice to measure and paints once, so it photographed the settle’s first instant, with every tile still invisible above its place.
Decision
Each edge of a field’s padding comes off once. A floating button leaves by class and is removed by a timer. A floor starts on the first frame that renders it.
TextEditor.laidOuttakes the left and right padding separately, asAreaEditordoes since ADR-0350.caretAreauses both as well.FloatSlotis whatFloatednow puts in the overlay layer: the button, and aProperty<Boolean>the slot is bound to. An overlay’s widget is fixed once made, so the slot cannot be swapped out, but a bound property can rebuild it.FloatedState.detach()sets the property, which putsleavingon the button, andbutton.float.leavingincontrols.cssis the exit:opacity: 0andscale(0.9)on--gb-motion-fastwithease-exit. §1.7’s rule 2 holds, because the exit is faster than the 160ms entrance and on the exit curve. The overlay is removed byhost.after(--gb-motion-fast), read throughBuildContext.duration. The state lets go at once, so a rebuild that attaches a new button while the old one leaves shows the two crossing, as a toast queue does.- A press on a leaving button does nothing. §1.7 says input is disabled the instant closing starts. The press asks its own slot’s switch rather than the state’s current slot, which by then may belong to the next button.
TileFloor.at(now)starts a settle or a fade that is waiting for a frame time. The canvas’sanimatingpredicate calls it, and the renderer asks that predicate on every render, painted or not.paintstill calls it too, so a painter used without the predicate behaves as before.gallery-motionis a golden of the screen at the offscreen renderer’s 200ms: the floor part way through its ripple, the swatches part way through a breath, the mark part way round. All three are functions of a virtual clock.
Consequences
- ADR-0352’s “the way out is not built” and ADR-0354’s “the screen has no golden” are no longer true, and both records say so in their status.
- A way out for overlays in general is still not built. Toasts have their own
Phase, and a hud or a tour goes when removed.FloatSlotis the pattern another overlay would follow, and nothing else has asked for one. - The box tree under a floating button gains a composition node. The Basic-screen golden is unchanged, because a stateless node draws nothing.