# makeover-timing
The time axis of the make-family design system.
[`makeover`](https://makenot.work/git/max/makeover) resolves colour,
[`makeover-geometry`](https://makenot.work/git/max/makeover-geometry) resolves
distance, and
[`makeover-layout`](https://makenot.work/git/max/makeover-layout) names what a
thing is without resolving anything. This crate answers the remaining question
of the same shape: how long.
## A duration is named by what it is waiting for
| `revert` | a control returning to its resting label after confirming | 1500ms |
| `clear` | a status line emptying itself | 2000ms |
| `dismiss` | a transient notice's lifetime | 3000ms |
| `debounce` | typing settling before the work starts | 150ms |
Whether a toast should live 3000ms or 3500ms is unanswerable on its own.
Whether a message is a toast or a banner is not. That is the whole argument for
the layer, and it is the same one `gap-peer` makes about six pixels.
## One duration per moment, on every renderer
A moment resolves to exactly one duration everywhere, whatever the renderer and
whatever the theme.
Distance has a renderer axis because a fingertip is coarser than a cursor and a
terminal cell is coarser than a pixel: the surface differs. Time does not
differ that way. A second is a second in a browser, in egui and in a terminal,
and the reader waiting it out is the same reader. A divergence between
renderers is a bug report.
## What is deliberately not a moment
**A race is not a moment.** Waiting 300ms before navigating because the write
"should" have landed, or 150ms before closing a dropdown so a mousedown can
beat the blur, is a synchronisation bug wearing a duration's clothes. Naming
those would launder them into design decisions. They get fixed per screen.
**Severity is not a moment, and it is not a fifth number.** A message the user
must not miss does not go away on its own. `makeover-layout` already has the
word for that (such a message is not `Notice::transient`), and a banner has no
lifetime.
**A poll interval is not a moment.** A retry backoff, a health check, an update
check: those are answerable from what they talk to, not from what a reader can
follow.
## Motion and cadence are separate axes
`Moment` says how long a state lasts. `Motion` says how long a change takes,
and `Cadence` says how often a repeating mark repeats. CSS draws the first line
itself, a `setTimeout` against a `transition-duration`: 300ms of fade and
3000ms of toast answer different questions.
`Motion` and `Cadence` both resolve to zero for a reader who has asked for less
motion. A web surface gets that as a `prefers-reduced-motion` block in the
generated stylesheet; every other renderer asks `activity_blink` with the
preference its platform reported.
## Using it
Web surfaces bake the stylesheet in at build time. Nothing here changes at
runtime, so there is no load-time JS step:
```rust
use makeover_timing::timing_css;
std::fs::write("static/timing.css", timing_css())?;
```
egui and ratatui surfaces resolve through `Duration` instead, which is the
reason this is a crate rather than a stylesheet:
```rust
use makeover_timing::{Moment, Motion, Persistence, notice_lifetime};
let settle = Moment::Debounce.duration();
let gone = notice_lifetime(Persistence::Transient).map(|life| life + Motion::Fade.duration());
```
`notice_lifetime` takes the description's answer to "does this go away on its
own" rather than `makeover-layout`'s `Notice`, so this crate stays off that
dependency graph.
## Licence
MIT.