"use client"; import React, { useState, useMemo } from "react"; import ToolLayout from "@/components/ToolLayout"; interface ShadowPreset { name: string; horizontal: number; vertical: number; blur: number; spread: number; color: string; opacity: number; inset: boolean; } const PRESETS: ShadowPreset[] = [ { name: "Default Soft", horizontal: 5, vertical: 5, blur: 10, spread: 0, color: "#000000", opacity: 0.3, inset: false, }, { name: "Subtle Card", horizontal: 0, vertical: 4, blur: 6, spread: -1, color: "#000000", opacity: 0.1, inset: false, }, { name: "Floating Depth", horizontal: 0, vertical: 20, blur: 25, spread: -5, color: "#000000", opacity: 0.15, inset: false, }, { name: "Hard Brutalism", horizontal: 6, vertical: 6, blur: 0, spread: 0, color: "#000000", opacity: 1, inset: false, }, { name: "Blue Glow", horizontal: 0, vertical: 0, blur: 25, spread: 5, color: "#3b82f6", opacity: 0.5, inset: false, }, { name: "Purple Halo", horizontal: 0, vertical: 12, blur: 30, spread: 2, color: "#8b5cf6", opacity: 0.35, inset: false, }, { name: "Inset Well", horizontal: 0, vertical: 4, blur: 8, spread: 0, color: "#000000", opacity: 0.25, inset: true, }, { name: "Emerald Light", horizontal: 0, vertical: 8, blur: 20, spread: -2, color: "#10b981", opacity: 0.4, inset: false, }, ]; // Helper to convert hex to RGB function hexToRgb(hex: string): { r: number; g: number; b: number } { let cleanHex = hex.replace("#", "").trim(); if (cleanHex.length === 3) { cleanHex = cleanHex .split("") .map((c) => c + c) .join(""); } const num = parseInt(cleanHex, 16); if (isNaN(num) || cleanHex.length !== 6) { return { r: 0, g: 0, b: 0 }; } return { r: (num >> 16) & 255, g: (num >> 8) & 255, b: num & 255, }; } export default function BoxShadowGeneratorPage() { // Shadow state variables const [horizontal, setHorizontal] = useState(5); const [vertical, setVertical] = useState(5); const [blur, setBlur] = useState(10); const [spread, setSpread] = useState(0); const [color, setColor] = useState("#000000"); const [opacity, setOpacity] = useState(0.3); const [inset, setInset] = useState(false); // Box & Preview customization const [boxColor, setBoxColor] = useState("#ffffff"); const [borderRadius, setBorderRadius] = useState(16); const [previewBg, setPreviewBg] = useState<"light" | "dark" | "checker">("light"); const [includeVendorPrefixes, setIncludeVendorPrefixes] = useState(false); const [codeFormat, setCodeFormat] = useState<"standard" | "full" | "tailwind">("standard"); const [copied, setCopied] = useState(false); // Computed RGBA string const rgbaColor = useMemo(() => { const { r, g, b } = hexToRgb(color); const roundedOpacity = Math.round(opacity * 100) / 100; return `rgba(${r}, ${g}, ${b}, ${roundedOpacity})`; }, [color, opacity]); // Computed box-shadow value const shadowValue = useMemo(() => { const insetText = inset ? "inset " : ""; return `${insetText}${horizontal}px ${vertical}px ${blur}px ${spread}px ${rgbaColor}`; }, [inset, horizontal, vertical, blur, spread, rgbaColor]); // Generated CSS Code const generatedCode = useMemo(() => { if (codeFormat === "tailwind") { const tailwindArbitrary = shadowValue.replace(/\s+/g, "_"); return `className="shadow-[${tailwindArbitrary}]"`; } let code = ""; if (includeVendorPrefixes) { code += `-webkit-box-shadow: ${shadowValue};\n`; code += `-moz-box-shadow: ${shadowValue};\n`; } code += `box-shadow: ${shadowValue};`; if (codeFormat === "full") { return `.my-custom-box {\n background-color: ${boxColor};\n border-radius: ${borderRadius}px;\n ${code.split("\n").join("\n ")}\n}`; } return code; }, [shadowValue, includeVendorPrefixes, codeFormat, boxColor, borderRadius]); const handleCopy = async () => { try { await navigator.clipboard.writeText(generatedCode); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { const textArea = document.createElement("textarea"); textArea.value = generatedCode; document.body.appendChild(textArea); textArea.select(); document.execCommand("copy"); document.body.removeChild(textArea); setCopied(true); setTimeout(() => setCopied(false), 2000); } }; const applyPreset = (preset: ShadowPreset) => { setHorizontal(preset.horizontal); setVertical(preset.vertical); setBlur(preset.blur); setSpread(preset.spread); setColor(preset.color); setOpacity(preset.opacity); setInset(preset.inset); }; const resetDefaults = () => { setHorizontal(5); setVertical(5); setBlur(10); setSpread(0); setColor("#000000"); setOpacity(0.3); setInset(false); setBoxColor("#ffffff"); setBorderRadius(16); }; return (
{/* Live Preview Area */}

Live Preview

Background:
{/* The Shadow Card */}

Shadow Preview

{horizontal}px {vertical}px {blur}px {spread}px

{/* Presets Gallery */}

Quick Presets

{PRESETS.map((preset) => { const { r, g, b } = hexToRgb(preset.color); const pRgba = `rgba(${r}, ${g}, ${b}, ${preset.opacity})`; const pShadow = `${preset.inset ? "inset " : ""}${preset.horizontal}px ${preset.vertical}px ${preset.blur}px ${preset.spread}px ${pRgba}`; return ( ); })}
{/* Shadow Control Sliders Grid */}
{/* Controls Column 1: Dimensions & Geometry */}

Shadow Geometry

{/* Horizontal Offset */}
setHorizontal(Number(e.target.value))} className="w-16 px-1.5 py-0.5 text-xs font-mono text-right border border-[#1a1a1a] bg-[#0a0a0a] text-gray-200 focus:ring-1 focus:ring-blue-900 focus:outline-none" /> px
setHorizontal(Number(e.target.value))} className="w-full h-2 bg-[#1a1a1a] appearance-none cursor-pointer accent-blue-600" />
-50px 0px 50px
{/* Vertical Offset */}
setVertical(Number(e.target.value))} className="w-16 px-1.5 py-0.5 text-xs font-mono text-right border border-[#1a1a1a] bg-[#0a0a0a] text-gray-200 focus:ring-1 focus:ring-blue-900 focus:outline-none" /> px
setVertical(Number(e.target.value))} className="w-full h-2 bg-[#1a1a1a] appearance-none cursor-pointer accent-blue-600" />
-50px 0px 50px
{/* Blur Radius */}
setBlur(Math.max(0, Number(e.target.value)))} className="w-16 px-1.5 py-0.5 text-xs font-mono text-right border border-[#1a1a1a] bg-[#0a0a0a] text-gray-200 focus:ring-1 focus:ring-blue-900 focus:outline-none" /> px
setBlur(Number(e.target.value))} className="w-full h-2 bg-[#1a1a1a] appearance-none cursor-pointer accent-blue-600" />
0px 50px 100px
{/* Spread Radius */}
setSpread(Number(e.target.value))} className="w-16 px-1.5 py-0.5 text-xs font-mono text-right border border-[#1a1a1a] bg-[#0a0a0a] text-gray-200 focus:ring-1 focus:ring-blue-900 focus:outline-none" /> px
setSpread(Number(e.target.value))} className="w-full h-2 bg-[#1a1a1a] appearance-none cursor-pointer accent-blue-600" />
-50px 0px 50px
{/* Controls Column 2: Color, Opacity & Box Appearance */}

Shadow Color & Box Styling

{/* Shadow Color & Inset Toggle */}
setColor(e.target.value)} className="absolute -top-2 -left-2 w-14 h-14 cursor-pointer border-0" />
setColor(e.target.value)} className="w-full px-2.5 py-1.5 text-xs font-mono border border-[#1a1a1a] bg-[#0a0a0a] text-gray-200 focus:ring-1 focus:ring-blue-900 focus:outline-none uppercase" />
{/* Inset Toggle Checkbox */}
{/* Opacity Slider */}
{ const val = Math.max(0, Math.min(1, Number(e.target.value))); setOpacity(isNaN(val) ? 0 : val); }} className="w-16 px-1.5 py-0.5 text-xs font-mono text-right border border-[#1a1a1a] bg-[#0a0a0a] text-gray-200 focus:ring-1 focus:ring-blue-900 focus:outline-none" /> ({Math.round(opacity * 100)}%)
setOpacity(Number(e.target.value))} className="w-full h-2 bg-[#1a1a1a] appearance-none cursor-pointer accent-blue-600" />
0 (Transparent) 0.5 1.0 (Opaque)
{/* Box Appearance: Box Background & Border Radius */}
{/* Box Color */}
setBoxColor(e.target.value)} className="absolute -top-2 -left-2 w-12 h-12 cursor-pointer border-0" />
setBoxColor(e.target.value)} className="w-full px-2 py-1 text-xs font-mono border border-[#1a1a1a] bg-[#0a0a0a] text-gray-200 focus:ring-1 focus:ring-blue-900 focus:outline-none uppercase" />
{/* Border Radius */}
{borderRadius}px
setBorderRadius(Number(e.target.value))} className="w-full h-2 bg-[#1a1a1a] appearance-none cursor-pointer accent-blue-600" />
{/* Generated CSS Code Block */}

Generated CSS

{codeFormat !== "tailwind" && ( )}
              {generatedCode}
            
); }