Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
DR.Simple_UI
Showing main. This can be ahead of the version your app has installed — check what a class says it needs before copying it. Releases (opens in a new tab)

Ranges and steppers

tier 2 — classes

The controls whose native internals have to be named per engine. Everything here restates one design in several vendor pseudo-elements.

The two engines expose different pseudo-elements and cannot be combined into one selector list: an unknown pseudo-element invalidates the whole rule, so a shared ::-webkit-…, ::-moz-… list applies in neither browser. Write one rule per engine.

Range

appearance: none removes the whole native widget, track included, so both are drawn here.

A range gives no readout. Put the value in text beside it with .form-range-value — the thumb position is not a number, and it is unreadable to a screen reader on its own. The readout is fixed-width and tabular, so the track does not shift while dragging.

85%

Reservations below this are held for a human decision.

<div class="form-field" style="max-width:420px">
    <label class="form-label" for="r-conf">Confidence threshold</label>
    <div class="dr-row">
        <input class="form-range" id="r-conf" type="range" min="0" max="100" value="85" />
        <span class="form-range-value">85%</span>
    </div>
    <p class="form-hint">Reservations below this are held for a human decision.</p>
</div>

Stepper

For a value adjusted one at a time. The native spinner is tiny, inconsistent between engines, and invisible until hover in some — so it is removed and replaced with two real buttons, which are also a usable size on touch.

Give each button an aria-label: + and are announced as “plus” and “minus” without saying what of. Hiding the native spinner needs both the Firefox property and the WebKit pseudo-element; neither implies the other.

<div class="form-field" style="max-width:220px">
    <label class="form-label" for="s-retry">Retry attempts</label>
    <div class="stepper">
        <button class="btn" type="button" aria-label="One fewer retry">&minus;</button>
        <input class="form-input" id="s-retry" type="number" value="3" min="0" max="9" />
        <button class="btn" type="button" aria-label="One more retry">+</button>
    </div>
</div>

Date and time

.form-input already covers the box. What needs naming is the calendar picker glyph, which the browser draws in a fixed colour we cannot reach — so it is inverted, by an amount that moves with the theme (--picker-invert).

The picker panel is dark because color-scheme is set on <html> from --color-scheme. That is also what makes a <select>'s option list and the native scrollbars match the theme.

<div class="field-grid" style="max-width:520px">
    <div class="form-field">
        <label class="form-label" for="d-from">Active from</label>
        <input class="form-input" id="d-from" type="date" value="2026-07-31" />
    </div>
    <div class="form-field">
        <label class="form-label" for="d-at">At</label>
        <input class="form-input" id="d-at" type="time" value="18:00" />
    </div>
</div>

Three sizes

The stepper takes its tier from its children: .btn-sm on both buttons and .form-input-sm on the input. A date or time input is a .form-input, so it takes the tier the same way.

Every row here is one control height, so the stepper, the date field and the button beside them agree at each size. Mixing tiers within a row is the one thing that makes a filter bar look assembled by accident.

<div class="dr-col dr-gap-2">
    <div class="dr-row-wrap">
        <div class="stepper">
            <button class="btn btn-sm" type="button" aria-label="One fewer retry">&minus;</button>
            <input class="form-input form-input-sm" type="number" value="3" min="0" max="9" aria-label="Retry attempts, small" />
            <button class="btn btn-sm" type="button" aria-label="One more retry">+</button>
        </div>
        <input class="form-input form-input-sm" type="date" value="2026-07-31" aria-label="Active from, small" />
        <button class="btn btn-sm" type="button">Apply</button>
    </div>
    <div class="dr-row-wrap">
        <div class="stepper">
            <button class="btn" type="button" aria-label="One fewer retry">&minus;</button>
            <input class="form-input" type="number" value="3" min="0" max="9" aria-label="Retry attempts" />
            <button class="btn" type="button" aria-label="One more retry">+</button>
        </div>
        <input class="form-input" type="date" value="2026-07-31" aria-label="Active from" />
        <button class="btn" type="button">Apply</button>
    </div>
    <div class="dr-row-wrap">
        <div class="stepper">
            <button class="btn btn-lg" type="button" aria-label="One fewer retry">&minus;</button>
            <input class="form-input form-input-lg" type="number" value="3" min="0" max="9" aria-label="Retry attempts, large" />
            <button class="btn btn-lg" type="button" aria-label="One more retry">+</button>
        </div>
        <input class="form-input form-input-lg" type="date" value="2026-07-31" aria-label="Active from, large" />
        <button class="btn btn-lg" type="button">Apply</button>
    </div>
</div>