System · Harbor

Animations

Fluent motion ramp, micro-only, reduced motion.

animations.md

Meridian — animations.md

Motion contract. Ranking: influences.md.
Primary absorb: Fluent 2 Motion (functional, natural, short) + Polaris “juicy but predictable” press + Meridian ban on scroll choreography.

1. Laws

  1. Motion is functional — identify next step, inform change, confirm action (Fluent).
  2. Micro only on application — no page-length choreography, scroll-jacking, or SVG grain.
  3. Hover quiet, press felt (Polaris).
  4. Top-level navigation fades — don’t slide entire app shells (Fluent top-level).
  5. Honor prefers-reduced-motion — WCAG / Fluent accessible motion.
  6. Constraint to focused element — no peripheral thrash.
  7. Cinema stages are still — optional enter fade ≤200ms; product is the subject.

2. Duration ramp (Fluent-shaped)

Aim fast and smooth; larger travel ⇒ slightly longer (Fluent duration).

TokenmsUse
motion-instant0–50Keyboard selection, toggles
motion-fast120–150Hover bg, focus ring settle
motion-normal180–220Popover enter/exit, tabs
motion-gentle240–280Dialog / sheet (rare)
motion-slow≥300Avoid in app chrome

Encoded preference in CSS: short transition-colors / transition-opacity; no spring theater on lists.

3. Easing

CurveWhen
Ease-outEnters / opens (start fast)
Ease-inExits
Ease-in-outMoves within layout
LinearSpinners / continuous rotate only

Fluent warns: linear feels unnatural for UI travel — reserve for loops (Motion).

Suggested cubic (Fluent easy-ease family): cubic-bezier(0.33, 0, 0.67, 1).

4. Allowed patterns

PatternFluent nameMeridian
Fade in/outEnter/Exit, Top levelDialogs, toasts, stage enter
Soft elevation changeElevationButton press depth / shadow token swap
Container resizeContainer transformResponsive reflow — prefer CSS, not JS film
Local press— (Polaris juicy)active:scale-[0.98] or bg flash ≤150ms
SpinnerCircleNotch animate-spin on agent “working”

5. Banned patterns

BanWhy
Scroll-linked storytellingCinema law / vestibular risk
Parallax hero stacksMarketing anti-pattern here
Staggered list of 50+ rowsFluent: stagger only when short
Layout thrash on hoverPredictability
Autoplaying decorative LottieNoise
mix-blend on fixed chromePaint jank (check:design)
content-visibility: auto size placeholdersScroll jump ban

6. Choreography rules

From Fluent choreography:

  • Prefer short stagger offsets; end related items together when possible
  • Emphasize important elements with slightly longer duration — not larger travel distance across the viewport
  • Large groups: sync, don’t stagger into multi-second intros

Meridian app default: no list stagger. Marketing may fade a single stage subject.

7. Reduced motion

globals.css already collapses transitions when prefers-reduced-motion: reduce.

Agent rules:

  • Provide non-motion equivalent (state text, ARIA live for agent updates)
  • Spinners may become static “Working…”
  • Never convey meaning by motion alone

8. Surface-specific motion

data-surfaceBudget
application / desktopInstant–fast only
mobileSystem-like; respect reduce
marketingStill cinema; optional opacity enter

9. Component recipes

ComponentMotion
ButtonColor 150ms; optional press scale
TabsUnderline/bg instant–fast
DialogFade + subtle zoom ≤200ms
SheetShort ease-out translate or fade (prefer fade on large)
Agent activity badgeNo bounce; color/token change
Site header theater dockColor 200ms only

10. Implementation map

ConcernWhere
Reduced motionapp/globals.css @media (prefers-reduced-motion)
Cinema bancinematicLaws.motion = "micro-only"
Drift lintscroll-choreography, mix-blend, content-visibility

11. Agent checklist

  • Purpose stated (feedback / enter / exit)
  • Duration ≤ 220ms for chrome
  • Reduced-motion path
  • No scroll choreography
  • Meaning not motion-only

Sources

Raw