Fill Link
Full-width link row that fills on hover, inverts its text and nudges its content inward.
From the Nitip template — see it in contextimport { FillLink } from "@/components/ui/fill-link";
const links = [
{ label: "Instagram", meta: "@studio", href: "#" },
{ label: "Dribbble", meta: "/studio", href: "#" },
{ label: "LinkedIn", meta: "/company/studio", href: "#" },
];
export default function FillLinkDemo() {
return (
<div className="w-full max-w-sm">
{links.map((link) => (
<FillLink key={link.label} href={link.href} meta={link.meta}>
{link.label}
</FillLink>
))}
</div>
);
}Installation
$ npx shadcn@latest add https://framebrick.co/r/fill-link.json1. Install the dependencies.
npm install motion clsx tailwind-merge2. Add
cn()tolib/utils.ts(skip if shadcn already created it).import { clsx, type ClassValue } from "clsx"; import { extendTailwindMerge } from "tailwind-merge"; /* tailwind-merge must know the site's type scale (globals.css) is a font size, not a text color — otherwise cn("text-h6 text-muted-foreground") drops text-h6. */ const twMerge = extendTailwindMerge({ extend: { classGroups: { "font-size": [{ text: ["display", "h1", "h2", "h3", "h4", "h5", "h6"] }] } }, }); export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }3. Copy the component into
components/ui/fill-link.tsx."use client"; import * as React from "react"; import Link from "next/link"; import { motion, type HTMLMotionProps, type Transition } from "motion/react"; import { cn } from "@/lib/utils"; /* * Fill Link — a full-width row that fills on hover, inverts its text and nudges its content inward * (from the Nitip "Social Media" rows). Use it for lists of links: socials, contact details, big menus. * The arrow rotates -45° so it points right, with Framer's button spring. * * Deviation from Framer: the fill and the text color are a CSS transition rather than a Motion animation, so the row * follows the theme (bg-foreground / text-background) instead of fixed black and white. */ const spring: Transition = { type: "spring", bounce: 0.2, duration: 0.4 }; const MotionLink = motion.create(Link); export type FillLinkProps = Omit<HTMLMotionProps<"a">, "href" | "children"> & { href: string; /** Shown at the right of the row before the arrow (a handle, a value, a date). */ meta?: React.ReactNode; arrow?: boolean; children?: React.ReactNode; }; export function FillLink({ href, meta, arrow = true, className, children, ...props }: FillLinkProps) { const external = /^https?:\/\//.test(href); return ( <MotionLink href={href} {...(external ? { target: "_blank", rel: "noopener noreferrer" } : {})} {...props} initial="rest" animate="rest" whileHover="hover" whileFocus="hover" className={cn( "flex w-full items-center justify-between gap-4 border-b py-3 text-sm text-foreground transition-colors duration-300", "hover:bg-foreground hover:text-background focus-visible:bg-foreground focus-visible:text-background focus-visible:outline-none", className, )} > <motion.span variants={{ rest: { paddingLeft: 0 }, hover: { paddingLeft: 12 } }} transition={spring} className="min-w-0 truncate font-medium" > {children} </motion.span> <motion.span variants={{ rest: { paddingRight: 0 }, hover: { paddingRight: 12 } }} transition={spring} className="flex shrink-0 items-center gap-2" > {meta && <span className="opacity-60">{meta}</span>} {arrow && ( <motion.svg aria-hidden viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" className="size-4" variants={{ rest: { rotate: 0 }, hover: { rotate: -45 } }} transition={spring} > <path d="M4.5 4.5l7 7M11.5 5.5v6h-6" /> </motion.svg> )} </motion.span> </MotionLink> ); }
Usage
import { FillLink } from "@/components/ui/fill-link";
<FillLink href="https://instagram.com/studio" meta="@studio">Instagram</FillLink>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| href | string | — | Link target. http(s) links open in a new tab. |
| meta | ReactNode | — | Shown at the right of the row before the arrow (a handle, a value, a date). |
| arrow | boolean | true | Show the arrow that turns from ↘ to → on hover. |
| …props | anchor props | — | Passed to the link. |
- Keyboard focus fills the row too, so it never looks inert while tabbing.
AI prompt
Paste into v0, Lovable, Bolt or Claude to rebuild this component in your own project.
Create a React + Tailwind CSS + Motion (motion/react) component called FillLink using Next.js Link. - A full-width row: label on the left, optional meta and an arrow on the right, border-b, py-3, text-sm, text-foreground. - On hover and on keyboard focus the row fills: bg-foreground with text-background, as a 300ms CSS color transition (theme colors, not fixed black and white). - The label slides 12px right and the right-hand group slides 12px left, with a spring (bounce 0.2, duration 0.4s). - The arrow (down-right) rotates -45° on hover so it points right, same spring. - External http(s) links open in a new tab with rel noopener noreferrer.











