Switch
An on/off control that takes effect immediately; a button with role switch and aria-checked, drawn as a square-cornered track with a square thumb that fills with the primary colour when on.
stable · light DOM · 4.0.0. Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.
Live variants
default
<j3w1-switch id="example-switch-default"><button class="switch" type="button" role="switch" aria-checked="false" aria-label="Wrap long lines">
<span class="switch-track"><span class="switch-thumb"></span></span>
</button></j3w1-switch>with-labels
<j3w1-switch id="example-switch-with-labels"><div class="switch-row">
<span class="switch-name" id="example-switch-with-labels-sw-follow-name">Follow system theme</span>
<button class="switch" type="button" role="switch" aria-checked="false" aria-labelledby="example-switch-with-labels-sw-follow-name">
<span class="switch-track"><span class="switch-thumb"></span></span>
<span class="switch-state" aria-hidden="true"><span class="switch-state-off">Off</span><span class="switch-state-on">On</span></span>
</button>
</div></j3w1-switch>Install and register
Use the exact version. If npm publication is pending, install the release tarball from the installation guide.
npm install --save-exact @j3w1/ui@4.0.0import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/switch.css';
import '@j3w1/ui/register/switch';Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.
Copy the complete implementation
npx --package=@j3w1/ui@4.0.0 j3w1-ui copy switch --out ./vendor/j3w1/switchThe CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.
Public API
attributes
| Name | Type / signature | Meaning |
|---|---|---|
disabled | CustomEvent | Disable the native controls owned by this instance. |
loading | CustomEvent | Mark the primary control busy and prevent duplicate activation. |
checked | CustomEvent | Default switch checked. |
value | CustomEvent | Default switch value. |
properties
| Name | Type / signature | Meaning |
|---|---|---|
disabled | boolean | Delegates disabled state to native children. |
name | string | Native control name; application form keys remain explicit. |
checked | boolean | Immediate switch state; a named switch exposes a matching native hidden form value. |
methods
| Name | Type / signature | Meaning |
|---|---|---|
refresh | (): void | Reconnect behavior after replacing native child structure. |
focus | (options?: FocusOptions): void | Focus the first usable native control. |
events
| Name | Type / signature | Meaning |
|---|---|---|
j3w1-change | CustomEvent | The user toggled the switch. |
Dependencies and verification
Registered components: none. Style dependencies: none.
Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.
Full implementation manifest, API and token dependency closure
Canonical specification
Switch switch
An on/off control that takes effect immediately; a button with role switch and aria-checked, drawn as a square-cornered track with a square thumb that fills with the primary colour when on.
Report an issue with this component
Tokens
Focus a role to inspect its values; follow its link for persistent usage and source details.
| Part | Role | Value | Use |
|---|---|---|---|
track.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
track.border | color.border.controlapproveduse | #c81a1a | D-041 owner correction: red control and floating-frame borders, distinct from rose text. |
track.bg-hover | color.interaction.hover.bgapproveduse | #1c1010 | D-041 interaction.hover.bg: color.primitive.modern.hover. |
track.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
track.bg-checked | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
track.bg-checked-hover | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover; pair with action.primary.text (D-033). |
track.ring-checked | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
track.bg-disabled | color.interaction.disabled.bgapproveduse | #0d0707 | D-041 interaction.disabled.bg: color.primitive.modern.panel. |
track.border-disabled | color.border.disabledapproveduse | #482b2b | D-041 border.disabled: color.primitive.modern.divider. |
thumb.bg | color.text.defaultapproveduse | #e99499 | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
thumb.bg-checked | color.action.primary.textapproveduse | #ffa2a7 | Primary action labels: bright rose across rest, hover and pressed states (D-033). |
thumb.bg-disabled | color.text.disabledapproveduse | #a3676b | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
name.text | color.text.defaultapproveduse | #e99499 | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
state.text | color.text.mutedapproveduse | #d4868b | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
name.text-disabled | color.text.disabledapproveduse | #a3676b | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
State matrix
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | With labels |
|---|---|---|
default |
Follow system theme
| |
hover | ||
focus-visible | ||
checked | ||
checked+focus-visible | ||
disabled | ||
checked+disabled |
Purpose
A setting that applies the moment it is toggled: notifications, wrap lines, follow system theme. A choice applied on submit is a checkbox. The switch is a button because it acts; it is not a form field.
Anatomy
Parts as listed in the anatomy; the thumb sits at the start when off and at the end when on.
States
| State | Visual | Non-colour channel |
|---|---|---|
| default | track 1px color.border.control on color.surface.input; thumb color.text.default at the start | thumb position |
| hover | off track → color.interaction.hover.bg; on track → color.action.primary.hover-bg | cursor: pointer; hover-capable pointers only |
| focus-visible | 1px dashed color.interaction.focus.ring at −2px on the track | the ring |
| checked | track fills color.action.primary.bg; thumb color.action.primary.text at the end; border stays color.border.control; the word reads “On” | aria-checked="true"; thumb position; the word |
| checked+focus-visible | the fill and a color.interaction.focus.ring-container ring on the track | position and ring |
| disabled | track color.interaction.disabled.bg, 1px color.border.disabled; thumb and name color.text.disabled | disabled; cursor: not-allowed |
| checked+disabled | as disabled, thumb at the end | disabled; aria-checked="true"; thumb position |
The thumb moves without animation by default; a host may transition
transform for at most 120ms under prefers-reduced-motion: no-preference. Precedence: disabled > checked > hover; focus-visible is
always drawn.
Keyboard
As in the key table; no arrow keys between switches. Toggling never moves focus.
Accessibility
On a native button only aria-checked is scripted. The name states what is
on (“Wrap long lines”), never the current value. Thumb position and, with
labels, the word also carry the state. The thumb and track edge meet 3:1,
the ring 3:1 off and on.
Portability
The thumb position is layout, not a transform, so it survives hosts with no animation. Hosts with a native switch use it, square it, and map the two fills.
Non-examples
A pill track or round thumb. A switch that applies on submit. A checkbox input styled as a switch. Colour as the only on/off difference. A green on colour. “On” as the name. Disabling by opacity. A slide longer than 120ms or one under reduced motion.
Contrast report
16 declared pairs measured on resolved sRGB values with the WCAG 2.x formula; 0 waived (decorative only), 0 failing. A pass is a design target for the named role, not an application-wide conformance claim.
| Pair | State | Kind | Foreground | Background | Ratio | Min | Result |
|---|---|---|---|---|---|---|---|
| switch default border | default | ui | color.border.control #c81a1a | color.surface.input #000000 | 3.62 | 3 | pass |
| switch hover border | hover | ui | color.border.control #c81a1a | color.interaction.hover.bg #1c1010 | 3.20 | 3 | pass |
| switch focus-visible border | focus-visible | ui | color.border.control #c81a1a | color.surface.input #000000 | 3.62 | 3 | pass |
| switch focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| switch checked text | checked | text | color.action.primary.text #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 4.5 | pass |
| switch checked+focus-visible text | checked+focus-visible | text | color.action.primary.text #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 4.5 | pass |
| switch checked+focus-visible outline | checked+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 3 | pass |
| thumb on the off track | — | ui | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 3 | pass |
| thumb on the hovered off track | hover | ui | color.text.default #e99499 | color.interaction.hover.bg #1c1010 | 8.10 | 3 | pass |
| thumb on the hovered on track | hover | ui | color.action.primary.text #ffa2a7 | color.action.primary.hover-bg #911410 | 4.75 | 3 | pass |
| on track edge against the panel (the border stays border.control) | checked | ui | color.border.control #c81a1a | color.surface.default #0d0707 | 3.44 | 3 | pass |
| visible name on the panel surface | — | text | color.text.default #e99499 | color.surface.default #0d0707 | 8.72 | 4.5 | pass |
| Off / On word on the panel surface | — | text | color.text.muted #d4868b | color.surface.default #0d0707 | 7.22 | 4.5 | pass |
| disabled thumb (exempt; house floor 3:1) | disabled | ui | color.text.disabled #a3676b | color.interaction.disabled.bg #0d0707 | 4.48 | 3 | pass |
| disabled name (exempt; house floor 3:1) | disabled | ui | color.text.disabled #a3676b | color.surface.default #0d0707 | 4.48 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #482b2b | color.interaction.disabled.bg #0d0707 | 1.58 | 1 | pass |
Keys
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus to and from the switch. |
| Space / Enter | Toggles aria-checked and applies the change at once. |
For agents
Component JSON ·Implementation brief ·Build a task kit ·
Related: checkbox radio-group icon-button field
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX