NavLink

Navigation link primitive. Distinct from Button because nav items communicate current page, group visually, and don't need the button's depth or weight. Reads only semantic tokens, so wrapping the nav in .dark flips it for free.

$npx shadcn@latest add https://ui.btng.studio/r/nav-link

Default group

A typical horizontal nav. The active link gets full foreground; others stay muted. aria-current='page' is set automatically.

On a dark surface

Wrap the navbar in .dark and every link flips automatically. No variant prop, no override — semantic tokens do the work.

Sizes

sm (28) · default (32) · lg (36). Default matches the 32px nav button spec from Untitled UI.

With framework Link

Use asChild to render as a Next.js Link (or React Router Link, etc.) while keeping the styling.

Reasoning

Navigation links are not buttons. Teams that reuse variant="ghost" for navbars always hit the same wall: ghost buttons are surface-relative, so dropping the navbar onto a dark hero section renders them invisible. The typical fix is either a bespoke ghost-inverse variant or inline className overrides. Neither scales.

NavLink sidesteps the problem by reading only semantic tokens..dark as a local scope flips those tokens for everything inside, so the same component works on light, dark, and future schemes without adding API surface.

The active prop does two things that always get forgotten: it sets aria-current="page" so assistive tech announces the current page, and it bumps the text to full foreground so it reads as the current location in the nav stack.