# Scrubbing

> Changing a value by dragging across what names it, without losing the click, the scroll or the way back.

Scrubbing changes a value by dragging sideways across something that names it:
a field's label, a letter or an icon at the start of the field. It is how the
numbers of an inspector are nudged while watching what they change, and it
should cost nothing: the label is still a label, the page still scrolls, and a
drag that was a mistake can be taken back.

## When to use

Offer scrubbing where a value is adjusted by eye — a position, a size, an angle,
an opacity — and the result is visible as it changes. A value people know and
type once — a price, a quantity in a form — gains nothing from it, and its label
is text to read rather than something to drag.

## Best practices

**Keep the click.** A press becomes a scrub only once it has travelled a few
pixels. Short of that it is a click, and a label's click gives its field focus
as it always did. The release of a scrub is not a click: the field is not given
focus, and on a phone no keyboard appears.

**Count travel in steps.** A fixed distance — about two pixels — makes one step
of the value, and what is left over waits for the next movement, so a slow drag
and a fast one over the same distance arrive at the same value. The distance
that made the press a scrub is not counted: crossing the threshold is not itself
a jump.

**Step as the keyboard steps.** A scrubbed step is the arrow key's step: it lands
on the same multiples, Shift makes it ten times larger and Option or Alt ten
times smaller, whichever is held as each step is made.

**Stop at the ends without winding up.** When the value reaches the end of its
range, further travel is spent and gone. The first movement back moves the
value; there is no distance to give back first. A range that is a cycle — an
angle — goes round instead.

**Follow the writing direction.** Toward the end of the line increases the
value: to the right in English, to the left in Arabic or Hebrew.

**Let the drag leave.** Once begun, a scrub continues wherever the pointer goes;
what was pressed holds the pointer until it is released.

**Take it back on Escape.** Escape during a scrub restores the value the scrub
began from, and the rest of the drag does nothing. That Escape belongs to the
scrub alone: a popover around the field stays open. A touch the system takes
over, and the window losing focus, take the scrub back in the same way.

**Report the change once.** Every value on the way is shown as it is reached;
the scrub as a whole is one change — one entry to undo — reported at the
release, and not at all if the value ended where it began or was taken back.

**Show what scrubs, and only that.** What scrubs shows a sideways resize cursor
and is not selected as text. A field that cannot be changed has a label that
does neither.

**Leave the controls inside alone.** Where a label wraps its field, a press in
the field is the field's: it places the caret and selects text as in any field.

## Platform considerations

**Mouse.** Where the browser allows, hold the pointer in place for the length of
the scrub, so that the edge of the screen does not end it, and let it go when
the scrub ends. This is never required: where the lock is refused or does not
exist, the scrub goes as far as the screen does. A browser gives the lock up on
Escape and keeps that key to itself, so losing the lock takes the scrub back.
Only the main button scrubs.

**Pen.** A pen is where it points: it is never locked, and otherwise scrubs as a
mouse does.

**Touch.** Only a sideways drag belongs to the scrub. A drag up or down that
starts on what scrubs still scrolls the page, and nothing else on the page gives
up any scrolling at all.

**Keyboard.** Scrubbing has no keyboard form of its own, and needs none: the
field's arrow keys make the same steps.

**Assistive technology.** What scrubs keeps its role and its name. A label is
still a label, and activating it without a pointer still gives its field focus.

## Behaviour

Each statement is verified by a test in Chromium, Chromium (touch), Firefox, WebKit, WebKit (touch), unless it names the environments it applies to or is marked open.

### Travel

- Travel is counted in whole steps, and what is left over waits for the next movement.
- A movement back counts as readily as one forward.
- Shift asks for a large step and Alt for a small one, as they do of the arrow keys.

### Dragging

- A press that does not travel is a click: nothing is scrubbed, and a label still gives its field focus.
- A drag sideways scrubs: once it has travelled three pixels, a step for every two more.
- A drag toward the start of the line counts down.
- In a line that runs right to left, the directions are mirrored.
- With Shift held the steps are large, with Alt small — whichever is held as each is made.
- The drag goes on outside what was pressed, which holds the pointer until it is released.
- The release of a drag is not a click: a label does not give its field focus.
- A drag from a handle inside a field, out across the field, neither selects in it nor gives it focus.
- After a finger's scrub, which ends in no click, a click made later is a click.
- A press on a control inside what scrubs is the control's.
- Only the main button scrubs.

### Taking it back

- Escape takes the scrub back, unheard by what contains it, and the rest of the drag scrubs nothing.
- Escape during a press that has not become a scrub is left for what contains it.
- The window losing focus takes the scrub back.
- A touch that the browser takes for itself, to scroll, takes the scrub back.

### Pointers

- What scrubs shows a sideways resize cursor, is not selected as text, and leaves scrolling up and down to the page.
- A pen and a finger scrub as a mouse does.
- A finger dragged sideways across a touchscreen scrubs. *(Chromium, Chromium (touch))*
- A mouse asks to be held in place once the scrub is under way; a pen and a finger never do. *(Chromium, Chromium (touch), Firefox, WebKit)*
- Where the browser allows, the mouse is held in place for the length of the scrub, and let go when it ends. *(Firefox)*
- When the browser takes the mouse back, as it does on Escape, the scrub is taken back. *(Firefox)*

## Reference implementation

React: `npx shadcn@latest add https://ui.publictheta.com/r/scrub.json`
