Skip to content

Dim Group

Point at one item of a group and the others dim, so the one you are on is the one you read.

$npx shadcn@latest add https://framebrick.co/r/dim-group.json

From the Lookis template, see it in context.

Examples

Service rows

Rows with a price and a round arrow: the others dim while the one you are on fills its arrow.

<DimGroup as="ul" className="divide-y border-y">
  {services.map((service) => (
    <li key={service.title} className="group/row flex items-center gap-3 py-3">
      <a href={service.href} className="flex-1">{service.title}</a>
      <span className="grid size-8 place-items-center rounded-full border group-hover/row:bg-foreground group-hover/row:text-background">
        <ArrowUpRight className="size-4 group-hover/row:rotate-45" />
      </span>
    </li>
  ))}
</DimGroup>

Installation

$ npx shadcn@latest add https://framebrick.co/r/dim-group.json

Usage

import { DimGroup } from "@/components/ui/dim-group";

<DimGroup as="ul" className="flex flex-col gap-1 text-lg">
  {links.map((link) => (
    <li key={link.href}>
      <a href={link.href}>{link.label}</a>
    </li>
  ))}
</DimGroup>

Props

PropTypeDefaultDescription
as"div" | "ul" | "ol" | "nav""div"The element drawn. Its direct children are the items.
dimnumber0.65Opacity of the items that are not pointed at, 0 to 1. The templates use 0.5; see the notes on contrast.
…propsHTML attributes—Passed to the element (className lays the items out).
  • Plain CSS: no JavaScript and no animation library, so it works on a server page. It needs :has(), which every current browser has (Firefox since 121).
  • The direct children are the items. The one under the pointer, or holding the focus ring, keeps full opacity; moving to another swaps them, and leaving the group brings every item back. Hover only applies where the device can hover; keyboard focus works everywhere.
  • The fade is Framer's spring (bounce 0, 0.4 s) as a CSS linear() curve, within 1% of it. A fade stays with reduced motion.
  • Opacity multiplies the text with it. Foreground text at 50% (what the templates use) measures 3.2 to 5.2:1 depending on the theme, at 60% 4.4:1 in the worst, and at 65% 5.1:1 or better in the four sample themes, light and dark: that is why 0.65 is the default. Text in muted-foreground is under 4.5:1 at any dim (2.5:1 at 65%), so give secondary text its own tone.
  • Lookis dims its service rows (spring, bounce 0) and its footer links (bounce 0.2) to half. Marai Neesu's social tiles fade to 30% while the hovered one shows its icon: that is a composition of its own, with group-hover classes on the items.

AI prompt

Paste into v0, Lovable, Bolt or Claude to rebuild this component in your own project.

Create a React + Tailwind CSS component called DimGroup — plain CSS, no JavaScript and no animation library, so it works in a server component.
- Props: as ("div" | "ul" | "ol" | "nav", div by default), dim (number, default 0.65), className and the HTML attributes. It renders that element with the dim as the CSS variable --dim.
- The direct children are the items. On a device that can hover, when a child is hovered every other child goes to opacity var(--dim): [@media(hover:hover)]:[&:has(>:hover)>:not(:hover)]:opacity-(--dim).
  The same for keyboard focus, as two rules (Tailwind does not generate a comma inside an arbitrary variant): a child that has :focus-visible itself,
  [&:has(>:focus-visible)>:not(:focus-visible)]:opacity-(--dim), and something inside a child that has it, [&:has(>*_:focus-visible)>:not(:has(:focus-visible))]:opacity-(--dim).
- Each child transitions opacity over 400ms on a spring (bounce 0) written as a CSS linear() curve, with cubic-bezier(0.22, 1, 0.36, 1) as the fallback for a browser without linear(). The fade stays with reduced motion.
- Keep the opacity at 0.65 or more for text so it keeps 4.5:1 on the page; muted-foreground text cannot be dimmed and keep it.

Be the first to know

Get new templates and components first

One short email when we release a template or a free component. No spam — unsubscribe anytime.

We use your email only for Framebrick updates. Privacy policy