Compliance
Track deadlines and reporting in one workspace.
Animation standards powered by GSAP — tokens, scroll reveals, stagger, and accessibility guidance.
Epicenter uses GSAP for choreographed motion —
scroll storytelling, staggered entrances, and coordinated timelines. The standard license is free for commercial
use. Motion tokens live in tokens/motion.css; the brand kit helper is in
lib/gsap/brand-kit.ts.
Use these CSS custom properties as the source of truth. Tokens are defined in
tokens/motion.css; the GSAP helper reads durations and eases from :root at runtime.
Add data-motion="reveal" to elements that fade and rise into view on scroll and reverse when you scroll back up.
Live preview
Scroll down in the stage to reveal each card — scroll up to replay.
Track deadlines and reporting in one workspace.
Board materials and minutes stay organized.
Scorecards align authorizers and schools.
Lightweight scroll entrances using the native
Intersection Observer API
— no GSAP dependency. Add data-scroll-reveal; the helper toggles
scroll-reveal--in once the element crosses the viewport threshold. Motion tokens drive the CSS transition.
Live preview
Scroll down to trigger each card — animations play once and do not scrub on scroll back.
Runs off the main scroll path — efficient for simple fade-and-rise entrances.
Uses --motion-duration-slow, --motion-reveal-y, and entrance easing from motion tokens.
List items can use transition-delay via --motion-stagger-base for a stepped feel.
Prefer GSAP scroll reveal when you need scrubbed, reversible motion tied to scroll position.
Mark a container with data-motion="stagger" and each child with
data-motion-child. Children animate in sequence on scroll and reverse when you scroll back up.
Live preview
Scroll the lifecycle steps into view — scroll up to replay the sequence.
Feature cards in a responsive bento layout — built from feature card
styles. Mark the grid with data-motion="bento", each card with
data-motion-bento-item, and inner copy with data-motion-bento-child. Cards
scale and rise on scroll as each card enters the viewport. Reverses when you scroll back up.
Live preview
Scroll the grid into view — uses the same .card--feature patterns from the cards page.
A shared calendar of expectations. Auto-routed submissions. A paper trail by default — so audits stop being events, and start being checkboxes.
Roll up performance, compliance status, and open items across your full portfolio — without switching tools or rebuilding spreadsheets.
Coordinate site visits, document requests, and follow-ups in one workflow.
Board packets, votes, and follow-ups on a single timeline authorizers can trust.
Shared goals and transparent reporting between authorizers and schools.
Pin a full-height panel and advance one card per viewport of scroll. Slide content uses
data-motion-slide-item for eyebrow → title → body entrance and exit on panels after the first.
The opening panel stays static; scroll drives animation from the second card onward.
Mark the viewport with data-motion="scroll-slider".
Live preview
When the panel below reaches the top, keep scrolling to move through each card.
Pin a stage and scrub a compact background tile into a full-screen panel. Mark the wrapper with
data-motion="scroll-expand"; the tile uses .brand-kit__motion-scroll-expand-bg
and copy uses data-motion-scroll-expand-content.
Live preview
When the stage below reaches the top, keep scrolling to grow the tile edge-to-edge.
An infinite logo strip built from /public/logo-timeline. Mark the wrapper with
data-motion="marquee" — GSAP loops the duplicated track slowly and continuously.
Live preview
Integration logos scroll on a seamless loop. Duration is controlled by --motion-marquee-duration.
Complements CSS hover transitions on buttons. Use data-motion="micro-press" for press feedback.
Loading morph runs on all brand kit buttons by default — click any button to preview.
Import and call the helpers from an Astro page script block:
---
// page.astro
---
<section>
<article data-motion="reveal">...</article>
<article data-scroll-reveal>...</article>
<ul data-motion="stagger">
<li data-motion-child>...</li>
</ul>
<div data-motion="scroll-slider" class="brand-kit__motion-scroll-slider">
<div class="brand-kit__motion-scroll-slider-track">...</div>
</div>
<div data-motion="scroll-expand" class="brand-kit__motion-scroll-expand">
<div class="brand-kit__motion-scroll-expand-pin">
<div class="brand-kit__motion-scroll-expand-bg" aria-hidden="true"></div>
<div data-motion-scroll-expand-content>...</div>
</div>
</div>
<div class="brand-kit__bento" data-motion="bento">
<article class="card card--feature" data-motion-bento-item>
<div class="card__inner">
<span class="card__icon" data-motion-bento-child>...</span>
<h3 class="card__title" data-motion-bento-child>...</h3>
</div>
</article>
</div>
<div data-motion="marquee" class="brand-kit__motion-marquee">
<div class="brand-kit__motion-marquee-track">...</div>
</div>
<button class="button button--primary">
<span class="button__label">Save changes</span>
<span class="button__trailing-icon material-symbols-outlined" aria-hidden="true">arrow_forward_ios</span>
<span class="button__spinner material-symbols-outlined" aria-hidden="true">donut_large</span>
</button>
</section>
<script>
import { startButtonLoading, stopButtonLoading } from '../../../lib/gsap/brand-kit'
import { initScrollReveal } from '../../../lib/brand-kit/scroll-reveal'
// Loading morph + GSAP motion from BrandKitLayout.astro
initScrollReveal()
// startButtonLoading(button) / stopButtonLoading(button) for app code
</script>
For Astro islands or SPA navigation, call ScrollTrigger.getAll().forEach((t) => t.kill()) before
re-initializing to avoid duplicate triggers.