Focus Dim Siblings
Hovering one card lifts it slightly while the others fade and soften, so attention lands on a single choice.
Create a card group where hovering one card dims its siblings, in pure CSS. Lay out three cards in a grid. Use :has() on the group: .group:has(.card:hover) .card:not(:hover) gets opacity 0.4, scale(0.97), and a 1px blur, while the hovered card gets translateY(-4px) and a stronger border. Do the same for :focus-within so keyboard focus behaves identically. Transition opacity, transform, and filter over 260ms with cubic-bezier(0.16, 1, 0.3, 1). Make each card a link or button so it can be focused. Under prefers-reduced-motion, keep the dimming but drop the scale, lift, and blur. The cards read "Starter", "Studio", and "Team".
Source
'use client'
import s from './demo.module.css'
export default function FocusDim({ labels }: { labels: Record<string, string> }) {
const plans = [
{ name: labels.a, price: 0 },
{ name: labels.b, price: 12 },
{ name: labels.c, price: 29 },
]
return (
<div className={s.group}>
{plans.map((p) => (
<a key={p.name} href="#" className={s.card} onClick={(e) => e.preventDefault()}>
<span className={s.name}>{p.name}</span>
<span className={s.price}>${p.price}</span>
<span className={s.per}>{labels.price}</span>
</a>
))}
</div>
)
}
.group {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
width: min(330px, 100%);
}
.card {
display: grid;
gap: 4px;
padding: 14px 12px;
border: 1px solid var(--stage-rule);
border-radius: 12px;
background: var(--stage-surface);
color: var(--stage-ink);
text-decoration: none;
transition: opacity 260ms var(--ease-out), transform 260ms var(--ease-out), filter 260ms var(--ease-out), border-color 260ms;
}
.card:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
.group:has(.card:hover) .card:not(:hover),
.group:has(.card:focus-visible) .card:not(:focus-visible) {
opacity: 0.4;
transform: scale(0.97);
filter: blur(1px);
}
.card:hover,
.card:focus-visible { transform: translateY(-4px); border-color: var(--stage-accent); }
.name { font: 500 var(--text-xs) / 1.2 var(--font-body); color: var(--stage-muted); }
.price { font: 700 var(--text-2xl) / 1 var(--font-display); }
.per { font: 500 0.625rem / 1 var(--font-mono); color: var(--stage-muted); text-transform: uppercase; }
@media (prefers-reduced-motion: reduce) {
.card, .card:hover, .card:focus-visible,
.group:has(.card:hover) .card:not(:hover),
.group:has(.card:focus-visible) .card:not(:focus-visible) { transform: none; filter: none; }
}