TextFree

Weight Breathing

Pure CSS animation: font-weight oscillates between 300 and 900 with compensated letter-spacing — text breathes without a single line of JavaScript.

CSSVariableAnimation
Breathe font-weight: 300 ↔ 900
Hover or click the scene to interact

This effect is free — the Text category contains 56 effects total, including 4 free. Effect.Labs has 811 vanilla effects. Explore the category →

Usage examples

PRODUCT
Architect
Build with purpose. Ship with confidence.

① Landing hero — Product name that breathes

WhenThe homepage opens on a large animated title to capture attention before the user scrolls.
WhyAt large sizes, the 300 → 900 oscillation is dramatic without being distracting — the slow 3-second rhythm conveys confidence rather than agitation.
Settingsfont-size 5–8 rem, animation-duration 3.5 s for an even more relaxed pace, letter-spacing compensated ± 3 px.
24 OCTOBER · PARIS
DESIGN SUMMIT
Typography · Motion · Systems

② Event poster — Full-screen animated title

WhenA digital invitation or poster for a concert, conference, or product launch features the title as the central graphic element.
WhyWeight variation replaces parallax or camera motion — cinematic effect with a single <code>@keyframes</code>, zero canvas.
Settingsfont-size 6–10 rem, text-transform uppercase, animation-duration 4 s, color with background-clip: text gradient if the font supports it.
STUDIO B
01:24:37
LIVE
REC
–12 dB

③ Status badge — 'LIVE' signal that breathes

WhenA video player, dashboard, or studio interface displays a short keyword (LIVE, RECORDING, IN PROGRESS) to signal ongoing activity without aggressive blinking.
WhyA short word at large size (3–4 rem) exploits the full 300 → 900 oscillation: the weight shift is immediately legible and acts as a life signal — more readable than aggressive blinking, more refined than a color pulse.
Settingsfont-size 2.5–4 rem, text-transform uppercase, animation-duration 2 s for a more alert pace, letter-spacing ± 1 px.

How it works

The effect is driven by a single @keyframes vfBreathe that simultaneously interpolates font-weight (300 → 900 → 300) and letter-spacing (2px → -1px → 2px) over 3 seconds. The ease-in-out curve produces gentle acceleration mid-oscillation — this rhythm is what makes the motion feel like breathing rather than a mechanical blink.

Smooth font-weight interpolation only works with a variable font (continuous wght axis): with Inter, Roboto Flex, or any variable typeface, the weight transitions smoothly from 300 to 900. With a static font, the browser snaps between available weights — usually just 400 and 700.

The simultaneous letter-spacing animation compensates for optical expansion: heavier weights take more horizontal space, and pulling tracking down to -1px at the peak visually stabilizes the word width. Without this adjustment, the text would appear to push its neighbors outward on every cycle.

The effect is 100% CSS — no requestAnimationFrame, no JS dependency. A single <span class="vf-breathing-text"> and the @keyframes rule are enough: the browser delegates all interpolation to its rendering engine.

Accessibility

  • prefers-reduced-motion not implemented: the provided code contains no @media (prefers-reduced-motion: reduce) block. To respect OS settings, add: @media (prefers-reduced-motion:reduce){.vf-breathing-text{animation:none;font-weight:600}}.
  • The animated text is a standard inline <span> — normally readable by screen readers, without any aria-hidden masking it.
  • Contrast: white text rgb(255,255,255) on background rgb(10,10,15) — ratio > 19:1, well above the WCAG AA threshold (4.5:1).
  • No keyboard interaction or focus required — the effect is purely decorative and creates no focus trap.
  • No content flashes faster than 3 Hz: the 3-second cycle is far below the WCAG 2.3.1 seizure risk threshold.

Browser compatibility

CSS animation is supported everywhere since IE 10; smooth font-weight interpolation requires a variable font with a wght axis.

Chrome 66+✓ Full
Firefox 62+✓ Full
Safari 11+✓ Full
Edge 17+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

Without a variable font, <code>font-weight</code> snaps to available static weights (usually 400/700) — the animation still runs but steps instead of interpolating smoothly.

The code

Copy the three blocks into your page. No dependencies.

HTML
<div class="vf-preview" style="width:100%;min-height:300px;">
  <div class="vf-breathing-scene">
    <span class="vf-breathing-text">Breathe</span>
    <span class="vf-breathing-sub">font-weight: 300 ↔ 900</span>
  </div>
</div>
CSS
.vf-breathing-scene  {
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   gap: 16px;
   }

.vf-breathing-text  {
   font-family: Inter, sans-serif;
   font-size: 2.5rem;
   color: rgb(255, 255, 255);
   animation: 3s ease-in-out 0s infinite normal none running vfBreathe;
   }

.vf-breathing-sub  {
   font-size: 0.75rem;
   color: rgba(255, 255, 255, 0.4);
   }

@keyframes vfBreathe  {
   
  0%, 100%  {
   font-weight: 300;
   letter-spacing: 2px;
   }
  50%  {
   font-weight: 900;
   letter-spacing: -1px;
   }
}
JavaScript (fx-0675)
// Effet CSS pur — pas de JavaScript nécessaire

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
animation-duration: 3s 3s Breathing cycle speed. 2 s = fast, 5 s = meditative. Edit the value in the .vf-breathing-text selector.
font-weight 300 → 900 300 / 900 Weight range inside @keyframes vfBreathe (at 0%/100% and 50%). Narrow to 400→700 for a subtler oscillation.
letter-spacing 2px → -1px 2px / -1px Tracking compensation inside @keyframes vfBreathe. Tune if your font has different optical spacing behavior.
font-family: Inter Inter, sans-serif Replace with any variable font with a wght axis (Roboto Flex, Manrope, Cabinet Grotesk…). Without a variable font, the animation will be stepped.
font-size: 2.5rem 2.5rem Text size in .vf-breathing-text. The effect is more striking from 3 rem upward.
animation-timing-function ease-in-out Cycle curve. cubic-bezier(0.4,0,0.2,1) = Material-style; linear = regular mechanical oscillation.
color: rgb(255,255,255) white Text color in .vf-breathing-text. Compatible with background-clip:text to animate a gradient alongside the weight.

FAQ

The animation is jerky — why?

Smooth font-weight interpolation requires a variable font with a continuous wght axis. With a static typeface, the browser snaps between available weights (usually 400 and 700). Switch to Inter Variable, Roboto Flex, or any other variable font for a true analog transition from 300 to 900.

How do I apply the effect to multiple texts with staggered timing?

Add animation-delay to each .vf-breathing-text. A negative delay (e.g. -1.5s) starts the second element mid-cycle, creating a wave effect: the first is light when the second is heavy. A negative delay starts immediately and avoids an initial flash at font-weight 300.

How do I respect prefers-reduced-motion?

The provided code does not include this media query. Add after the effect rules: @media (prefers-reduced-motion:reduce){.vf-breathing-text{animation:none;font-weight:600}}. This freezes the text at a readable intermediate weight without hiding it.