Components

Helpers

PhoenixPaper.Ripple, Elevation, Shape and the motion tokens: the plumbing every component is built on. Color has its own guide.

Ripple

The Material ripple: a circle that expands from the click point and fades. A vanilla inline onclick, no hook or bundler. On by default on buttons, FABs, linked list items and rail items; off by default on Switch, Checkbox and RadioGroup (their 40dp state layer already gives feedback); always off with paperize={false}.

ripple: true (default) vs. ripple: false

Option Description
ripple the boolean attr on interactive components (see each component's options)
PhoenixPaper.Ripple.on_click/1 the onclick script, or nil when disabled (the attribute is dropped)
PhoenixPaper.Ripple.container_classes/1,2 the relative/overflow-hidden the ripple needs; the /2 form takes a position for components with a position attr
Show code
<.pp_button>Ripples (default)</.pp_button>
<.pp_button ripple={false}>No ripple</.pp_button>

Elevation

MD3 has six elevation levels (0–5) with its own shadows; PhoenixPaper.Elevation.class/1 maps a level (clamped to 5) to a pp-elevation-N class. MD3 mostly separates surfaces by color (the surface-container scale, see Paper), and keeps shadows for things that float.

Levels 0–5

0
1
2
3
4
5
Option Description
Elevation.class(level) the literal "pp-elevation-N" class, level 0–5
Show code
<div class={["rounded-pp-md bg-pp-surface-container-low p-4", PhoenixPaper.Elevation.class(3)]}>Level 3</div>

Shape

The MD3 corner scale as --radius-pp-* tokens and rounded-pp-* utilities, including M3 Expressive's lg_increased, xl_increased and xxl. PhoenixPaper.Shape.class/1,2 maps a token to its class, optionally on one edge (:top, :bottom, :start, :end).

The scale

none
xs
sm
md
lg
lg_increased
xl
xl_increased
xxl
full
Option Description
Shape.class(token) all four corners
Shape.class(token, :top | :bottom | :start | :end) only that edge's two corners
Show code
<div class={["size-14 bg-pp-primary-container", PhoenixPaper.Shape.class(:xl_increased)]} />
<div class={["size-14 bg-pp-primary-container", PhoenixPaper.Shape.class(:lg, :top)]} />

<%!-- or the utilities directly --%>
<div class="size-14 rounded-pp-xxl bg-pp-primary-container" />

Motion

M3 Expressive's motion as springs: spatial springs (for position and size; they can overshoot) and effects springs (for color and opacity; no overshoot), each fast/default/slow, as CSS linear() easings sampled from the real springs. Components use them already; pp-motion-* utilities set the transition for your own elements. data-pp-motion="standard" on an ancestor switches to the calmer standard scheme, prefers-reduced-motion is respected, and the classic MD3 duration easings are available as ease-pp-*.

Hover a row: the square slides with that spring

spatial-fast
spatial-default
spatial-slow
effects-fast
effects-default
effects-slow
Option Description
pp-motion-spatial-fast / -default / -slow transition timing + duration for movement and size
pp-motion-effects-fast / -default / -slow transition timing + duration for color and opacity
data-pp-motion="standard" on any ancestor: the standard (non-Expressive) motion scheme
ease-pp-standard / -emphasized (and -accelerate / -decelerate) MD3's cubic-bezier easings, for your own transition-* utilities
Show code
<%!-- A spring for movement --%>
<div class="transition-transform pp-motion-spatial-default hover:translate-x-4">...</div>

<%!-- An effects spring for color --%>
<div class="bg-pp-surface-container transition-colors pp-motion-effects-fast hover:bg-pp-secondary-container">...</div>

<%!-- The calmer standard scheme for a whole subtree --%>
<div data-pp-motion="standard">...</div>

z7ealth 2026