Design Lounge
Install

Nº 204 of 520

Copy, share and copy-email buttons

A copy-citation button with a copy-to-check morph and tooltip, a Share button that opens a compact keyboard-ready menu, and an email chip that copies on click.

Preview

Open full size
1280 × 800 · 16.8 KB · 274 lines

Use it

Free to use · credit appreciated

1 Once, in your project

npx skills add SusanAcharya/Design-Lounge

2 Then ask

Using the Design Lounge skill, build "Copy, share and copy-email buttons" (piece button-copy-share) in my stack. Match its brief; restyle it onto my product's theme.

No skill? Paste the brief instead. It carries the numbers, tokens and behaviour.

Brief

233 lines

Build brief for a coding agent. Rebuild this piece in the reader’s stack. If they haven’t said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don’t “improve” them.

What it is

Three small “take this with you” controls on an essay page in “Vellum”, a fictional quarterly of letters. The page is warm paper with a serif text column. A Share pill on the byline row opens a compact menu: Copy link (with the short URL shown), Email to a friend, and Send as a text. A Copy button sits beside a “Cite this essay” block. On click it writes the citation to the clipboard, its two-squares icon spins out while a check draws in, the pill fills forest green, and a tooltip says “Copied to clipboard” for 1.6s. At the foot, an email chip “letters@vellum.press” copies the address. Its text slides up to “Copied. Paste anywhere” and slides back after 1.8s.

The detail worth copying is the honest fallback. If the clipboard API is refused, which happens in iframes, on insecure origins and with permissions off, the button selects the text and tells you to press Ctrl or ⌘ + C. It never claims “Copied” when nothing was copied.

contact-giant-email-copy is a whole contact section built around one giant address. This piece is the set of small inline controls.

Reference behaviour

  1. First frame: a 680px column centred on paper. There are two faint 1px vertical rules at ±400px from centre, like page edges.
  2. Masthead: “Vellum” (italic 28px/700) left, “No. 41 · Autumn letters” right (12px uppercase), over a 2px ink rule.
  3. Kicker “ESSAY” in forest, title “The case for slow correspondence” (50px/500), dek in italic 21px.
  4. Byline row between two hairlines: “By Margit Sallow · 12 min read · 3 Oct 2026” left, and a 40px outline pill “Share” with an arrow-out icon right.
  5. One paragraph of body text (18px/1.6) with a forest drop cap spanning two lines.
  6. The cite block: surface fill, 1px line, 3px forest left rule. “CITE THIS ESSAY” label, then “Sallow, M. (2026). The case for slow correspondence. Vellum, 41, 12–19.” On the right, a 40px outline pill with a copy icon and “Copy”, min-width 104px.
  7. Foot row: “Write back. Letters run in the winter issue.” left, and the email chip right (36px: mail icon, address, 26px round copy badge).
  8. Hover or focus Copy: a dark tooltip “Copy citation” fades in 10px above, rising 4px (160ms), with a 5px arrow.
  9. Click Copy: the citation text goes to the clipboard. The copy icon fades and turns −12° while scaling to .6, and the check stroke draws (320ms after 60ms). The label crossfades “Copy” → “Copied” (6px vertical slide). The pill fills --accent with paper text. The tooltip text becomes “Copied to clipboard” and stays visible. After 1600ms everything reverts and the tooltip text resets. Clicking again within the hold restarts it.
  10. Copy refused: the citation paragraph is selected, the tooltip reads “Selected. Press Ctrl or ⌘ + C”, and no check is shown.
  11. Click Share (or ArrowDown on it): the menu opens under the button, right-aligned, 264px wide. It scales .96 → 1 and moves −4px → 0 from the top-right origin over 180ms. The button inverts to an ink fill. Focus goes to “Copy link”. ArrowUp on the button opens it with focus on the last item.
  12. In the menu: ArrowDown and ArrowUp cycle, Home and End jump, Escape closes and refocuses Share, and Tab closes and moves on. Clicking outside closes it. Hover and focus both paint the item --accent-soft.
  13. Copy link: copies “vellum.press/41/slow”. The item label becomes “Link copied” and its link icon morphs to a check. 650ms later the menu closes, focus returns to Share, and Share’s tooltip shows “Link copied” for 1400ms.
  14. Email to a friend is a mailto: link with the subject and the URL in the body. Send as a text is an sms: link. Each closes the menu.
  15. Click the chip: copies “letters@vellum.press”. The chip fills forest, the address slides up out of a 36px window while “Copied. Paste anywhere” slides up into it (240ms), and the badge icon turns to a check. It reverts after 1800ms.

Structure

1280 × 800, column 680px centred, faint page-edge rules at ±400px
┌──────────────────────────────────────────────────────────────┐
│ Vellum                                NO. 41 · AUTUMN LETTERS │
│━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━│ 2px ink
│ ESSAY                                                         │
│ The case for slow correspondence                    (50px)    │
│ Why a letter that takes a week to arrive still says…  italic  │
│───────────────────────────────────────────────────────────────│
│ By Margit Sallow · 12 min read · 3 Oct 2026        ( ⇪ Share )│ 40px
│───────────────────────────────────────────── ┌ menu 264 ─────┐│
│ T he postcard came eleven days after it was  │ Copy link      ││
│   sent. By then the weather it described…    │ vellum.press/… ││
│                                              │───────────────-││
│ ┃ CITE THIS ESSAY                            │ Email a friend ││
│ ┃ Sallow, M. (2026). The case for… ( ⧉ Copy )│ Send as a text ││
│                                              └────────────────┘│
│ Write back. Letters run in the winter issue. ( mail letters@… ⧉ ) │ 36px
└──────────────────────────────────────────────────────────────┘
  • <main class="col">: .mast, p.kick, <h1>, p.dek, .by (byline + .share-wrap), p.body, .cite (.cite-t + button.copy), .foot (text + button.chip).
  • .share-wrap is position: relative. It holds button.share[aria-haspopup=menu][aria-expanded][aria-controls] and div.menu[role=menu], which holds a button[role=menuitem], a div[role=separator] and two a[role=menuitem]. All items have tabindex="-1" (roving focus).
  • Tooltips are span.tip[aria-hidden] inside their buttons. The live region carries the real announcement.
  • The icon morph .ico holds two absolutely stacked SVGs: .cp (copy or link) and .ok (check).
  • The chip’s .swap is a 36px overflow: hidden grid window with two spans in one cell.

Tokens

:root {
  --paper: #f1ebdf;         /* page */
  --surface: #f9f5ec;       /* cite block, menu, chip */
  --line: #d9cfbd;
  --ink: #1e1b16;           /* text, tooltip, open Share fill */
  --ink-2: #575043;         /* dek, byline */
  --ink-3: #71695a;         /* labels, outline ring of pills */
  --accent: #2e5a43;        /* forest: drop cap, kicker, copied fill, focus */
  --accent-soft: #dce6dc;   /* menu item hover/focus */
  --on-accent: #f9f5ec;

  --serif: "Alegreya", Georgia, serif;
  --ui: "Commissioner", system-ui, sans-serif;

  --r: 6px;                 /* tooltip, menu items */
  --r-menu: 10px;
  --r-pill: 999px;

  --t-micro: 160ms;
  --t-menu: 180ms;
  --t-check: 320ms;
  --hold-copy: 1600ms;
  --hold-chip: 1800ms;
  --hold-link: 1400ms;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --shadow-menu: 0 18px 40px rgba(30, 27, 22, .16), 0 2px 6px rgba(30, 27, 22, .08);
}

Typography

RoleFamilySizeWeightLine-heightTrackingNotes
MastheadAlegreya italic28px7001−0.01em
Issue lineCommissioner12px50010.12emUPPERCASE --ink-2
KickerCommissioner12px60010.14emUPPERCASE --accent
TitleAlegreya50px5001.04−0.02em36px under 640px
DekAlegreya italic21px4001.40--ink-2
BodyAlegreya18px4001.60drop cap 62px/500 forest
BylineCommissioner14px4001.40name 600 --ink
Cite labelCommissioner11px60010.14emUPPERCASE --ink-3
CitationAlegreya16px4001.450journal title italic
Button / chipCommissioner14px50010
TooltipCommissioner12.5px5001.20paper on ink
Menu itemCommissioner14px5001.250hint 12.5px/400 --ink-3

The serif carries the reading. Every control is in Commissioner.

Motion

ElementTriggerPropertyFrom → ToDurationEasingReduced motion
.tiphover, focus-visible, doneopacity, translateY0, 4px → 1, 0160ms--ease1ms
.ico .cpdoneopacity, scale, rotate1, 1, 0 → 0, .6, −12°160 / 320ms--ease / --ease-out1ms
.ico .ok pathdonestroke-dashoffset20 → 0320ms, delay 60ms--easedrawn
Copy labeldoneopacity, translateYcrossfade, ±6px160ms--ease1ms
Copy / chip filldonebackground, coloroutline → --accent160ms--ease1ms
.menuopenopacity, translateY, scale0, −4px, .96 → 1, 0, 1180ms--ease / --ease-out1ms
.menuclosesame, reversed; visibility flips after 180ms180ms1ms
chip .swap spansdonetranslateY, opacity0 → −100%, and 100% → 0240ms--ease-out1ms
buttons:activetranslateY0 → 1pxinstantkept

Restart a confirmation on a repeat click: clear the timer, remove .done, read offsetWidth, add .done, start a new timer.

States

  • Pill rest: transparent, 1px inset --ink-3 ring, ink text. Hover rgba(30,27,22,.05).
  • Copy done: forest fill, paper text, check icon, “Copied”, tooltip “Copied to clipboard” pinned.
  • Copy refused: citation selected (native highlight), tooltip “Selected. Press Ctrl or ⌘ + C”, no fill change.
  • Share open: ink fill, paper text, aria-expanded="true". Its tooltip is suppressed while open.
  • Share after copy link: tooltip “Link copied” pinned for 1400ms.
  • Menu item hover / focus: --accent-soft background, no outline (the fill is the focus indicator, 13:1 against ink text).
  • Menu item done: label “Link copied”, check icon in --accent.
  • Chip rest: surface fill, 1px --line ring, hover ring --ink-3. Done: forest fill, paper text, badge rgba(249,245,236,.16) with a check.
  • Focus-visible: 2px --accent outline, 3px offset on pills and the chip.

Accessibility

  • Copy has the fixed label “Copy citation”. Its visible label and tooltip are aria-hidden. The result goes to a polite live region: “Citation copied to clipboard.” or “Citation selected. Press Control or Command plus C to copy.”
  • The chip’s label is “Copy email address letters@vellum.press”. It announces “Email address copied.”
  • Share is a menu button: aria-haspopup="menu", aria-expanded, aria-controls="menu". The menu is role="menu" labelled “Share this essay”. Items are role="menuitem" with roving tabindex="-1" focus.
  • Keys: Enter, Space or ArrowDown opens on the first item. ArrowUp opens on the last. Inside: ArrowUp and ArrowDown wrap, Home and End jump, Escape closes and returns focus to Share, Tab closes and continues the tab order.
  • After Copy link, focus returns to Share. Do not leave focus inside a closed menu.
  • The menu uses visibility: hidden when closed, so its items can’t be reached by screen reader browse mode.
  • Contrast: --ink-3 on --surface is 4.99:1 and on --paper 4.57:1. Paper on forest is 7.3:1. Ink on --accent-soft is 13.4:1. The tooltip, paper on ink, is 14.5:1.
  • Hit targets: pills 40px, chip 36px (pointer web; 44px on touch), menu items at least 44px.

Responsive rules

  • ≥ 1280: 680px column, as specified.
  • 1024–1279, 768–1023: unchanged. The column is min(680px, 100%).
  • < 640: title 36px, dek 18px. The issue line in the masthead is hidden. The cite block stacks with Copy under the citation, left-aligned. The menu is min(264px, 100vw − 48px), still right-aligned to Share. The foot wraps with the chip on its own line.
  • The page grid uses minmax(0, 1fr) so nothing can push past 375px.

Acceptance checklist

Always

  • Copy writes to the clipboard with navigator.clipboard.writeText, falls back to a hidden textarea with execCommand('copy'), and if both fail, selects the text and says how to copy.
  • “Copied” appears only after a successful write.
  • The copy icon and the check are stacked in one box. The button doesn’t change width (min-width 104px, labels in one grid cell).
  • The confirmation holds for a fixed time and restarts on a repeat click.
  • Share is a real menu button with arrow keys, Home and End, Escape (refocus) and click-outside.
  • The Share menu’s copy-link item shows the URL it will copy.
  • The email chip copies the address and swaps its text inside a fixed-height window.
  • Results are announced in a polite live region. Tooltips are decorative.

This demo

  • Citation: “Sallow, M. (2026). The case for slow correspondence. Vellum, 41, 12–19.”
  • Link: “vellum.press/41/slow”. Email: “letters@vellum.press”.
  • Holds: 1600ms (copy), 1400ms (link), 1800ms (chip). Menu opens 180ms. Check draws 320ms.
  • Menu items: Copy link, Email to a friend, Send as a text.
  • The accent is #2e5a43 on #f1ebdf paper. The tooltip is #1e1b16.

Implementation notes

Copy with an honest fallback.

async function copyText(text) {
  try { await navigator.clipboard.writeText(text); return true; }
  catch {
    const ta = Object.assign(document.createElement('textarea'), { value: text, readOnly: true });
    ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0';
    document.body.append(ta); ta.select();
    let ok = false; try { ok = document.execCommand('copy'); } catch {}
    ta.remove(); return ok;
  }
}
copy.onclick = async () => {
  if (await copyText(citation.textContent)) { tip.textContent = 'Copied to clipboard'; flash(copy, 1600); say('Citation copied to clipboard.'); }
  else { selectNode(citation); tip.textContent = 'Selected. Press Ctrl or ⌘ + C'; }
};

Restartable confirmation.

function flash(el, ms, onEnd) {
  clearTimeout(el._t);
  el.classList.remove('done'); void el.offsetWidth; el.classList.add('done');
  el._t = setTimeout(() => { el.classList.remove('done'); onEnd?.(); }, ms);
}

Menu that animates out and still hides from assistive tech. Delay the visibility flip on close by the fade duration, and flip it immediately on open.

.menu { opacity: 0; transform: translateY(-4px) scale(.96); transform-origin: top right; visibility: hidden;
  transition: opacity 180ms var(--ease), transform 180ms var(--ease-out), visibility 0s 180ms; }
.menu.open { opacity: 1; transform: none; visibility: visible;
  transition: opacity 180ms var(--ease), transform 180ms var(--ease-out), visibility 0s; }

Common mistakes:

  • Showing “Copied” in a .then() without a .catch(). In a sandboxed iframe the write rejects, and the user is told a lie.
  • Putting the tooltip text in aria-label and changing it, so screen readers re-read the whole button. Use the live region.
  • A share menu of brand logos. Use verbs with plain icons, and show the link.
  • Using display: none for the closed menu. The close animation can’t run.
  • Swapping the chip text without a fixed-height window. The chip jumps in height or width.
  • Forgetting to return focus to Share after an item closes the menu.

Details

Palette
Type
Alegreya · Commissioner
Motion
Subtle motion
Build
One session
Designed bySusan AcharyaKathmandu

Studied

Wickret

Let the big words move. Letters can break apart and come back as new words.

All sources