Menu

Hyper Text

A cyberpunk-inspired scramble text effect that cycles random characters before resolving to the final string. Triggers on mount and hover, with configurable duration and accessible screen-reader text.

Preview

Live interactive preview.

Installation

Add this component to your project using the CLI:

terminal
npx -y vui-registry-cli-v1@latest add hyper-text

Source Code

hyper-text.tsx
'use client'

import { useEffect, useRef, useState } from 'react'
import { cn } from '@/lib/utils'

export type HyperTextProps = {
  text?: string
  className?: string
  duration?: number
  animateOnMount?: boolean
}

export default function HyperText({
  text = 'Velocity UI',
  className,
  duration = 800,
  animateOnMount = true,
}: HyperTextProps) {
  const [displayText, setDisplayText] = useState(text.split(''))
  const intervalRef = useRef<NodeJS.Timeout | null>(null)
  const alphabets = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+'

  const startAnimation = () => {
    let iteration = 0
    const tickMs = Math.max(20, Math.floor(duration / (text.length * 3)))

    if (intervalRef.current) clearInterval(intervalRef.current)

    intervalRef.current = setInterval(() => {
      setDisplayText(
        text.split('').map((letter, index) => {
          if (letter === ' ') return letter
          if (index < iteration) return text[index]
          return alphabets[Math.floor(Math.random() * alphabets.length)]
        })
      )

      if (iteration >= text.length) {
        if (intervalRef.current) clearInterval(intervalRef.current)
      }
      iteration += 1 / 3
    }, tickMs)
  }

  useEffect(() => {
    if (animateOnMount) startAnimation()
    return () => {
      if (intervalRef.current) clearInterval(intervalRef.current)
    }
  }, [text, animateOnMount])

  return (
    <div className="flex flex-col items-center justify-center min-h-[200px] p-8 bg-neutral-50 dark:bg-neutral-950 rounded-xl">
      <div
        className={cn(
          'flex overflow-hidden py-2 cursor-pointer select-none text-3xl sm:text-4xl font-bold tracking-tight text-neutral-900 dark:text-white',
          className
        )}
        onMouseEnter={startAnimation}
      >
        <span className="sr-only">{text}</span>
        {displayText.map((char, i) => (
          <span key={i} className="font-mono inline-block w-[0.6em] text-center">
            {char}
          </span>
        ))}
      </div>
      <p className="mt-4 text-xs text-neutral-500 dark:text-neutral-400">Hover to replay the scramble effect</p>
    </div>
  )
}

Dependencies

  • framer-motion: latest

Props

Component property reference.

NameTypeDefaultDescription
textstring"Velocity UI"Final text to resolve to after scrambling.
classNamestringundefinedAdditional Tailwind classes for the text container.
durationnumber800Total animation duration in milliseconds.
animateOnMountbooleantrueWhether to play the scramble effect when the component mounts.
Context Worth Keeping In Orbit

Most components here are inspired by outstanding libraries and creators in the ecosystem. I don’t claim to be the original author — this is my space for learning, rebuilding, and understanding great work at a deeper level.

I’m still a student of the craft, constantly studying the best and translating what I learn through my own perspective. Every piece reflects curiosity, respect for the community, and small creative touches that feel true to me.

I’ve done my best to credit inspirations properly. If anything is missing or inaccurate, I truly appreciate a message so it can be corrected with care.