AI CSS Animations

Copy to Clipboard Button

A command box whose copy button flips to a check and 'Copied' on click, holds for a moment, then resets by itself. The pressed state snaps in and the rest state returns on a delayed transition.

Also known as: copy button css, copied button animation, copy code button.

$ npm i @acme/ui

HTML

<div class="btn-copy-clipboard">
  <code class="btn-copy-clipboard-code"><span class="btn-copy-clipboard-prompt">$</span> npm i @acme/ui</code>
  <button class="btn-copy-clipboard-btn" type="button" aria-label="Copy to clipboard">
    <span class="btn-copy-clipboard-icons" aria-hidden="true">
      <svg viewBox="0 0 24 24"><rect x="8.5" y="8.5" width="11" height="11" rx="2.5"/><path d="M15.5 8.5V6.5A2 2 0 0 0 13.5 4.5H6.5A2 2 0 0 0 4.5 6.5v7a2 2 0 0 0 2 2h2"/></svg>
      <svg viewBox="0 0 24 24"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>
    </span>
    <span class="btn-copy-clipboard-text" aria-hidden="true"><span>Copy</span><span>Copied</span></span>
  </button>
</div>

CSS

.btn-copy-clipboard {
  --c: var(--btn-color, #3b82f6);
  font-size: var(--btn-size, 18px);
  display: inline-flex;
  align-items: center;
  gap: 1.1em;
  padding: 0.4em 0.4em 0.4em 1.1em;
  border-radius: 0.75em;
  background: color-mix(in srgb, var(--c) 12%, #0f172a);
  color: #e2e8f0;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08), 0 0.5em 1.2em -0.6em rgba(2, 6, 23, 0.6);
}
.btn-copy-clipboard-code {
  font: 500 0.9em/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
}
.btn-copy-clipboard-prompt { color: color-mix(in srgb, var(--c) 60%, #fff); }
.btn-copy-clipboard-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.6em 0.85em;
  border: 0;
  border-radius: 0.5em;
  background-color: rgba(255, 255, 255, 0.09);
  color: #fff;
  font: 600 0.85em/1 system-ui, sans-serif;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.3s ease 1.2s, transform 0.25s cubic-bezier(0.3, 0.7, 0.4, 1.5);
}
.btn-copy-clipboard-icons {
  position: relative;
  width: 1.15em;
  height: 1.15em;
}
.btn-copy-clipboard-icons svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.3s ease 1.2s, opacity 0.3s ease 1.2s;
}
.btn-copy-clipboard-icons svg:last-child {
  stroke-width: 3;
  opacity: 0;
  transform: scale(0.3) rotate(-30deg);
}
.btn-copy-clipboard-text {
  display: grid;
  overflow: hidden;
}
.btn-copy-clipboard-text span {
  grid-area: 1 / 1;
  transition: transform 0.3s cubic-bezier(0.3, 0.7, 0.4, 1) 1.2s, opacity 0.3s ease 1.2s;
}
.btn-copy-clipboard-text span:last-child {
  opacity: 0;
  transform: translateY(100%);
}
.btn-copy-clipboard-btn:focus-visible {
  outline: 2px solid var(--c);
  outline-offset: 0.15em;
}
.btn-copy-clipboard-btn:active {
  background-color: var(--c);
  transform: scale(0.94);
  transition-delay: 0s;
}
.btn-copy-clipboard-btn:active .btn-copy-clipboard-icons svg {
  transition: transform 0.35s cubic-bezier(0.3, 0.7, 0.4, 1.5), opacity 0.15s ease;
}
.btn-copy-clipboard-btn:active .btn-copy-clipboard-icons svg:first-child {
  opacity: 0;
  transform: scale(0.3) rotate(30deg);
}
.btn-copy-clipboard-btn:active .btn-copy-clipboard-icons svg:last-child {
  opacity: 1;
  transform: none;
}
.btn-copy-clipboard-btn:active .btn-copy-clipboard-text span {
  transition: transform 0.3s cubic-bezier(0.3, 0.7, 0.4, 1), opacity 0.2s ease;
}
.btn-copy-clipboard-btn:active .btn-copy-clipboard-text span:first-child {
  opacity: 0;
  transform: translateY(-100%);
}
.btn-copy-clipboard-btn:active .btn-copy-clipboard-text span:last-child {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .btn-copy-clipboard, .btn-copy-clipboard *, .btn-copy-clipboard::before, .btn-copy-clipboard::after, .btn-copy-clipboard *::before, .btn-copy-clipboard *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Tailwind CSS version

The same effect using Tailwind utility classes. No custom CSS or config needed.

<div class="inline-flex items-center gap-4 rounded-xl bg-slate-900 py-1.5 pl-4 pr-1.5 text-slate-200 ring-1 ring-inset ring-white/10">
  <code class="whitespace-nowrap font-mono text-sm"><span class="text-blue-400">$</span> npm i @acme/ui</code>
  <button class="group flex items-center gap-2 rounded-lg bg-white/10 px-3 py-2 text-xs font-semibold text-white [transition:background-color_.3s_ease_1.2s,transform_.25s] active:scale-95 active:bg-blue-500 active:[transition-delay:0s]" type="button" aria-label="Copy to clipboard">
    <span class="relative h-4 w-4" aria-hidden="true">
      <svg class="absolute inset-0 h-4 w-4 fill-none stroke-current stroke-2 transition duration-300 delay-[1200ms] group-active:scale-0 group-active:opacity-0 group-active:delay-0" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round"><rect x="8.5" y="8.5" width="11" height="11" rx="2.5"/><path d="M15.5 8.5V6.5A2 2 0 0 0 13.5 4.5H6.5A2 2 0 0 0 4.5 6.5v7a2 2 0 0 0 2 2h2"/></svg>
      <svg class="absolute inset-0 h-4 w-4 scale-0 fill-none stroke-current stroke-[3] opacity-0 transition duration-300 delay-[1200ms] group-active:scale-100 group-active:opacity-100 group-active:delay-0" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>
    </span>
    <span class="grid overflow-hidden" aria-hidden="true">
      <span class="col-start-1 row-start-1 transition duration-300 delay-[1200ms] group-active:-translate-y-full group-active:opacity-0 group-active:delay-0">Copy</span>
      <span class="col-start-1 row-start-1 translate-y-full opacity-0 transition duration-300 delay-[1200ms] group-active:translate-y-0 group-active:opacity-100 group-active:delay-0">Copied</span>
    </span>
  </button>
</div>

Customizing this snippet

  • Color: set --btn-color on the element or a parent. Currently previewing #3b82f6.
  • Base size: set --btn-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 18px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar buttons