Motion Lab  /  Heroes  /  Word Reveal  /  Specimen

Movement begins with intention and ends with clarity.

Gedachten ontvouwen zich woord voor woord—elk syllabe een kleine aankomst.

Copy-snippets — drie lagen

Mechanisme — GSAP / ScrollTrigger logica
// Mechanisme: hero-word-reveal
// Kopieer 1-op-1. Geen stijlkeuzes.
import gsap from 'https://esm.sh/gsap@3.12.5';
import { ScrollTrigger } from 'https://esm.sh/gsap@3.12.5/ScrollTrigger';

gsap.registerPlugin(ScrollTrigger);

const HEADLINE_TEXT = 'Jouw headline hier';
const SELECTOR = '.hero-headline';

function splitWords(el, text) {
  el.innerHTML = text.split(" ")
    .map(function(w) { return '<span class="word-wrap"><span class="word">' + w + '</span></span>'; })
    .join(" ");
}

function initReveal() {
  const headline = document.querySelector(SELECTOR);
  if (!headline) return;
  splitWords(headline, HEADLINE_TEXT);
  const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  if (reduced) {
    headline.querySelectorAll('.word').forEach(w => {
      w.style.opacity = '1';
      w.style.transform = 'none';
    });
    return;
  }
  gsap.fromTo(
    headline.querySelectorAll('.word'),
    { yPercent: 110, autoAlpha: 0 },
    { yPercent: 0, autoAlpha: 1, duration: 1.4, stagger: 0.09, ease: 'expo.out',
      scrollTrigger: { trigger: headline, start: 'top 85%', once: true } }
  );
}

document.readyState === 'loading'
  ? document.addEventListener('DOMContentLoaded', initReveal)
  : initReveal();
Skeleton — DOM-structuur + minimal CSS
<!-- Skeleton: hero-word-reveal
     DOM + structurele CSS. Geen stijlkeuzes. -->

<section class="hero" aria-labelledby="hero-headline">
  <div class="hero__inner">
    <h1 id="hero-headline" class="hero-headline" tabindex="0">
      <!-- JS vult .word-wrap / .word spans in -->
    </h1>
    <p class="hero__subtitle"></p>
  </div>
</section>

/* Minimal structural CSS */
.hero-headline .word-wrap { display: inline-block; overflow: hidden; vertical-align: bottom; }
.hero-headline .word { display: inline-block; will-change: transform, opacity; }
.hero-headline:focus { outline: 2px solid currentColor; outline-offset: 4px; }
Lege styling-template — CSS vars + class hooks
/* Lege styling-template: hero-word-reveal
   Vul alle var(--...) in per merk. Verander geen class-namen. */

:root {
  --hero-bg:             /* achtergrondkleur */;
  --hero-color:          /* tekstkleur */;
  --hero-font-heading:   /* serif of display font */;
  --hero-font-body:      /* body / subtitle font */;
  --hero-heading-size:   /* clamp(..., ...vw, ...) */;
  --hero-heading-weight: /* 300 / 400 / 700 */;
  --hero-heading-ls:     /* letter-spacing, bijv. -0.02em */;
  --hero-heading-lh:     /* line-height */;
  --hero-subtitle-size:  /* clamp(...) */;
  --hero-padding-x:      /* clamp(...) */;
  --hero-padding-y:      /* clamp(...) */;
}

.hero { background: var(--hero-bg); color: var(--hero-color); padding: var(--hero-padding-y) var(--hero-padding-x); }
.hero__inner { max-width: /* bijv. 1100px */; margin: 0 auto; }
.hero-headline { font-family: var(--hero-font-heading); font-size: var(--hero-heading-size); font-weight: var(--hero-heading-weight); letter-spacing: var(--hero-heading-ls); line-height: var(--hero-heading-lh); }
.hero__subtitle { font-family: var(--hero-font-body); font-size: var(--hero-subtitle-size); }
.hero__eyebrow { font-variant: small-caps; letter-spacing: /* ... */; text-transform: uppercase; }
.hero__rule { height: 1px; background: /* rgba(...) */; }