22 animation primitives — marquee, typewriter, particle systems, text effects, number tickers, and motion wrappers for landing pages, dashboards, and interactive UIs.
Module: PhiaUi.Components.Animation
import PhiaUi.Components.AnimationAll components:
- Respect
prefers-reduced-motion— animations disable automatically for users who prefer reduced motion - Use vanilla JS hooks — no npm packages
- Are server-rendered — content is in the DOM before JavaScript runs
Background patterns (gradient mesh, dot grids, bokeh, SVG waves, etc.) live in their own module — see Background.
Table of Contents
Marquee & Orbit
Background Effects
Text Effects
Motion Wrappers
UI Animations
marquee
Infinite horizontal scrolling ticker. Loops child content seamlessly. Hook: PhiaMarquee.
<%!-- Logo cloud --%>
<.marquee speed={40} gap={32} class="py-4">
<%= for logo <- @logos do %>
<img src={logo.url} alt={logo.name} class="h-8 grayscale hover:grayscale-0 transition-all" />
<% end %>
</.marquee>
<%!-- Testimonial ticker --%>
<.marquee speed={30} pause_on_hover={true}>
<%= for quote <- @quotes do %>
<.testimonial_card {quote} class="w-72 mx-4 shrink-0" />
<% end %>
</.marquee>
<%!-- Reverse direction --%>
<.marquee reverse={true} speed={25}>
<%= for tag <- @tags do %>
<.badge class="mx-2"><%= tag %></.badge>
<% end %>
</.marquee>Attrs: speed (px/s, default 30), gap (px), reverse (boolean), pause_on_hover (boolean), class
orbit
Elements orbit around a central point. Hook: PhiaOrbit.
<.orbit id="skills-orbit" radius={120} speed={20}>
<:center>
<.avatar size="xl"><.avatar_image src={@profile.avatar} /></.avatar>
</:center>
<:items>
<img src="/icons/elixir.svg" class="h-8 w-8" />
<img src="/icons/phoenix.svg" class="h-8 w-8" />
<img src="/icons/tailwind.svg" class="h-8 w-8" />
<img src="/icons/postgresql.svg" class="h-8 w-8" />
</:items>
</.orbit>Attrs: id (required), radius (px), speed (seconds for full orbit), class
aurora
Animated aurora borealis gradient — soft, shifting colours. Reuses the --animate-aurora theme token.
<div class="relative h-64 overflow-hidden rounded-xl bg-zinc-900">
<.aurora colors={["#3b82f6", "#8b5cf6", "#ec4899"]} />
<div class="relative z-10 p-8 text-white">Hero content</div>
</div>Attrs: colors (list of CSS colors), class
meteor_shower
Animated shooting meteors across a dark background. Hook: PhiaMeteor.
<div class="relative h-96 bg-black overflow-hidden rounded-xl">
<.meteor_shower count={30} color="rgba(255,255,255,0.8)" />
<div class="relative z-10">Content</div>
</div>Attrs: count (integer, default 20), color, class
dot_pattern
Repeating SVG dot pattern background with optional radial fade mask.
<div class="relative bg-white dark:bg-zinc-950">
<.dot_pattern class="opacity-50" />
<div class="relative z-10 p-12">Page content</div>
</div>grid_pattern
SVG grid line pattern with optional fade mask.
<div class="relative">
<.grid_pattern stroke_color="rgba(0,0,0,0.08)" />
<div class="relative z-10">Content</div>
</div>ripple_bg
Expanding concentric circle ripples from a centre point.
<div class="relative h-64 flex items-center justify-center bg-primary/5 rounded-xl overflow-hidden">
<.ripple_bg color="rgba(99,102,241,0.15)" count={4} duration={3} />
<.icon name="wifi" size="lg" class="relative z-10 text-primary" />
</div>particle_bg
Canvas particle system with connecting lines. Hook: PhiaParticleBg.
<div class="relative h-screen bg-zinc-950">
<.particle_bg
id="hero-particles"
count={80}
color="rgba(99,102,241,0.5)"
connect={true}
/>
<div class="relative z-10 flex items-center justify-center h-full">
<h1 class="text-white text-5xl font-bold">PhiaUI</h1>
</div>
</div>Attrs: id (required), count (integer), color, connect (boolean), speed (float)
shimmer_text
Text with a sweeping shimmer highlight.
<.shimmer_text class="text-4xl font-bold">
PhiaUI
</.shimmer_text>typewriter
Types text character by character, with optional blinking cursor. Hook: PhiaTypewriter.
<.typewriter
id="hero-type"
phrases={["Build faster.", "Ship confidently.", "Own your UI."]}
speed={80}
pause={2000}
/>Attrs: id (required), phrases (list of strings), speed (ms per char), pause (ms between phrases), loop (boolean)
word_rotate
Rotates through a list of words with a fade transition. Hook: PhiaWordRotate.
<h1 class="text-4xl font-bold flex gap-3 items-center">
Build
<.word_rotate
id="rotating-word"
words={["faster", "smarter", "better", "with confidence"]}
class="text-primary"
/>
</.h1>text_scramble
Scrambles and resolves text character by character (Matrix-style). Hook: PhiaTextScramble.
<.text_scramble id="scramble-1" text="PhiaUI" class="text-5xl font-mono font-bold" />fade_in
Fades and slides content into view on scroll. Hook: PhiaScrollReveal.
<.fade_in id="feature-1" direction={:up} delay={0}>
<.feature_card title="Fast" description="829 components, zero bloat." />
</.fade_in>
<.fade_in id="feature-2" direction={:up} delay={100}>
<.feature_card title="Accessible" description="Full WAI-ARIA on all interactive components." />
</.fade_in>Attrs: id (required), direction (:up | :down | :left | :right), delay (ms), duration (ms)
float
Gently floats content up and down continuously.
<.float amplitude={8} duration={3}>
<img src="/hero-graphic.svg" class="w-64" />
</.float>Attrs: amplitude (px, default 6), duration (seconds, default 4), class
tilt_card
Card that tilts in 3D on mouse movement. Hook: PhiaTiltCard.
<.tilt_card id="product-card" max_tilt={15} class="rounded-xl overflow-hidden">
<.image_card src="/product.jpg" title="Product Name" />
</.tilt_card>spotlight
Radial spotlight that follows cursor inside the container. Hook: PhiaSpotlight.
<.spotlight id="hero-spotlight" color="rgba(99,102,241,0.15)">
<div class="p-12">
<.heading level={1}>Build something great</.heading>
</div>
</.spotlight>animated_border
Animates a gradient around the element border.
<.animated_border class="rounded-xl p-0.5">
<div class="bg-card rounded-xl p-6">
<h3>Special offer</h3>
</div>
</.animated_border>pulse_ring
Pulsing ring around an element — draws attention.
<div class="relative">
<.pulse_ring color="rgba(99,102,241,0.4)" />
<.button variant="default">New feature</.button>
</div>number_ticker
Counts up to a target value with easing. Hook: PhiaNumberTicker.
<.number_ticker id="mrr" value={24500} prefix="$" duration={1500} />
<.number_ticker id="users" value={18723} suffix=" users" duration={2000} />Attrs: id (required), value (number), prefix, suffix, duration (ms), start (initial value)
typing_indicator
Three animated dots — for chat "is typing" states.
<%= if @contact_is_typing do %>
<div class="flex items-center gap-2">
<.avatar size="xs"><.avatar_fallback name={@contact.name} /></.avatar>
<.typing_indicator />
</div>
<% end %>wave_loader
Five vertical bars that animate like a sound wave.
<.wave_loader class="text-primary" />
<.wave_loader size="lg" class="text-muted-foreground" />confetti_burst
Canvas confetti explosion. Hook: PhiaConfetti.
<.confetti_burst id="success-confetti" />
<%!-- Trigger from LiveView --%>
<%!-- push_event(socket, "confetti", %{id: "success-confetti"}) --%>Real-world: Hero section with animation stack
<section class="relative min-h-screen overflow-hidden bg-zinc-950">
<%!-- Background layers --%>
<.particle_bg id="hero-bg" count={60} color="rgba(99,102,241,0.3)" connect={true} />
<%!-- Foreground content --%>
<div class="relative z-10 flex flex-col items-center justify-center min-h-screen px-4 text-center">
<.fade_in id="hero-badge" direction={:down} delay={0}>
<.badge variant="outline" class="text-white border-white/20 mb-6">
v0.1.17 — 829 components
</.badge>
</.fade_in>
<.fade_in id="hero-title" direction={:up} delay={100}>
<h1 class="text-6xl font-bold text-white mb-4">
Build Phoenix UIs
<br />
<.shimmer_text class="text-indigo-400">in minutes</.shimmer_text>
</h1>
</.fade_in>
<.fade_in id="hero-cta" direction={:up} delay={300}>
<div class="flex gap-4 mt-8">
<.glow_button color="#6366f1" phx-click="get_started">Get started</.glow_button>
<.button variant="outline" class="text-white border-white/20">View docs</.button>
</div>
</.fade_in>
</div>
</section>