NewCSS + small JS · mask · keyframes
Squiggle
A hand-drawn wavy underline draws in under the label, and keeps gently flowing while you're there.
A free wavy underline animation in HTML and CSS (with a few lines of JavaScript). Also known as squiggly underline, hand-drawn underline, SVG underline hover or animated link underline.
- Class
.CCSquiggle- Weight
- 1.9 KB
- Needs
- CSS + a few lines of JS
- Best for
- Links, friendly brands, editorial buttons
Tune it
Every snippet on this page follows these settings.
How the Squiggle hover effect works
- Script wraps each label so the underline can sit right under the words.
- The underline is a bar of accent colour masked by a small repeating wave SVG.
- On hover the bar grows from 0 to the label's width, and the mask slides sideways so the wave keeps flowing.
Add it to your site
- Add the styles. Link
https://coyoteconcept.com/lab/cc-lab.cssonce, or paste the CSS from any snippet below. - Set a colourway. Wrap your content in
class="CCTheme CCEmber", or any other colourway. - Add the classes. For example
<button class="CCButton CCSquiggle">. - Add the script. Include
https://coyoteconcept.com/lab/cc-lab.js, or the few lines of JavaScript in the snippet.
Core components
Squiggle on button, tabs, dropdown, card.
More ways to use it
The same class on headline, nav bar, badge.
The Squiggle code
The plain CSS and JavaScript behind the effect. Colours, speed and strength come from the --cc- custom properties; the snippets above bundle it with the component styles and your settings.
Show the CSS
.CCSquiggle .CCSquiggle-t { position: relative; }
.CCSquiggle .CCSquiggle-t::after {
content: "";
position: absolute;
left: 0;
bottom: -.42em;
width: 0;
height: .38em;
pointer-events: none;
background: var(--cc-accent);
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='6'%3E%3Cpath d='M0 3 Q4 0 8 3 T16 3' fill='none' stroke='black' stroke-width='1.6'/%3E%3C/svg%3E") 0 50% / 16px 100% repeat-x;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='6'%3E%3Cpath d='M0 3 Q4 0 8 3 T16 3' fill='none' stroke='black' stroke-width='1.6'/%3E%3C/svg%3E") 0 50% / 16px 100% repeat-x;
transition: width calc(.45s / var(--cc-speed)) cubic-bezier(.6, 0, .2, 1);
animation: ccSquiggle calc(.6s / var(--cc-speed)) linear infinite;
}
.CCSquiggle:is(:hover, :focus-visible, :focus-within) .CCSquiggle-t::after { width: 100%; }
/* on an accent-filled label (solid button, the selected tab) the line takes the label's own colour */
.CCSquiggle:is(.CCButton--solid, .CCBadge) .CCSquiggle-t::after,
.CCSquiggle [aria-selected="true"] .CCSquiggle-t::after { background: currentColor; }
@keyframes ccSquiggle { to { -webkit-mask-position: 16px 50%; mask-position: 16px 50%; } }
@media (prefers-reduced-motion: reduce) { .CCSquiggle .CCSquiggle-t::after { animation: none; transition: none; } }Show the JavaScript
document.querySelectorAll(".CCSquiggle").forEach(function (el) {
if (el.dataset.ccSquiggle) return; el.dataset.ccSquiggle = 1;
var targets = el.matches("button, h1, h2, h3, .CCBadge") ? [el] : [].slice.call(el.querySelectorAll("h3, a, [role=tab]"));
targets.forEach(function (t) {
[].slice.call(t.childNodes).forEach(function (node) {
if (node.nodeType !== 3 || !node.textContent.trim()) return;
var box = document.createElement("span"); box.className = "CCSquiggle-t"; box.textContent = node.textContent.trim();
t.replaceChild(box, node);
});
});
});Pairs well with
- Moonrise
- Ember Sparks
- Border Beam
- Glare Sweep
- Spotlight
- Magnetic
- Ember Glow
- Shimmer Border
- 3D Tilt
- Ripple
- Liquid Fill
- Ink Pour
- Jelly
- Neon
- Trace
- Northern Lights
- Stack Pop
- Metal
- Arrow Slide
- Sonar
- Soft Press
- Grain
- Pixel
- Goo
Avoid on the same element Howl Glitch (text), Text Scramble (text), Letter Wave (text), Text Roll (text), Focus Blur (text), Flip (text).
Browser support
| Chrome & Edge | 0+ |
|---|---|
| Safari | 0+ |
| Firefox | 0+ |
Older browsers show the element without the effect: nothing breaks.
Accessibility
- Decorative only: the label is untouched.
- Shows on keyboard focus too.
- Holds still under reduced motion.
Performance
- 1.9 KB of code, no libraries.
- Animates transforms, opacity and gradients, which browsers handle cheaply.
- This page pauses every animation that’s scrolled out of view. Worth copying if you use lots of them.
Questions about Squiggle
What is a wavy underline animation?
A hand-drawn wavy underline draws in under the label, and keeps gently flowing while you're there. Squiggle is a free wavy underline animation, also known as squiggly underline or hand-drawn underline, that works on buttons, tab bars, dropdowns, cards and more.
How do you make a wavy underline animation in CSS?
Script wraps each label so the underline can sit right under the words. The underline is a bar of accent colour masked by a small repeating wave SVG. On hover the bar grows from 0 to the label's width, and the mask slides sideways so the wave keeps flowing.
How do I add Squiggle to a button?
Add the class CCSquiggle next to CCButton, inside an element with the CCTheme class, then include the Squiggle CSS and its few lines of JavaScript. The Copy button on this page gives you all of it in one snippet.
Does Squiggle need JavaScript?
Yes, a few lines. They're included in every snippet and in the hosted cc-lab.js file.
Which browsers support Squiggle?
Chrome and Edge 0+, Safari 0+ and Firefox 0+. Older browsers show the element without the effect, so nothing breaks.
Is Squiggle accessible?
Decorative only: the label is untouched. Shows on keyboard focus too. Holds still under reduced motion.
Can I use Squiggle in React, Tailwind, WordPress, Shopify, Webflow or Squarespace?
Yes. There's a ready-made React component and a Tailwind version on this page. On WordPress, Shopify, Webflow, Wix or Squarespace, paste the CSS into the site's custom code or custom CSS area, then add the class CCSquiggle to a button or block, and paste the JavaScript into the footer code area.
Is Squiggle free for commercial use?
Yes. It's free for personal and client projects, with no sign-up. Credit and a link back are appreciated but never required.