Dotted underlines that animate on hover
Every link on this site sits on a row of small, round, light grey dots. On hover the dots turn the text colour and slide down two pixels. It is a small detail, but it makes links feel quiet at rest and responsive when you reach for them.
Why not text-decoration?
text-decoration-style: dotted gets close, but browsers draw the dots differently and their spacing is tied to the line thickness. Drawing the dots with a repeating radial gradient gives the same result everywhere:
.link-dotted {
--dot-color: #d4d4d8;
--dot-offset: 3px;
padding-bottom: 4px;
background-image: radial-gradient(
circle at center,
var(--dot-color) 0.75px,
transparent 1.25px
);
background-size: 4px 2.5px;
background-repeat: repeat-x;
background-position: 0 calc(100% - var(--dot-offset));
}
.link-dotted:hover {
--dot-color: currentColor;
--dot-offset: 1px;
}Making the colour transition
Gradients can't be transitioned directly, and a plain custom property changes in a single step. Registering the property with @property gives it a type, so the browser can interpolate it like any other colour:
@property --dot-color {
syntax: '<color>';
inherits: true;
initial-value: transparent;
}
.link-dotted {
transition: --dot-color 200ms ease-out, background-position 200ms ease-out;
}The movement comes for free: changing --dot-offset changes the computed background-position, which is an ordinary animatable property.
The layout gotcha
The background is painted on the element's box, and that box depends on how the element is laid out:
- An inline link's box hugs the font's ascent and descent, so the dots land just under the text.
- A block or inline-block link — including any child of a flex container — uses the full line height, which pushes the dots a few pixels lower.
The simplest rule is to keep the class on inline elements. When a link has to live inside a flex row, wrap it in a <span> so the wrapper becomes the flex item and the link stays inline.