dECIPHER CARD

A high-fidelity terminal-style card component. Hover to initiate a brute-force decryption sequence that reveals the hidden message and background content.

Live Preview

Decipher CardCARD_01

Dependencies

npm installframer-motion

Utils

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Code

import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";

const DecipherCard = ({
  title = "CARD_01",
  targetText = "I BUILT THIS SH*T, ME. BRICK BY BRICK.",
  bgImage = "",
}) => {
  const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#%&@*+=-";
  const [displayText, setDisplayText] = useState("");
  const [bgText, setBgText] = useState("");
  const [isHovered, setIsHovered] = useState(false);
  const [isRevealed, setIsRevealed] = useState(false);

  const generateNoise = (length: number) => {
    return Array.from(
      { length },
      () => chars[Math.floor(Math.random() * chars.length)],
    ).join("");
  };

  useEffect(() => {
    let interval: any;
    let revealTimeout: any;

    if (isHovered) {
      interval = setInterval(() => {
        setBgText(generateNoise(1600));
      }, 50);

      revealTimeout = setTimeout(() => {
        setIsRevealed(true);
        clearInterval(interval);
        let iteration = 0;
        const innerInterval = setInterval(() => {
          setDisplayText(
            targetText
              .split("")
              .map((letter, index) => {
                if (targetText[index] === " ") return " ";
                if (index < iteration) return targetText[index];
                return chars[Math.floor(Math.random() * chars.length)];
              })
              .join(""),
          );
          if (iteration >= targetText.length) clearInterval(innerInterval);
          iteration += 1;
        }, 30);
      }, 1500);
    } else {
      setIsRevealed(false);
      setBgText(generateNoise(1600)); // Static noise for rest mode
      setDisplayText("");
      clearTimeout(revealTimeout);
    }

    return () => {
      clearInterval(interval);
      clearTimeout(revealTimeout);
    };
  }, [isHovered, targetText]);

  return (
    <div className="relative flex items-center justify-center">
      <motion.div
        onMouseEnter={() => setIsHovered(true)}
        onMouseLeave={() => setIsHovered(false)}
        className="z-10 relative w-80 h-[450px] bg-black border border-white/20 rounded-sm overflow-hidden p-6 flex flex-col justify-between transition-colors duration-500 hover:border-white/80 shadow-2xl"
        style={{ fontFamily: "'Space Mono', monospace" }}
      >
        <AnimatePresence>
          {!isRevealed && (
            <motion.div
              initial={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 0.5, ease: "anticipate" }}
              className="absolute cursor-default w-80 h-[450px] inset-0 z-30 bg-black p-4 rounded-sm overflow-hidden pointer-events-none break-all"
            >
              <p className="text-white overflow-hidden h-[410px] font-mono text-[10px] leading-tight text-justify opacity-80">
                {bgText}
              </p>
            </motion.div>
          )}
        </AnimatePresence>

        <div className="absolute inset-0 pointer-events-none bg-[linear-gradient(rgba(18,16,16,0)_50%,rgba(0,0,0,0.25)_50%)] bg-[length:100%_4px] z-20 opacity-20" />

        <div
          className="flex justify-between items-start z-10 h-full w-full bg-cover bg-center bg-no-repeat"
          style={{
            backgroundImage: bgImage !== "" ? bgImage : "url('https://res.cloudinary.com/dmqwpwo6c/image/upload/v1776147234/franklin_re7o3r.webp')",
          }}
        >
          <div className="flex flex-col">
            <span className="text-[10px] text-white/40 tracking-[0.3em] font-light italic uppercase">
              Decipher Card
            </span>
            <span className="text-[10px] text-white/80 tracking-widest">
              {title}
            </span>
          </div>
          <div className="w-1.5 h-1.5 bg-cyan-400 shadow-[0_0_8px_#22d3ee]" />
        </div>

        <div className="z-10 py-10">
          <motion.h2 className="text-xl font-bold tracking-tighter leading-tight text-white">
            {displayText}
          </motion.h2>
        </div>
      </motion.div>
    </div>
  );
};

export default DecipherCard;

Props

PropTypeDefaultDescription
titlestringCARD_01Top-left label
targetText stringI BUILT THIS SH*T, ME. BRICK BY BRICK.Decoded msg
bgImage string-image for background