Installation
SDS Motion Forge ships as one CSS file and two optional JS engines. The CSS is always required. The JS files are only needed for specific features — include only what you use.
CDN — no build step
Add the <link> to your <head>.
Add the <script> tags before </body> only if you use those features.
<!-- ① ALWAYS REQUIRED — core CSS (600+ animations, ~39 KB gzip) --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/motion.min.css" />
<!-- ② OPTIONAL — scroll engine (~0.6 KB gzip) --> <!-- Add this ONLY if you use data-sds scroll-triggered animations --> <script src="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/sds-scroll.min.js"></script> <!-- ③ OPTIONAL — interactive engine (~2.4 KB gzip) --> <!-- Add this ONLY if you use cursor-reactive classes: --> <!-- sds-word-morph · sds-magnetic-pull · sds-shockwave --> <!-- sds-scatter-return · sds-spring-kerning · sds-repulsion-field --> <script src="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/motion-interactive.min.js"></script>
npm
// ① ALWAYS REQUIRED — import the core CSS import '@salkomdesignstudio/sds-motion-forge/dist/motion.min.css'; // ② OPTIONAL — scroll engine (only for data-sds scroll animations) import '@salkomdesignstudio/sds-motion-forge/dist/sds-scroll.min.js'; // ③ OPTIONAL — interactive engine (only for cursor-reactive classes) import '@salkomdesignstudio/sds-motion-forge/dist/motion-interactive.min.js';
What gets installed
| File | Size (gzip) | When you need it |
|---|---|---|
dist/motion.min.css | ~39 KB gzip | Always — core library, 600+ animations |
dist/sds-scroll.min.js | ~0.6 KB | When using data-sds scroll-gated animations |
dist/motion-interactive.min.js | ~2.4 KB | When using cursor-reactive animations (word-morph, magnetic-pull, shockwave…) |
src/motion.css | ~366 KB raw | Custom PostCSS builds only |
Choose your setup
Pick the row that matches what you need — each one builds on the previous.
| You want… | Files to include |
|---|---|
| CSS-only animations (text, buttons, inputs, cards, loaders) | motion.min.css only |
Scroll-triggered animations (data-sds attribute) |
motion.min.css + sds-scroll.min.js |
| Cursor / hover interactive animations (word-morph, magnetic-pull, shockwave…) | motion.min.css + motion-interactive.min.js |
| Everything | motion.min.css + sds-scroll.min.js + motion-interactive.min.js |
Full CDN setup (all features)
<head> (and closing <body> tag) to enable every feature.
<!-- Step 1 · Core CSS — required for ALL animations --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/motion.min.css" />
<!-- Step 2 · Scroll engine — only if you use data-sds scroll animations --> <script src="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/sds-scroll.min.js"></script> <!-- Step 3 · Interactive engine — only if you use cursor-reactive animations --> <!-- (sds-word-morph, sds-magnetic-pull, sds-shockwave, sds-scatter-return…) --> <script src="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/motion-interactive.min.js"></script>
Quick Start
Copy this complete template into any HTML file — it includes the CSS and both optional JS engines. Delete the script tags you don't need.
<!DOCTYPE html> <html lang="en"> <head> <!-- ① REQUIRED — core CSS (600+ animations, ~39 KB gzip) --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/motion.min.css" /> </head> <body> <!-- your HTML with animation classes --> <h1 class="sds-velvet-drop">Hello World</h1> <button class="sds-btn-magnetic">Click me</button> <input class="sds-input-voltage" placeholder="Type here" /> <span class="sds-loader-orbital"></span> <div class="sds-card-float">Card</div> <!-- ② OPTIONAL — scroll engine (~0.6 KB gzip) --> <!-- Needed only when using data-sds scroll-triggered animations --> <script src="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/sds-scroll.min.js"></script> <!-- ③ OPTIONAL — interactive engine (~2.4 KB gzip) --> <!-- Needed for: sds-word-morph sds-magnetic-pull sds-shockwave --> <!-- sds-scatter-return sds-spring-kerning sds-repulsion-field --> <script src="https://cdn.jsdelivr.net/npm/@salkomdesignstudio/sds-motion-forge/dist/motion-interactive.min.js"></script> </body> </html>
npm import (JS / TS projects)
// ① Required — import CSS once import '@salkomdesignstudio/sds-motion-forge/dist/motion.min.css'; // ② Optional — scroll engine import '@salkomdesignstudio/sds-motion-forge/dist/sds-scroll.min.js'; // ③ Optional — interactive engine (word-morph, magnetic-pull, shockwave…) import '@salkomdesignstudio/sds-motion-forge/dist/motion-interactive.min.js';
sds-scroll.min.js when you use data-sds attributes.
Add motion-interactive.min.js only when you use cursor-reactive
classes (they show a Needs JS badge in the live gallery).
Framework Integration
Import the CSS once in your entry file. No component, no wrapper, no setup beyond that.
import '@salkomdesignstudio/sds-motion-forge/dist/motion.min.css'; // Then use className anywhere: <h1 className="sds-grand-entrance">Hello React</h1>
import '@salkomdesignstudio/sds-motion-forge/dist/motion.min.css'; <!-- template --> <h1 class="sds-zoom-punch">Hello Vue</h1>
@import '@salkomdesignstudio/sds-motion-forge/dist/motion.min.css'; <!-- or add to angular.json → architect.build.options.styles --> <!-- template --> <h1 class="sds-door-open">Hello Angular</h1>
<script> import '@salkomdesignstudio/sds-motion-forge/dist/motion.min.css'; </script> <h1 class="sds-elastic-bounce">Hello Svelte</h1>
<!-- Classes never conflict — all SDS classes are prefixed sds- --> <button class="px-6 py-3 rounded-full bg-blue-600 text-white sds-btn-plasma"> Tailwind + Motion </button> <div class="rounded-xl shadow-lg p-6 sds-card-levitate"> Ambient float </div>
/* Import the source into your PostCSS pipeline */ @import '@salkomdesignstudio/sds-motion-forge/src/motion.css';
Text Animations
Apply to any element containing text. Animations are CSS @keyframes — they fire once on load unless a loop modifier is added.
<h1 class="sds-velvet-drop">Soft drop-in with blur</h1> <p class="sds-liquid-rise">Fluid upward reveal</p> <span class="sds-grand-entrance">Combined combo entrance</span> <span class="sds-refract sds-loop">RGB split loop</span>
All 104 text animation classes
Physics per-character animations
These 9 classes animate each character individually using the --i CSS variable as a stagger index. Each character must be wrapped in a <span class="sds-char" style="--i:N">.
motion-interactive.min.js once and these classes will split text automatically — no manual span markup needed.
<h1 class="sds-drop-settle"> <span class="sds-char" style="--i:0">H</span> <span class="sds-char" style="--i:1">e</span> <span class="sds-char" style="--i:2">l</span> <span class="sds-char" style="--i:3">l</span> <span class="sds-char" style="--i:4">o</span> </h1>
For sds-explode-formation, add per-character displacement variables:
<span class="sds-explode-formation"> <span class="sds-char" style="--i:0;--dx:-60px;--dy:-40px;--dr:-30deg">S</span> <span class="sds-char" style="--i:1;--dx:20px;--dy:-60px;--dr:45deg">D</span> <span class="sds-char" style="--i:2;--dx:70px;--dy:-20px;--dr:-20deg">S</span> </span>
Button Animations
Apply directly to <button> or <a> elements. Effects are ambient — no hover or click required.
<button class="sds-btn-magnetic">Magnetic pulse</button> <button class="sds-btn-aurora">Aurora border</button> <button class="sds-btn-hologram">Hologram flicker</button>
All 100 button classes
Input Animations
State-based effects for <input> and <textarea> elements. Some trigger on :focus, others run continuously as ambient loops.
<input class="sds-input-focus" placeholder="Focus glow" /> <input class="sds-input-voltage" placeholder="Voltage pulse" /> <input class="sds-input-shake" placeholder="Shake on error" /> <input class="sds-input-glow-border" placeholder="Animated glow border" />
Floating label wrapper — sds-input-elevate-wrap requires a specific DOM structure:
<div class="sds-input-elevate-wrap"> <label for="email">Email address</label> <input id="email" type="email" placeholder=" " /> </div>
All 100 input classes
Card Animations
Entry and ambient effects for card and container elements.
<div class="sds-card-float">Ambient float</div> <div class="sds-card-holo">Holographic tilt</div> <div class="sds-card-levitate">Levitate with depth shadow</div>
Eight classes animate direct child elements and require children to be visible:
<div class="sds-card-stagger"> <div>Item 1</div> <div>Item 2</div> <div>Item 3</div> </div> <!-- also: sds-card-cascade, sds-card-slice, sds-card-accordion-in, sds-card-checker, sds-card-fan-spread, sds-card-layers, sds-card-reveal-stack -->
All 100 card classes
Loader Animations
Add a class to an empty <span>. The animation is built entirely with CSS pseudo-elements — no child elements needed for most loaders.
<span class="sds-loader-orbital"></span> <span class="sds-loader-fluid"></span> <span class="sds-loader-arc"></span> <span class="sds-loader-skeleton"></span>
Sixteen loaders animate direct child elements and require children:
<!-- Wave: 5 bars --> <div class="sds-loader-wave"> <span></span><span></span><span></span><span></span><span></span> </div> <!-- Dots: 3 dots --> <div class="sds-loader-dots"> <span></span><span></span><span></span> </div> <!-- Grid: 9 items (3×3) --> <div class="sds-loader-grid"> <span></span><!-- ×9 --> </div> <!-- sds-loader-cascade and sds-loader-signal: variable children -->
All 100 loader classes
Scroll Animations
Scroll animation classes fire immediately when applied. Gate them on viewport entry using one of three methods.
Method 1 — JS scroll engine (recommended, works everywhere)
Load sds-scroll.min.js once. Add data-sds to any animated element. The engine adds sds-play on viewport entry.
<script src="dist/sds-scroll.min.js" defer></script> <!-- Plays once on first entry --> <div data-sds class="sds-scroll-rise">Rises on scroll</div> <!-- Replays every time element re-enters the viewport --> <div data-sds data-sds-repeat class="sds-scroll-spring">Replays on re-entry</div> <!-- Wait 300ms after viewport entry --> <div data-sds data-sds-delay="300" class="sds-scroll-blur">Delayed</div>
| Attribute | Required | Effect |
|---|---|---|
data-sds | Yes | Gates animation until viewport entry. Script adds sds-play when element enters. |
data-sds-repeat | No | Removes sds-play on exit so the animation replays on every re-entry. |
data-sds-delay="N" | No | Wait N milliseconds after entering the viewport before adding sds-play. |
Method 2 — Inline IntersectionObserver (paste-once, zero dependencies)
<div data-sds-scroll class="sds-scroll-rise">Content</div> <script> // Paste once per page — activates all [data-sds-scroll] elements (function(){ var io = new IntersectionObserver(function(es){ es.forEach(function(e){ if(e.isIntersecting){ e.target.classList.add('sds-in'); io.unobserve(e.target); } }); }, { threshold: 0.08, rootMargin: '0px 0px -40px 0px' }); document.querySelectorAll('[data-sds-scroll]').forEach(function(el){ io.observe(el); }); })(); </script>
Method 3 — CSS scroll-driven (Chrome / Edge 115+, zero JS)
<div class="sds-scroll-auto sds-scroll-rise">Zero JS, animates via CSS scroll-driven API</div>
Stagger scroll animations (need children)
<div data-sds class="sds-scroll-stagger-pop"> <div>Item 1</div> <div>Item 2</div> <div>Item 3</div> </div> <!-- also: sds-scroll-stagger-grid, sds-scroll-cascade -->
All 100 scroll classes
Interactive Animations
Load motion-interactive.min.js once. Add a class. Zero JavaScript from you — the engine auto-wires all listeners, physics loops, and cleanup.
<script src="dist/motion-interactive.min.js" defer></script>
| Class | Trigger | Effect | Options |
|---|---|---|---|
sds-word-morph | Auto (interval) | Cycles through data-words with blur/translate | data-words="A,B,C" required |
sds-jelly-hover | mouseenter | Squash + stretch spring | — |
sds-scatter-return | mouseenter / mouseleave | Chars scatter, spring back on leave | — |
sds-shockwave | click | Radial impulse force from click point | — |
sds-spring-kerning | mousemove | Mouse X → letter-spacing −12px…+12px | — |
sds-magnetic-pull | mousemove | Cursor attracts each character span | data-sds-radius="N" (default 110) |
sds-repulsion-field | mousemove | Cursor pushes each character span away | data-sds-radius="N" (default 110) |
<h2 class="sds-word-morph" data-words="Fast,Beautiful,Precise">Fast</h2> <h2 class="sds-magnetic-pull" data-sds-radius="140">Magnetic</h2> <h2 class="sds-shockwave">Click for shockwave</h2> <span class="sds-scatter-return">Hover to scatter</span>
SPA / dynamic content API
After inserting new elements into the DOM, call these to initialize or clean up:
// Scan a container for new interactive elements window.SDSInteractive.scan(containerElement); // Initialize a single element manually window.SDSInteractive.initEl(element); // Remove all listeners / intervals (call before removing element) window.SDSInteractive.cleanup(element);
Modifiers
Stack any modifier on any animation class. All SDS classes are sds- prefixed — they never conflict with your existing CSS.
<span class="sds-velvet-drop sds-slow sds-delay-3">Slow, delayed</span> <span class="sds-refract sds-loop sds-alt">Alternating loop</span> <span class="sds-neon-flare sds-loop sds-xslow sds-pause-hover">Pauses on hover</span>
| Class | Effect |
|---|---|
sds-fast | 0.5× duration (half speed) |
sds-normal | 1× duration (reset to default) |
sds-slow | 1.6× duration |
sds-xslow | 2.2× duration |
sds-delay-1 | 0.1s start delay |
sds-delay-2 | 0.2s start delay |
sds-delay-3 | 0.3s start delay |
sds-delay-4 | 0.4s start delay |
sds-delay-5 | 0.5s start delay |
sds-delay-6 | 0.6s start delay |
sds-delay-8 | 0.8s start delay |
sds-delay-10 | 1.0s start delay |
sds-loop | Infinite iteration |
sds-alt | Alternating direction each loop |
sds-once | Play once, stop in final state |
sds-fill-both | Hold both first and last keyframe |
sds-fill-forward | Hold last keyframe after completion |
sds-pause-hover | Pause animation when element is hovered |
sds-delay-7 and sds-delay-9 do not exist — the sequence steps 6 → 8 → 10 to match 0.6s, 0.8s, 1.0s timing intervals.
Stagger & Children Animations
Some animation classes target direct child elements (> *) rather than the element itself. These produce no visible effect without children.
| Category | Classes requiring child elements |
|---|---|
| Text | sds-kinetic-wave, sds-arc-orbit, sds-signal-wave, sds-stagger-chars |
| Text (physics) | sds-gravity-bounce, sds-drop-settle, sds-wave-cascade, sds-center-burst, sds-explode-formation |
| Cards | sds-card-cascade, sds-card-slice, sds-card-stagger |
| Loaders | sds-loader-cascade, sds-loader-dots, sds-loader-grid, sds-loader-signal, sds-loader-wave |
| Scroll | sds-scroll-stagger-pop, sds-scroll-stagger-grid, sds-scroll-cascade |
For char-split text animations (physics + wave), wrap each character in a span with class="sds-char" and a --i custom property:
<h1 class="sds-kinetic-wave"> <span class="sds-char" style="--i:0">M</span> <span class="sds-char" style="--i:1">o</span> <span class="sds-char" style="--i:2">t</span> <span class="sds-char" style="--i:3">i</span> <span class="sds-char" style="--i:4">o</span> <span class="sds-char" style="--i:5">n</span> </h1>
motion-interactive.min.js — it automatically splits text into sds-char spans for all the above classes. Screen reader accessibility is preserved with aria-label on the parent.
Theming
Override CSS custom properties to match your brand. Changes propagate to every animation that uses that variable.
:root { --sds-primary: #0015d1; /* Brand colour — glows, gradients, text effects */ --sds-primary-dim: rgba(0, 21, 209, 0.08); --sds-primary-glow: rgba(0, 21, 209, 0.30); --sds-accent: #d10028; /* Accent / error colour */ --sds-success: #00c48c; /* Success colour */ --sds-duration: 0.8s; /* Base animation duration */ --sds-easing: cubic-bezier(0.22, 1, 0.36, 1); }
Scope to a specific section — changes only affect animations inside that container:
.hero { --sds-primary: #7c3aed; --sds-primary-glow: rgba(124, 58, 237, 0.35); --sds-duration: 1.2s; }
TypeScript & Autocomplete
The package ships a complete index.d.ts with typed unions for every class. Full autocomplete in VS Code, WebStorm, and any TypeScript-aware editor.
| Exported type | Classes |
|---|---|
SdsTextAnimation | 104 text animation classes |
SdsButtonAnimation | 100 button animation classes |
SdsInputAnimation | 100 input animation classes |
SdsCardAnimation | 100 card animation classes |
SdsLoaderAnimation | 100 loader animation classes |
SdsScrollAnimation | 100 scroll animation classes |
SdsInteractiveAnimation | 7 JS-engine animation classes |
SdsModifier | 18 modifier classes |
SdsScrollGate | sds-play, sds-scroll-auto |
SdsAnimationClass | Union of all the above |
import type { SdsTextAnimation, SdsModifier } from '@salkomdesignstudio/sds-motion-forge'; function anim(base: SdsTextAnimation, ...mods: SdsModifier[]): string { return [base, ...mods].join(' '); } const cls = anim('sds-velvet-drop', 'sds-slow', 'sds-delay-2'); // → 'sds-velvet-drop sds-slow sds-delay-2'
import type { SdsButtonAnimation } from '@salkomdesignstudio/sds-motion-forge'; interface ButtonProps { animation?: SdsButtonAnimation; children: React.ReactNode; } export function Button({ animation = 'sds-btn-magnetic', children }: ButtonProps) { return <button className={animation}>{children}</button>; }
Accessibility
Motion Forge respects prefers-reduced-motion automatically across all three layers. No configuration required.
| Layer | Behavior under prefers-reduced-motion: reduce |
|---|---|
| CSS core | All animations collapse to 0.01ms — elements immediately reach their final state |
| Scroll gate | sds-play added immediately to all [data-sds] elements — no observer delay |
| Interactive engine | Transitions and physics loops skipped; static final state shown |
sds-word-morph | Still cycles words but skips blur/translate — instant word swap |
Screen reader support: The interactive engine's text splitter sets aria-label on the parent element to preserve the full readable text, then marks each <span class="sds-char"> with aria-hidden="true".
Browser Support
The core library targets all modern browsers. IE 11 is not supported.
| Feature | Support |
|---|---|
Core CSS animations (@keyframes) | Chrome 80+, Firefox 78+, Safari 14+, Edge 80+ |
clip-path animations | All targets — -webkit- prefixed via autoprefixer |
backdrop-filter | Chrome 76+, Firefox 103+, Safari 9+ (with -webkit-) |
Scroll gate data-sds | IntersectionObserver — all modern browsers |
CSS scroll-driven (sds-scroll-auto) | Chrome / Edge 115+ only — graceful fallback (element visible, animation skipped) |
| Interactive engine | WeakMap + MutationObserver — all modern browsers |
| IE 11 | Not supported |
Contributing
SDS Motion Forge is MIT-licensed and open to contributions. Here is how to get involved.
Reporting a bug
Open an issue and include: the affected class name(s), what you expected, what actually happened, your browser and version, and a minimal reproduction (HTML snippet or CodePen).
Requesting an animation
Open an issue with the label animation-request and describe the motion effect with a reference (gif, video, or live demo link).
Local development setup
git clone https://github.com/salkomdesignstudio/SDS-Motion-Forge.git cd SDS-Motion-Forge npm install
Available scripts
| Command | What it does |
|---|---|
npm run build | Build CSS + JS engines for production |
npm run build:css | Build CSS only (PostCSS → cleancss) |
npm run build:js | Build JS engines only (terser + source maps) |
npm run build:dev | Build CSS with source maps for development |
npm run build:watch | Watch CSS and rebuild on save |
npm run build:verify | Run the 8-stage pre-publish verification gate |
npm run release:check | Full check: build + verify + publint + pack |
Adding a new animation
- All CSS animations live in
src/motion.css - Class naming:
sds-[descriptive-name]— lowercase, hyphenated - Keyframe naming:
sds-camelCaseNamematching the class (e.g..sds-velvet-drop→@keyframes sds-velvetDrop) - Use
--sds-durationand--sds-easingin the animation shorthand - Add
will-change: transform, opacityfor GPU-composited animations - The global
prefers-reduced-motionblock handles reduced motion automatically - Use
animation-fill-mode: bothso the element holds its pre-animation state
After adding a CSS animation: add the class to REQUIRED_CLASSES and the @keyframes name to REQUIRED_KEYFRAMES in verify-build.js, and add the TypeScript type to the appropriate union in index.d.ts.
Submitting a PR
- Fork the repo and create a branch:
git checkout -b feat/my-animation - Make changes in
src/— never edit files indist/directly - Run
npm run buildto regenerate dist artifacts - Run
npm run build:verify— all 8 checks must pass - Open a PR against
mainwith a description of what the animation does
Changelog
A detailed changelog is maintained in the repository.
Current: v5.0.0
- 245 new animation effects — every category now ships exactly 100 effects (Text: 104, Buttons: 100, Inputs: 100, Cards: 100, Loaders: 100, Scroll: 100)
- v5 motion token scale —
--sds-duration-*and--sds-ease-*design tokens (W3C format) - Lossless category split — import only the categories you use
- 600+ animation classes typed in
index.d.ts - 100%
prefers-reduced-motioncoverage including child-driven animations - Interactive engine: cursor-reactive physics (magnetic, shockwave, scatter, repulsion)
- New package exports for React, Web Components, and Tailwind
Previous: v4.0.3
- Fixed critical
rootMargincrash insds-scroll.min.js - Completed TypeScript types — all 177 animation classes typed
- Fixed gallery accessibility (keyboard navigation, aria labels)
- JS engine sizes: sds-scroll −20%, motion-interactive −25%