Elastic Underline Nav
A nav underline that stretches with the distance it travels and settles back to width.
DOM + CSS1 dep1 file · 4 KB TSX
Props
"use client";
import { useEffect, useRef, useState } from "react";
import { motion } from "framer-motion";
/**
* Gate the scheme before a caller-supplied string reaches an href.
* A javascript: or data:text/html URL executes on click, so any link list
* fed from a CMS, a database or user submissions is an XSS sink without this.
*/
const SAFE_HREF = /^(?:https?:|mailto:|tel:|\/|#|\.)/i;
const safeHref = (h: string) => (SAFE_HREF.test(h.trim()) ? h : "#");
export function ElasticUnderlineNav({
items,
stretch = 0.5,
thickness = 2,
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}Details
About this component
A shared `layoutId` pill slides between nav items at a constant width, which is smooth and entirely inert. Squash-and-stretch is what animators have used for a century to make motion read as physical: a body deforms in proportion to how fast it is moving and recovers on arrival. Deriving the bar's horizontal scale from the distance between the previous and new positions reproduces exactly that — a jump to the neighbouring item barely deforms, a jump across the whole nav stretches noticeably, and the difference is legible without being cartoonish. Two supporting choices carry it. The travel and the stretch use separate springs, with the stretch looser, so the bar recovers its width slightly after it arrives rather than in lockstep, which is what keeps the settle from feeling mechanical. And positions come from measuring each item's real rect rather than dividing the container by item count, so labels of different lengths — the normal case — track correctly. Focus mirrors hover, so the underline is not a mouse-only affordance.
Curator’s note
Built for viberdy 2.1. Scaling by distance travelled is squash-and-stretch — the detail a shared-layoutId pill structurally cannot do.
