Interaction blueprint

Weightless toggle

A firm toggle with a deliberate settle. The distance is short and the state change is unmissable.

Playground

Playhead 0ms / 240ms

Build it

Choose a target and adapt the source to your interface.

65 lines
index.html
<button class="toggle" type="button" role="switch" aria-checked="false">
  <span class="toggle__track" aria-hidden="true"
    ><span class="toggle__thumb"></span
  ></span>
  Notifications
</button>

<script>
  const toggle = document.querySelector('.toggle');
  toggle.addEventListener('click', () =>
    toggle.setAttribute(
      'aria-checked',
      String(toggle.getAttribute('aria-checked') !== 'true'),
    ),
  );
</script>

<style>
  .toggle {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    min-height: 44px;
    padding: 6px;
    border: 0;
    background: transparent;
    color: inherit;
    cursor: pointer;
  }
  .toggle__track {
    display: block;
    width: 56px;
    height: 32px;
    padding: 4px;
    border-radius: 99px;
    background: #536174;
    transition: background 240ms ease-out;
  }
  .toggle__thumb {
    display: block;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: white;
    transform: translateX(0);
    transition: transform 240ms cubic-bezier(0.4, 0, 0.2, 1);
  }
  .toggle[aria-checked='true'] .toggle__track {
    background: #175bc5;
  }
  .toggle[aria-checked='true'] .toggle__thumb {
    transform: translateX(24px);
  }
  .toggle:focus-visible {
    outline: 2px solid #175bc5;
    outline-offset: 4px;
    border-radius: 6px;
  }
  @media (prefers-reduced-motion: reduce) {
    .toggle__track,
    .toggle__thumb {
      transition: none;
    }
  }
</style>

Animation breakdown

Frame phases, motion intent, implementation constraints and accessible alternatives.

240ms
01

Rest: knob rests left.

02

0–160ms: it crosses the track.

03

160–240ms: it settles on the far edge as the track changes colour.

Motion intent

A firm toggle with a deliberate settle. The distance is short and the state change is unmissable.

This duration leaves enough time to read the state change while keeping the interface responsive.

The fast acceleration and long ease-out make the state feel responsive while giving the final pixels time to settle.

Motion specification

Trigger
A binary preference changes immediately.
Timing
240ms
Easing
cubic-bezier(.4, 0, .2, 1)

Animated properties

translatebackground-colorshadow

Implementation notes

  • Keep off and on aligned to the same visual anchor.
  • Drive the transition from semantic state, not a timer, when the trigger is interactive.
  • Use translate, background-color, shadow and avoid layout work inside the animation loop.

Reduced motion

Swap to the final state instantly, preserving colour, label, and accessible state.

  • Use a minimum 44 by 44 pixel interactive target.
  • Expose the off and on states with aria-pressed, aria-expanded, or an updated accessible label.
  • Keep visible keyboard focus independent from the animation.

Avoid

  • Do not add bounce unless a real object pickup or spring settle is part of the interaction.
  • Do not hide the final state from assistive technology.
  • Do not make the user wait for animation before the underlying state updates.

Related blueprints

Browse all blueprints