Text Reveal

A text with a moving blob that reveals the text as it passes.

Light shines brightest in shadow

1. Copy the component file

Create a new file called text-reveal-animation.jsx in your reusable components folder (for example /src/components/) and paste the following code into it.

text-reveal-animation.jsx

import { memo, useCallback, useEffect, useRef } from "react";
import styles from "./text-reveal-animation.module.css";

const TextRevealAnimation = (props) => {
  const {
    text
  } = props;

  const textRef = useRef(null);

  const updateTextSize = useCallback(() => {
    const width = Math.round(textRef.current.getBoundingClientRect().width);
    textRef.current.style.setProperty("--animation-name", styles["reveal-text-keyframes"]);
    textRef.current.style.setProperty("--width", `${width}px`);
  }, []);

  useEffect(() => {
    const resizeOberver = new ResizeObserver(updateTextSize);
    resizeOberver.observe(textRef.current);
    updateTextSize();
    return () => {
      resizeOberver.disconnect();
    }
  }, [updateTextSize]);

  return (
    <span 
      className={styles["reveal-text"]}
      ref={textRef}
    >
      <span>
        {text}
      </span>
    </span>
  );
};

export default memo(TextRevealAnimation);

2. Copy the CSS module file

In the same folder, create a file called text-reveal-animation.module.css and paste the following CSS.

text-reveal-animation.module.css

.reveal-text {
  --background: transparent;
  --text-color: white; /* should be set same as background */
  --blob-color: #0b0b0c;
  --blob-shadow-color: #222;
  --animation-name: none;
  --animation-duration: 4s;
  --width: 0px; /* dynamically set by js */

  @media (prefers-color-scheme: dark) {
    --text-color: #0b0b0c;
    --blob-color: white;
    --blob-shadow-color: #ddd;
  }

  position: relative;
  display: inline-block;
  background: var(--background);
  font: inherit;
  white-space: nowrap;
  word-break: keep-all;
  text-transform: inherit;

  >span {
    padding: 0 2px;
    color: var(--text-color);
    mix-blend-mode: lighten;

    @media (prefers-color-scheme: dark) {
      mix-blend-mode: darken;
    }
  }

  /* blob */
  &:before {
    content: "";
    position: absolute;
    top: 50%;
    left: 0px;
    transform: translate(0px, -50%);
    height: 100%;
    aspect-ratio: 1;
    background: var(--blob-color);
    border-radius: 50%;
    box-shadow: inset 0px 0px 32px 8px var(--blob-shadow-color);
    animation: var(--animation-name) var(--animation-duration) ease-in-out infinite;
  }
}

@keyframes reveal-text-keyframes {
  0%,
  100% {
    transform: translate(0px, -50%);
  }

  50% {
    transform: translate(calc(var(--width) - 100%), -50%);
  }
}

3. Use the component

Now you can import and use the component anywhere in your project.

text-reveal-animation-preview.jsx

import TextRevealAnimation from "@/components/text-effects/text-reveal-animation/text-reveal-animation";

/* keep text on center */
const wrapperStyles = {
  textAlign: "center",
  padding: "24px",
  textWrap: "balance",
};

const TextRevealAnimationPreview = () => {
  return (
    <div style={wrapperStyles}>
      <h2>
        Light shines brightest in  {" "}
        <TextRevealAnimation
          text="shadow"
        />    
      </h2>
    </div>
  );
};

export default TextRevealAnimationPreview;

Props

Configure the component with the following props:

PropTypeRequiredDefaultDescription
textstringYesThe text content to be revealed with the animation effect.