--- title: Liquid Glass Chart description: A 3D glassmorphic chart visualizer featuring 3D glass pillar bars with specular glare, smooth SVG liquid wave area paths, crystal line curves, interactive glass tooltips, and 6 color crystal variants. --- ## Installation CLI Manual ```bash npx lightswind@latest add liquid-glass-chart ``` Copy and paste the following code into your project. ```tsx "use client"; import React, { useState } from "react"; import { cn } from "@/components/lib/utils"; import { motion, AnimatePresence } from "framer-motion"; export type LiquidGlassChartType = "bar" | "area" | "line"; export type LiquidGlassChartVariant = | "aurora" | "emerald" | "cyan" | "purple" | "primary" | "glass"; export interface LiquidGlassChartDataPoint { label: string; value: number; secondaryValue?: number; } export interface LiquidGlassChartProps { data: LiquidGlassChartDataPoint[]; type?: LiquidGlassChartType; variant?: LiquidGlassChartVariant; title?: string; description?: string; height?: number; showGrid?: boolean; showTooltip?: boolean; showValues?: boolean; className?: string; } /* ─── Variant Theme Configurations ───────────────────────────────── */ const VARIANT_CONFIG: Record< LiquidGlassChartVariant, { primaryGrad: string; barGlass: string; glow: string; stroke: string; tooltipBg: string; badgeBg: string; } > = { aurora: { primaryGrad: "from-violet-400 via-fuchsia-500 to-violet-700", barGlass: "bg-gradient-to-b from-violet-400/90 via-fuchsia-500/75 to-violet-800/85 border-white/60 dark:border-white/30 shadow-[0_12px_28px_-4px_rgba(139,92,246,0.55),inset_0_2.5px_1.5px_rgba(255,255,255,0.95),0_0_0_1px_rgba(255,255,255,0.65),inset_0_-4px_8px_rgba(60,20,120,0.4)]", glow: "shadow-[0_0_40px_rgba(139,92,246,0.35)]", stroke: "#c084fc", tooltipBg: "bg-violet-950/85 border-violet-400/50 text-white shadow-violet-900/50", badgeBg: "bg-white/40 dark:bg-white/15 text-slate-900 dark:text-white border-white/80 dark:border-white/25", }, emerald: { primaryGrad: "from-emerald-400 via-teal-500 to-emerald-700", barGlass: "bg-gradient-to-b from-emerald-400/90 via-emerald-500/75 to-teal-800/85 border-white/60 dark:border-white/30 shadow-[0_12px_28px_-4px_rgba(16,185,129,0.55),inset_0_2.5px_1.5px_rgba(220,255,235,0.95),0_0_0_1px_rgba(255,255,255,0.65),inset_0_-4px_8px_rgba(5,80,50,0.4)]", glow: "shadow-[0_0_40px_rgba(16,185,129,0.35)]", stroke: "#34d399", tooltipBg: "bg-emerald-950/85 border-emerald-400/50 text-white shadow-emerald-900/50", badgeBg: "bg-white/40 dark:bg-white/15 text-slate-900 dark:text-white border-white/80 dark:border-white/25", }, cyan: { primaryGrad: "from-sky-400 via-blue-500 to-indigo-700", barGlass: "bg-gradient-to-b from-sky-400/90 via-blue-500/75 to-indigo-800/85 border-white/60 dark:border-white/30 shadow-[0_12px_28px_-4px_rgba(14,165,233,0.55),inset_0_2.5px_1.5px_rgba(220,240,255,0.95),0_0_0_1px_rgba(255,255,255,0.65),inset_0_-4px_8px_rgba(0,60,130,0.4)]", glow: "shadow-[0_0_40px_rgba(14,165,233,0.35)]", stroke: "#38bdf8", tooltipBg: "bg-sky-950/85 border-sky-400/50 text-white shadow-sky-900/50", badgeBg: "bg-white/40 dark:bg-white/15 text-slate-900 dark:text-white border-white/80 dark:border-white/25", }, purple: { primaryGrad: "from-purple-400 via-fuchsia-500 to-purple-800", barGlass: "bg-gradient-to-b from-purple-400/90 via-fuchsia-500/75 to-purple-900/85 border-white/60 dark:border-white/30 shadow-[0_12px_28px_-4px_rgba(168,85,247,0.55),inset_0_2.5px_1.5px_rgba(240,220,255,0.95),0_0_0_1px_rgba(255,255,255,0.65),inset_0_-4px_8px_rgba(70,20,120,0.4)]", glow: "shadow-[0_0_40px_rgba(168,85,247,0.35)]", stroke: "#e879f9", tooltipBg: "bg-purple-950/85 border-purple-400/50 text-white shadow-purple-900/50", badgeBg: "bg-white/40 dark:bg-white/15 text-slate-900 dark:text-white border-white/80 dark:border-white/25", }, primary: { primaryGrad: "from-rose-400 via-red-500 to-rose-800", barGlass: "bg-gradient-to-b from-rose-400/90 via-red-500/75 to-rose-900/85 border-white/60 dark:border-white/30 shadow-[0_12px_28px_-4px_rgba(244,63,94,0.55),inset_0_2.5px_1.5px_rgba(255,220,220,0.95),0_0_0_1px_rgba(255,255,255,0.65),inset_0_-4px_8px_rgba(130,15,20,0.4)]", glow: "shadow-[0_0_40px_rgba(244,63,94,0.35)]", stroke: "#fb7185", tooltipBg: "bg-rose-950/85 border-rose-400/50 text-white shadow-rose-900/50", badgeBg: "bg-white/40 dark:bg-white/15 text-slate-900 dark:text-white border-white/80 dark:border-white/25", }, glass: { primaryGrad: "from-white/90 via-white/50 to-white/20", barGlass: "bg-gradient-to-b from-white/75 via-white/40 to-white/15 dark:from-white/30 dark:via-white/15 dark:to-white/5 border-white/90 dark:border-white/30 shadow-[0_12px_28px_-4px_rgba(15,23,42,0.25),inset_0_2.5px_1.5px_rgba(255,255,255,0.98),0_0_0_1px_rgba(255,255,255,0.85),inset_0_-4px_8px_rgba(0,0,0,0.15)]", glow: "shadow-[0_0_40px_rgba(255,255,255,0.35)]", stroke: "#ffffff", tooltipBg: "bg-slate-900/90 border-white/30 text-white shadow-black/50", badgeBg: "bg-white/40 dark:bg-white/15 text-slate-900 dark:text-white border-white/80 dark:border-white/25", }, }; export const LiquidGlassChart: React.FC = ({ data, type = "bar", variant = "aurora", title, description, height = 260, showGrid = true, showTooltip = true, showValues = true, className, }) => { const [hoveredIdx, setHoveredIdx] = useState(null); const cfg = VARIANT_CONFIG[variant]; const maxValue = Math.max(...data.map((d) => d.value), 10); const svgPadding = 28; const svgWidth = 500; const svgHeight = height; // Compute smooth bezier SVG path for area/line chart const points = data.map((d, i) => { const x = svgPadding + (i * (svgWidth - svgPadding * 2)) / (data.length - 1 || 1); const y = svgHeight - svgPadding - (d.value * (svgHeight - svgPadding * 2)) / maxValue; return { x, y, ...d }; }); const pathD = points.reduce((acc, point, i, a) => { if (i === 0) return `M ${point.x},${point.y}`; const prev = a[i - 1]; const cx = (prev.x + point.x) / 2; return `${acc} C ${cx},${prev.y} ${cx},${point.y} ${point.x},${point.y}`; }, ""); const areaD = `${pathD} L ${points[points.length - 1]?.x || 0},${ svgHeight - svgPadding } L ${points[0]?.x || 0},${svgHeight - svgPadding} Z`; return ( {/* Top Gloss Glare */} {/* Bottom Prism Refraction */} {/* Card Header */} {(title || description) && ( {title && ( {title} )} {description && ( {description} )} {type.toUpperCase()} )} {/* Chart Canvas Area */} {/* Horizontal Background Grid Lines */} {showGrid && ( {[0, 1, 2, 3].map((_, idx) => ( ))} )} {/* ── 3D LIQUID GLASS BAR CHART ─────────────────────────────── */} {type === "bar" && ( {data.map((item, idx) => { const heightPercent = Math.max( (item.value / maxValue) * 100, 10 ); const isHovered = hoveredIdx === idx; return ( setHoveredIdx(idx)} onMouseLeave={() => setHoveredIdx(null)} className="relative flex-1 h-full flex flex-col items-center justify-end group cursor-pointer" > {/* 3D Liquid Capsule Bar */} {/* Top Curved Glare Specular Highlight Pill */} {/* Bottom Prism Refraction */} {/* Value tag above bar */} {showValues && ( {item.value} )} {/* Bottom Label */} {item.label} ); })} )} {/* ── AREA / LINE CHART ─────────────────────────────────────── */} {(type === "area" || type === "line") && ( {/* Area Gradient Fill */} {type === "area" && ( )} {/* Glowing Crystal Stroke Line */} {/* Data Nodes */} {points.map((pt, idx) => ( setHoveredIdx(idx)} onMouseLeave={() => setHoveredIdx(null)} /> ))} {/* Labels Row */} {data.map((item, idx) => ( {item.label} ))} )} {/* ── Interactive Glass Tooltip ─────────────────────────────── */} {showTooltip && hoveredIdx !== null && data[hoveredIdx] && ( {data[hoveredIdx].label} {data[hoveredIdx].value.toLocaleString()} units )} ); }; LiquidGlassChart.displayName = "LiquidGlassChart"; export default LiquidGlassChart; ``` ## Usage ```tsx import LiquidGlassChart from "@/components/lightswind/liquid-glass-chart" ``` ```tsx import LiquidGlassChart from "@/components/lightswind/liquid-glass-chart"; const DATA = [ { label: "Jan", value: 420 }, { label: "Feb", value: 680 }, { label: "Mar", value: 590 }, { label: "Apr", value: 890 }, { label: "May", value: 740 }, { label: "Jun", value: 1120 }, ]; export function Demo() { return ( ); } ```
{description}