Skip to content

Drawn Underline

Hand-drawn underline that draws itself (SVG pathLength) beneath a word when it scrolls into view — a loose, slightly wavy pen stroke.

organic-soft/effect/drawn-underline
Open ↗

Source

"use client";

import type { ReactNode } from "react";
import { motion, useReducedMotion } from "motion/react";
import { cn } from "@/lib/utils";

export interface DrawnUnderlineProps {
  children: ReactNode;
  /** Stroke color token. */
  tone?: "primary" | "clay";
  delay?: number;
  className?: string;
}

/** Hand-drawn underline that draws itself (SVG pathLength) beneath a word when it scrolls into view — a loose, slightly wavy pen stroke. */
export function DrawnUnderline({ children, tone = "clay", delay = 0.2, className }: DrawnUnderlineProps) {
  const reduce = useReducedMotion();
  return (
    <span className={cn("relative inline-block whitespace-nowrap", className)}>
      {children}
      <svg
        aria-hidden
        viewBox="0 0 200 16"
        preserveAspectRatio="none"
        className={cn("absolute -bottom-2 left-0 h-[0.35em] w-full overflow-visible", tone === "clay" ? "text-da-secondary-fg/70" : "text-da-primary")}
      >
        <motion.path
          d="M3 11C40 5 80 3 120 6s60 4 77 1"
          fill="none"
          stroke="currentColor"
          strokeWidth="4"
          strokeLinecap="round"
          initial={reduce ? false : { pathLength: 0 }}
          whileInView={{ pathLength: 1 }}
          viewport={{ once: true, amount: 0.8 }}
          transition={{ duration: 0.9, delay, ease: [0.45, 0, 0.2, 1] }}
        />
      </svg>
    </span>
  );
}

export default DrawnUnderline;

modules/organic-soft/effect/drawn-underline/index.tsx

Props

PropTypeDefaultDescription
children*ReactNode—Content.
tone"primary" | "clay"—Stroke color token.
delaynumber—Delay.
classNamestring—Extra classes on the root.

Other text variants in Organic Soft

Text in other art directions

Animated

Glitch Text

Print-misregistration glitch: two offset copies of the text in accent and secondary colors jitter in horizontal slices, constantly or only on hover/focus. Pure CSS, blend mode adapts to dark.

BrutalistText
Animated

Marquee Band

Tilted full-bleed ticker band with giant uppercase words scrolling infinitely between ruled strokes. Seamless loop, direction and speed props, three color tones.

BrutalistText
Animated

Scramble Text

Decoder-style mono text: random glyphs resolve left to right into the final string when it scrolls into view, and again on hover or focus. Great for version numbers, stats and labels.

BrutalistText
Animated

Typewriter Text

Headline with a static prefix and a rotating last word that is typed character by character, held, deleted and replaced, followed by a blinking block cursor.

BrutalistText
Animated

Count Up

Number that counts up with an expo-out ease and tabular digits when scrolled into view; prefix, suffix, decimals and locale-aware thousands separators.

MinimalText
Animated

Gradient Text

Inline text filled with a slowly flowing gradient built from the primary and accent tokens (bg-clip-text); animated or static. Pure CSS.

MinimalText
Animated

Rotating Words

Headline with a static prefix and a highlighted word that slides up to the next one on an interval, inside a slot whose width animates to fit each word.

MinimalText
Animated

Text Shimmer

Loading/live text effect: a light band sweeps across muted text using a clipped gradient. For "thinking…", "generating…" or live status labels. Pure CSS, server-safe.

MinimalText
Animated

Digit Roll

Odometer-style number: each digit rolls vertically through 0–9 to its value (staggered from the left) when it enters the viewport, in tabular display figures.

Mono CleanText
Animated

Outline Fill

Outlined display text (hairline stroke, transparent fill) that fills solid ink from bottom to top on hover/focus via a clipped duplicate layer — for big navigation links. Pure CSS.

Mono CleanText
Animated

Scramble Text

Mono text that resolves from random glyphs to the real string left-to-right when it enters the viewport (and again on hover); final text is always exposed to assistive tech.

Mono CleanText
Animated

Tracking In

Headline whose letter-spacing contracts from very wide to the tight display tracking while fading in — a calm, precise entrance for short words.

Mono CleanText
Animated

Count Up

Number that counts up with an ease-out when scrolled into view, formatted with thousands separators (prefix/suffix, decimals); tabular figures prevent jitter. Final value for screen readers.

Neo CorporateText
Animated

Shimmer Text

Text filled with a primary→accent-fg gradient and a slow light sweep passing across it every few seconds — for a single highlighted phrase in a headline. Pure CSS.

Neo CorporateText
Animated

Typed Words

Headline with a rotating keyword typed and erased character by character in primary, with a blinking caret; reduced motion swaps whole words. Full sentence exposed to screen readers.

Neo CorporateText
Animated

Word Reveal

Headline whose words slide up from behind a mask one after another when scrolled into view — crisp, no blur or bounce. The element keeps its plain text for screen readers.

Neo CorporateText
Animated

Emoji Swap

Headline whose last word is a pastel pill (emoji + word) that pops out and swaps to the next one on an interval — teams, freelancers, students…

Soft FlatText
Animated

Highlight Text

Marker highlight that draws itself behind a word or phrase — left to right, slightly tilted and rounded — when it scrolls into view; four pastel tones.

Soft FlatText
Animated

Rolling Number

Odometer-style number: each digit rolls vertically to its value with a staggered bounce, on first view and whenever the value changes.

Soft FlatText
Animated

Wave Text

Bouncy text where letters hop one after another in a wave — always, or on hover/focus of the text or a parent .group. Pure CSS.

Soft FlatText