Docs
Metis textMetis text
Hover draws an underline in from the left; on exit it retracts back out to the right.
requires interactionhover
Loading...
Installation
CLI
pnpm dlx shadcn@latest add https://animata.design/r/text/metis-text.json
The CLI installs metis-text.tsx and metis-text.css together.
Manual
Run the following command
mkdir -p animata/text && touch animata/text/metis-text.tsx animata/text/metis-text.cssPaste the code
@layer components {
.metis-text::after {
content: "";
position: absolute;
left: 0;
top: 100%;
width: 100%;
height: 1px;
background: currentcolor;
pointer-events: none;
transform-origin: 100% 50%;
transform: scale3d(0, 1, 1);
transition: transform 0.3s;
}
.metis-text:hover::after,
.metis-text:focus-visible::after {
transform-origin: 0% 50%;
transform: scale3d(1, 1, 1);
}
.group:hover .metis-text--group::after,
.group:focus-visible .metis-text--group::after {
transform-origin: 0% 50%;
transform: scale3d(1, 1, 1);
}
@media (prefers-reduced-motion: reduce) {
.metis-text::after {
transition: none;
}
}
}import type React from "react";
import { cn } from "@/lib/utils";
import "./metis-text.css";
export interface MetisTextProps extends React.HTMLAttributes<HTMLElement> {
children: React.ReactNode;
as?: "span" | "a" | "p";
/**
* When true, the underline reacts to an ancestor with `.group` on
* `:hover` / `:focus-visible` — useful inside card links.
* @default false
*/
groupHover?: boolean;
/** Classes for the inner label wrapper — put `truncate` / `line-clamp-*` here. */
labelClassName?: string;
}
/**
* Codrops Metis underline — scaleX reveal with flipped transform-origin on enter/exit.
* Keep truncation on `labelClassName`, not the root, so the line is not clipped.
* @see https://github.com/codrops/LineHoverStyles/
*/
export default function MetisText({
children,
as: Tag = "span",
groupHover = false,
className,
labelClassName,
...props
}: MetisTextProps) {
return (
<Tag
className={cn(
"metis-text relative inline-block max-w-full",
groupHover && "metis-text--group",
className,
)}
{...props}
>
<span className={cn("block", labelClassName)}>{children}</span>
</Tag>
);
}Usage
import MetisText from "@/animata/text/metis-text";
<MetisText className="text-sm text-foreground">About us</MetisText>Inside a card or list row, pass groupHover so the line follows the whole link, not just the label. Put truncate or line-clamp-* on labelClassName — if you clip the root, the underline gets clipped too.
<a href="..." className="group block">
<MetisText groupHover labelClassName="truncate text-sm" className="text-foreground">
Site title
</MetisText>
</a>How it works
Layout classes (relative, inline-block, max-w-full, block on the label) live on the TSX via Tailwind. The Metis scaleX underline — ::after, transform-origin flip on enter/exit, and .group hover — lives in metis-text.css because pseudo-elements and origin swaps are awkward in utility classes alone.
Credits
Built by sudha
Underline pattern from Codrops Line Hover Styles (Metis effect).