Living Gateways to the World's Most Coveted Escapes.
Add one attribute to any word, link or phrase. Every stroke is generated fresh — its own tilt, pressure and ragged edge — then drawn in like a marker.
Three ways to mark it
The attribute value picks the style. Leave it empty for the default brush stroke.
A slow morning in the Dolomites
data-underline
Nothing feels quite like it
data-underline="double"
Private villas with ocean views
data-underline="highlight"
Always behind the text, at any strength
Ink sits behind every letter in the surrounding text, so a highlight's overshoot never covers the word before it. Set transparency with an 8-digit hex color, or with data-underline-opacity.
An island known for its slow sunsets
style="--underline-color: #ffb38a8c"
Every room opens to the garden
data-underline-opacity="0.45"
Links draw on hover, phrases follow the line
Links get a hover trigger automatically and wipe away when you leave. Long phrases that wrap get one stroke per line, drawn one after the other as if by hand.
Wake up in a Kyoto ryokan, lunch at a cliffside taverna, and end the day on the deck of a gulet.
Hover or tab to the links.
Some places stay with you. The kind of light that makes you forget to check the time is one of them.
Resize the window — the strokes re-flow with the text.
Add the attribute at runtime
A MutationObserver picks up any element that gains (or loses) data-underline — CMS lists, Finsweet attributes, tabs, whatever renders later. Click words below to toggle the attribute live.
From hidden coves in Mallorca to quiet terraces above Lake Como, we curate stays that feel less like hotels and more like belonging somewhere.
Each click sets or removes data-underline on a <span>.
Drop-in for Webflow
Paste the CSS block in the page or site <head> and the script before </body>. Then add custom attributes in the Designer.
| Attribute | Values | Notes |
|---|---|---|
data-underline | "" · brush · double · highlight | Turns the effect on. Any other value falls back to brush. |
data-underline-trigger | scroll · load · hover · manual | Default is scroll, or hover on links and buttons. |
data-underline-delay | ms | Wait before drawing. |
data-underline-duration | ms | Override. By default duration scales with stroke length. |
data-underline-opacity | 0–1 or % | Per-element opacity. Or put alpha in the color itself: #d9c7ff80. |
data-underline-seed | number | Lock a specific stroke shape. |
data-underline-hover | (on an ancestor) | Hover this parent to draw underlines inside it — e.g. a card. |
/* Style per element, per class, or globally */ .footer-link { --underline-color: #ffc8a6; --underline-thickness: .14; } .tag-mark { --underline-color: #d9c7ff80; } /* 8-digit hex = 50% alpha */ .soft { --underline-opacity: .6; } // JS API (window.HandUnderline) HandUnderline.draw(el) // draw one (works with trigger="manual") HandUnderline.erase(el) // wipe it away HandUnderline.replay() // replay everything (or pass an element) HandUnderline.refresh() // regenerate after changing size/thickness vars