Skip to the page
Tactile UI

Kit part · Forms · Inputs · Numbers · Sliders & faders

Scrub field

A number you can type or drag, the way a pro tool’s value field works: an engraved ruler under the numerals slides beneath a fixed index as the value moves.

Element
<tui-scrub> + <input>
Needs
kit.css + kit.mjs
Rules
scrub.rules.md · 6
Finishes
Aluminium · Graphite · Oak · Walnut · Porcelain · Black · Daylight · Night · Paper · Phosphor · E-paper · AMOLED
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts

Seats open in 11 days. Want one email that morning?

Variants

Plain · agent temperature
With a unit · kW, ms, tokens
RegionWeight
eu-west
us-east
ap-south
Compact · in a table cell
Inspector row · label on the left
Detents · half degrees
Error · what was typed stays
Disabled · set by policy

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartRestHoverScrubbingFocusAt a stopErrorDisabled
Field
Compact

Graphite

PartRestHoverScrubbingFocusAt a stopErrorDisabled
Field
Compact

API

NameKindValuesWhat it does
<tui-scrub>elementwraps one <input>The well: the numerals on top, the ruler along its floor. Builds itself around the input it is given.
<input>elementname · valueThe form control, kept real: its value posts; it becomes a text spinbutton with inputmode="decimal".
min · maxattribute0 · 100The stops. Values are clamped to them; typed values past them are too, with a knock at the stop.
stepattribute1What an arrow key, the wheel and a detent move by. The ruler ticks once per step.
precisionattributethe step’s decimalsDecimals the value keeps. A drag can land between steps down to this; Alt steps a tenth when it allows.
unit · unit-textattributee.g. "ms" · "milliseconds"Printed after the numerals, and spoken in aria-valuetext. unit-text is the spoken form when the printed one reads badly.
px-per-stepattribute1.5–8, from the rangeDrag rate at slow speed. Faster drags gain up to ×4, smoothly.
labelattributetextAn aria-label for the input when there is no printed label, as in a table cell.
data-detentattributeDrags snap to whole steps; each one moves the ruler a notch and taps the phone’s motor where it has one.
data-lockattributePointer lock once a drag passes its dead zone, for endless drags. Refused or unsupported, the drag carries on as usual.
data-boundattribute"min" · "max"Set while the value rests on a stop. Read it; the part writes it.
data-scrubbing · data-errorattributeSet during a drag, and while typed text is not a number.
.tui-scrub--compactclass26px numerals over a ruler of ticks only: for table cells and dense inspectors.
.tui-scrub-rowclasson the .tui-fieldThe label printed on the left, the field on the right: an inspector row.
.tui-scrub__gripclassadded to the labelThe field’s printed label becomes a drag handle too. Added by the part.
.value · .inputpropertynumber · <input>Read or set the number (setting fires nothing); reach the input.
input · changeeventon the <input>input while the value moves; change when it settles: drag released, key pressed, text taken.
Dragpointer3px dead zonePress the ruler or the label and drag; right is more. Under 3px it is a click, and you are typing. Esc puts the value back.
Shift · Altmodifier×10 · ×0.1For drags, arrow keys and the wheel alike.
↑ → · ↓ ←keyboard± stepStep up and down. While you are typing, ← and → move the caret instead.
PageUp · PageDown · Home · Endkeyboard±10 steps · min · maxBig steps and the stops. While typing, Home and End move the caret.
Enter · Escapekeyboardtake · revertTake what was typed, or put back the last good number.
Wheelpointerwhile focusedSteps the value only when the field has focus, so scrolling the page never changes it.
.is-hover · .is-down · .is-focusclasson <tui-scrub>Forced states, for documentation and specimens only.

Markup

<label class="tui-field">
  <span class="tui-label">Temperature</span>
  <tui-scrub min="0" max="2" step="0.1" precision="2">
    <input name="temperature" value="0.70">
  </tui-scrub>
  <span class="tui-hint">0 repeats itself; 2 wanders.</span>
</label>

<label class="tui-field">
  <span class="tui-label">Rate limit</span>
  <tui-scrub min="0" max="6000" step="10" unit="/min" unit-text="requests per minute" data-lock>
    <input name="rate" value="600">
  </tui-scrub>
</label>
import '/kit/kit.mjs';   // defines <tui-scrub>

const temp = document.querySelector('tui-scrub input[name="temperature"]');
temp.addEventListener('input', () => preview(Number(temp.value)));   // while it moves
temp.addEventListener('change', () => save(Number(temp.value)));     // when it settles

// Or read and set the number on the element; setting fires nothing.
const field = temp.closest('tui-scrub');
field.value = 0.2;

Needs kit.css and kit.mjs. How to install the kit.

Accessibility

  • The input is a role="spinbutton" with aria-valuenow, aria-valuemin, aria-valuemax and an aria-valuetext that carries the unit (“600 requests per minute”). Its name is the printed label that wraps it.
  • Everything a drag does, the keyboard does: arrows, Page keys, Home and End, typing. Dragging is never required.
  • Typed text that is not a number sets aria-invalid and a custom validity message, and the hint below says the range; nothing typed is thrown away.
  • Only the ruler and the label take touch-action: none: on a phone the page scrolls from anywhere else, and a tap on the numerals opens the decimal keypad.
  • The ruler and index are decoration for the number above them (aria-hidden). Reduced motion stops the ruler gliding and the stop’s knock; the number, the stop and the lit index still show.

How it’s built

One custom element around a real input. The ruler is a single strip whose ticks are three repeating backgrounds (fine every step, a longer one halfway, a long one at each round-numbered major) each an ink line with a lit line beside it, so the cuts read engraved; the numbers are spans placed along it. The strip moves by transform only, under a fixed index: a printed pointer over a hairline that turns amber with a lamp’s glow while you set the value. Past each end a heavy stop and hatching are printed on the strip, so at min or max the stop arrives under the index; a push past it knocks the hairline 1.5px and lets it settle. The window’s ends are masked away, as a tape curls over its drums. Drags use pointer capture, a 3px dead zone and a smoothed speed-to-gain curve capped at ×4; data-lock adds pointer lock.

Rules

scrub.rules.mdIn the brain

Type it, or drag the ruler. Keys move by the step, a drag reaches the precision, and the index lights only while you are setting it.

6 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

Field

Text entry in a recess, the label printed above it, and a hint that says how to fix an error.

Stepper

A small integer with − and + keys either side, in one well.

Knob

A rotary control for continuous or stepped values where space is square. Its knurl and pointer turn; its light does not.