NEW
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
Learn more
Skip to content
Docs

Underline hover text

A customizable text component with an underline hover effect.

requires interactionhover
Loading...

Installation

CLI

pnpm dlx shadcn@latest add https://animata.design/r/text/underline-hover-text.json

Manual

Run the following command

It will create a new file called underline-hover-text.tsx inside the components/animata/text directory.

mkdir -p components/animata/text && touch components/animata/text/underline-hover-text.tsx

Paste the code

Open the newly created file and paste the following code:

"use client";
import type React from "react";
 
import { cn } from "@/lib/utils";
 
export interface UnderlineHoverTextProps {
  text: string;
  /**
   * Resting text color. Tailwind class string.
   */
  textColor?: string;
  /**
   * Text color applied on hover. Pass `hover:text-foo` style class, or leave
   * empty to keep the resting color.
   */
  hoverTextColor?: string;
  /**
   * Background color class applied to the underline stroke. Pass a plain
   * class like `bg-zinc-900` (or any `bg-*`). The component drives visibility
   * itself via the parent's `:hover` — if you pass a `hover:`-prefixed class
   * here it'll never apply (the stroke element is not the hovered element)
   * and the stroke will appear colorless on hover.
   */
  hoverColor?: string;
  fontSize?: string;
  fontWeight?: string;
  className?: string;
}
 
const UnderlineHoverText: React.FC<UnderlineHoverTextProps> = ({
  text,
  textColor = "text-zinc-900 dark:text-zinc-100",
  hoverTextColor = "",
  hoverColor = "bg-zinc-900 dark:bg-zinc-100",
  fontSize = "text-2xl",
  fontWeight = "font-medium",
  className,
}) => {
  return (
    <span
      className={cn(
        "group/underline relative inline-block cursor-pointer px-1 pb-1.5",
        "tracking-tight",
        "transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]",
        "will-change-transform hover:-translate-y-[2px]",
        fontSize,
        textColor,
        fontWeight,
        hoverTextColor,
        className,
      )}
    >
      <span className="relative z-10">{text}</span>
      {/* Soft baseline — always present, muted */}
      <span
        aria-hidden
        className="pointer-events-none absolute bottom-0 left-0 h-px w-full bg-current opacity-25"
      />
      {/* Hover stroke — sweeps outward from the center */}
      <span
        aria-hidden
        className={cn(
          "pointer-events-none absolute -bottom-px left-1/2 h-[3px] w-0 -translate-x-1/2 rounded-full",
          "transition-[width] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]",
          "group-hover/underline:w-full",
          hoverColor,
        )}
      />
    </span>
  );
};
 
export default UnderlineHoverText;

Credits

Built by Chiranjibi Ranabhat