3cosystem
Motion System v1.0
brand.3cosystem.net/motion

Movement with meaning

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.

.fadeInUpOpacity 0 + translateY(16px) → visible. Primary entrance animation.
Card appeared
@keyframes fadeInUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } } .fade-in-up { animation: fadeInUp 300ms cubic-bezier(0.0, 0.0, 0.2, 1) both; }
.fadeOutDownVisible → opacity 0 + translateY(8px). Standard exit.
Notification dismissed
@keyframes fadeOutDown { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(8px); } } .fade-out-down { animation: fadeOutDown 200ms cubic-bezier(0.4, 0.0, 1.0, 1.0) both; }
.scaleInOpacity 0 + scale(0.96) → visible. Used for modals and popovers.
Modal opened
@keyframes scaleIn { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } } .scale-in { animation: scaleIn 300ms cubic-bezier(0.34, 1.56, 0.64, 1.0) both; }
.meshPulseSubtle 6s infinite breathing. Background ambience for active surfaces.
@keyframes meshPulse { 0%, 100% { opacity: 0.6; transform: scale(1); } 50% { opacity: 1; transform: scale(1.03); } } .mesh-pulse { animation: meshPulse 6s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
Stagger Pattern

Sequential entrance with a cap

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:
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } /* Exception: opacity fades are safe — they don't trigger vestibular issues */ .fade-transition { transition: opacity 150ms ease !important; } }
Never skip animation entirely
State changes still need to communicate. Instant jumps are confusing. Use opacity fades as a safe fallback.
Test in both modes
Use the OS setting or the Chrome DevTools emulation. Every demo on this page has been verified in reduced-motion mode.
Opacity is always safe
Opacity transitions do not cause vestibular issues. They are the only motion type allowed in reduced mode.
Read the preference in JavaScript
For complex animations, check window.matchMedia('(prefers-reduced-motion: reduce)').matches before starting.
// Check preference before starting complex animations function useReducedMotion(): boolean { const [prefersReduced, setPrefersReduced] = useState(() => window.matchMedia("(prefers-reduced-motion: reduce)").matches ); useEffect(() => { const mq = window.matchMedia("(prefers-reduced-motion: reduce)"); const handler = (e: MediaQueryListEvent) => setPrefersReduced(e.matches); mq.addEventListener("change", handler); return () => mq.removeEventListener("change", handler); }, []); return prefersReduced; } // Usage: function HeroAnimation() { const reduced = useReducedMotion(); // Skip physics-based animation; use opacity fade only return ( <div style={{ animation: reduced ? "none" : "fadeInUp 500ms cubic-bezier(0,0,0.2,1) both", opacity: 1, // always visible — never hidden }}> {children} </div> ); }
Surface Motion

Personality within the system

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; }
3cosystem Motion & Animation
Powered by R3SET · R3SET Enterprises, Delaware B-Corp
brand.3cosystem.net/motion