Text animations

Reveal Text

A vertical reveal text effect. Detects whether the mouse enters from the top or bottom and slides a colored text shadow in from that direction.

Creative
Developer
Twitter
@design_eng
Instagram
@photos_daily
LinkedIn
/in/professional
Dribbble
/creative_shots

System Architecture

Explore how we build scalable, resilient systems that handle millions of requests with minimal latency.

Read Case Study →

Security Protocols

Deep dive into our zero-trust architecture and how we secure data at rest and in transit.

View Security Report →

Installation

npx shadcn@latest add https://satisui.xyz/r/reveal-text.json

Manual

No third-party dependencies are required. Copy the following code into components/satisui/reveal-text.tsx.

import React, { useRef, CSSProperties, MouseEvent } from 'react';

/**
 * Props for the RevealText component.
 */
interface RevealTextProps {
  /** The text content to display and animate. */
  text: string;
  /** The primary color of the text in its resting state. Defaults to #ffffff. */
  color?: string;
  /** The color of the text that slides in on hover. Defaults to #00ffcc. */
  revealColor?: string;
  /** Optional CSS classes for the container (e.g., font size, margin). */
  className?: string;
}

interface VerticalStyle extends CSSProperties {
  '--text-clr'?: string;
  '--clr'?: string;
  '--dir-y'?: number;
  '--travel-dist'?: string;
}

/**
 * A vertical reveal text effect.
 * Detects whether the mouse enters from the top or bottom and slides
 * a colored text shadow in from that direction.
 */
const RevealText: React.FC<RevealTextProps> = ({
  text,
  color = '#ffffff',
  revealColor = '#00ffcc',
  className = 'text-4xl',
}) => {
  const containerRef = useRef<HTMLDivElement>(null);

  const handleMouseEnter = (e: MouseEvent<HTMLDivElement>) => {
    if (!containerRef.current) return;

    const rect = containerRef.current.getBoundingClientRect();
    const centerY = rect.top + rect.height / 2;

    // Determine direction: 1 (down) if entering from top, -1 (up) if entering from bottom
    const direction = e.clientY < centerY ? 1 : -1;

    containerRef.current.style.setProperty('--dir-y', direction.toString());
  };

  const containerStyle: VerticalStyle = {
    '--text-clr': color,
    '--clr': revealColor,
    '--dir-y': 1,
    // Using 1.2em instead of 1em ensures the text shadow is pushed
    // slightly past the container edge, preventing sub-pixel bleeding at rest.
    '--travel-dist': '1.2em',
    color: color,
  };

  const spanStyle: VerticalStyle = {
    textShadow:
      '0px calc(var(--dir-y) * var(--travel-dist) * -1) 0px var(--clr)',
    color: 'var(--text-clr)',
  };

  return (
    <div
      ref={containerRef}
      className={`group inline-block relative overflow-hidden leading-tight cursor-default ${className}`}
      style={containerStyle}
      onMouseEnter={handleMouseEnter}
    >
      <span
        className='
          block will-change-transform 
          transition-transform duration-300 ease-out 
          group-hover:translate-y-[calc(var(--dir-y)*var(--travel-dist))]
        '
        style={spanStyle}
      >
        {text}
      </span>
    </div>
  );
};

export default RevealText;

Usage

Import the component and customize the text, colors, and styling as needed.

import RevealText from '@/components/reveal-text';

export default function RevealTextDemo() {
  return (
    <div className='flex flex-col items-center justify-center gap-12 min-h-[300px] bg-neutral-950 p-8'>
      {/* Basic Usage with defaults */}
      <RevealText text='Hover Me' />

      {/* Customized Usage for Headlines */}
      <RevealText
        text='Design Engineering'
        className='text-6xl font-bold tracking-tighter'
        color='#e5e5e5'
        revealColor='#fbbf24' // Amber
      />
    </div>
  );
}

Props

PropTypeDefaultDescription
textstringRequiredThe text content to display and animate.
colorstring'#ffffff'The primary color of the text in its resting state.
revealColorstring'#00ffcc'The color of the text that slides in on hover.
classNamestring'text-4xl'Optional CSS classes for the container.