Flader
Hover highlight
Et gitter af kort, hvor én markering glider hen til kortet under musen eller med tastaturfokus.
Forhåndsvisning
Brug
hover-highlight-demo.tsx
import { HoverHighlight } from '@/components/hover-highlight'
const items = [
{
title: 'Websites',
text: 'Fast pages that are easy to find on Google.',
href: '#websites',
},
{
title: 'Shops',
text: 'Sell online with payments and stock in one place.',
href: '#shops',
},
{
title: 'Bookings',
text: 'Customers book a time without calling.',
href: '#bookings',
},
{
title: 'Hosting',
text: 'Updates, backups and monitoring included.',
href: '#hosting',
},
{
title: 'SEO',
text: 'Structured data, speed and local search.',
href: '#seo',
},
{
title: 'Support',
text: 'A real person who answers the same day.',
href: '#support',
},
]
export default function HoverHighlightDemo() {
return <HoverHighlight items={items} className="lg:grid-cols-2" />
}Kilde
hover-highlight.tsx
import { AnimatePresence, LayoutGroup, MotionConfig, motion } from 'motion/react'
import { useId, useState, type ReactNode } from 'react'
import { cn } from '@/lib/utils'
export type HoverHighlightItem = {
title: string
text: ReactNode
href?: string
}
export type HoverHighlightProps = {
items: HoverHighlightItem[]
className?: string
}
/**
* A grid of cards where one soft highlight glides to the card under the pointer or with keyboard focus.
* Under reduced motion the highlight appears on the card without sliding.
*/
export function HoverHighlight({ items, className }: HoverHighlightProps) {
const [on, setOn] = useState<number | null>(null)
const group = useId()
return (
<MotionConfig reducedMotion="user">
<LayoutGroup id={group}>
<ul className={cn('grid gap-2 sm:grid-cols-2 lg:grid-cols-3', className)} onPointerLeave={() => setOn(null)}>
{items.map((item, i) => {
const Tag = item.href ? 'a' : 'div'
return (
<li key={item.title} className="relative" onPointerEnter={() => setOn(i)}>
<AnimatePresence>
{on === i && (
<motion.span
layoutId="highlight"
aria-hidden
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{
opacity: 0,
transition: { duration: 0.15, delay: 0.1 },
}}
transition={{
type: 'spring',
stiffness: 420,
damping: 36,
}}
className="absolute inset-0 rounded-2xl bg-muted"
/>
)}
</AnimatePresence>
<Tag
{...(item.href ? { href: item.href } : {})}
onFocus={() => setOn(i)}
onBlur={() => setOn(null)}
className="relative grid h-full gap-1 rounded-2xl border bg-card/60 p-5 outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
<span className="font-medium">{item.title}</span>
<span className="text-sm text-muted-foreground">{item.text}</span>
</Tag>
</li>
)
})}
</ul>
</LayoutGroup>
</MotionConfig>
)
}Installér
npx shadcn@latest add https://mikkelmanniche.dk/lab/r/hover-highlight.jsonMed registret tilføjet (components.json)
npx shadcn@latest add @manniche/hover-highlight