# Slide Highlight Nav

> Navigation where a highlight pill glides between links on hover or focus and fades out on leave.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/components/slide-highlight-nav
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/slide-highlight-nav.json
```

Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Dependencies

No extra dependencies.

## Usage

```tsx title="components/slide-highlight-nav-demo.tsx"
"use client"

import { SlideHighlightNav } from "@/components/slide-highlight-nav"

const links = [
  { label: "Overview", href: "#overview" },
  { label: "Components", href: "#components" },
  { label: "Templates", href: "#templates" },
  { label: "Pricing", href: "#pricing" },
  { label: "Docs", href: "#docs" },
]

export default function SlideHighlightNavDemo() {
  return <SlideHighlightNav links={links} defaultActive="#components" />
}
```

## Source

```tsx title="components/slide-highlight-nav.tsx"
"use client"

import * as React from "react"

import { cn } from "@/lib/utils"

export type SlideHighlightLink = { label: string; href: string }

export interface SlideHighlightNavProps extends Omit<React.ComponentProps<"nav">, "children"> {
  links: SlideHighlightLink[]
  defaultActive?: string
}

// A pill follows the pointer or keyboard focus from link to link and fades out when it leaves.
export function SlideHighlightNav({ links, defaultActive, className, ...props }: SlideHighlightNavProps) {
  const [active, setActive] = React.useState(defaultActive ?? links[0]?.href)
  const [box, setBox] = React.useState<{ left: number; width: number } | null>(null)
  const [shown, setShown] = React.useState(false)

  const point = (element: HTMLElement) => {
    setBox({ left: element.offsetLeft, width: element.offsetWidth })
    setShown(true)
  }

  return (
    <nav
      aria-label="Main"
      onPointerLeave={() => setShown(false)}
      onBlur={(event) => {
        if (!event.currentTarget.contains(event.relatedTarget)) setShown(false)
      }}
      className={cn("relative inline-flex max-w-full items-center overflow-x-auto rounded-full border bg-background p-1 text-foreground", className)}
      {...props}
    >
      <span
        aria-hidden
        className={cn("absolute inset-y-1 rounded-full bg-accent transition-[left,width,opacity] duration-300 ease-[cubic-bezier(0.32,0.72,0,1)]", shown ? "opacity-100" : "opacity-0")}
        style={{ left: box?.left ?? 0, width: box?.width ?? 0, ...(shown ? null : { transitionProperty: "opacity" }) }}
      />
      {links.map((link) => (
        <a
          key={link.href}
          href={link.href}
          aria-current={active === link.href ? "page" : undefined}
          onPointerEnter={(event) => point(event.currentTarget)}
          onFocus={(event) => point(event.currentTarget)}
          onClick={(event) => {
            event.preventDefault()
            setActive(link.href)
          }}
          className="relative z-10 rounded-full px-4 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground aria-[current=page]:text-foreground focus-visible:text-foreground focus-visible:outline-none"
        >
          {link.label}
          {active === link.href && <span aria-hidden className="absolute bottom-0.5 left-1/2 size-1 -translate-x-1/2 rounded-full bg-primary" />}
        </a>
      ))}
    </nav>
  )
}
```
