The Underline That Stops Before the Arrow
Every text link on the redesigned homepage has a one-pixel underline that slides in from the left on hover and stops before the arrow icon. Getting all three behaviors right took four specific CSS choices.
The underline itself
A ::after pseudo-element draws the line. height: 1px, background: currentColor. That last property means the underline inherits whatever color the text already is, so the same rule works in the hero section (dark ink on off-white) and on the dark contact band (light text on navy) without overrides.
.studio-text-link::after {
content: '';
position: absolute;
bottom: 3px;
left: 0;
width: calc(100% - 32px);
height: 1px;
background: currentColor;
transform: scaleX(0);
transform-origin: left;
transition: transform .35s cubic-bezier(.16,1,.3,1);
}
Sliding, not growing
The underline starts at transform: scaleX(0) and moves to scaleX(1) on hover. The alternative would be animating width from 0 to 100%. That also produces a growing line, but width is a layout property: the browser recalculates layout on every animation frame. scaleX is a composited transform, handled on the GPU. The visual result is identical. The CPU cost is not.
The reveal runs left to right because transform-origin: left is set on the pseudo-element. Without it, a scale animation expands from the center.
Stopping before the icon
Each text link is an inline-flex container: the label text, a 16px gap, and an arrow icon 16px wide. Combined, the icon area is 32px. The underline width is calc(100% - 32px).
No JavaScript. No resize observer. No measured pixel width. The gap value and icon size are both fixed in the CSS, so the calculation holds anywhere the class appears. If the icon size changes, one number changes in one place.
Touch devices
All hover rules sit inside @media (hover: hover) and (pointer: fine). A tap on iOS briefly applies the :hover state and leaves it. Without the media query, the underline would appear after a tap and stay there until the next interaction. The query targets devices with a true pointing device and hover capability, filtering out touchscreens.
The icon also moves
On hover, the arrow icon translates 2px right and 2px up: transform: translate(2px, -2px). That transition runs on the same ease curve as the underline. The underline slides in from the left. The icon nudges toward its destination. Both motions say the same thing.
@media (hover: hover) and (pointer: fine) {
.studio-text-link:hover::after { transform: scaleX(1); }
.studio-text-link:hover svg { transform: translate(2px,-2px); }
}
Two rules. Nothing touches the DOM. No state, no JavaScript, no library. The full animation is the pseudo-element and the SVG reacting to a single :hover.