v3 · Gravity · Motion

Light moves. Elements don’t.

The interaction budget — enter, exit, press, spring — is inherited from the foundation unchanged. What v3 adds is atmosphere: three sanctioned light sweeps plus a handful of life signals that keep the scene breathing between interactions. The two systems don’t compete — sweeps run on the stage, the budget runs on what the user touches.

00

The foundation budget

Unchanged from the foundation. Four categories, one principle: premium motion is restraint plus precision, under 300ms, GPU-compositable properties only. Every button on this page obeys it — hover and press them.

Enter — new content arrives
--ox-enter220ms
--ox-easecubic-bezier(0.23,1,0.32,1)
fromopacity 0, translateY(12px) scale(0.95)
Exit — old content departs
--ox-exit130ms
ratio1.7× faster than enter
toopacity 0, translateY(-8px) scale(0.97)
Press — tactile confirmation
--ox-press80ms
machinedtranslateY(1px), brightness(0.94)
guard@media (hover:hover) and (pointer:fine)
Spring — physics delight
--ox-ease-springcubic-bezier(0.34,1.56,0.64,1)
machined CTA180ms, hover −2px / 1.07 brightness
neverdocument / case / reading contexts
Additional — inherited unchanged
--ox-ease-drawercubic-bezier(0.32,0.72,0,1)
--ox-ease-in-outcubic-bezier(0.77,0,0.175,1)
Asymmetric enter / exit — live 220ms in · 130ms out
Art. 28 RGPD — subcontratante de dados
01

The three sanctioned sweeps

GRAMMAR-v3 §6. Light moves; elements don’t. Nothing here translates, scales, or rotates a layout element — every sweep is a background-position, transform-on-a-pseudo-element, or opacity pass across something that stays put.

Phrase shineox-shine · 7s
A third way. background-position 100% → 0%, 250% bg-size, background-clip: text
Horizon breatheox-horizon · 10s

opacity 0.75 → 1 · line + soft under-glow

Frame sheen — the product theater recipeox-sheen · 12s
app.outlex.ai
Does our DPA cover Art. 28?
LexiSubstantially compliant — 2 gaps found.
A specular bar crosses the glass every 12s: translateX(-140% → 160%) skewX(-18deg) on a pseudo-element, opacity of the surface never changes. Pairs with the 8° tilt / hover-flattens-to-3° / floor-shadow recipe — GRAMMAR-v3 §5.

One sweep dose per element, per GRAMMAR-v3 §1: a page section gets one phrase shine, a theater frame gets one sheen loop. Never stack sweeps on the same object.

02

Life signals

Ambient, not sweeping — these communicate state inside product surfaces, and the theater scene itself. A dead artifact (a panel with no teal pulse, no caret, no breathing scene) is a brand violation, not just a missed detail.

Ambient breatheox-ambient · 11s
always dark opacity 1→0.65 · scale 1→1.07 · blur 110px
Mark breatheox-breathe · 4s
Teal pulse — two cyclesox-teal-pulse
Lexi · thinking 1.2s — inline product dot
v3 · Gravity 1.6s — hero / nav badge
Caretox-caret · 1.1s steps(1)
The DPA is substantially compliant
03

The hero-rise entrance

The only sanctioned entrance for marketing hero content. 26px rise + fade, staggered 120ms per element, 0.9s cubic-bezier(0.16,1,0.3,1). Page-local — not a token-file keyframe, so copy the recipe rather than importing a name.

Replayhero-rise · 900ms · +120ms stagger
The Outlex Standard. The foundation defined the tokens. v3 defines the cinematography. LiveBook a demo
.hero > * { animation: hero-rise 0.9s cubic-bezier(0.16,1,0.3,1) both; }
.hero > *:nth-child(1) { animation-delay: 0ms;   }
.hero > *:nth-child(2) { animation-delay: 120ms; }
.hero > *:nth-child(3) { animation-delay: 240ms; }
@keyframes hero-rise {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: translateY(0);    }
}
/* No parallax gimmicks, no scroll-jacking — GRAMMAR-v3 §6. */
04

Canonical keyframe names

These seven live in tokens-v3-gravity.css. The names are API — do not rename, do not redeclare locally. Everything on this page references them by name.

KeyframeCycleMotionUsed by
ox-shine7s --ox-shine-cyclebackground-position 100%→0%.phrase (sage/ink italic phrase)
ox-sheen12s --ox-sheen-cycletranslateX(-140%→160%) skewX(-18deg).frame__sheen span (product theater glass)
ox-ambient11s --ox-ambient-cycleopacity 1→.65, scale 1→1.07.scene__ambient, .exhibit__ambient
ox-horizon10sopacity .75→1.horizon::before / ::after (line + glow)
ox-teal-pulse1.2s / 1.6sopacity .7→1, scale 1→1.25.dot-label--live::before (1.2s) · .chip--live::before (1.6s)
ox-caret1.1s steps(1)opacity 1↔0 blink.caret (typed verdict)
ox-breathe4s --ox-breathe-cycledrop-shadow color cross-fade, blue→teal.hero__mark / .mark-breathe

hero-rise is deliberately absent from this table — GRAMMAR-v3 §9b files it as an implementation recipe (§03 above), not a token-file keyframe, because its stagger delays are per-element and set by the consuming page.

05

Where sweeps sit vs. the budget

The one question every implementer asks: does a sweep relax the interaction budget? No. They operate on different objects, at different frequencies, for different reasons.

Sweeps are ambient, marketing- and product-chrome motion — continuous, looping, felt rather than clicked, and never gated behind an interaction. They never block input and never appear on a table row, a form field, or anything read at 100+ views/day. Interactions — every button, drawer, modal, toast, and hover in the product — still obey the strict foundation budget: enter 220ms, exit 130ms, press 80ms. Sweeps don’t widen that budget; they run one layer above it, on the scene itself.

Sweeps — the scene
  • · Continuous, infinite loop
  • · Runs whether or not the user acts
  • · Lives on hero copy, product frames, section dividers
  • · One dose per object (§01)
Budget — the interaction
  • · One-shot, tied to a user action
  • · Fires only on enter / exit / press
  • · Lives on modals, drawers, toasts, buttons, cards
  • · Frequency-gated — see §06
06

What never moves

Inherited from the foundation, unchanged. Premium legal software signals trust through restraint — animate less as usage frequency increases (Emil Kowalski’s frequency rule).

High-frequency interactions — never animate
ElementFrequencyReason
Table row content, text in cells100+/dayAny delay feels like latency, not polish.
Keyboard shortcuts (⌘K, ⌘/, bulk actions)100+/day0ms is the only correct answer for power users.
Sidebar navigation itemsdozens/sessionNo hover lift, no transition — used every session.
Filter / sort changes on listsdozens/sessionData updates should feel instant.
Document viewer content (legal text)reading modeMotion competes with reading focus.
Error states and validation messagesurgencyUrgency signals should appear instantly.
Price and billing numbers (confirmed state)trust contextConfirmed numbers must feel stable and certain.
Allowed with explicit justification
ElementAllowed motionWhy it earns animation
Modal openscale(0.97)→1, opacity 0→1, 220msFirst-time action per user intent — earns arrival.
Drawer / bottom sheettranslateY(100%)→0, 350ms springSpatial orientation — user needs to register origin.
Toast / notificationtranslateY(-100%)→0, 400ms easeRare event, needs to announce itself.
Card hover lifttranslateY(-4px), 160msAffordance at browse frequency.
Button pressscale(0.97) / translateY(1px), 80msTactile confirmation of an intentional action.
Credit balance changespring ticker, ~300msMakes the cost of AI use tangible.
07

Reduced-motion discipline

prefers-reduced-motion: reduce does not mean zero animation — it means no spatial movement. tokens-v3-gravity.css already collapses every animation and transition duration to 0.01ms globally, sweeps included; the guidance below is for anything you build outside that blanket rule (JS tickers, canvas, custom keyframes).

Disable under reduced motion
  • translateY, translateX, rotate, tilt
  • scale changes > 5% (frame tilt, hover lift)
  • sheen sweep (replace with a static opacity pulse)
  • ambient orb/tint drift
  • page transitions with displacement
Safe to keep short (50ms)
  • opacity fades (state changes)
  • color transitions (hover feedback)
  • teal pulse dot (opacity-only variant)
  • focus ring appearances
  • skeleton pulse (opacity only, no shimmer)
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
/* Global, in tokens-v3-gravity.css — no per-component opt-out needed. */