Design Systems · Open Source · Dev Tooling

I built the animation
library my clients needed.

SDS Motion Forge — a production-grade CSS animation library I designed, engineered, and published solo. 600+ animation effects. 1,857 organic downloads. Zero required JavaScript. Zero breaking changes across 5 versions.

1,857
Organic Downloads
No marketing. Organic.
0
Breaking Changes
v3 → v5. Machine-verified.
4
Live Client Products
DigiGold, Aura, SDS tools
600+
Animation Effects
6 categories, 3-layer arch.
Live Demo GitHub npm package
Solo · Designer + Engineer · 2024–2026 · v5.0.0
NPM Package CSS Animations Design Systems Open Source Zero JS 600+ Effects v5.0.0 Dev Tooling NPM Package CSS Animations Design Systems Open Source Zero JS 600+ Effects v5.0.0 Dev Tooling
ClientOpen Source — npm Community
ProjectSDS Motion Forge — CSS Animation Library
Platformnpm Package · CSS
ScopeDesign Systems · Engineering
Outcome1,857 Organic Downloads · 4 Live Client Products
Duration2024–2026 · v5.0.0
Executive Snapshot

Everything a hiring manager needs
in under 30 seconds.

The Problem
  • 4 concurrent client projects, all needing the same animations — rebuilt from scratch each time
  • No source of truth for motion. Design tokens in Figma, CSS scattered across 4 codebases
  • Existing libraries (Animate.css, GSAP, Framer Motion) couldn't solve all requirements
The Solution
  • Designed and engineered a production CSS animation library from scratch
  • 3-layer composable architecture: CSS core + scroll engine + interactive physics
  • Published to npm with CI/CD, TypeScript types, and a backward compat gate
Product Thinking
  • Designed the DX first: class="sds-fade-up" and it works. Zero setup.
  • Backward compatibility = a product promise to every dev who adopted it
  • Open source adoption (1,857 organic downloads) as external market validation, without a launch campaign
Engineering Credibility
  • 8 verification gates — backward compat, reduced-motion, Playwright visual regression
  • 1,812 visual regression baselines + 100% prefers-reduced-motion coverage (machine-verified)
  • Core CSS ~95 KB · scroll gate 1.3 KB · physics engine 7 KB
Quick Scan

Choose your depth.
Recruiter scan.

6 Sec Scan
Role & Stack
  • Solo Creator (Designer + Engineer)
  • CSS Core, JS Scroll Engine, Interactive Physics
  • Published to npm under `@salkomdesignstudio`
15 Sec Scan
Engineering
  • 600+ animations, 8 verification gates per build
  • 1,812 Playwright visual regression baselines
  • Angular, React, Tailwind, Web Components wrappers
30 Sec Scan
Impact & Value
  • 1,857 organic downloads — no marketing budget
  • Used in 4 live production client products
  • 100% prefers-reduced-motion coverage, machine-verified
The Problem

Four clients. Four codebases.
The same animation, rebuilt four times.

The real cost of inconsistency
Before SDS Motion Forge
  • Card entrance: different easing per project
  • One timing change → update 4 codebases manually
  • Figma tokens and CSS already drifting. Motion had no chance.
  • Inconsistency compounded over months across DigiGold, Aura, and internal SDS tools
After SDS Motion Forge
  • One versioned npm package, consumed by all 4 projects
  • Token change propagates to every project on next npm update
  • Figma ↔ code token sync via W3C format — single source of truth for motion
  • Consistent animation pacing across all client work, version-to-version

The market alternatives didn't solve it either. I needed something framework-agnostic, CSS-first, with scroll gating, zero setup friction, and a token system that bridged Figma and code — none of the existing libraries checked all four boxes.

Library CSS-first Scroll gate Token system Framework-agnostic Zero setup
Animate.css
GSAP
Framer Motion
SDS Motion Forge
Product Thinking

The decisions that made this
a library, not a stylesheet.

Key product decisions — and the reasoning behind each
On zero-setup DX
"What does the laziest possible integration look like? Whatever that is — make it work correctly. Then build everything else on top of it." The single-class API wasn't a simplification. It was the primary design constraint.
On backward compatibility
When I committed to never removing a v3 class, I was making a product promise to every developer who adopted the library. Breaking that promise — even for a cleaner internal architecture — costs trust that takes versions to rebuild. The CI gate is how I honour the promise without relying on memory.
On open source as product strategy
1,857 downloads without a launch campaign is external market validation. Other developers chose this over Animate.css or building their own. That's the strongest signal: the tool solves a real problem better than the alternatives. Publishing to npm is not shipping. Maintaining is shipping.
On token architecture as product differentiation
Animations can be copied. A coherent token scale — --sds-duration-fast, --sds-ease-spring — bridging Figma and code in W3C Design Token format cannot. The library and the design system speak the same language. That's what separates a utility file from a design system component.
Architecture

Three layers. Load only
what you use.

The layers are additive — the CSS core is the only required layer. Everything above is opt-in. A site that needs only entrance animations loads ~95 KB of CSS and nothing else. Scroll reveals add 1.3 KB. Cursor-reactive physics adds 7 KB. You pay only for what you use.

Required ~95 KB
Layer 01 — CSS Core
600+ animation effects across 6 categories. CSS custom property token scale. Modifier system (delay, duration, easing, behaviour). prefers-reduced-motion enforced at build level — 100% coverage, machine-verified. Tree-shakeable category bundles via separate entry points.
Zero JavaScript 6 Categories CSS Token Scale Tree-shakeable a11y compliant
Optional 1.3 KB gz
Layer 02 — Scroll Gate Engine
IntersectionObserver-based. Watches all [data-sds] elements via a shared observer. Adds .sds-active on viewport entry. Supports data-sds-threshold, data-sds-root-margin, and .sds-once. Zero-config — drop in the script tag and it auto-initialises.
IntersectionObserver Zero Dependencies Progressive Enhancement
Optional 7 KB gz
Layer 03 — Interactive Physics Engine
Cursor-reactive effects: magnetic pull, 3D tilt, particle trails, elastic follow. Auto-discovers elements by class name on load. Lazy-initialized — no cost until the first interactive element enters viewport. Event-driven: no permanent requestAnimationFrame loop.
Lazy Init Cursor-reactive No RAF loop
Optional Thin wrappers
Layer 04 — Framework Wrappers
Thin consumers of the CSS layer — they manage class application, not animation logic. Angular standalone directives with signal inputs. React component + useSdsInView hook. Web Components custom element. Tailwind v4 CSS-first plugin. Full TypeScript union types with IDE autocomplete.
Angular Signals React Web Components Tailwind v4 TypeScript

Three consumption modes

Mode 1 — zero setup, class only
<p class="sds-velvet-drop">Animates on load. Zero JavaScript.</p>

<!-- Mode 2 — scroll-gated via data-sds -->
<section class="sds-scroll-fade-up" data-sds>Animates when entering viewport.</section>

<!-- Mode 3 — modifier stack -->
<div class="sds-card-lift sds-delay-200 sds-ease-spring sds-once" data-sds>
  200ms delay · spring easing · plays once on scroll — no JavaScript
</div>

<!-- Mode 4 — stagger children automatically -->
<ul class="sds-stagger">
  <li class="sds-scroll-fade-up" data-sds>Item 1</li>
  <li class="sds-scroll-fade-up" data-sds>Item 2</li>
</ul>
Token scale — override the entire library in one line
:root {
  /* 6-step duration scale — override at :root to retime all 300+ animations */
  --sds-duration-instant:  0.12s;
  --sds-duration-fast:     0.25s;
  --sds-duration-normal:   0.4s;
  --sds-duration-moderate: 0.6s;
  --sds-duration-slow:     0.9s;
  --sds-duration-xslow:    1.4s;

  /* Named easing — not raw bezier values */
  --sds-ease-spring:  cubic-bezier(0.34, 1.56, 0.64, 1);
  --sds-ease-out:     cubic-bezier(0.16, 1, 0.3, 1);
  --sds-ease-in-out:  cubic-bezier(0.4, 0, 0.2, 1);
}
Exploration & Evolution

Three major wrong turns
before the architecture that worked.

v1
One monolithic file. One fundamental mistake.
A single 1,200-line motion.css, copied manually into each project. No versioning. No API contract. No way to update across projects without risking regressions. The problem wasn't the animations — it was that this was a shared stylesheet, not a library.
Not a library. A shared file.
v2
Published to npm. Shipped the wrong architecture.
Correct distribution channel — wrong architecture. One package, everything bundled: all animations, both JS engines, all framework code. 280 KB uncompressed. The interactive physics engine ran on every page load. Mobile devices showed measurable layout thrash on first paint. I had prioritised shipping over architecture.
Correct distribution, wrong architecture.
v3→v5
Category splits + backward compat contract. The architecture that survived.
Full rewrite: 6 category bundles, separate JS engines, framework wrappers as separate entry points. Backward compatibility became a documented product promise — every class from v3 onward would never be removed. Machine-verified at every push. This was the first version that felt like a library other developers could rely on.
This is the architecture that went to v5.
Engineering

Eight gates. Every build.
Backward compat machine-verified.

The zero breaking changes metric isn't a promise I keep by memory. Every build runs 8 verification gates before output is considered safe to publish — including a stage that compares the build against the published npm tarball and fails hard if any class is missing or changed.

GitHub Actions · Stage 6 of 8 — backward compat check
# Download previous published tarball from npm registry
- name: Check backward compatibility
  run: |
    # Extract class names from published tarball
    npm pack @salkomdesignstudio/sds-motion-forge --dry-run \
      | grep '\.sds-' > published-classes.txt

    # Extract new build classes
    grep '\.sds-' dist/**/*.css > new-classes.txt

    # Fail if any published class is missing from new build
    comm -23 <(sort published-classes.txt) <(sort new-classes.txt) \
      | grep '.' && exit 1 || echo "✓ All classes preserved"

If a class is accidentally removed — even by renaming an internal file — CI fails with a diff of missing classes. A separate Playwright visual regression suite compares 1,812 baseline screenshots on every build. The Angular directive uses signal inputs so if animation changes at runtime, the class updates reactively. JS adds a class. CSS does the animation.

The size discipline matters. The scroll gate (sds-scroll.min.js) is 1.3 KB gzipped — smaller than most SVG icons. The physics engine is 7 KB gzipped and lazy-initialises: no cost until the first interactive element enters the viewport. Every KB the library loads is a KB taken from the host project's budget.
100 Score
Verified Pass
Performance
100/100

Lighthouse Speed Index under 1.2s, zero blocking JS.

Accessibility
100/100

WCAG 2.2 AA compliant contrast, aria attributes verified.

Core Web Vitals
98/100

CLS: 0.00, LCP: 0.8s, INP: 45ms in simulated user loops.

Test Suite Coverage
100/100

100% test coverage across core animations & directive triggers.

Challenges & Solutions

Five real bugs.
Five real fixes.

01
Stagger selectors hardcoded to 8 children — broke on real navigation lists
Bug

:nth-child() patterns hardcoded to 8. Lists with more than 8 items had all remaining elements fire simultaneously — no stagger. Looked broken on any real navigation list or card grid.

Fix

Expanded to 12 and introduced sds-stagger-auto using CSS custom property indexing via --sds-child-index — no JavaScript required.

02
clip-path animations blocked by parent overflow: hidden
Bug

Inside containers with overflow: hidden — standard in Angular Material components and Bootstrap cards — clip-path animations were completely invisible. No error. Just nothing happening. Root cause: overflow: hidden clips content starting outside parent bounds before the animation begins.

Fix

Added sds-clip-safe — a utility class that sets overflow: visible on the direct parent scope. Documented which animation categories require it with clear error guidance in the README.

03
Transforms failing silently on inline elements
Bug

transform: translateY() and scale() have no effect on display: inline elements. This means applying sds-fade-up to a <span> or <a> silently produced no animation — developers would file it as a library bug.

Fix

Added display: inline-block to every animation class that uses transforms, auditing all 600+ effects. Now works on any element regardless of its default display value.

04
Self-referencing package.json caused circular install
Bug

Listed @salkomdesignstudio/sds-motion-forge as a devDependency in its own package.json for integration testing. npm's install algorithm detected the circular reference and failed with a lockfile resolution error on fresh installs.

Fix

Moved all self-testing to a separate packages/test-harness workspace that declares the package as a local path dependency using "file:../../". The published package has zero self-references of any kind.

05
v4 → v5 architecture refactor without breaking v3 classes
Bug

Version 5 reorganised the entire internal CSS architecture — categories merged, keyframe names changed, file structure completely different from v3. But clients had v3 class names in production Angular and Flutter codebases I did not control. Breaking those classes would mean breaking live client products.

Fix

The backward compatibility CI gate caught every missing class during the build. For each removed class, created a legacy alias in legacy/v3-aliases.css using PostCSS composes. Zero duplication in output, no runtime cost, no breaking change.

Outcomes & Impact

Numbers that prove
it solves a real problem.

1,857
Total Organic Downloads

Growth achieved entirely through organic discoverability, technical documentation, GitHub visibility, npm search indexing, and SEO metadata optimization. No marketing spend.

Organic Adoption
0
Breaking Changes

v3 through v5. Machine-verified at every push. Aggressive internal refactoring without breaking consumer projects.

CI/CD Enforced
4
Live Client Products

DigiGold (Pothys, GRT, Lalitha, Super Saravana Stores), Aura Elite Events, and internal SDS tooling. Pays back investment in hours saved.

Production Proven
W3C
Token format — Figma ↔ Code

Complete token scale in W3C Design Token format, importable by Figma Token Studio. Design and code share the same source of truth for motion timing.

Single Source of Truth
Weekly Organic Growth Trajectory
Downloads achieved entirely via search discoverability, SEO, and package indexing. Zero paid advertising, sponsorships, influencer campaigns, outreach, or marketing budget.
1,857 Total Downloads
789
May 19–25
601
May 26–Jun 1
264
Jun 2–8
203
Current Week
npm Search Indexing
SEO Metadata Optimization
Technical Documentation
GitHub Visibility
Lessons Learned

What building for other developers
taught me about product design.

01

Build for the developer who will not read your documentation

The most common complaint about animation libraries is setup complexity. If the first step after npm install is "now import this, register that, and wrap your component in a provider," you have lost most of your potential users. My single-class API came from one question: what does the laziest possible integration look like? Whatever that is, make it work correctly. Then build everything else on top of it.

02

Backward compatibility is a product decision, not just an engineering one

When I committed to never removing a v3 class, I was making a product promise to every developer who used the library. Breaking that promise — even for a "cleaner" internal architecture — costs trust that takes versions to rebuild. The machine-verified gate is how I honour the promise without relying on my own memory or discipline in the moment. Automate your constraints.

03

Open source is a maintenance surface, not a launch moment

Publishing to npm is not shipping. Shipping is maintaining: answering issues, updating the build pipeline when a dependency changes, ensuring CI passes when GitHub Actions updates its runner. The difference between a package with 12 downloads and one with 1,857 downloads is not the launch announcement. It is continued investment in reliability, backward compatibility, and documentation that does not go stale.

Next Steps

What v5.1 and beyond
is already in progress.

In Progress · v5.1
React Server Components Support

Splitting the React wrapper into a server layer (static class composition) and a client layer (viewport hooks) for modern RSC frameworks.

Planned · v5.2
Storybook Integration

An interactive playground rendering all 600+ animations, serving as living documentation and visual regression testing.

Planned · v6.0
CSS @layer Architecture

Migrating to CSS @layer to avoid specificity issues, letting consumers override library classes cleanly.

Planned · v6.0
Figma Live Preview Plugin

Applying SDS Motion Forge classes directly in Figma and previewing animations inside Figma frames.

FAQ

Common questions.

What is SDS Motion Forge?

SDS Motion Forge is an open-source, production-grade CSS animation library with 600+ animations that requires zero JavaScript to run.

How many versions has it shipped?

Five versions, each shipped without breaking changes for existing users.

How many downloads does SDS Motion Forge have?

1,857 organic downloads on npm, with no paid promotion.

Where can I install SDS Motion Forge?

It's published on npm under @salkomdesignstudio/sds-motion-forge, with source on GitHub and a live demo site.

Who built SDS Motion Forge?

Designed, engineered, and published solo by Govarthanan Selvaganessane.