Site Commander

Developer library · 36 free recipes

Effects, ready to ship.

Free motion recipes for real websites. Browse practical CSS and JavaScript patterns, copy a focused implementation, or let an LLM fetch the exact effect through the Site Commander API or MCP.

const effect = "fade-up-reveal";
await ship(effect);

The foundation

Native recipes

Small, composable patterns you can paste into a plain HTML site, a CMS, or a generated staging build.

Interactive preview
Scroll revealsCC0-style snippets

Staggered list

A calm sequence for cards or list items, with the delay controlled by a CSS custom property.

[data-sc-effect="stagger"] [data-sc-stagger-item] {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 450ms ease, transform 450ms ease;
  transition-delay: calc(var(--sc-index, 0) * 70ms);
}
[data-sc-effect="stagger"].is-visible [data-sc-stagger-item] {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  [data-sc-effect="stagger"] [data-sc-stagger-item] { opacity: 1; transform: none; transition: none; }
}

const staggerObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(({isIntersecting, target}) => {
    if (!isIntersecting) return;
    target.querySelectorAll('[data-sc-stagger-item]').forEach((item, index) => {
      item.style.setProperty('--sc-index', index);
    });
    target.classList.add('is-visible');
    observer.unobserve(target);
  });
}, { threshold: 0.12 });
document.querySelectorAll('[data-sc-effect="stagger"]').forEach((el) => staggerObserver.observe(el));
Interactive preview
Micro-interactionsCC0-style snippets

Spotlight hover

A pointer-following radial highlight that adds depth without a heavy visual layer.

[data-sc-effect="spotlight"] {
  --sc-x: 50%;
  --sc-y: 50%;
  background: radial-gradient(circle at var(--sc-x) var(--sc-y), rgba(116, 125, 255, .22), transparent 34%), #141822;
  transition: background 180ms ease, transform 180ms ease;
}
[data-sc-effect="spotlight"]:hover { transform: translateY(-2px); }

document.querySelectorAll('[data-sc-effect="spotlight"]').forEach((card) => {
  card.addEventListener('pointermove', (event) => {
    const box = card.getBoundingClientRect();
    card.style.setProperty('--sc-x', `${event.clientX - box.left}px`);
    card.style.setProperty('--sc-y', `${event.clientY - box.top}px`);
  }, { passive: true });
});
Interactive preview
Micro-interactionsCC0-style snippets

Magnetic button

A restrained pointer response for a primary CTA that remains keyboard and touch friendly.

[data-sc-effect="magnetic"] {
  transition: transform 180ms cubic-bezier(.2, .8, .2, 1);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  [data-sc-effect="magnetic"] { transition: none; }
}

document.querySelectorAll('[data-sc-effect="magnetic"]').forEach((button) => {
  button.addEventListener('pointermove', (event) => {
    const box = button.getBoundingClientRect();
    const x = (event.clientX - (box.left + box.width / 2)) * .12;
    const y = (event.clientY - (box.top + box.height / 2)) * .12;
    button.style.transform = `translate(${x}px, ${y}px)`;
  }, { passive: true });
  button.addEventListener('pointerleave', () => { button.style.transform = ''; });
});
Interactive preview
NavigationCC0-style snippets

Scroll progress line

A single fixed progress line that gives long-form pages a clear sense of place.

<div data-sc-effect="scroll-progress" aria-hidden="true"></div>

[data-sc-effect="scroll-progress"] {
  position: fixed; inset: 0 auto auto 0; z-index: 10;
  width: 100%; height: 3px; transform: scaleX(0); transform-origin: left;
  background: #5b5ce2; pointer-events: none;
}

const progress = document.querySelector('[data-sc-effect="scroll-progress"]');
let progressTicking = false;
window.addEventListener('scroll', () => {
  if (progressTicking) return;
  progressTicking = true;
  requestAnimationFrame(() => {
    const max = document.documentElement.scrollHeight - window.innerHeight;
    progress.style.transform = `scaleX(${max > 0 ? window.scrollY / max : 0})`;
    progressTicking = false;
  });
}, { passive: true });
Interactive preview
BackgroundsCC0-style snippets

Cursor glow

A soft ambient glow for a hero or dark section that never blocks content or clicks.

[data-sc-effect="cursor-glow"] {
  --sc-x: 50%; --sc-y: 50%;
  background: radial-gradient(420px circle at var(--sc-x) var(--sc-y), rgba(91, 92, 226, .3), transparent 70%), #111318;
}

document.querySelectorAll('[data-sc-effect="cursor-glow"]').forEach((section) => {
  section.addEventListener('pointermove', (event) => {
    const box = section.getBoundingClientRect();
    section.style.setProperty('--sc-x', `${event.clientX - box.left}px`);
    section.style.setProperty('--sc-y', `${event.clientY - box.top}px`);
  }, { passive: true });
});
Interactive preview
BackgroundsCC0-style snippets

Animated gradient mesh

A low-frequency CSS background motion that gives a hero a little life without adding an image asset.

[data-sc-effect="gradient-mesh"] { position: relative; isolation: isolate; overflow: hidden; background: #111318; }
[data-sc-effect="gradient-mesh"]::before {
  content: ""; position: absolute; inset: -35%; z-index: -1;
  background: radial-gradient(circle at 20% 30%, #5b5ce2 0 12%, transparent 38%), radial-gradient(circle at 80% 70%, #9db7ff 0 8%, transparent 34%);
  filter: blur(30px); opacity: .38; animation: sc-mesh 16s ease-in-out infinite alternate;
}
@keyframes sc-mesh { to { transform: translate3d(4%, -3%, 0) scale(1.08); } }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="gradient-mesh"]::before { animation: none; } }
Interactive preview
Text motionCC0-style snippets

Split text reveal

A small line-by-line headline reveal that avoids a runtime text-splitting dependency.

[data-sc-effect="split-text"] [data-sc-line] {
  display: block; clip-path: inset(0 0 100% 0); transform: translateY(1em);
  transition: clip-path 700ms cubic-bezier(.2, .8, .2, 1), transform 700ms cubic-bezier(.2, .8, .2, 1);
  transition-delay: calc(var(--sc-index, 0) * 90ms);
}
[data-sc-effect="split-text"].is-visible [data-sc-line] { clip-path: inset(0); transform: none; }
@media (prefers-reduced-motion: reduce) {
  [data-sc-effect="split-text"] [data-sc-line] { clip-path: inset(0); transform: none; transition: none; }
}

document.querySelectorAll('[data-sc-effect="split-text"]').forEach((heading) => {
  heading.querySelectorAll('[data-sc-line]').forEach((line, index) => line.style.setProperty('--sc-index', index));
  requestAnimationFrame(() => heading.classList.add('is-visible'));
});
Interactive preview
Micro-interactionsCC0-style snippets

Hover lift card

A subtle elevation cue for clickable cards that keeps the hit area stable and the focus state visible.

[data-sc-effect="hover-lift"] {
  transition: transform 180ms ease, box-shadow 180ms ease;
}
[data-sc-effect="hover-lift"]:hover,
[data-sc-effect="hover-lift"]:focus-visible {
  transform: translateY(-5px);
  box-shadow: 0 16px 30px rgba(17, 19, 24, .14);
}
@media (prefers-reduced-motion: reduce) {
  [data-sc-effect="hover-lift"] { transition: none; }
}
Interactive preview
Micro-interactionsCC0-style snippets

Button ripple

A contained click ripple that confirms activation without replacing the button label or focus ring.

[data-sc-effect="ripple"] { position: relative; overflow: hidden; isolation: isolate; }
[data-sc-effect="ripple"] .sc-ripple {
  position: absolute; width: 12px; aspect-ratio: 1; border-radius: 50%;
  background: currentColor; opacity: .22; transform: scale(0);
  animation: sc-ripple 550ms ease-out forwards; pointer-events: none;
}
@keyframes sc-ripple { to { opacity: 0; transform: scale(18); } }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="ripple"] .sc-ripple { display: none; } }

document.querySelectorAll('[data-sc-effect="ripple"]').forEach((button) => {
  button.addEventListener('click', (event) => {
    const box = button.getBoundingClientRect();
    const ripple = document.createElement('span');
    ripple.className = 'sc-ripple';
    ripple.style.left = `${event.clientX - box.left - 6}px`;
    ripple.style.top = `${event.clientY - box.top - 6}px`;
    button.append(ripple);
    ripple.addEventListener('animationend', () => ripple.remove(), { once: true });
  });
});
Interactive preview
ComponentsCC0-style snippets

Accordion disclosure

An accessible disclosure pattern with native button semantics and a restrained height transition.

<button type="button" aria-expanded="false" aria-controls="answer-1">What is included?</button>
<div id="answer-1" hidden>Short, useful supporting content.</div>

const trigger = document.querySelector('[aria-controls="answer-1"]');
const panel = document.getElementById(trigger.getAttribute('aria-controls'));
trigger.addEventListener('click', () => {
  const open = trigger.getAttribute('aria-expanded') === 'true';
  trigger.setAttribute('aria-expanded', String(!open));
  panel.hidden = open;
});
Interactive preview
ComponentsCC0-style snippets

Tooltip label

A compact contextual label for icon-only controls, shown on hover and keyboard focus.

[data-sc-tooltip] { position: relative; }
[data-sc-tooltip]::after {
  content: attr(data-sc-tooltip); position: absolute; left: 50%; bottom: calc(100% + 8px);
  transform: translate(-50%, 4px); opacity: 0; pointer-events: none; white-space: nowrap;
  padding: 5px 7px; border-radius: 5px; background: #111318; color: #fff;
  font: 12px/1.2 system-ui, sans-serif; transition: opacity 150ms ease, transform 150ms ease;
}
[data-sc-tooltip]:hover::after,
[data-sc-tooltip]:focus-visible::after { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) { [data-sc-tooltip]::after { transition: none; } }
Interactive preview
NavigationCC0-style snippets

Active nav underline

A sliding active indicator that makes the current navigation context obvious.

[data-sc-effect="nav-underline"] { --sc-underline-x: 0%; --sc-underline-width: 0px; position: relative; }
[data-sc-effect="nav-underline"]::after {
  content: ""; position: absolute; left: var(--sc-underline-x); bottom: -5px;
  width: var(--sc-underline-width); height: 2px; background: currentColor;
  transition: left 180ms ease, width 180ms ease;
}
@media (prefers-reduced-motion: reduce) { [data-sc-effect="nav-underline"]::after { transition: none; } }

const nav = document.querySelector('[data-sc-effect="nav-underline"]');
nav.querySelectorAll('a').forEach((link) => {
  link.addEventListener('pointerenter', () => {
    nav.style.setProperty('--sc-underline-x', `${link.offsetLeft}px`);
    nav.style.setProperty('--sc-underline-width', `${link.offsetWidth}px`);
  });
  link.addEventListener('click', (event) => {
    event.preventDefault();
    nav.querySelectorAll('a').forEach((item) => item.removeAttribute('aria-current'));
    link.setAttribute('aria-current', 'page');
  });
});
Interactive preview
ComponentsCC0-style snippets

Tabs switcher

A keyboard-friendly tab switcher with real selected state and a small content transition.

<div data-sc-effect="tabs-switcher">
  <div role="tablist"><button type="button" role="tab" aria-selected="true" aria-controls="tab-a">A</button><button type="button" role="tab" aria-selected="false" aria-controls="tab-b">B</button></div>
  <div id="tab-a" role="tabpanel">First view</div><div id="tab-b" role="tabpanel" hidden>Second view</div>
</div>

document.querySelectorAll('[data-sc-effect="tabs-switcher"] [role="tab"]').forEach((tab) => {
  tab.addEventListener('click', () => {
    const group = tab.closest('[role="tablist"]').parentElement;
    group.querySelectorAll('[role="tab"]').forEach((item) => item.setAttribute('aria-selected', String(item === tab)));
    group.querySelectorAll('[role="tabpanel"]').forEach((panel) => panel.hidden = panel.id !== tab.getAttribute('aria-controls'));
  });
});
Interactive preview
Text motionCC0-style snippets

Count-up number

A short numeric transition that gives a metric a sense of arrival without hiding the final value.

<span data-sc-effect="count-up" data-value="240">240</span>

const count = document.querySelector('[data-sc-effect="count-up"]');
const end = Number(count.dataset.value || 240);
const start = performance.now();
function tick(now) {
  const progress = Math.min((now - start) / 900, 1);
  count.textContent = Math.round(end * (1 - Math.pow(1 - progress, 3))).toLocaleString();
  if (progress < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
Interactive preview
Text motionCC0-style snippets

Typewriter text

A short, decorative typewriter cue for a headline or status line, with the full copy preserved for assistive technology.

<span data-sc-effect="typewriter" data-text="Build with clarity">Build with clarity</span>

const node = document.querySelector('[data-sc-effect="typewriter"]');
const text = node.dataset.text || node.textContent;
node.setAttribute('aria-label', text);
node.textContent = '';
let index = 0;
const type = () => { node.textContent = text.slice(0, ++index); if (index < text.length) setTimeout(type, 55); };
type();
Interactive preview
Text motionCC0-style snippets

Pauseable marquee

A low-priority looping strip with a pause control, suitable for decorative keywords or partner labels.

[data-sc-effect="marquee"] { overflow: hidden; }
[data-sc-effect="marquee"] .sc-marquee-track { display: flex; width: max-content; animation: sc-marquee 18s linear infinite; }
[data-sc-effect="marquee"]:has(button:focus-visible) .sc-marquee-track,
[data-sc-effect="marquee"].is-paused .sc-marquee-track { animation-play-state: paused; }
@keyframes sc-marquee { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="marquee"] .sc-marquee-track { animation: none; } }

<div data-sc-effect="marquee"><div class="sc-marquee-track"><span>Clarity · Motion · Speed · </span><span aria-hidden="true">Clarity · Motion · Speed · </span></div><button type="button" data-sc-marquee-toggle>Pause</button></div>
<script>
  const marquee = document.querySelector('[data-sc-effect="marquee"]');
  marquee.querySelector('[data-sc-marquee-toggle]').addEventListener('click', (event) => {
    marquee.classList.toggle('is-paused');
    event.currentTarget.textContent = marquee.classList.contains('is-paused') ? 'Play' : 'Pause';
  });
</script>
Interactive preview
BackgroundsCC0-style snippets

Shimmer skeleton

A restrained loading placeholder that communicates waiting without pretending content has arrived.

[data-sc-effect="shimmer"] { position: relative; overflow: hidden; background: #e8ecf4; }
[data-sc-effect="shimmer"]::after {
  content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.7), transparent);
  animation: sc-shimmer 1.5s ease-in-out infinite;
}
@keyframes sc-shimmer { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="shimmer"]::after { animation: none; } }
Interactive preview
BackgroundsCC0-style snippets

Blob morph background

A soft organic background shape for a creative hero, kept behind content and disabled for reduced motion.

[data-sc-effect="blob-morph"] { position: relative; isolation: isolate; overflow: hidden; }
[data-sc-effect="blob-morph"]::before {
  content: ""; position: absolute; width: 58%; aspect-ratio: 1; inset: 12% auto auto 22%; z-index: -1;
  border-radius: 62% 38% 41% 59% / 53% 44% 56% 47%; background: #aab1ff; filter: blur(4px);
  animation: sc-blob 9s ease-in-out infinite alternate;
}
@keyframes sc-blob { to { border-radius: 36% 64% 57% 43% / 41% 52% 48% 59%; transform: rotate(18deg) scale(1.08); } }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="blob-morph"]::before { animation: none; } }
Interactive preview
Text motionCC0-style snippets

Underline sweep

A bright underline that draws attention to a link while leaving the text and focus state intact.

[data-sc-effect="underline-sweep"] { background: linear-gradient(currentColor, currentColor) 0 100% / 0 2px no-repeat; transition: background-size 220ms ease; }
[data-sc-effect="underline-sweep"]:hover,
[data-sc-effect="underline-sweep"]:focus-visible { background-size: 100% 2px; }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="underline-sweep"] { transition: none; } }
Interactive preview
Media & layoutCC0-style snippets

Image zoom frame

A restrained image scale on hover that keeps the crop contained and does not alter the layout.

[data-sc-effect="image-zoom"] { overflow: hidden; }
[data-sc-effect="image-zoom"] img { display: block; width: 100%; transition: transform 350ms ease; }
[data-sc-effect="image-zoom"]:hover img,
[data-sc-effect="image-zoom"]:focus-within img { transform: scale(1.06); }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="image-zoom"] img { transition: none; } }
Interactive preview
Media & layoutCC0-style snippets

Pointer parallax layer

A small pointer-based depth shift for a decorative layer, with touch and reduced-motion fallbacks.

<div data-sc-effect="parallax"><div data-sc-parallax-layer>Focal visual</div></div>
<style>[data-sc-effect="parallax"] { overflow: hidden; } [data-sc-parallax-layer] { transition: transform 180ms ease; }</style>
<script>
const scene = document.querySelector('[data-sc-effect="parallax"]');
const layer = scene.querySelector('[data-sc-parallax-layer]');
scene.addEventListener('pointermove', (event) => {
  const box = scene.getBoundingClientRect();
  const x = (event.clientX - box.left) / box.width - .5;
  const y = (event.clientY - box.top) / box.height - .5;
  layer.style.transform = `translate3d(${x * 12}px, ${y * 12}px, 0)`;
}, { passive: true });
scene.addEventListener('pointerleave', () => { layer.style.transform = ''; });
Interactive preview
ComponentsCC0-style snippets

Toast notification

A compact status message for a completed action, with live-region semantics and an explicit close affordance.

<button type="button" data-sc-toast-trigger>Save</button>
<div data-sc-effect="toast" role="status" hidden>Saved just now <button type="button" data-sc-toast-close aria-label="Close">×</button></div>
<script>
const toast = document.querySelector('[data-sc-effect="toast"]');
document.querySelector('[data-sc-toast-trigger]').addEventListener('click', () => {
  toast.hidden = false;
  window.setTimeout(() => { toast.hidden = true; }, 3200);
});
toast.querySelector('[data-sc-toast-close]').addEventListener('click', () => { toast.hidden = true; });
Interactive preview
ComponentsCC0-style snippets

Toggle switch

A clear on/off control with a native checkbox underneath the visual switch.

<label class="switch"><input type="checkbox"><span aria-hidden="true"></span><b>Enable updates</b></label>

.switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.switch input { position: absolute; opacity: 0; }
.switch span { width: 38px; height: 22px; border-radius: 999px; background: #cfd6e3; transition: background 180ms ease; }
.switch span::after { content: ""; display: block; width: 18px; height: 18px; margin: 2px; border-radius: 50%; background: #fff; transition: transform 180ms ease; }
.switch input:checked + span { background: #5b5ce2; }
.switch input:checked + span::after { transform: translateX(16px); }
.switch input:focus-visible + span { outline: 3px solid #aab1ff; outline-offset: 3px; }
Interactive preview
Scroll revealsCC0-style snippets

Timeline draw

A vertical progress line that draws as milestones enter the viewport.

<div data-sc-effect="timeline"><i aria-hidden="true"></i><span data-sc-timeline-step>Plan</span><span data-sc-timeline-step>Build</span><span data-sc-timeline-step>Ship</span></div>
<style>[data-sc-effect="timeline"] { --sc-timeline-progress: 0%; position: relative; display: flex; gap: 16px; } [data-sc-effect="timeline"] > i { position: absolute; left: 0; right: 0; top: 50%; height: 2px; background: #d9deec; } [data-sc-effect="timeline"] > i::after { content: ""; display: block; width: var(--sc-timeline-progress); height: 100%; background: #5b5ce2; } [data-sc-effect="timeline"] > span { position: relative; padding: 5px; background: #fff; }</style>
<script>
const timeline = document.querySelector('[data-sc-effect="timeline"]');
const timelineObserver = new IntersectionObserver((entries) => entries.forEach((entry) => {
  if (entry.isIntersecting) {
    entry.target.classList.add('is-visible');
    const visible = timeline.querySelectorAll('.is-visible').length;
    const total = timeline.querySelectorAll('[data-sc-timeline-step]').length;
    timeline.style.setProperty('--sc-timeline-progress', `${total > 1 ? ((visible - 1) / (total - 1)) * 100 : 100}%`);
  }
}), { threshold: .35 });
timeline.querySelectorAll('[data-sc-timeline-step]').forEach((step) => timelineObserver.observe(step));
Interactive preview
Micro-interactionsCC0-style snippets

Card flip reveal

A click-to-reveal two-sided card that works with keyboard focus and keeps both sides in the DOM.

[data-sc-effect="card-flip"] { perspective: 800px; }
[data-sc-effect="card-flip"] .sc-flip-inner { transition: transform 500ms ease; transform-style: preserve-3d; }
[data-sc-effect="card-flip"] .sc-flip-inner > * { position: absolute; inset: 0; display: grid; place-items: center; backface-visibility: hidden; }
[data-sc-effect="card-flip"] .sc-flip-inner > strong { transform: rotateY(180deg); }
[data-sc-effect="card-flip"].is-flipped .sc-flip-inner { transform: rotateY(180deg); }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="card-flip"] .sc-flip-inner { transition: none; } }

<button type="button" data-sc-effect="card-flip" aria-pressed="false"><span class="sc-flip-inner"><b>Front</b><strong>Back</strong></span></button>
<script>
  const flip = document.querySelector('[data-sc-effect="card-flip"]');
  flip.addEventListener('click', () => { const open = flip.classList.toggle('is-flipped'); flip.setAttribute('aria-pressed', String(open)); });
</script>

Bring a library when it earns its weight

Free library starters

Version-pinned starting points for open-source libraries. Review the license and performance tradeoff before adding another dependency.

Interactive preview
AOSMIT

AOS fade-up starter

A minimal, version-pinned Animate On Scroll setup for teams that want a maintained preset library.

<link rel="stylesheet" href="https://unpkg.com/aos@2.3.4/dist/aos.css">
<script src="https://unpkg.com/aos@2.3.4/dist/aos.js"></script>
<script>
  document.querySelectorAll('[data-sc-effect="aos-fade-up"]').forEach((node) => node.setAttribute('data-aos', 'fade-up'));
  AOS.init({ once: true, duration: 650, offset: 80, disable: window.matchMedia('(prefers-reduced-motion: reduce)').matches });
</script>

<article data-aos="fade-up">Your content</article>
Interactive preview
MotionMIT

Motion spring entrance

A small modern Motion starter for a springy entrance when CSS transitions need more control.

<div data-sc-effect="motion-spring">Your content</div>
<script type="module">
  import { animate } from 'https://cdn.jsdelivr.net/npm/motion@12.23.24/+esm';
  const node = document.querySelector('[data-sc-effect="motion-spring"]');
  const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  animate(node, { opacity: [0, 1], y: [24, 0] }, reduceMotion ? { duration: 0 } : { type: 'spring', stiffness: 260, damping: 24 });
</script>
Interactive preview
LenisMIT

Lenis smooth scroll

A smooth-scroll baseline for sites where scroll-linked motion is part of the interaction model.

<script type="module">
  import Lenis from 'https://cdn.jsdelivr.net/npm/lenis@1.3.25/+esm';
  const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const wrapper = document.querySelector('[data-sc-effect="lenis-smooth-scroll"]') || window;
  const lenis = new Lenis({ wrapper, autoRaf: !reduceMotion, anchors: true });
  window.siteCommanderLenis = lenis;
  if (reduceMotion) lenis.stop();
  window.matchMedia('(prefers-reduced-motion: reduce)').addEventListener('change', ({matches}) => {
    if (matches) lenis.stop(); else lenis.start();
  });
</script>
Interactive preview
Embla CarouselMIT

Embla carousel starter

A small, accessible carousel baseline with touch gestures and explicit previous/next controls.

Interactive preview
Splitting.jsMIT

Splitting.js text starter

A character or word splitting starter for expressive headings that need precise sequencing.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/splitting@1.1.0/dist/splitting.css">
<script src="https://cdn.jsdelivr.net/npm/splitting@1.1.0/dist/splitting.min.js"></script>
<h2 data-sc-effect="splitting-text" data-splitting>Make it move</h2>
<style>[data-sc-effect="splitting-text"] .char { display: inline-block; opacity: 0; transform: translateY(.7em); animation: sc-split-in 650ms ease forwards; animation-delay: calc(var(--char-index, 0) * 60ms); } @keyframes sc-split-in { to { opacity: 1; transform: none; } } @media (prefers-reduced-motion: reduce) { [data-sc-effect="splitting-text"] .char { animation: none; opacity: 1; transform: none; } }</style>
<script>Splitting({ target: document.querySelector('[data-sc-effect="splitting-text"]'), by: 'chars' });</script>
Interactive preview
TypewriterJSMIT

TypewriterJS starter

A pinned typewriter starter for short rotating phrases, with an accessible static fallback.

<span id="typed" data-sc-effect="typed-text" aria-label="Build with clarity">Build with clarity</span>
<script src="https://unpkg.com/typewriter-effect@2.22.0/dist/core.js"></script>
<script>
  const node = document.querySelector('[data-sc-effect="typed-text"]');
  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) node.textContent = 'Build with clarity';
  else {
    const typewriter = new Typewriter(node, { loop: true, delay: 48 });
    typewriter.typeString('Build with clarity').pauseFor(1200).deleteAll().typeString('Ship with confidence').start();
  }
</script>
Interactive preview
CountUp.jsMIT

CountUp.js metric starter

A lightweight number animation starter for metrics that enter the viewport.

<span id="metric" data-sc-effect="countup-metric" data-value="240">240</span>
<script type="module">
  import { CountUp } from 'https://cdn.jsdelivr.net/npm/countup.js@2.8.0/+esm';
  const node = document.querySelector('[data-sc-effect="countup-metric"]');
  if (!matchMedia('(prefers-reduced-motion: reduce)').matches) new CountUp(node, Number(node.dataset.value)).start();
</script>
Interactive preview
VanillaTiltMIT

VanillaTilt card

A restrained 3D tilt starter for one featured card or product preview, with glare kept optional.

<script src="https://cdn.jsdelivr.net/npm/vanilla-tilt@1.8.1/dist/vanilla-tilt.min.js"></script>
<div data-sc-effect="vanilla-tilt">Featured visual</div>
<script>
  if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
    VanillaTilt.init(document.querySelectorAll('[data-sc-effect="vanilla-tilt"]'), {
      max: 7, speed: 500, scale: 1.015, glare: true, 'max-glare': .14
    });
  }
</script>

For GPTs and other agents

Tell the LLM how to use the library.

Give the model a clear selection loop. It should discover, fetch, compose, then explain where it placed the effect instead of inventing a new dependency.

Recommended instruction

Use this in a system or developer prompt when the model can access the REST API or MCP.

When a user asks for website motion or an interaction effect:
1. Search Site Commander’s effects catalog before writing a new animation.
2. Prefer a native recipe when it meets the request; use a free MIT library only when it materially reduces complexity.
3. Fetch the chosen effect by effect_id and inspect its reduced-motion and accessibility notes.
4. Compose the effect for the user’s real selector. Keep the selector scoped and do not rewrite unrelated styles.
5. Return the exact files or insertion points, explain the dependency (if any), and mention keyboard, touch, and prefers-reduced-motion behavior.
6. Never add a library from an untrusted CDN or use a versionless URL when a pinned URL is available.

Use the Site Commander tools `sitecommander_effects_search`, `sitecommander_effects_fetch`, and `sitecommander_effects_compose` for this workflow.
No command center required.The catalog is public and read-only. Use the REST endpoints from any site or connect the bearer-authenticated MCP endpoint to an agent.