Interaction blueprint

Checkbox path draw

A path draw confirms selection with a clear, accessible state change.

Playground

Playhead 0ms / 220ms

Build it

Choose a target and adapt the source to your interface.

91 lines
index.html
<button
  class="selection-control"
  type="button"
  role="checkbox"
  aria-checked="false"
  aria-label="Checkbox path draw"
>
  <svg viewBox="0 0 24 24" aria-hidden="true">
    <rect class="selection-box" x="3" y="3" width="18" height="18" rx="4" />
    <path class="selection-check" d="m7 12 3 3 7-7" pathLength="1" />
  </svg>
</button>

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

<style>
  .selection-control {
    display: inline-grid;
    place-items: center;
    width: 44px;
    height: 44px;
    padding: 10px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: #175bc5;
    cursor: pointer;
  }
  .selection-control svg {
    width: 24px;
    height: 24px;
    overflow: visible;
  }
  .selection-control:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 4px;
  }
  .selection-outline {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linejoin: round;
  }
  .selection-box {
    fill: white;
    stroke: currentColor;
    stroke-width: 2;
    transition: fill 80ms ease-out;
  }
  .selection-check {
    fill: none;
    stroke: white;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    transition: stroke-dashoffset 140ms ease-out;
  }
  .selection-control[aria-checked='true'] .selection-box {
    fill: currentColor;
  }
  .selection-control[aria-checked='true'] .selection-check {
    stroke-dashoffset: 0;
    transition-delay: 80ms;
  }
  @keyframes selection-pop {
    0%,
    100% {
      transform: scale(1);
    }
    35% {
      transform: scale(1.08);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    .selection-control *,
    .selection-control[aria-checked='true'] * {
      animation: none;
      transition: none;
    }
  }
</style>

Animation breakdown

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

260ms
01

Rest: empty square.

02

0–80ms: brand fill appears.

03

80–220ms: check path draws from lower left to upper right.

04

220–260ms: hold.

Motion intent

A path draw confirms selection with a clear, accessible state change.

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 selection changes.
Timing
260ms
Easing
cubic-bezier(.22, 1, .36, 1)

Animated properties

transformopacity

Implementation notes

  • Keep unchecked and checked aligned to the same visual anchor.
  • Drive the transition from semantic state, not a timer, when the trigger is interactive.
  • Use transform, opacity 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 unchecked and checked 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