Tailwind + JSMIT licensed

Tailwind Active Nav Link with Gradient Text

A dashboard header where the current page is styled entirely from aria-current="page": a tinted pill, gradient label and a gradient underline that scales in. The script only moves one attribute — every visual lives in aria-[current=page]: and group-aria-[current=page]: variants, so styling and accessibility can never disagree.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="ttg-07 w-full min-h-svh bg-ttg07-bg font-ttg07-sans text-ttg07-ink" aria-label="Active navigation link gradient demo" data-ttg07>
  <header class="border-b border-ttg07-line bg-white">
    <div class="mx-auto flex h-16 max-w-5xl items-center gap-6 px-5">
      <a href="#" class="flex flex-none items-center gap-2 text-[17px] font-extrabold tracking-[-0.02em]"><span aria-hidden="true" class="grid size-7 place-items-center rounded-lg bg-linear-135 from-ttg07-a to-ttg07-b text-xs text-white">N</span>Nexus</a>
      <nav class="-mx-1 flex flex-1 gap-1 overflow-x-auto px-1 [scrollbar-width:none]" aria-label="Main">
        <a href="#" data-ttg07-link class="group relative inline-flex min-h-11 flex-none items-center rounded-lg px-3.5 text-[14.5px] font-medium text-ttg07-mut transition-colors after:absolute after:inset-x-3 after:-bottom-[10px] after:h-0.5 after:origin-center after:scale-x-0 after:rounded-full after:bg-linear-to-r after:from-ttg07-a after:to-ttg07-b after:transition-transform after:duration-300 hover:bg-ttg07-ink/5 hover:text-ttg07-ink focus-visible:outline-2 focus-visible:outline-ttg07-a aria-[current=page]:bg-ttg07-a/8 aria-[current=page]:font-semibold aria-[current=page]:after:scale-x-100 motion-reduce:after:transition-none"><span class="group-aria-[current=page]:bg-linear-to-r group-aria-[current=page]:from-ttg07-a group-aria-[current=page]:to-ttg07-b group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent">Dashboard</span></a>
        <a href="#" data-ttg07-link aria-current="page" class="group relative inline-flex min-h-11 flex-none items-center rounded-lg px-3.5 text-[14.5px] font-medium text-ttg07-mut transition-colors after:absolute after:inset-x-3 after:-bottom-[10px] after:h-0.5 after:origin-center after:scale-x-0 after:rounded-full after:bg-linear-to-r after:from-ttg07-a after:to-ttg07-b after:transition-transform after:duration-300 hover:bg-ttg07-ink/5 hover:text-ttg07-ink focus-visible:outline-2 focus-visible:outline-ttg07-a aria-[current=page]:bg-ttg07-a/8 aria-[current=page]:font-semibold aria-[current=page]:after:scale-x-100 motion-reduce:after:transition-none"><span class="group-aria-[current=page]:bg-linear-to-r group-aria-[current=page]:from-ttg07-a group-aria-[current=page]:to-ttg07-b group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent">Analytics</span></a>
        <a href="#" data-ttg07-link class="group relative inline-flex min-h-11 flex-none items-center rounded-lg px-3.5 text-[14.5px] font-medium text-ttg07-mut transition-colors after:absolute after:inset-x-3 after:-bottom-[10px] after:h-0.5 after:origin-center after:scale-x-0 after:rounded-full after:bg-linear-to-r after:from-ttg07-a after:to-ttg07-b after:transition-transform after:duration-300 hover:bg-ttg07-ink/5 hover:text-ttg07-ink focus-visible:outline-2 focus-visible:outline-ttg07-a aria-[current=page]:bg-ttg07-a/8 aria-[current=page]:font-semibold aria-[current=page]:after:scale-x-100 motion-reduce:after:transition-none"><span class="group-aria-[current=page]:bg-linear-to-r group-aria-[current=page]:from-ttg07-a group-aria-[current=page]:to-ttg07-b group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent">Projects</span></a>
        <a href="#" data-ttg07-link class="group relative inline-flex min-h-11 flex-none items-center rounded-lg px-3.5 text-[14.5px] font-medium text-ttg07-mut transition-colors after:absolute after:inset-x-3 after:-bottom-[10px] after:h-0.5 after:origin-center after:scale-x-0 after:rounded-full after:bg-linear-to-r after:from-ttg07-a after:to-ttg07-b after:transition-transform after:duration-300 hover:bg-ttg07-ink/5 hover:text-ttg07-ink focus-visible:outline-2 focus-visible:outline-ttg07-a aria-[current=page]:bg-ttg07-a/8 aria-[current=page]:font-semibold aria-[current=page]:after:scale-x-100 motion-reduce:after:transition-none"><span class="group-aria-[current=page]:bg-linear-to-r group-aria-[current=page]:from-ttg07-a group-aria-[current=page]:to-ttg07-b group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent">Settings</span></a>
      </nav>
      <span aria-hidden="true" class="grid size-9 flex-none place-items-center rounded-full bg-linear-135 from-ttg07-a to-ttg07-b text-xs font-bold text-white ring-2 ring-white ring-offset-1 ring-offset-ttg07-line max-sm:hidden">AK</span>
    </div>
  </header>
  <main class="mx-auto max-w-5xl px-5 py-10">
    <h1 class="text-[28px] font-extrabold tracking-[-0.03em]"><span data-ttg07-page>Analytics</span> <span class="bg-linear-to-r from-ttg07-a to-ttg07-b bg-clip-text text-transparent">overview</span></h1>
    <p class="mt-1.5 text-[15px] text-ttg07-mut">Your performance at a glance for the last 30 days.</p>
    <dl class="mt-8 grid gap-4 sm:grid-cols-3">
      <div class="flex flex-col-reverse gap-1 rounded-2xl border border-ttg07-line bg-white p-5 shadow-[0_1px_2px_rgb(0_0_0/0.04)]"><dt class="text-[13px] text-ttg07-mut">Revenue</dt><dd class="text-[28px] font-bold tracking-tight tabular-nums">$124K</dd></div>
      <div class="flex flex-col-reverse gap-1 rounded-2xl border border-ttg07-line bg-white p-5 shadow-[0_1px_2px_rgb(0_0_0/0.04)]"><dt class="text-[13px] text-ttg07-mut">Sessions</dt><dd class="text-[28px] font-bold tracking-tight tabular-nums">8,432</dd></div>
      <div class="flex flex-col-reverse gap-1 rounded-2xl border border-ttg07-line bg-white p-5 shadow-[0_1px_2px_rgb(0_0_0/0.04)]"><dt class="text-[13px] text-ttg07-mut">Conversion</dt><dd class="text-[28px] font-bold tracking-tight tabular-nums">3.4%</dd></div>
    </dl>
    <p class="mt-6 text-[13px] text-ttg07-mut">Click any nav item — the script only moves <code class="rounded bg-ttg07-ink/5 px-1.5 py-0.5 text-ttg07-ink">aria-current</code>.</p>
  </main>
</section>
@import "tailwindcss";

@theme {
  --color-ttg07-bg: #f6f8fb;
  --color-ttg07-bg: oklch(0.977 0.005 255);
  --color-ttg07-ink: #0f1626;
  --color-ttg07-ink: oklch(0.2 0.03 260);
  --color-ttg07-mut: #556072;
  --color-ttg07-mut: oklch(0.48 0.03 260);
  --color-ttg07-line: #e2e7ef;
  --color-ttg07-line: oklch(0.925 0.01 260);
  --color-ttg07-a: #2f6cf0;
  --color-ttg07-a: oklch(0.57 0.2 262);
  --color-ttg07-b: #8a4cf0;
  --color-ttg07-b: oklch(0.57 0.23 295);
  --font-ttg07-sans: 'Geist', system-ui, sans-serif;
}

.ttg-07 {
  width: 100%;
  min-height: 100svh;
}
(() => {
  document.querySelectorAll('[data-ttg07]').forEach((root) => {
    if (root.dataset.wired) return;
    root.dataset.wired = '1';
    const links = root.querySelectorAll('[data-ttg07-link]');
    const page = root.querySelector('[data-ttg07-page]');
    links.forEach((link) => {
      link.addEventListener('click', (e) => {
        e.preventDefault();
        links.forEach((l) => l.removeAttribute('aria-current'));
        link.setAttribute('aria-current', 'page');
        if (page) page.textContent = link.textContent.trim();
      });
    });
  });
})();

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-ttg07-bg: #f6f8fb;
  --color-ttg07-bg: oklch(0.977 0.005 255);
  --color-ttg07-ink: #0f1626;
  --color-ttg07-ink: oklch(0.2 0.03 260);
  --color-ttg07-mut: #556072;
  --color-ttg07-mut: oklch(0.48 0.03 260);
  --color-ttg07-line: #e2e7ef;
  --color-ttg07-line: oklch(0.925 0.01 260);
  --color-ttg07-a: #2f6cf0;
  --color-ttg07-a: oklch(0.57 0.2 262);
  --color-ttg07-b: #8a4cf0;
  --color-ttg07-b: oklch(0.57 0.23 295);
  --font-ttg07-sans: 'Geist', system-ui, sans-serif;
}

.ttg-07 {
  width: 100%;
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400..800&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • .tg-07__link--active and its two pseudo-element rules became aria-[current=page]: variants — the class is gone and the state is now announced to assistive tech.
  • The original put bg-clip-text and the tint on the same element, then rebuilt the tint in ::after at z-index:-1; the port splits pill and label across link and span.
  • UI upgrade: the underline scales in from the centre, the page heading follows the active link, and the nav scrolls horizontally on phones instead of overflowing.
  • UI upgrade: 44px nav targets, focus rings, tabular KPI numbers and a semantic <dl>.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Active Nav Link with Gradient Text
Source: https://codefronts.com/motion/tailwind-css-text-gradient/tailwind-active-nav-link-with-gradient-text/

A dashboard header where the current page is styled entirely from aria-current="page": a tinted pill, gradient label and a gradient underline that scales in. The script only moves one attribute &mdash; every visual lives in aria-[current=page]: and group-aria-[current=page]: variants, so styling and accessibility can never disagree.
## HTML
```html
<section class="ttg-07 w-full min-h-svh bg-ttg07-bg font-ttg07-sans text-ttg07-ink" aria-label="Active navigation link gradient demo" data-ttg07>
  <header class="border-b border-ttg07-line bg-white">
    <div class="mx-auto flex h-16 max-w-5xl items-center gap-6 px-5">
      <a href="#" class="flex flex-none items-center gap-2 text-[17px] font-extrabold tracking-[-0.02em]"><span aria-hidden="true" class="grid size-7 place-items-center rounded-lg bg-linear-135 from-ttg07-a to-ttg07-b text-xs text-white">N</span>Nexus</a>
      <nav class="-mx-1 flex flex-1 gap-1 overflow-x-auto px-1 [scrollbar-width:none]" aria-label="Main">
        <a href="#" data-ttg07-link class="group relative inline-flex min-h-11 flex-none items-center rounded-lg px-3.5 text-[14.5px] font-medium text-ttg07-mut transition-colors after:absolute after:inset-x-3 after:-bottom-[10px] after:h-0.5 after:origin-center after:scale-x-0 after:rounded-full after:bg-linear-to-r after:from-ttg07-a after:to-ttg07-b after:transition-transform after:duration-300 hover:bg-ttg07-ink/5 hover:text-ttg07-ink focus-visible:outline-2 focus-visible:outline-ttg07-a aria-[current=page]:bg-ttg07-a/8 aria-[current=page]:font-semibold aria-[current=page]:after:scale-x-100 motion-reduce:after:transition-none"><span class="group-aria-[current=page]:bg-linear-to-r group-aria-[current=page]:from-ttg07-a group-aria-[current=page]:to-ttg07-b group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent">Dashboard</span></a>
        <a href="#" data-ttg07-link aria-current="page" class="group relative inline-flex min-h-11 flex-none items-center rounded-lg px-3.5 text-[14.5px] font-medium text-ttg07-mut transition-colors after:absolute after:inset-x-3 after:-bottom-[10px] after:h-0.5 after:origin-center after:scale-x-0 after:rounded-full after:bg-linear-to-r after:from-ttg07-a after:to-ttg07-b after:transition-transform after:duration-300 hover:bg-ttg07-ink/5 hover:text-ttg07-ink focus-visible:outline-2 focus-visible:outline-ttg07-a aria-[current=page]:bg-ttg07-a/8 aria-[current=page]:font-semibold aria-[current=page]:after:scale-x-100 motion-reduce:after:transition-none"><span class="group-aria-[current=page]:bg-linear-to-r group-aria-[current=page]:from-ttg07-a group-aria-[current=page]:to-ttg07-b group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent">Analytics</span></a>
        <a href="#" data-ttg07-link class="group relative inline-flex min-h-11 flex-none items-center rounded-lg px-3.5 text-[14.5px] font-medium text-ttg07-mut transition-colors after:absolute after:inset-x-3 after:-bottom-[10px] after:h-0.5 after:origin-center after:scale-x-0 after:rounded-full after:bg-linear-to-r after:from-ttg07-a after:to-ttg07-b after:transition-transform after:duration-300 hover:bg-ttg07-ink/5 hover:text-ttg07-ink focus-visible:outline-2 focus-visible:outline-ttg07-a aria-[current=page]:bg-ttg07-a/8 aria-[current=page]:font-semibold aria-[current=page]:after:scale-x-100 motion-reduce:after:transition-none"><span class="group-aria-[current=page]:bg-linear-to-r group-aria-[current=page]:from-ttg07-a group-aria-[current=page]:to-ttg07-b group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent">Projects</span></a>
        <a href="#" data-ttg07-link class="group relative inline-flex min-h-11 flex-none items-center rounded-lg px-3.5 text-[14.5px] font-medium text-ttg07-mut transition-colors after:absolute after:inset-x-3 after:-bottom-[10px] after:h-0.5 after:origin-center after:scale-x-0 after:rounded-full after:bg-linear-to-r after:from-ttg07-a after:to-ttg07-b after:transition-transform after:duration-300 hover:bg-ttg07-ink/5 hover:text-ttg07-ink focus-visible:outline-2 focus-visible:outline-ttg07-a aria-[current=page]:bg-ttg07-a/8 aria-[current=page]:font-semibold aria-[current=page]:after:scale-x-100 motion-reduce:after:transition-none"><span class="group-aria-[current=page]:bg-linear-to-r group-aria-[current=page]:from-ttg07-a group-aria-[current=page]:to-ttg07-b group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent">Settings</span></a>
      </nav>
      <span aria-hidden="true" class="grid size-9 flex-none place-items-center rounded-full bg-linear-135 from-ttg07-a to-ttg07-b text-xs font-bold text-white ring-2 ring-white ring-offset-1 ring-offset-ttg07-line max-sm:hidden">AK</span>
    </div>
  </header>
  <main class="mx-auto max-w-5xl px-5 py-10">
    <h1 class="text-[28px] font-extrabold tracking-[-0.03em]"><span data-ttg07-page>Analytics</span> <span class="bg-linear-to-r from-ttg07-a to-ttg07-b bg-clip-text text-transparent">overview</span></h1>
    <p class="mt-1.5 text-[15px] text-ttg07-mut">Your performance at a glance for the last 30 days.</p>
    <dl class="mt-8 grid gap-4 sm:grid-cols-3">
      <div class="flex flex-col-reverse gap-1 rounded-2xl border border-ttg07-line bg-white p-5 shadow-[0_1px_2px_rgb(0_0_0/0.04)]"><dt class="text-[13px] text-ttg07-mut">Revenue</dt><dd class="text-[28px] font-bold tracking-tight tabular-nums">$124K</dd></div>
      <div class="flex flex-col-reverse gap-1 rounded-2xl border border-ttg07-line bg-white p-5 shadow-[0_1px_2px_rgb(0_0_0/0.04)]"><dt class="text-[13px] text-ttg07-mut">Sessions</dt><dd class="text-[28px] font-bold tracking-tight tabular-nums">8,432</dd></div>
      <div class="flex flex-col-reverse gap-1 rounded-2xl border border-ttg07-line bg-white p-5 shadow-[0_1px_2px_rgb(0_0_0/0.04)]"><dt class="text-[13px] text-ttg07-mut">Conversion</dt><dd class="text-[28px] font-bold tracking-tight tabular-nums">3.4%</dd></div>
    </dl>
    <p class="mt-6 text-[13px] text-ttg07-mut">Click any nav item — the script only moves <code class="rounded bg-ttg07-ink/5 px-1.5 py-0.5 text-ttg07-ink">aria-current</code>.</p>
  </main>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-ttg07-bg: #f6f8fb;
  --color-ttg07-bg: oklch(0.977 0.005 255);
  --color-ttg07-ink: #0f1626;
  --color-ttg07-ink: oklch(0.2 0.03 260);
  --color-ttg07-mut: #556072;
  --color-ttg07-mut: oklch(0.48 0.03 260);
  --color-ttg07-line: #e2e7ef;
  --color-ttg07-line: oklch(0.925 0.01 260);
  --color-ttg07-a: #2f6cf0;
  --color-ttg07-a: oklch(0.57 0.2 262);
  --color-ttg07-b: #8a4cf0;
  --color-ttg07-b: oklch(0.57 0.23 295);
  --font-ttg07-sans: 'Geist', system-ui, sans-serif;
}

.ttg-07 {
  width: 100%;
  min-height: 100svh;
}
```

## JavaScript
```js
(() => {
  document.querySelectorAll('[data-ttg07]').forEach((root) => {
    if (root.dataset.wired) return;
    root.dataset.wired = '1';
    const links = root.querySelectorAll('[data-ttg07-link]');
    const page = root.querySelector('[data-ttg07-page]');
    links.forEach((link) => {
      link.addEventListener('click', (e) => {
        e.preventDefault();
        links.forEach((l) => l.removeAttribute('aria-current'));
        link.setAttribute('aria-current', 'page');
        if (page) page.textContent = link.textContent.trim();
      });
    });
  });
})();
```

How this works

Why two elements. bg-clip-text consumes the element's background, so the tinted pill and the gradient text can't share one box. The link owns the pill; an inner span owns the gradient:

<a aria-current="page" class="group … aria-[current=page]:bg-ttg07-a/8">
  <span class="group-aria-[current=page]:bg-linear-to-r
    group-aria-[current=page]:bg-clip-text group-aria-[current=page]:text-transparent …">Analytics</span>
</a>

The attribute is the state. The original toggled a --active class and never set aria-current, so screen readers didn't know which page was current. Here the script sets aria-current and nothing else; styles follow.

Without JS the server-rendered aria-current still styles the right link.

Make it yours

  • Top-tab style: change after:-bottom-[11px] to after:-top-[11px].
  • Vertical sidebar: replace the underline with after:inset-y-2 after:left-0 after:w-[3px] after:scale-y-0 aria-[current=page]:after:scale-y-100.
  • Stronger pill: raise /8 to /14.
  • Router integration: set aria-current from your router's active-match helper instead of the click handler.

Gotchas — read before shipping

  • Use aria-current="page" for navigation, aria-selected for tabs — they are different ARIA patterns with different keyboard expectations.
  • group-aria-* reads the nearest .group; name it (group/nav) if links sit inside another group.
  • The underline offset depends on the header height — keep h-16 and min-h-11 in sync if you change either.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Attribute-selector variants are universal; the JS is progressive — without it the server-set state still renders.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…