Toggle

Level 1. A track and a knob: one control that is either on or off and changes something the moment it moves. 3 placements on 3 painted screens, and all three are the same switch, Reverse sort on Favorites, and all three are OFF. Measured 2026-08-08 in a browser, at 390 and 1280, in both pointer branches.

3 placements 40x24, everywhere 5 faces 4 of them never on screen 1 role of its own

Three placements, and the best argument in the kit for a page

A shelf shows what the screens wear. This control's screens wear one face of five. Every placement in the product is aria-checked="false" and none is disabled, so the ON face, the ON hover, the ON press and the disabled face have zero placements anywhere in ui-visual/ and exist only in the stylesheet. A vitrine built by walking the screens would show an off switch and stop, and the four rules that decide what happens when somebody uses it would be invisible to everyone except a person reading CSS.

The level and the reason for it stay on the atoms page.

It left filters.css, and it was never part of it

Until 2026-08-05 this control lived in the sort filter's file. A switch and a sort menu are two families and the only thing they shared was a file name: the menu is a <details> holding a panel holding a list of labelled radios, which is a molecule, and this is one control with a knob, which is an atom. The stand was already saying so out loud by needing two specimens for one component.

It is also what an empty containment is written to catch. filters contained nothing that the arithmetic could see, so it was called an atom by a calculation with no information in it, and the inventory and the old vitrine printed that as a decision somebody had taken. Nobody had.

Two rules did not come with it, and they are a grey-box fossil. The old file opened with a hairline border, a 10px face, 2/8 padding and a bold weight for the checked state. Every .toggle in both trees carries role="switch", measured, zero exceptions, and the [role=switch] rule re-declares the border, the padding and the cursor and sets font-size:0. So the fossil painted nothing, and the bold weight was a weight on a 0px face. Deleted rather than carried.

The five faces, four of which the product has never shown

off, and it is the only one with a placement

Vault, darkDaylight, light

on, 0 placements

Vault, darkDaylight, light

The knob slides from 3 to 19 and the track fills with the flat action colour. It is the one place in this system where brass is a flat ground rather than a gradient, and that single fact is what forces the press below.

disabled, 0 placements

Vault, darkDaylight, light

aria-disabled rather than the disabled attribute, so the control keeps its place in the tab order and can still say why it is unavailable.

The remaining two are hover and press, which no static page can show at all. They are in the table below with the rules that draw them.

The pointer, and the one role that exists for one control

statewhat happens
hover, off--bg-control-hover
press, off--bg-pressed, the system's one pressed ground
press, on--color-action-pressed, brass held down
hover, onnothing, deliberately
disabled--opacity-disabled and not-allowed, and both pointer rules exclude it by attribute so a dead switch cannot light up

An ON switch is a flat action ground, so neither of the system's two press mechanisms fits it. Settling onto --bg-pressed would read as "it turned itself off", and there is no gradient angle to reverse because this is the one brass surface that is not a gradient. It is the only control that asked for a role of its own, so it got one, and nothing else in the system needs it.

The hover and press rules both exclude [aria-checked=true] as well as the disabled state, so neither swaps the brass ground out from under a switch that is on. Before 2026-08-05 the control had no pointer state at all: its only feedback was the knob sliding, which is the RESULT of the click and not a sign the control is live.

40x24, and the token was wrong before it was short

at 390at 1280with a finger
the track40x2440x2440x24, unchanged
the knob16x16, inset 3 from the top and 3 from the left, and 19 from the left when on

It was 40x22 until 2026-08-05, which is two pixels under the smallest floor there is. A switch is a target you hit rather than one you aim at, so the fix was the control's own box and not an invisible pseudo-element stretched over it: a hit area no measurement in this repository can see is a fix nobody can check.

And the token was wrong before it was short. The height read --icon-22, which is the size of a MARK, asked to be the height of a CONTROL. --size-24 is a size primitive and is what the other end of the same declaration, --size-40, already read. The knob is unchanged at 16 and re-centred: inner height 22, so the inset is 3 rather than 2.

24 clears WCAG 2.5.8 AA with nothing to spare and fails 2.5.5 AAA at 44, and this control is not in the one touch floor in components/base.css. That was not a decision either: the floor was assembled from the six per-file lists that existed on the day, and this file had never written one. Three placements, one screen family, and a real question about whether a 24px switch is reachable with a thumb. docs/backlog.md 55.

The name, and why the label is a text node nobody can see

font-size:0 collapses the button's own text to nothing, and the text is still there: Reverse sort, a real node in the document. A zero font size is not display:none, so assistive technology still reaches it, and the product additionally writes an aria-label, which wins.

The name belongs to voice/ and not to this file, which is why nothing here says what a switch should be called. What this file owns is that the control HAS a place for a name and does not draw it.

The rule

The state is the attribute, and the attribute is the selector. Everything here is keyed to [role=switch], [aria-checked] and [aria-disabled], so the drawing and the accessibility tree cannot disagree: there is no class that could say on while the attribute says off.

A target is the control's own box. Not a pseudo-element, not the label beside it, not a padded ancestor. Anything else is a hit area no reading of the page can find.

A control needs a sign that it is live, and the result of the click is not that sign. The knob arriving is the outcome; the ground answering is the control.

The anti-rule

Never take a control's height from an icon token. --icon-22 is the size of a mark. It landed this control two pixels under the smallest accessibility floor there is, and it read as a deliberate 22 to everyone who saw it afterwards.

Never carry a rule across a move without checking that it paints. Two rules came within one edit of being copied into this file, and both were drawing on a face with font-size:0 and a border that was already re-declared.

Never let a pointer state reach a control that is on or dead. A hover that replaces the brass ground says the switch turned itself off, and a disabled switch that lights up says it is available.

Never judge this component from the screens. Four of its five faces have no placement in the product, and a stand built by walking screens would have shown one grey oval.

Where the rest of it is

The level and its reason: the atoms page. The stylesheet: components/toggle.css. The file it left, and the sort menu it was never part of: components/filters.css. The pressed brass role and every other colour: components/tokens.css and DESIGN.md. The open question about its height: docs/backlog.md 55.