web design
COMPONENT H / CODE REVEAL · LINE STAGGER + COPY
reveal.ts TypeScript
// Honor reduced motion: never animate when the user opts out.const reduce = () => matchMedia("(prefers-reduced-motion: reduce)").matches;function reveal(targets: Element[]) {  const io = new IntersectionObserver((entries) => {    entries.forEach((e) => {      if (e.isIntersecting) {        e.target.classList.add("is-in");        io.unobserve(e.target);      }    });  }, { threshold: 0.2 });  targets.forEach((t) => io.observe(t));  return io;}
GUIDE / CODE REVEAL

Overview

A code display with editor chrome: filename, language tag, line numbers, and a copy button. Lines reveal top to bottom on enter. The syntax palette is deliberately monochrome (white keywords and strings over grey body) so the accent token stays free for the rest of the page.

When to use

Showing a real snippet on a project page or a writing post. This is a code display, not a fake shell: real source, a language label, no pretend prompts or output. One or two per page, paired with a sentence of context.

Spec

Tokens
--surface panel, monochrome syntax
Motion
line stagger 0.05s, fade-up 0.4s
A11y
figure/figcaption, copy aria-label, reduced-motion static
Copy
navigator.clipboard, COPIED state in the accent

Anti-patterns

No fake terminal prompts or mocked output. No syntax-highlighter dependency, the three tokens cover the palette. Do not colorize with green or blue, they break the kit.

<!-- Requires GSAP: https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js -->

<style>
:root{
  --black:#050505;--surface:#0C0C0C;--white:#FFFFFF;--grey:#A3A3A3;--grey-dim:#6B6B6B;--accent:#22D3EE;
  --line:rgba(255,255,255,.16);--line-faint:rgba(255,255,255,.07);
  --font-display:'Space Grotesk',system-ui,sans-serif;--font-mono:'JetBrains Mono',ui-monospace,monospace;
  --dur-fast:150ms;--dur-base:300ms;--ease-out:cubic-bezier(.16,1,.3,1);
}
*{margin:0;padding:0;box-sizing:border-box;border-radius:0!important}
body{background:var(--black);color:var(--white);font-family:var(--font-display);min-height:100vh;padding:64px 48px}
.code{max-width:720px;margin:0 auto;border:1px solid var(--line);background:var(--surface);font-family:var(--font-mono);font-size:13px}
.code-head{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line-faint)}
.code-file{color:var(--grey);font-size:12px}
.code-lang{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--grey);border:1px solid var(--line);padding:2px 6px}
.code-copy{margin-left:auto;background:none;border:1px solid var(--line);color:var(--grey);font-family:inherit;font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:4px 10px;cursor:pointer;transition:border-color var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out)}
.code-copy:hover{border-color:var(--white);color:var(--white)}
.code-copy:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.code-copy.is-on{color:var(--accent);border-color:var(--accent)}
.code-body{display:grid;grid-template-columns:auto 1fr}
.code-gutter{margin:0;padding:16px 12px 16px 16px;text-align:right;color:var(--grey-dim);user-select:none;border-right:1px solid var(--line-faint);white-space:pre}
.code-pre{margin:0;padding:16px;overflow-x:auto}
.code-line{display:block;white-space:pre;color:var(--grey)}
.tok-c{color:var(--grey-dim);font-style:italic}
.tok-k{color:var(--white);font-weight:500}
.tok-s{color:var(--white)}
</style>

<figure class="code" id="code">
  <figcaption class="code-head">
    <span class="code-file">reveal.ts</span>
    <span class="code-lang">TypeScript</span>
    <button class="code-copy" id="copy" type="button" aria-label="Copy code">Copy</button>
  </figcaption>
  <div class="code-body">
    <pre class="code-gutter" id="gutter" aria-hidden="true"></pre>
    <pre class="code-pre" id="pre"><span class="code-line"><span class="tok-c">// Honor reduced motion: never animate when the user opts out.</span></span><span class="code-line"><span class="tok-k">const</span> reduce = () => matchMedia(<span class="tok-s">"(prefers-reduced-motion: reduce)"</span>).matches;</span><span class="code-line"><span class="tok-k">function</span> reveal(targets: Element[]) {</span><span class="code-line">  <span class="tok-k">const</span> io = <span class="tok-k">new</span> IntersectionObserver((entries) => {</span><span class="code-line">    entries.forEach((e) => {</span><span class="code-line">      <span class="tok-k">if</span> (e.isIntersecting) {</span><span class="code-line">        e.target.classList.add(<span class="tok-s">"is-in"</span>);</span><span class="code-line">        io.unobserve(e.target);</span><span class="code-line">      }</span><span class="code-line">    });</span><span class="code-line">  }, { threshold: <span class="tok-s">0.2</span> });</span><span class="code-line">  targets.forEach((t) => io.observe(t));</span><span class="code-line">  <span class="tok-k">return</span> io;</span><span class="code-line">}</span></pre>
  </div>
</figure>

<script>
/* raw source for the copy button, kept in sync with the tokenized pre above */
const RAW = `// Honor reduced motion: never animate when the user opts out.
const reduce = () => matchMedia("(prefers-reduced-motion: reduce)").matches;
function reveal(targets: Element[]) {
  const io = new IntersectionObserver((entries) => {
    entries.forEach((e) => {
      if (e.isIntersecting) {
        e.target.classList.add("is-in");
        io.unobserve(e.target);
      }
    });
  }, { threshold: 0.2 });
  targets.forEach((t) => io.observe(t));
  return io;
}`;

const pre = document.getElementById("pre");
const lines = gsap.utils.toArray(pre.querySelectorAll(".code-line"));
// ponytail: gutter derived from line count, so it can never drift from the code
document.getElementById("gutter").textContent =
  lines.map((_, i) => String(i + 1).padStart(2, "0")).join("\n");

function intro(){
  return gsap.timeline({delay:0.3, defaults:{ease:"power3.out"}})
    .set(pre, {autoAlpha:1})
    .from(lines, {autoAlpha:0, y:8, duration:0.4, stagger:0.05});
}
gsap.set(pre, {autoAlpha:0});

let tl = null;
const mm = gsap.matchMedia();
mm.add({
  full:   "(prefers-reduced-motion: no-preference)",
  reduce: "(prefers-reduced-motion: reduce)"
}, (ctx) => {
  if (ctx.conditions.full) tl = intro();
  else gsap.set(pre, {autoAlpha:1});
});

const copy = document.getElementById("copy");
copy.addEventListener("click", async () => {
  try { await navigator.clipboard.writeText(RAW); } catch (e) { /* clipboard blocked */ }
  copy.textContent = "Copied";
  copy.classList.add("is-on");
  setTimeout(() => { copy.textContent = "Copy"; copy.classList.remove("is-on"); }, 1500);
});
</script>