Animations
Named motion styles for product UI — a shared vocabulary for humans and agents.
Named animations we reuse across the system. Prefer these names in specs, tickets, and prompts so designers, engineers, and agents describe the same behavior.
| Term | What it is |
|---|---|
| spring-pop | Enter motion style — scale overshoot + fade in |
| soft-dismiss | Exit motion style — quiet opacity fade |
| spin-slow | Decorative loop — slow continuous rotation |
SpringReveal | React component — pairs spring-pop + soft-dismiss with height collapse |
HeightReveal | React component — height-only accordion, no scale |
| morph-button | Full-width CTA that expands up into confirm / success, then reverse-morphs back onto the same button. See Morph Button. |
Motion styles (spring-pop, soft-dismiss, spin-slow) are CSS utilities in
@sophic/portal-ui/theme.css — use the classnames when you own the shell.
Animation components (SpringReveal, HeightReveal) are React helpers in
@sophic/portal-ui that wire enter/exit for common open/close patterns. Prefer them
over hand-rolling the grid + class toggles below.
Motion styles
Spring pop
Name: spring-pop
Class: animate-spring-pop-in
In-place reveal that grows from the center with a short overshoot, then settles. Feels springy rather than a hard cut or a plain fade.
| Spec | Value |
|---|---|
| Duration | 480ms |
| Easing | cubic-bezier(0.22, 1.45, 0.36, 1) |
| Scale | 0.92 → 1.02 → 1 |
| Opacity | 0 → 1 |
Use when: Settings panels, inline cards, or style that should “pop” into place from a trigger nearby (e.g. gear → chart settings).
Avoid when: Full-page transitions, continuous loops, or large surfaces where overshoot would feel jumpy.
<div className="origin-center animate-spring-pop-in motion-reduce:animate-none">
{/* panel content */}
</div>Soft dismiss
Name: soft-dismiss
Class: animate-soft-dismiss
Exit companion to spring-pop. Quiet opacity fade only — no exit scale.
Pair it with a slow height collapse on the shell (grid-rows ~320ms) so
content below slides up instead of popping away.
| Spec | Value |
|---|---|
| Fade | animate-soft-dismiss — opacity 1 → 0, 320ms, ease-out |
| Height (shell) | 1fr → 0fr, ~320ms, cubic-bezier(0.22, 1, 0.36, 1) |
| Scale | none on exit |
Use when: Closing anything that entered with spring-pop.
import { cn } from "@sophic/portal-ui/lib/utils";
{/* Fade utility */}
<div className="animate-soft-dismiss motion-reduce:animate-none">
{/* panel content */}
</div>
{/* Typical shell: height morph + fade (prefer SpringReveal) */}
<div
className={cn(
"grid transition-[grid-template-rows] duration-[320ms]",
"ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none",
open ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
)}
>
<div className="min-h-0 overflow-hidden">
<div
className={
open
? "origin-center animate-spring-pop-in"
: "animate-soft-dismiss motion-reduce:animate-none"
}
>
{children}
</div>
</div>
</div>Spin slow
Name: spin-slow
Class: animate-spin-slow
Quiet continuous rotation for small decorative glyphs — not a loading spinner. Product home greeting uses it on the morning sun only (9s full turn); afternoon / evening / night icons stay static.
| Spec | Value |
|---|---|
| Duration | 9s |
| Easing | linear |
| Loop | infinite |
Use when: Morning greeting sun, other tiny ambient style that should rotate slowly — not every icon in a set.
Avoid when: Loading states — use default animate-spin with
SpinnerGapIcon instead. Large surfaces where rotation feels distracting.
Always pair with motion-reduce:animate-none.
<SunIcon
size={20}
weight="bold"
className="text-warning-foreground animate-spin-slow motion-reduce:animate-none"
aria-hidden
/>spin-slow — 9s linear loop for small decorative glyphs (e.g. home greeting).
Animation components
React components in @sophic/portal-ui — import and pass open to drive enter/exit.
Use these instead of hand-rolling the motion-style shells above.
SpringReveal
React component — @sophic/portal-ui/components/ui/spring-reveal
Pairs spring-pop (enter) with soft-dismiss + height collapse (exit). Use for settings panels and similar in-flow reveals.
Content below moves up as the panel folds away.
Combined enter/exit via the SpringReveal React component (not the raw CSS
classes alone).
import { SpringReveal } from "@sophic/portal-ui/components/ui/spring-reveal";
<SpringReveal open={open}>{children}</SpringReveal>
{/* Chart gear settings keep the bounce: */}
<SpringReveal open={open} spring="springy">{children}</SpringReveal>Import from @sophic/portal-ui/components/ui/spring-reveal. Pass open to drive
enter/exit; optional className lands on the height-morph shell.
Enter defaults to soft (spring="soft"); pass spring="springy" for the
original overshoot (chart gear). Mounting already open skips the enter pop.
HeightReveal
React component — @sophic/portal-ui/components/ui/height-reveal
Height-only accordion (0fr ↔ 1fr). No scale enter. Clips while collapsed
and during the height morph; content fades from washed-out to full in parallel.
overflow-visible once fully open so list hover washes (-mx) are not
clipped.
Use when: See more / accordion lists on instrument and fund sections.
Avoid when: Gear / settings panels — use SpringReveal instead.
- Extra row one
- Extra row two
- Extra row three
Height morph with a washed-out fade-in — no scale pop. Open state keeps overflow visible for list hover washes.
import { HeightReveal } from "@sophic/portal-ui/components/ui/height-reveal";
<HeightReveal open={open}>{children}</HeightReveal>Pass open; optional className lands on the height-morph shell.
Changelog
- 5 August 2026 — spin-slow decorative loop (
animate-spin-slow, 9s) for small glyphs; first use on home greeting icons. Full notes - 5 August 2026 — Home greeting time-of-day icons; morning sun spins with spin-slow, other periods static. Full notes
- 4 August 2026 —
HeightRevealpromoted to@sophic/portal-ui(peer ofSpringReveal) for list See more expands. Full notes - 4 August 2026 —
SpringRevealenter defaults to soft;spring="springy"restores the foundation bounce (gear settings). - 1 August 2026 — Chart details strip uses
SpringReveal(same enter / exit as gear settings). Full notes - 30 July 2026 — Instrument dials adopt spring-pop / soft-dismiss via
SpringRevealfor settings. Full notes - 30 July 2026 — Animations foundation catalog +
SpringRevealhelper. Full notes