Pular para o conteúdo

Switch

components specs/components/switch.kmd

ON/OFF toggle with immediate effect. Material parity (`/components/switch`). Distinct from Checkbox — switch implies "this changes state right now"; checkbox implies "I'm submitting this value with the form."

Quando esta spec se aplica

Triggers primários

Todos os triggers

Corpo da especificação

Spec — Switch

Facet Visual do Koder Design. Material parity: https://m3.material.io/components/switch.

Anatomy

OFF:  ●────         (handle left, track muted)
ON:   ────●         (handle right, track accent)
  • Track: 52×32 px (default), radius-full pill
  • Handle (thumb): 24 px circle, shifts left↔right
  • Hit zone: 48×48 px

R1 — Switch vs Checkbox

Critical distinction:

Use Switch when…Use Checkbox when…
Action takes effect IMMEDIATELYAction is part of a FORM submit
Single setting, no form contextMultiple options or terms acceptance
User can see the system change (WiFi, mute)The change applies on Save
Quick toggle in toolbarPer-row selection in a list

Rule of thumb: if user must click a "Save" button after, use checkbox. If the change persists the moment they toggle, use switch.

R2 — States

StateTrackHandle
OFF + enabledsurface-variant bgsurface circle
OFF + hovertext-muted 8% bg+ ring outline
OFF + focused+ focus ring+ focus ring
ON + enabledaccent bgaccent-on circle
ON + hoveraccent-strong bg+ slight scale
ON + focused+ focus ring+ focus ring
Disabled38% opacity38% opacity

R3 — Optional icons

Switch can show icons inside handle/track:

StyleVisual
No icons (default)Plain pill
Handle iconSmall icon inside handle (24 px) — check/x or feature glyph
Track iconsIcons at both ends of track (smaller, indicating state semantics)

Optional; doesn't change behavior. Material 3 default includes check/x in handle for accessibility (visual state cue beyond color).

R4 — Animation

  • Handle slides smoothly left↔right on toggle (motion-fast, ~150ms)
  • Track color animates simultaneously
  • Reduced-motion: instant (no slide)
  • Sound (optional, app-specific): subtle click on toggle

R5 — Optimistic state

When toggling has async effect (server save), follow optimistic pattern:

  1. User toggles → switch UI immediately shifts to new state
  2. Async call kicks off in background
  3. If success: stay
  4. If failure: snap back to previous state + show error snackbar ("Couldn't enable notifications. Try again.")

NEVER show a spinner WITHIN the switch on async — it freezes the visual feedback.

R6 — Settings convention

In Settings pages, switch rows have this canonical layout:

┌────────────────────────────────────────────────────┐
│ [icon?]  Title                              [●──]  │
│          Optional description text                 │
└────────────────────────────────────────────────────┘
  • Icon (leading, optional)
  • Title (title-medium)
  • Description (body-small muted) below title
  • Switch right-aligned (or left in RTL)
  • Whole row clickable

KoderSettingsTile.toggle() widget (per future settings/patterns.kmd) — exposed by koder_kit.

R7 — Accessibility

  • <input type="checkbox" role="switch"> (HTML standard, both attributes)
  • OR native Flutter Switch widget
  • aria-checked="true" / "false" (set automatically with role=switch)
  • Visible focus ring
  • Keyboard: Space toggles
  • Screen reader announces: "Switch, Notifications, on" (or off)
  • High-contrast: not just color — handle position is the primary cue

R8 — Forbidden patterns

  • ❌ Switch for form submission semantics (use checkbox)
  • ❌ Switch without label (always provide accessible name)
  • ❌ Switch that requires confirm dialog after toggle (defeats the "immediate" semantic — use checkbox + button instead)
  • ❌ More than ~10 switches in one page (becomes overwhelming; group into sub-sections)
  • ❌ Tri-state switch (use radio group for >2 options)

R9 — Per-preset variation

PresetVisual
material3Pill track + circular handle
ios_cupertinoSame shape, slightly larger; uses iOS green for ON
gnomeLess rounded pill (radius-md instead of radius-full)
windows_11Mica backdrop track + handle
brutalistSharp rectangle track + sharp square handle
terminal_classicASCII [off] / [ON ] text
  • components/checkbox.kmd — vs switch decision
  • interaction/states.kmd — state visuals
  • themes/color-roles.kmd — accent track ON
  • themes/motion.kmd — slide animation

Requirements (testable)

Requirement: Toggle applies immediately {#req-switch-immediate-effect}

A Switch SHALL apply its state change the moment the user toggles it, with no intervening "Save" button or confirmation dialog; if a confirm step or deferred-submit semantic is required, a Checkbox SHALL be used instead (per R1 and R8).

Scenario: Toggling persists the change instantly

  • GIVEN a Switch bound to an immediate-effect setting in the OFF state
  • WHEN the user activates the Switch
  • THEN the underlying setting SHALL transition to ON without requiring any further action
  • AND no "Save" button SHALL be needed to commit the change

Scenario: No confirmation dialog after toggle

  • GIVEN a Switch controlling a setting
  • WHEN the user toggles the Switch
  • THEN no confirmation dialog SHALL be presented after the toggle
  • AND the new state SHALL take effect directly

Requirement: State reflects value via handle position and track color {#req-switch-state-visuals}

A Switch SHALL render its OFF state with the handle at the leading edge over a muted (surface-variant) track and its ON state with the handle at the trailing edge over an accent track; a disabled Switch SHALL render at 38% opacity (per R2).

Scenario: OFF state visuals

  • GIVEN a Switch in the OFF and enabled state
  • WHEN the Switch is rendered
  • THEN the handle SHALL be positioned at the leading (left) edge
  • AND the track SHALL use the surface-variant background color

Scenario: ON state visuals

  • GIVEN a Switch in the ON and enabled state
  • WHEN the Switch is rendered
  • THEN the handle SHALL be positioned at the trailing (right) edge
  • AND the track SHALL use the accent background color

Scenario: Disabled state dimming

  • GIVEN a Switch in the disabled state
  • WHEN the Switch is rendered
  • THEN both the track and handle SHALL be drawn at 38% opacity

Requirement: Handle slides on toggle and respects reduced motion {#req-switch-animation}

A Switch SHALL animate the handle sliding between the leading and trailing edges while the track color animates simultaneously; when reduced-motion is active, the Switch SHALL snap instantly to the new state with no slide (per R4).

Scenario: Handle slides on normal toggle

  • GIVEN a Switch in the OFF state with reduced-motion disabled
  • WHEN the user toggles it to ON
  • THEN the handle SHALL animate its position from the leading edge to the trailing edge
  • AND the track color SHALL animate from the muted color to the accent color at the same time

Scenario: Reduced motion snaps instantly

  • GIVEN a Switch in the OFF state with reduced-motion enabled
  • WHEN the user toggles it to ON
  • THEN the handle SHALL move to the trailing edge with no sliding animation

Requirement: Optimistic async state with rollback {#req-switch-optimistic-state}

When toggling triggers an asynchronous effect, a Switch SHALL update its visual state immediately (optimistic), and on failure SHALL snap back to the previous state and surface an error message; it SHALL NOT display a spinner within the Switch during the async operation (per R5).

Scenario: Failed async call rolls back and shows error

  • GIVEN a Switch in the OFF state whose toggle triggers an async server save
  • WHEN the user toggles it to ON and the async call fails
  • THEN the Switch SHALL snap back to the OFF state
  • AND an error message (snackbar) SHALL be shown to the user

Scenario: No in-switch spinner during async

  • GIVEN a Switch whose toggle triggers a pending async call
  • WHEN the async call is in progress
  • THEN no spinner SHALL be rendered within the Switch
  • AND the Switch SHALL remain showing the optimistic new state

Requirement: Accessible switch semantics and keyboard operation {#req-switch-accessibility}

A Switch SHALL expose switch semantics (role="switch" with aria-checked reflecting its value, or the native Switch widget), SHALL always carry an accessible name, SHALL toggle on the Space key, and SHALL show a visible focus ring (per R7 and R8).

Scenario: Space key toggles the switch

  • GIVEN a Switch in the OFF state that has keyboard focus
  • WHEN the user presses the Space key
  • THEN the Switch SHALL toggle to the ON state
  • AND aria-checked SHALL be set to "true"

Scenario: Switch exposes an accessible name and role

  • GIVEN a rendered Switch labeled "Notifications"
  • WHEN an assistive technology inspects the control
  • THEN it SHALL report a switch role with the accessible name "Notifications"
  • AND the current on/off state SHALL be conveyed via aria-checked

Scenario: Focus shows a visible ring

  • GIVEN a Switch that does not have focus
  • WHEN the user moves keyboard focus to the Switch
  • THEN a visible focus ring SHALL be rendered around the control

Referências