SVG✨ Premium

Wave Path

A gradient wavy line that gently swings while dashes run along it — stretchable to full width or to a simple underline without losing its stroke thickness.

CSSWave

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →

3 usage examples

Swell line under the headline — sailing club home page — Wave Path example 1

① Swell line under the headline — sailing club home page

WhenThe hero of a sailing club: under the large "Autumn regattas" headline, a turquoise wave spans the full width and slowly swings; below it, the next three races and the sign-up button.
WhyThe swell separates the headline from the calendar and sets a sea mood without a photo. Stretched over 540 px, it keeps a thin, crisp stroke.
Settings--wavp-w:100%, --wavp-h:56px, transparent background; gradient --wavp-c1:#38bdf8, --wavp-c2:#22d3ee, --wavp-c3:#a5f3fc; faint line --wavp-trace:rgba(165,243,252,.16).
Wave while recording a voice message — carpooling app — Wave Path example 2

② Wave while recording a voice message — carpooling app

WhenThe conversation with a driver before a trip: at the bottom, the input bar is in recording mode, red dot, running duration, small red-orange wave flowing, the word "Recording" and the send button. The button switches back to the text field.
WhyRunning dashes say "recording" at a glance, in very little space; the duration and the written word keep the exact information.
Settings--wavp-w:150px, --wavp-h:28px, gradient #e11d48 → #f43f5e → #fb923c, --wavp-flow:.7s for faster dashes; the scene shows or hides the recording bar.
Wavy underline on hover — podcast episode list — Wave Path example 3

③ Wavy underline on hover — podcast episode list

WhenThe episode list of a local history podcast: when the mouse goes over a row (or the title gets keyboard focus), the title lights up and a thin amber wave appears below it, exactly as wide as the title.
WhyA living underline marks the targeted row more clearly than a plain line, and stays light: 16 px tall, a single animated row at a time.
SettingsOne copy per title, --wavp-w:100% of the link width, --wavp-h:16px; gradient #f59e0b → #f97316 → #ef4444. Scene CSS: root at opacity:0, 1 on the row's :hover and the link's :focus-visible.

How it works

The drawing is a 220 × 80 unit SVG holding the same wave twice (four arches, from x = 10 to x = 210): .wavp-bg, a faint solid line (indigo at 20%), and .wavp-line on top, in 20-by-10 dashes colored by a horizontal gradient indigo #6366f1 → violet #8b5cf6 → magenta #d946ef.

Two motions overlap. The dashes run along the curve thanks to @keyframes wavpDash (the dash offset goes from 0 to −30 in 1 s, looping). And the wave itself swings: a SMIL <animate attributeName="d"> flips the first arch (control point from y = 10 to y = 70), the following arches follow by symmetry, back and forth in 2 s with a soft start and end. The same swing is applied to the faint line so dashes and background stay on top of each other.

The SVG stretches to any box (preserveAspectRatio="none") and the stroke keeps its 4 px thanks to vector-effect: non-scaling-stroke: the same wave works as a large full-width swell or as a 16 px tall underline. The small script gives each copy its own gradient (wavp-grad-1, -2…), so a hidden copy doesn't take the others' gradient with it, and handles reduced motion.

Accessibility

  • Decorative: the SVG has aria-hidden="true" and focusable="false". Used as an indicator ("Recording"), pair it with visible text, as in the second example.
  • Reduced motion: with prefers-reduced-motion: reduce, the CSS stops the running dashes and the script pauses the SMIL swing at its starting position: a still wave of gradient dashes remains. The setting is followed live.
  • Hover and keyboard: the effect reacts to nothing by itself. As a link underline, show it on the link's :hover and :focus-visible, so keyboard navigation gets the same cue.
  • Gentle pace: 2 s swing, steady dash flow, no blinking.

Browser compatibility

SMIL <animate> on the d attribute, CSS animation of stroke-dashoffset, vector-effect: non-scaling-stroke, stop-color in CSS, pauseAnimations(), prefers-reduced-motion. Measured in Chromium, Firefox and WebKit (Playwright): 0 errors, two copies with two distinct gradients, 2nd copy still colored when the 1st is hidden, wave that swings in all three engines, frozen frames under reduced motion.

Chrome 74+✓ Full (measured)
Firefox 63+✓ Full (measured)
Safari 10.1+✓ Full (measured in WebKit)
Edge 79+✓ Full (Chromium engine)
Safari iOS 10.3+✓ WebKit engine, not verified on a real device
Chrome Android 74+✓ Full (Chromium engine)

Without SMIL, the wave stays still and only the dashes run. Without JavaScript, a single copy per page renders correctly; with several copies, they share the first one's gradient.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0608-root">
  <div class="wavp-stage">
    <svg class="wavp-svg" viewBox="0 0 220 80" preserveAspectRatio="none" aria-hidden="true" focusable="false">
      <defs>
        <linearGradient id="wavp-grad" gradientUnits="userSpaceOnUse" x1="10" y1="0" x2="210" y2="0">
          <stop class="wavp-c1" offset="0"/>
          <stop class="wavp-c2" offset=".5"/>
          <stop class="wavp-c3" offset="1"/>
        </linearGradient>
      </defs>
      <path class="wavp-bg" d="…"><animate attributeName="d" dur="2s" …/></path>
      <path class="wavp-line" stroke="url(#wavp-grad)" d="…"><animate attributeName="d" dur="2s" …/></path>
    </svg>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Size (CSS — --wavp-w / --wavp-h) 200px / 73px The wave stretches to the given box, the stroke stays at 4 px. 100% / 24px gives a thin full-width line.
Gradient (CSS — --wavp-c1, --wavp-c2, --wavp-c3) #6366f1 / #8b5cf6 / #d946ef Left, middle and right colors of the dashes.
Background line (CSS — --wavp-trace) rgba(99,102,241,.2) The faint wave visible between dashes; transparent to keep only the dashes.
Dash flow (CSS — --wavp-flow) 1s Time to move by one dash; shorter = faster.
Wave swing (HTML — dur of both <animate> tags) 2s Use the same value on both tags so the faint line and the dashes stay aligned.
Block background and height (CSS — --wavp-page, --wavp-root-h) #0a0a0f / 300px transparent and 0px to place the wave under a heading, in a button or under a link.

FAQ

Yes. The swing is a SMIL animation of the d attribute, which Safari supports like Chrome and Firefox; the CSS d property does not exist in Safari, which is why it is not used.
No. The gradient needs an id; if two copies shared the same one, hiding the copy that holds it would take the other's color away. The script therefore gives each copy a unique id. If you inject a copy after page load, run the script again.
Place the root at the bottom of the link (position:absolute; left:0; right:0; bottom:0), with --wavp-w:100%, --wavp-h:16px, --wavp-page:transparent and --wavp-root-h:0px, at opacity:0; set it to 1 on :hover and :focus-visible. The stroke keeps its 4 px even when flattened.