Dark animated border navigation menu with HTML and CSS
Blog

Animated Border Navigation Menu with HTML and CSS

Create a navigation menu with a border that animates into view on hover, keyboard focus, or the current-page state. This effect uses HTML and CSS only, so it adds no JavaScript dependency.

Quick answer: Set each link to position: relative, draw its outline with a ::after pseudo-element, then transition its opacity and scale. Reveal it with :hover, :focus-visible, and a current-page class.

Use the live example below to try the interaction. The source ZIP contains a standalone HTML file, stylesheet, and README.

What you will build

This responsive navigation bar wraps on narrow screens. Hovering or focusing a link reveals a rounded border; the current page can keep its border visible. The example uses no JavaScript, external fonts, images, or build tools.

HTML for the animated navigation menu

Use a <nav> landmark with an accessible name. Each item is a regular link. Replace the sample fragment destinations with real page URLs. Mark the active destination with aria-current="page" so its state is not communicated by color alone.

CSS for the animated border effect

The ::after pseudo-element draws the border without extra markup. It starts transparent and slightly smaller, then transitions to full size and opacity on hover, keyboard focus, or the current-page state. The explicit focus outline keeps keyboard focus easy to see.

The links use flex-wrap to move onto another line on small screens. Adjust the spacing, radius, and colors to match your design. Keep the transition short and avoid fast repeating animation.

How the CSS animation works

  1. The link uses position: relative to contain its pseudo-element.
  2. The pseudo-element fills the link and draws a border using the --border-color custom property.
  3. The hidden state uses opacity: 0 and scale(.88).
  4. Hover, keyboard focus, or the active-page class changes opacity and scale.
  5. The CSS transition animates those changes. The visible label stays in the HTML.

Customize the navigation border

  • Change colors: edit --border-color on a link or its :nth-child() selector.
  • Use square corners: reduce border-radius on the link.
  • Highlight the current page: add is-current and aria-current="page" to the matching link.
  • Adjust motion: change the transition duration and easing while keeping movement restrained.

Accessibility and reduced motion

Do not make hover the only way to reveal the border. The example also styles :focus-visible for keyboard users and keeps the link label readable. The stylesheet honors prefers-reduced-motion because the animation is decorative and can be reduced without hiding information. See MDN’s guides to :hover and prefers-reduced-motion.

Common issues

The border appears behind the text

Keep the link positioned and the pseudo-element behind its contents. isolation: isolate confines that stacking layer to the link.

The effect does not work with the keyboard

Add :focus-visible alongside :hover. Keep a visible focus outline and check whether theme styles remove it.

The border appears on every link

The demo keeps the current page selected with .is-current. Remove that class from other links and apply it only to the active page.

Frequently asked questions

Does an animated CSS border menu need JavaScript?

No. CSS transitions and pseudo-elements create the border effect. JavaScript is not needed for hover, focus, or current-page styling.

Does this navigation work on touch screens?

Yes. The menu items are regular links and do not rely on hover to reveal essential information. The border is decorative.

Can I use the effect on buttons or cards?

Yes. Apply the positioned-element and pseudo-element pattern, then provide clear hover and keyboard-focus states for interactive controls.

How do I select the highlighted menu item?

Add .is-current and aria-current="page" to the link for the page being viewed. A server-rendered site can set these attributes from the current route.

Download the source code

Download the ZIP, extract the files into one folder, and open index.html in a browser. Replace the sample link destinations and labels before using the navigation on a live site.

References

CSS HOVER EFFECT

Animated border navigation

Hover over a link or use Tab and Shift+Tab to see its border animate.

Try moving keyboard focus between the links.

HTML: Navigation markup
<nav class="demo-nav" aria-label="Demo navigation">
  <a class="nav-link is-current" href="#home" aria-current="page">Home</a>
  <a class="nav-link" href="#about">About</a>
  <a class="nav-link" href="#services">Services</a>
  <a class="nav-link" href="#projects">Projects</a>
  <a class="nav-link" href="#contact">Contact</a>
</nav>
CSS: Animated border effect
.demo-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  padding: 14px;
  border: 1px solid #e3e9f4;
  border-radius: 18px;
  background: #f8faff;
}

.nav-link {
  --border-color: #315cc9;
  position: relative;
  isolation: isolate;
  display: inline-flex;
  min-height: 48px;
  align-items: center;
  justify-content: center;
  padding: 0 18px;
  border-radius: 10px;
  color: #263650;
  font-weight: 700;
  text-decoration: none;
}

.nav-link::after {
  position: absolute;
  z-index: -1;
  inset: 0;
  border: 2px solid var(--border-color);
  border-radius: inherit;
  content: "";
  opacity: 0;
  transform: scale(.88);
  transition: opacity .22s ease, transform .22s ease;
}

.nav-link:hover,
.nav-link:focus-visible,
.nav-link.is-current {
  color: #244db4;
  background: #eaf0ff;
}

.nav-link:hover::after,
.nav-link:focus-visible::after,
.nav-link.is-current::after {
  opacity: 1;
  transform: scale(1);
}

.nav-link:nth-child(2) { --border-color: #7c4dce; }
.nav-link:nth-child(3) { --border-color: #e06b55; }
.nav-link:nth-child(4) { --border-color: #138b8b; }
.nav-link:nth-child(5) { --border-color: #d24f91; }

.nav-link:focus-visible {
  outline: 3px solid #17233c;
  outline-offset: 4px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .01ms !important;
  }
}