motion-font
Animates one or more variable font axes (weight, optical size, width, or any custom axis) using Motion spring physics.
Three trigger modes: view (viewport entry), hover (mouse/focus), and scroll (axis tracks scroll position).
Requires a variable font with the targeted axes.
Hover: weight
The most common use: bold on hover with a spring-back on leave.
<motion-font axis="wght" from="300" to="800" trigger="hover">
Hover to bold
</motion-font>View: viewport reveal
Animates the axis once when the element scrolls into view. Ideal for hero headings that gain weight on entrance. Set
<motion-font axis="wght" from="100" to="900" duration="1.2" trigger="view">
Weight reveal
</motion-font>Scroll: tied to position
The axis value maps directly to scroll progress: from when the element enters the viewport, to when it exits. No spring; direct 1:1 mapping. Works best in real page context. See the showcase.
<motion-font axis="wght" from="100" to="900" trigger="scroll">
Scroll weight
</motion-font>Multiple axes
Use the axes prop to animate several axes at once. Format: space-separated tag:from:to triplets. Overrides axis, from, and to.
<!-- weight + optical size -->
<motion-font axes="wght:200:800 opsz:8:32" trigger="hover">
Hover me
</motion-font>Bounce
Control spring overshoot with bounce (0–1). Higher values overshoot and snap back.
bounce=0 bounce=0.3 bounce=0.6 <motion-font axis="wght" from="300" to="800" trigger="hover" bounce="0.3">
Bouncy bold
</motion-font>Delay
Delay the animation start in seconds. This is useful for staggering multiple reveals.
<motion-font axis="wght" from="100" to="900" trigger="view" delay="0">First line</motion-font>
<motion-font axis="wght" from="100" to="900" trigger="view" delay="0.15">Second line</motion-font>
<motion-font axis="wght" from="100" to="900" trigger="view" delay="0.3">Third line</motion-font>Loop
Add loop to spring to to, hold, spring back to from and repeat, like a
headline that breathes. hold sets how long it stays at to, gap how long it
rests at from.
<motion-font axis="wght" from="200" to="800" loop hold="1.2" gap="0.4">Breathe</motion-font>
With trigger="view" the cycle runs only while the text is on screen, and once is ignored.
Add pause-on-hover to freeze it while the pointer is over the text.
Properties
| Attribute | Property | Type | Default | Description |
|---|---|---|---|---|
| | | string | 'wght' | Font axis tag: |
| | | string | '' | Multi-axis: space-separated |
| | | number | 300 | Starting axis value. Ignored when |
| | | number | 700 | Target axis value. Ignored when |
| | | number | 0.6 | Spring duration hint in seconds. |
| | | number | 0 | Spring bounciness 0–1. 0 = no overshoot, 1 = very bouncy. |
| | | number | 0 | Delay before animation starts in seconds. |
| | | string | 'view' | |
| | | boolean | true | For |
| | | boolean | false | Spring to |
| | | number | 1.6 | With |
| | | number | 0.5 | With |
| | | boolean | false | Pause the loop while the pointer is over the text, resuming on leave |
Methods
| Method | Type | Default | Description |
|---|---|---|---|
| | void | — | Reset axis to |
Variable font required: the targeted axis must exist in the loaded font. Inter supports wght (100–900) and opsz (14–32). For wdth use a font like Roboto Flex.
JavaScript API
Trigger the font programmatically with play() and cancel().
Accessibility
Respects prefers-reduced-motion: when enabled, axis animations are skipped, the font renders at its target value immediately and loop never starts.