Motion in the 3cosystem is purposeful, not decorative. Every animation communicates state, guides attention, or provides feedback — never for spectacle alone.
4 easing curves5 duration tokens4 standard keyframesReduced motion first
Easing Curves
Four curves, one language
Every animated element picks one of these four curves. The curve encodes the intent — entering, leaving, changing state, or delighting the user.
entranceease-out
cubic-bezier(0.0, 0.0, 0.2, 1.0)
Elements entering the screen
exitease-in
cubic-bezier(0.4, 0.0, 1.0, 1.0)
Elements leaving the screen
standardease-in-out
cubic-bezier(0.4, 0.0, 0.2, 1.0)
State changes, interactive feedback
springovershoot
cubic-bezier(0.34, 1.56, 0.64, 1.0)
Playful emphasis, bouncy confirmations
Duration Scale
Time as a design token
Five named duration tokens cover every animation scenario from instant feedback to deliberate hero moments. Never hardcode milliseconds — reference these tokens so motion can be tuned globally.
--dur-feedbackButton press, checkbox toggle
100ms
--dur-transitionColor changes, hover states
200ms
--dur-entranceElements animating in
300ms
--dur-complexMulti-step, page transitions
500ms
--dur-slowLarge hero animations
800ms
Token
Value
Usage
--dur-feedback
100ms
Button press, checkbox toggle
--dur-transition
200ms
Color changes, hover states
--dur-entrance
300ms
Elements animating in
--dur-complex
500ms
Multi-step, page transitions
--dur-slow
800ms
Large hero animations
Standard Animations
Four keyframe definitions
These four named animations form the core library. All surfaces inherit them from the global token layer — never redefine locally.
Lists and grids animate in with a 50ms stagger per item. For lists longer than 5 items, the delay caps at 200ms to avoid making users wait too long before seeing the full list.
8 items — stagger delay capped at 200ms
List item 1
delay: 0ms
List item 2
delay: 50ms
List item 3
delay: 100ms
List item 4
delay: 150ms
List item 5
delay: 200ms (capped)
List item 6
delay: 200ms (capped)
List item 7
delay: 200ms (capped)
List item 8
delay: 200ms (capped)
⚠
Cap at 200ms for lists with more than 5 items
Without a cap, a 20-item list would make users wait 1000ms before seeing the last row. Always clamp: Math.min(index * 50, 200)
// Stagger pattern — cap at 200ms for long lists
const STAGGER_STEP = 50; // ms per item
const STAGGER_CAP = 200; // never exceed this
function StaggerList({ items }) {
return (
<ul>
{items.map((item, i) => (
<li
key={item.id}
style={{
animation: `fadeInUp 300ms cubic-bezier(0,0,0.2,1) ${Math.min(i * STAGGER_STEP, STAGGER_CAP)}ms both`,
}}
>
{item.label}
</li>
))}
</ul>
);
}
Scroll Policy
Scroll behavior rules
The 3cosystem has strict rules about scroll behavior to preserve native UX and avoid fighting the browser's scroll engine.
✕ Don't
Do not set scroll-behavior: smooth on the html element. It disrupts natural scrolling, breaks keyboard navigation timing, and conflicts with reduced-motion preferences.
/* ✕ Never do this */
html {
scroll-behavior: smooth; /* breaks native UX */
}
✓ Do
Use scrollIntoView({ behavior: "smooth" }) for programmatic anchor navigation only. This gives you control without polluting the global scroll context.
// ✓ Programmatic smooth scroll only
function scrollTo(id: string) {
const el = document.getElementById(id);
if (el) el.scrollIntoView({ behavior: "smooth" });
}
✓ Do
Always set scroll-padding-top on the root to account for the 48px fixed header. This ensures anchor targets aren't hidden behind the nav.
/* ✓ Account for 48px fixed header */
html {
scroll-padding-top: 80px; /* header (48px) + breathing room */
}
/* And on each section for scrollIntoView() fallback */
section {
scroll-margin-top: 80px;
}
Reduced Motion
Accessibility first
Motion sensitivity is real. Every animation in the system has a graceful fallback — never skip animation entirely, but reduce it to the minimum needed to communicate state.
Simulate reduced motion on this page:
The global reduced-motion block lives in tokens.css and is automatically applied system-wide:
Each surface inherits the base motion tokens but adjusts easing preference and duration range to match its personality. The rules stay consistent; the feel shifts.
M3ETEnergetic
Favors spring easing for confirmations and entrance animations. Shorter durations keep the UI feeling snappy and social.
Easing preferencespring, entrance
Duration range100–300ms
Stagger cap150ms
Signature motionScale bounce on connection formed
R3CHARGECalm
Uses slower durations and standard easing. Motion should feel restorative, never jarring. meshPulse for ambient backgrounds.
Easing preferencestandard, entrance
Duration range300–800ms
Stagger cap200ms
Signature motionLong fade on session completion
N3TWORKPrecise
Clean standard easing throughout. Exit animations are crisp. CRM data tables animate rows in with minimal stagger.
Easing preferencestandard, exit
Duration range100–300ms
Stagger cap100ms
Signature motionSlide-in for pipeline stage changes
SP3AK EASYReflective
Slow, deliberate motion supports storytelling. Long entrances for narrative sections. Reduced stagger density.
Easing preferenceentrance, standard
Duration range300–800ms
Stagger cap200ms
Signature motionParagraph fade-in during story reveal
How to add surface-specific motion overrides
/* In each surface's theme CSS */
/* M3ET — energetic, spring-forward */
[data-surface="m3et"] {
--dur-entrance: 200ms; /* faster than base */
--ease-primary: cubic-bezier(0.34, 1.56, 0.64, 1.0); /* spring */
}
/* R3CHARGE — calm, deliberate */
[data-surface="r3charge"] {
--dur-entrance: 400ms; /* slower than base */
--ease-primary: cubic-bezier(0.4, 0.0, 0.2, 1.0); /* standard */
}
/* Usage in components — never hardcode per-surface values */
.card-enter {
animation: fadeInUp var(--dur-entrance) var(--ease-primary) both;
}