Sliding Underline

The underline draws in from the left when you hover a link and leaves through the right, so it reads as passing through.

  • CSS
  • Text
  • Hover
  • Added

AI prompt

Create navigation links with a sliding underline in pure CSS. Each link is position: relative with an ::after line (height 2px, bottom -4px, full width, accent colour) set to transform: scaleX(0) with transform-origin: right. On :hover and :focus-visible, switch to transform-origin: left and scaleX(1). Transition transform over 300ms with cubic-bezier(0.16, 1, 0.3, 1). Because the origin flips, the line enters from the left and exits to the right. Under prefers-reduced-motion, show the line instantly without the slide. The links read "Work", "About", and "Contact".

Source

Demo.tsx
'use client'

import s from './demo.module.css'

export default function UnderlineSlide({ labels }: { labels: Record<string, string> }) {
  return (
    <nav className={s.nav}>
      {[labels.a, labels.b, labels.c].map((t) => (
        <a key={t} href="#" className={s.link} onClick={(e) => e.preventDefault()}>
          {t}
        </a>
      ))}
    </nav>
  )
}
demo.module.css
.nav {
  display: flex;
  gap: 28px;
}

.link {
  position: relative;
  color: var(--stage-ink);
  font: 500 var(--text-xl) / 1.2 var(--font-display);
  text-decoration: none;
}

.link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 2px;
  background: var(--stage-accent);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 300ms var(--ease-out);
}

.link:hover::after,
.link:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

.link:focus-visible { outline: none; }

@media (prefers-reduced-motion: reduce) {
  .link::after { transition: none; }
}

Suggest an idea

Seen a motion or a theme you would like to find here? Describe it, and it opens as a prefilled GitHub issue for you to post.

What are you suggesting?

Needs a GitHub account. Suggestions are public.