Susegad UI
Register
Theme
Palette

Components

Reach

A contact block in an address: email and WhatsApp as real links (built by waLink and mailtoLink), a copy-the-number button that appears only where the clipboard works and says in words that it copied, and a line on reply times.

quiet warm playful

Open the live demo npx susegad add reach

Stands on: Core, Core: components, Stamp, Tokens

The prompt

the prompt

The "get in touch" block of a homestay, a clinic, a shop or a one-person studio.

Email and WhatsApp as real links, a button that copies the number and says so, and a line on when replies usually come.

<link rel="stylesheet" href="susegad/components/reach/reach.css">
<script type="module" src="susegad/components/reach/reach.js"></script>

<sg-reach>
  <address>
    <p class="sg-reach-line"><span class="sg-reach-label">Email</span>
      <a href="mailto:hello@example.com?subject=A%20project">hello@example.com</a></p>
    <p class="sg-reach-line"><span class="sg-reach-label">WhatsApp</span>
      <a href="https://wa.me/919000012345?text=Hello!">+91 90000 12345</a>
      <button type="button" class="sg-reach-copy" data-copy="+91 90000 12345" hidden>Copy number</button></p>
    <p class="sg-reach-when">Replies usually come the same day.</p>
  </address>
</sg-reach>

The prompt

Make a contact block web component around an <address>. Each way to reach someone is a line with a small label and a real link: mailto: for email (spaces in the subject as %20, never +), and https://wa.me/<digits>?text=<message> for WhatsApp, the full international number in digits and a first message already written. Give pure functions that build and read back those links, for a build step. Ship the copy-the-number button with hidden in the HTML and show it only when the clipboard can be used, since without JavaScript it could do nothing. When it's pressed, copy the number, change its label to Copied for a moment, and say "Copied +91 … Paste it into WhatsApp or your phone." in a polite status line inside the block; if copying fails, say so and give the number to select. Give it three registers. Quiet: plain lines, labels in small capitals. Warm: an inland letter, a pale blue card that stays pale blue at night, with fold lines at a third and two thirds, a perforated left edge and a printed postage square, the words in the hand. Playful: a rubber-stamped card, a double-ruled frame inked round it at a slight tilt with ink starvation and an off-register ghost on the frame only, so the words and links stay crisp.

Words to code

When you sayTechniqueWhat happens
real links … built and read backpure corewaLink(number, text), mailtoLink(email, { subject, body }), readWaLink(href), digitsOf(). waLink refuses a number that isn't 8 to 15 digits with its country code.
shown only when the clipboard can be usedprogressive enhancementThe button is hidden in the HTML; the element unhides it when navigator.clipboard.writeText (or the older copy command) exists.
say … in a polite status linelive regionA <p role="status"> added inside the block; the line clears after six seconds so the next press is news again. The toast was considered and not used: it needs a region on the page, and this answers a press right beside it.
stays pale blue at nightcolor-schemecolor-scheme: light on the card, so every light-dark() token inside reads its daytime value; the tests check the words on that paper in both palettes.
pale blue, not greyrelative colouroklch(from var(--sg-info) 0.91 0.032 h): the palette's info hue, pale and a little saturated (#CCE6F1 in Susegad). The first try, 13% info mixed into the raised paper, measured chroma 0.016 at a yellow hue, which reads grey; the check reads the paper's pixels and fails on it. Browsers without relative colour get that mix.
perforated left edgeCSS maskA repeating radial gradient cuts half-circles down the left edge; a drop-shadow filter on the host replaces the box-shadow the mask would cut off.
ink starvation … on the frame onlyreuseThe stamp component's stampPose() and inkMask() (stamp.core.js), painted once to a canvas and used as the frame's CSS mask.

Accessibility

  • An <address> of labelled links; each link's text is the address or number itself.
  • The copy button is a real button, 32 px tall; its result is spoken once through the status line.
  • The status line reads as normal text, not only colour.
  • Forced colours: the cards get a system border and lose their masks.

<sg-reach> is a contact block around an <address>: email and WhatsApp links, a copy-the-number button, and a line on reply times.

import { waLink, mailtoLink } from './susegad/components/reach/reach.core.js';

waLink('+91 90000 12345', 'Hello! I saw your site.');
// https://wa.me/919000012345?text=Hello!%20I%20saw%20your%20site.
mailtoLink('hello@example.com', { subject: 'A project' });
// mailto:hello@example.com?subject=A%20project

readWaLink(href) returns { digits, text }, for a test that the site's links are right.

Markup

ElementWhat it is
<address>required
p.sg-reach-lineone way to reach you: a .sg-reach-label and a link
button.sg-reach-copy[data-copy][hidden]copies data-copy; keep hidden in the HTML
p.sg-reach-whenoptional: when replies usually come

The element adds a <p class="sg-reach-status" role="status"> when there is a copy button.

Attributes and events

seedthe playful frame's tilt and ink
registerquiet, warm, playful
sg-reach-copyafter a copy; detail: { value, ok }

Registers

Look
quietplain lines, labels in small capitals
warman inland letter: pale blue (the palette's info hue, oklch(from var(--sg-info) 0.91 0.032 h)) by day and by night, fold lines, a perforated edge, a postage square
playfula rubber-stamped card: an ink-starved double frame at a slight tilt

Nothing moves.

Reused

The playful frame uses stampPose() and inkMask() from packages/components/stamp/stamp.core.js.

Accessibility

  • Without JavaScript: the links work and the copy button stays hidden.
  • The copy result is said in words, once: "Copied +91 90000 12345. Paste it into WhatsApp or your phone." A failure says so and gives the number.
  • The inland letter's words are tested at 4.5:1 on its paper, and the focus ring at 3:1.