COPY BUTTON: Clipboard-copy affordance with a success flash
VARIANTS: Reference number | URL share
.btn-copy composes .btn .btn-icon .btn-outline-secondary —
it adds nothing but the success-state colour. data-controller="copy"
writes the paired value to the clipboard, then flashes the button green with a
check icon for two seconds and announces "Copied" to screen readers.
Reference number: Pairs with a read-only .form-control
via data-copy-target="source" — the button reads the input's live
value, not a static string, so the same markup works no matter what
the reference number actually is.
Quote this reference when contacting admissions.
<div class="input-group" data-controller="copy"> <input class="form-control" value="REF-2026-004821" readonly data-copy-target="source"> <button class="btn btn-outline-secondary btn-icon btn-copy" aria-label="Copy reference number" data-copy-target="button" data-action="click->copy#copy"> <i class="fa-regular fa-copy" aria-hidden="true" data-copy-target="icon"></i> </button> <span class="tw:sr-only" aria-live="polite" data-copy-target="status"></span></div>
URL share: Same markup pattern for a shareable link. Try it —
click the button to see the flash: .is-copied tints the button with
the state-success trio and the icon swaps fa-regular fa-copy → fa-solid fa-check
for two seconds, then reverts.
data-controller="copy" contract — only the source input's value differs.
Quick Reference
Base: .btn-copy (composes .btn .btn-icon .btn-outline-secondary)
Success flash: .is-copied (state-success trio, applied by the controller for 2s)
Controller: data-controller="copy" | data-copy-target="source|button|icon|status" | click->copy#copy
Fallback: execCommand("copy") when the async Clipboard API is unavailable; a failed copy announces a manual-copy message via the status region instead of throwing