Interaction blueprint

Copy confirmation

The control confirms a silent clipboard action right where it happened.

Playground

Playhead 0ms / 260ms

Build it

Choose a target and adapt the source to your interface.

107 lines
index.html
<div class="copy-demo">
  <button class="copy-button" type="button" data-copied="false">
    <span class="copy-icon" aria-hidden="true"
      ><svg class="copy-pages" viewBox="0 0 24 24">
        <rect x="8" y="3" width="12" height="15" rx="2" />
        <path d="M16 18v3H4V6h4" /></svg
      ><svg class="copy-check" viewBox="0 0 24 24">
        <path d="m5 12 4 4L19 6" /></svg
    ></span>
    Copy sample text
  </button>
  <p class="copy-status" role="status"></p>
</div>

<script>
  const button = document.querySelector('.copy-button');
  const status = document.querySelector('.copy-status');
  let timer;
  button.addEventListener('click', async () => {
    button.disabled = true;
    clearTimeout(timer);
    try {
      await navigator.clipboard.writeText('Sample text');
      button.dataset.copied = 'true';
      status.textContent = 'Copied to clipboard.';
      timer = setTimeout(() => {
        button.dataset.copied = 'false';
        status.textContent = '';
      }, 1800);
    } catch {
      button.dataset.copied = 'false';
      status.textContent = 'Copy failed. Check browser clipboard permission.';
    } finally {
      button.disabled = false;
    }
  });
</script>

<style>
  .copy-demo {
    display: grid;
    justify-items: center;
    width: 280px;
    max-width: 100%;
    gap: 10px;
  }
  .copy-status {
    box-sizing: border-box;
    width: 100%;
    min-height: 40px;
    margin: 0;
    text-align: center;
    font: 12px/20px system-ui;
    color: var(--stage-muted, #68778c);
  }
  .copy-button {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    padding: 10px 16px;
    border: 1px solid #68778c;
    border-radius: 6px;
    background: white;
    color: #20252d;
    font: 14px system-ui;
    cursor: pointer;
  }
  .copy-icon {
    display: inline-grid;
    width: 24px;
    height: 24px;
    place-items: center;
  }
  .copy-icon svg {
    grid-area: 1 / 1;
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    transition:
      transform 260ms cubic-bezier(0.22, 1, 0.36, 1),
      opacity 260ms ease-out;
  }
  .copy-check {
    opacity: 0;
    transform: translateY(4px);
  }
  .copy-button[data-copied='true'] .copy-pages {
    opacity: 0;
    transform: translateY(-4px);
  }
  .copy-button[data-copied='true'] .copy-check {
    opacity: 1;
    transform: none;
  }
  .copy-button:focus-visible {
    outline: 2px solid #175bc5;
    outline-offset: 4px;
  }
  @media (prefers-reduced-motion: reduce) {
    .copy-icon svg {
      transition: none;
    }
  }
</style>

Animation breakdown

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

260ms
01

Rest: overlapping document outline.

02

0–100ms: top page lifts 4px.

03

100–220ms: checkmark replaces copy glyph.

04

220–260ms: hold before reset.

Motion intent

The control confirms a silent clipboard action right where it happened.

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
The copy action succeeds.
Timing
260ms
Easing
cubic-bezier(.22, 1, .36, 1)

Animated properties

transformopacity

Implementation notes

  • Keep copy and copied 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 copy and copied 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