Menu Links That Dim or Blur Their Siblings on Hover

A navigation effect that draws the eye by pushing everything else back. When one link is hovered or focused, the other links in the same group fade to low opacity or go out of focus with a blur, and return when the pointer leaves. It is shown twice: in a small row of header links and in a full menu of large serif titles with a number, an arrow and a short description each. A switch changes the large menu between blur and dim. The effect itself is three CSS rules built on the `:has` selector, with no JavaScript. On phones the descriptions are hidden and the titles shrink.

Code

// Adapted from Animata (https://animata.design/docs/container/sibling-focus-nav)
// Copyright (c) Animata. MIT License: https://we.inc/components/licenses
import { useEffect, useState } from "react";
import { ArrowUpRight } from "lucide-react";

const links = [
  { label: "Selected work", detail: "Fourteen projects, 2019 to now" },
  { label: "The studio", detail: "Six people above a bakery in Leith" },
  { label: "Journal", detail: "Notes on type, print and process" },
  { label: "Workshops", detail: "Letterpress weekends, spring dates" },
  { label: "Say hello", detail: "New projects from January" },
];

const quickLinks = ["Index", "Shop", "Press", "Contact"];

type Mode = "dim" | "blur";

export default function MenuLinksThatDimOrBlurSiblings() {
  const [mode, setMode] = useState<Mode>("blur");
  // Until someone points at the menu, a pretend hover walks down the list.
  const [demo, setDemo] = useState<number | null>(0);

  useEffect(() => {
    if (demo === null || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const timer = setTimeout(() => setDemo((i) => (i === null ? null : (i + 1) % links.length)), 2400);
    return () => clearTimeout(timer);
  }, [demo]);

  const stopDemo = () => setDemo(null);

  return (
    <section className="bg-[#10231c] px-5 py-8 text-[#f3ecdc] sm:px-10 sm:py-10" onPointerEnter={stopDemo} onFocus={stopDemo}>
      <style>{`
        .sibling-dim-menu a { transition: opacity 280ms ease-out, filter 280ms ease-out; }
        /* The whole effect: when one link is hovered or focused, style every other link in the same group. */
        .sibling-dim-menu[data-mode="dim"]:has(a:hover) a:not(:hover),
        .sibling-dim-menu[data-mode="dim"]:has(a:focus-visible) a:not(:focus-visible),
        .sibling-dim-menu[data-mode="dim"][data-demo] a:not([data-on]) { opacity: 0.28; }
        .sibling-dim-menu[data-mode="blur"]:has(a:hover) a:not(:hover),
        .sibling-dim-menu[data-mode="blur"]:has(a:focus-visible) a:not(:focus-visible),
        .sibling-dim-menu[data-mode="blur"][data-demo] a:not([data-on]) { filter: blur(4px); opacity: 0.5; }
        .sibling-dim-menu a .sibling-dim-menu-arrow { transition: transform 280ms ease-out, opacity 280ms ease-out; opacity: 0; transform: translate(-8px, 8px); }
        .sibling-dim-menu a:hover .sibling-dim-menu-arrow,
        .sibling-dim-menu a:focus-visible .sibling-dim-menu-arrow,
        .sibling-dim-menu a[data-on] .sibling-dim-menu-arrow { opacity: 1; transform: none; }
        @media (prefers-reduced-motion: reduce) {
          .sibling-dim-menu a, .sibling-dim-menu a .sibling-dim-menu-arrow { transition: none; }
          .sibling-dim-menu[data-mode="blur"]:has(a:hover) a:not(:hover),
          .sibling-dim-menu[data-mode="blur"]:has(a:focus-visible) a:not(:focus-visible) { filter: none; opacity: 0.45; }
        }
      `}</style>
      <div className="mx-auto max-w-5xl">
        <div className="flex flex-wrap items-center justify-between gap-x-6 gap-y-3 border-b border-[#f3ecdc]/20 pb-5">
          <a href="#" className="rounded font-serif text-xl italic focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#f3ecdc]">
            Morrow &amp; Pine
          </a>
          {/* Small row: links are spaced with padding, not gaps, so the hover never drops between them. */}
          <nav aria-label="Quick links" data-mode="dim" className="sibling-dim-menu -mx-3 flex">
            {quickLinks.map((label) => (
              <a key={label} href="#" className="inline-flex min-h-[44px] items-center rounded px-3 text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#f3ecdc]">
                {label}
              </a>
            ))}
          </nav>
        </div>

        <div className="mt-6 flex flex-wrap items-center justify-between gap-3">
          <p className="text-xs font-medium uppercase tracking-[0.2em] text-[#f3ecdc]/70">Menu</p>
          <div role="group" aria-label="Effect on the other links" className="flex rounded-full border border-[#f3ecdc]/30 p-1">
            {(["blur", "dim"] as Mode[]).map((m) => (
              <button
                key={m}
                type="button"
                aria-pressed={mode === m}
                onClick={() => setMode(m)}
                className={`h-10 rounded-full px-4 text-sm font-medium capitalize transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#f3ecdc] ${
                  mode === m ? "bg-[#f3ecdc] text-[#10231c]" : "text-[#f3ecdc] hover:bg-[#f3ecdc]/10"
                }`}
              >
                {m}
              </button>
            ))}
          </div>
        </div>

        <nav aria-label="Main" data-mode={mode} data-demo={demo === null ? undefined : ""} className="sibling-dim-menu mt-2 flex flex-col">
          {links.map((link, i) => (
            <a
              key={link.label}
              href="#"
              data-on={demo === i ? "" : undefined}
              className="group flex items-baseline gap-4 rounded-lg py-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#f3ecdc] sm:gap-6 sm:py-2.5"
            >
              <span className="w-6 shrink-0 text-xs tabular-nums text-[#f3ecdc]/70 sm:w-8 sm:text-sm">0{i + 1}</span>
              <span className="font-serif text-4xl leading-none tracking-tight sm:text-6xl">{link.label}</span>
              <ArrowUpRight aria-hidden="true" className="sibling-dim-menu-arrow hidden h-8 w-8 shrink-0 self-center text-[#e8b86d] sm:block" />
              <span className="ml-auto hidden max-w-[14rem] text-right text-sm text-[#f3ecdc]/80 md:block">{link.detail}</span>
            </a>
          ))}
        </nav>

        <p className="mt-6 border-t border-[#f3ecdc]/20 pt-5 text-sm text-[#f3ecdc]/80">
          Point at a link, or tab through the menu: the one you are on stays sharp and the rest step back.
        </p>
      </div>
    </section>
  );
}

Install with one command: npx shadcn@latest add https://we.inc/r/menu-links-that-dim-or-blur-their-siblings-on-hover.json

When to use it

How to customize it

Accessibility

Questions

How does one link change the style of the others?

The rule reads: when the group has a link that is hovered, style every link in it that is not hovered. In CSS that is `.group:has(a:hover) a:not(:hover)`. The `:has` part lets the parent react to a child, which older CSS could not do.

Which browsers support the :has selector?

All current versions of Chrome, Edge, Safari and Firefox support it. In an old browser without support the rule is ignored and the links simply stay at full strength, so nothing breaks.

Why are the links spaced with padding and not gaps?

A gap between links is empty space that belongs to no link, so the hover would drop for a moment as the pointer crosses it and every link would flash back. Padding keeps the pointer over a link the whole way.

What is the moving highlight when the page loads?

It is a small demo that pretends to hover each link in turn so the effect is visible without a mouse. It stops for good as soon as a pointer or keyboard focus enters the section. Remove the `demo` state and the two `data` attributes to drop it.

Does the blur hurt performance?

A blur filter on a handful of text links is cheap. Avoid applying it to dozens of large elements or to images at once, where it can cost frames on low-powered phones; use the dim mode there.

Adapted from Sibling Focus Nav in Animata, used under its open-source licence. Licences and credits

Related: Menu Links with Rolling Letter Swap on Hover, Link Underline That Fills Into a Highlight on Hover, Navbar with Centered Links, CTA Button and Mobile Menu.

More tabs and navigation or all free UI components. Start for free to have a full site built around it.

More in Navigation

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap