ADR-039 ActifActive contract

Implementing `agtc-toggle`

DateDate
2026-06-01
DécideursDecision makers
Design System Lead

Reference UX patterns applied

Added 2026-06-01 via the ux-pattern-review workflow (ADR-036). Decision: T1–T7 approved, state indicated by knob position alone. Detail and links: guidelines/components/toggle.md § REFERENCE UX PATTERNS.

PatternSource
role="switch" + aria-checkedNN/g — toggle switch
Immediate effect (no submit)NN/g
State via knob position (not color alone)NN/g · WCAG 1.4.1
Clearly bounded knob, contrast ≥ 3:1NN/g · WCAG 1.4.11
Concise, front-loaded label describing the "on" stateNN/g
Touch target ≥ 24pxIxDF · WCAG 2.5.8
Binary onlyNN/g

Context

The toggle complements checkbox/radio: it's the control for immediate-effect settings. Three questions guided the decisions:

1. Semanticsrole="switch" vs. a plain checkbox. 2. State indicator — position alone vs. adding on/off (I/O) labels. 3. "Off" track color — no neutral-medium semantic token exists.


Decisions

Decision 1 — Hidden native <input type="checkbox" role="switch">

The accessible element is a native checkbox promoted to a switch via role. It provides the role, checked state, keyboard handling (Space), and accessible name (via the enclosing <label>). The styled track and knob are decorative (aria-hidden).

Rejected alternative: <div role="switch"> with manual keyboard handling — fragile and unnecessary since the native element provides everything (WCAG 4.1.2 anti-pattern).


Decision 2 — State signaled by knob position (non-color indicator)

The knob slides left (off) / right (on). Position is an indicator independent of color (WCAG 1.4.1); track color (gray → teal) only reinforces it.

Rejected alternative: I/O labels within the track. Discarded to stay clean (the iOS/Android standard); position alone is sufficient as a non-color indicator.

The knob is white with a drop shadow, guaranteeing a contrast ≥ 3:1 against the track in both states (WCAG 1.4.11).


Decision 3 — track-off = proxy to primitive.color.gray.9

No neutral-medium gray semantic token exists. track-off references primitive.color.gray.9 (#8d8d8d) directly, chosen to give ~3.3:1 contrast between the white knob and the track (WCAG 1.4.11). This is a component-level proxy (the same approach as card.elevated.shadow) until a dedicated semantic token is created — governed by the Design System Lead.


v1 scope

IncludedExcluded (v2)
off/on/hover/focus/disabled statesOn/off (I/O) labels in the track
Immediate effect (agtc-change)sm/lg size
role="switch", Space keyboardloading (async) state

Tokens added to component.json

TokenReference
--agtc-toggle-default-track-offprimitive.color.gray.9 (proxy)
--agtc-toggle-default-track-off-hoverprimitive.color.gray.10 (proxy)
--agtc-toggle-default-track-onsemantic.color.action.primary
--agtc-toggle-default-track-on-hoversemantic.color.action.primary-hover
--agtc-toggle-default-knobsemantic.color.background.surface
--agtc-toggle-default-border-focussemantic.color.border.focus
--agtc-toggle-default-labelsemantic.color.text.primary

Patterns UX de référence appliqués

Ajouté le 2026-06-01 via le workflow ux-pattern-review (ADR-036). Décision : T1–T7 approuvés, indicateur d'état par position du curseur seule. Détail et liens : guidelines/components/toggle.md § PATTERNS UX DE RÉFÉRENCE.

PatternSource
role="switch" + aria-checkedNN/g — toggle switch
Effet immédiat (pas de submit)NN/g
État par position du curseur (pas couleur seule)NN/g · WCAG 1.4.1
Curseur délimité, contraste ≥ 3:1NN/g · WCAG 1.4.11
Label concis frontload décrivant l'état « on »NN/g
Cible tactile ≥ 24pxIxDF · WCAG 2.5.8
Binaire uniquementNN/g

Contexte

Le toggle complète checkbox/radio : c'est le contrôle des réglages à effet immédiat. Trois questions ont guidé les décisions :

1. Sémantiquerole="switch" vs simple checkbox. 2. Indicateur d'état — position seule vs ajout de libellés on/off (I/O). 3. Couleur de la piste « off » — aucun token sémantique neutre-médium n'existe.


Décisions

Décision 1 — <input type="checkbox" role="switch"> natif masqué

L'élément accessible est un checkbox natif promu en switch via role. Il fournit le rôle, l'état coché, la gestion clavier (Espace) et le nom accessible (via le <label> englobant). La piste et le curseur stylés sont décoratifs (aria-hidden).

Alternative rejetée : <div role="switch"> avec gestion clavier manuelle — fragile et inutile puisque le natif fournit tout (anti-pattern WCAG 4.1.2).


Décision 2 — État signalé par la position du curseur (indicateur non-couleur)

Le curseur glisse à gauche (off) / à droite (on). La position est un indicateur indépendant de la couleur (WCAG 1.4.1) ; la couleur de la piste (gris → teal) ne fait que renforcer.

Alternative rejetée : libellés I/O dans la piste. Écartés pour rester épuré (standard iOS/Android) ; la position suffit comme indicateur non-couleur.

Le curseur est blanc avec une ombre portée, garantissant un contraste ≥ 3:1 avec la piste dans les deux états (WCAG 1.4.11).


Décision 3 — track-off = proxy vers primitive.color.gray.9

Aucun token sémantique de gris neutre-médium n'existe. track-off référence directement primitive.color.gray.9 (#8d8d8d), choisi pour donner ~3.3:1 entre le curseur blanc et la piste (WCAG 1.4.11). C'est un proxy de composant (même approche que card.elevated.shadow) jusqu'à la création d'un token sémantique dédié — gouverné par le Design System Lead.


Périmètre v1

InclusExclu (v2)
États off/on/hover/focus/disabledLibellés on/off (I/O) dans la piste
Effet immédiat (agtc-change)Taille sm/lg
role="switch", clavier EspaceÉtat loading (async)

Tokens ajoutés dans component.json

TokenRéférence
--agtc-toggle-default-track-offprimitive.color.gray.9 (proxy)
--agtc-toggle-default-track-off-hoverprimitive.color.gray.10 (proxy)
--agtc-toggle-default-track-onsemantic.color.action.primary
--agtc-toggle-default-track-on-hoversemantic.color.action.primary-hover
--agtc-toggle-default-knobsemantic.color.background.surface
--agtc-toggle-default-border-focussemantic.color.border.focus
--agtc-toggle-default-labelsemantic.color.text.primary
← ADR-038 ADR-040 →