Decorated Text

PreviousNext

a decorative text frame with corner accents and a compact entrance effect.

Docs
scrollxuicomponent

Preview

Loading preview…
components/ui/decorated-text.tsx
import React from "react";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";

interface DecoratedTextProps {
  text?: string;
  children?: React.ReactNode;
  className?: string;
  bordered?: boolean;
  showIcons?: boolean;
  iconClassName?: string;
  glowEffect?: boolean;
  flicker?: boolean;
}

function DecoratedText({
  text,
  children,
  className,
  bordered = true,
  showIcons = true,
  iconClassName,
  glowEffect = true,
  flicker = true,
}: DecoratedTextProps) {
  const content = text || children;

  return (
    <motion.span
      className={cn(
        "group/decorated relative inline-block",
        bordered && "border border-black/20 dark:border-white/20",
        className
      )}
    >
      {flicker && (
        <motion.span
          initial={{ opacity: 0 }}
          animate={{ opacity: [0, 1, 0.4, 1, 0] }}
          transition={{
            duration: 0.45,
            times: [0, 0.25, 0.5, 0.75, 1],
            ease: "easeOut",
          }}
          className="absolute inset-0 bg-black/10 dark:bg-white/10 pointer-events-none"
        />
      )}

      <motion.span
        initial={flicker ? { opacity: 0 } : false}
        animate={
          flicker
            ? { opacity: [0, 1, 0.3, 1] }
            : { opacity: 1 }
        }
        transition={
          flicker
            ? { duration: 1.5, ease: "easeInOut", times: [0, 0.4, 0.65, 1] }
            : undefined
        }
        className="relative z-10 inline-block leading-none"
      >
        {content}
      </motion.span>

      {showIcons && (
        <>
          <Icon
            className={cn(
              "absolute top-0 left-0 -translate-x-1/2 -translate-y-1/2 h-3 w-3 text-black dark:text-white",
              iconClassName
            )}
          />
          <Icon
            className={cn(
              "absolute bottom-0 left-0 -translate-x-1/2 translate-y-1/2 h-3 w-3 text-black dark:text-white",
              iconClassName
            )}
          />
          <Icon
            className={cn(
              "absolute top-0 right-0 translate-x-1/2 -translate-y-1/2 h-3 w-3 text-black dark:text-white",
              iconClassName
            )}
          />
          <Icon
            className={cn(
              "absolute bottom-0 right-0 translate-x-1/2 translate-y-1/2 h-3 w-3 text-black dark:text-white",
              iconClassName
            )}
          />
        </>
      )}

      {glowEffect && (
        <motion.span
          className="absolute inset-0 bg-gradient-to-r from-blue-500/10 via-purple-500/10 to-pink-500/10 opacity-0 blur-xl transition-opacity duration-500 rounded-sm group-hover/decorated:opacity-100 pointer-events-none"
          initial={{ opacity: 0 }}
          whileHover={{ opacity: 1 }}
        />
      )}
    </motion.span>
  );
}

function Icon({ className, ...props }: React.SVGProps<SVGSVGElement>) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      fill="none"
      viewBox="0 0 24 24"
      strokeWidth="1.5"
      stroke="currentColor"
      className={className}
      {...props}
    >
      <path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m6-6H6" />
    </svg>
  );
}

export { DecoratedText };

Installation

npx shadcn@latest add @scrollxui/decorated-text

Usage

import { DecoratedText } from "@/components/decorated-text"
<DecoratedText />