--- title: Liquid Glass Collapsible description: A 3D glassmorphic expandable panel component featuring modular subcomponents (Trigger, Content), smooth Framer Motion spring height expansion, specular glare overlay, and 7 color crystal variants. --- ## Installation CLI Manual ```bash npx lightswind@latest add liquid-glass-collapsible ``` 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"; import { ChevronDown } from "lucide-react"; export type LiquidGlassCollapsibleVariant = | "glass" | "aurora" | "emerald" | "cyan" | "purple" | "primary" | "dark"; export interface LiquidGlassCollapsibleProps { open?: boolean; defaultOpen?: boolean; onOpenChange?: (open: boolean) => void; variant?: LiquidGlassCollapsibleVariant; disabled?: boolean; className?: string; children: React.ReactNode; } /* ─── Variant Theme Configurations ───────────────────────────────── */ interface VariantConfig { container: string; divider: string; contentBg: string; contentText: string; prismColor: string; /** Top glare gradient — white for glass, tinted for colored variants */ glareGradient: string; /** Height class for the top glare overlay */ glareHeight: string; } const VARIANT_CONFIG: Record = { glass: { container: "bg-gradient-to-b from-white/65 via-white/35 to-white/15 dark:from-white/24 dark:via-white/12 dark:to-white/6 border border-white/90 dark:border-white/28 text-slate-900 dark:text-white shadow-[0_20px_50px_-8px_rgba(15,23,42,0.22),inset_0_2.5px_1.5px_0px_rgba(255,255,255,0.95),0_0_0_1px_rgba(255,255,255,0.85)] dark:shadow-[0_20px_50px_-8px_rgba(0,0,0,0.8),inset_0_2.5px_1.5px_0px_rgba(255,255,255,0.3),0_0_0_1px_rgba(255,255,255,0.18)]", divider: "from-transparent via-white/70 dark:via-white/25 to-transparent", contentBg: "bg-gradient-to-b from-white/30 via-white/20 to-white/10 dark:from-white/12 dark:via-white/6 dark:to-white/3", contentText: "text-slate-800 dark:text-slate-100", prismColor: "rgba(255,255,255,0.45)", glareGradient: "linear-gradient(180deg, rgba(255,255,255,0.92) 0%, rgba(255,255,255,0.50) 40%, rgba(255,255,255,0) 100%)", glareHeight: "h-[55%]", }, aurora: { container: "bg-gradient-to-b from-violet-300/70 via-fuchsia-400/62 to-violet-600/68 border border-white/70 dark:border-white/30 text-white shadow-[0_20px_50px_-8px_rgba(139,92,246,0.40),inset_0_2.5px_1.5px_rgba(240,220,255,0.90),0_0_0_1px_rgba(255,255,255,0.65)]", divider: "from-transparent via-white/50 to-transparent", contentBg: "bg-gradient-to-b from-violet-400/20 via-fuchsia-500/15 to-violet-600/20", contentText: "text-white", prismColor: "rgba(200,180,255,0.30)", glareGradient: "linear-gradient(180deg, rgba(255,245,255,0.85) 0%, rgba(240,220,255,0.40) 42%, rgba(255,255,255,0) 100%)", glareHeight: "h-[42%]", }, emerald: { container: "bg-gradient-to-b from-emerald-300/70 via-emerald-500/62 to-teal-600/68 border border-white/70 dark:border-white/30 text-white shadow-[0_20px_50px_-8px_rgba(16,185,129,0.40),inset_0_2.5px_1.5px_rgba(220,255,235,0.90),0_0_0_1px_rgba(255,255,255,0.65)]", divider: "from-transparent via-white/50 to-transparent", contentBg: "bg-gradient-to-b from-emerald-400/20 via-emerald-500/15 to-teal-600/20", contentText: "text-white", prismColor: "rgba(180,255,220,0.30)", glareGradient: "linear-gradient(180deg, rgba(235,255,245,0.85) 0%, rgba(180,255,220,0.40) 42%, rgba(255,255,255,0) 100%)", glareHeight: "h-[42%]", }, cyan: { container: "bg-gradient-to-b from-sky-300/70 via-blue-400/62 to-indigo-500/68 border border-white/70 dark:border-white/30 text-white shadow-[0_20px_50px_-8px_rgba(14,165,233,0.40),inset_0_2.5px_1.5px_rgba(220,240,255,0.90),0_0_0_1px_rgba(255,255,255,0.65)]", divider: "from-transparent via-white/50 to-transparent", contentBg: "bg-gradient-to-b from-sky-400/20 via-blue-500/15 to-indigo-500/20", contentText: "text-white", prismColor: "rgba(180,220,255,0.30)", glareGradient: "linear-gradient(180deg, rgba(225,242,255,0.85) 0%, rgba(180,220,255,0.40) 42%, rgba(255,255,255,0) 100%)", glareHeight: "h-[42%]", }, purple: { container: "bg-gradient-to-b from-purple-300/70 via-fuchsia-400/62 to-purple-700/68 border border-white/70 dark:border-white/30 text-white shadow-[0_20px_50px_-8px_rgba(168,85,247,0.40),inset_0_2.5px_1.5px_rgba(240,220,255,0.90),0_0_0_1px_rgba(255,255,255,0.65)]", divider: "from-transparent via-white/50 to-transparent", contentBg: "bg-gradient-to-b from-purple-400/20 via-fuchsia-500/15 to-purple-700/20", contentText: "text-white", prismColor: "rgba(210,180,255,0.30)", glareGradient: "linear-gradient(180deg, rgba(245,235,255,0.85) 0%, rgba(210,180,255,0.40) 42%, rgba(255,255,255,0) 100%)", glareHeight: "h-[42%]", }, primary: { container: "bg-gradient-to-b from-rose-300/70 via-red-400/62 to-rose-600/68 border border-white/70 dark:border-white/30 text-white shadow-[0_20px_50px_-8px_rgba(244,63,94,0.40),inset_0_2.5px_1.5px_rgba(255,220,220,0.90),0_0_0_1px_rgba(255,255,255,0.65)]", divider: "from-transparent via-white/50 to-transparent", contentBg: "bg-gradient-to-b from-rose-400/20 via-red-500/15 to-rose-600/20", contentText: "text-white", prismColor: "rgba(255,200,200,0.30)", glareGradient: "linear-gradient(180deg, rgba(255,238,240,0.85) 0%, rgba(255,200,200,0.40) 42%, rgba(255,255,255,0) 100%)", glareHeight: "h-[42%]", }, dark: { container: "bg-gradient-to-b from-slate-700/75 via-slate-800/68 to-zinc-900/75 border border-white/25 dark:border-white/20 text-white shadow-[0_20px_50px_-8px_rgba(0,0,0,0.55),inset_0_2.5px_1.5px_0px_rgba(255,255,255,0.20),0_0_0_1px_rgba(255,255,255,0.15)]", divider: "from-transparent via-white/20 to-transparent", contentBg: "bg-gradient-to-b from-slate-800/40 via-zinc-900/35 to-slate-900/40", contentText: "text-slate-200", prismColor: "rgba(255,255,255,0.12)", glareGradient: "linear-gradient(180deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.10) 42%, rgba(255,255,255,0) 100%)", glareHeight: "h-[42%]", }, }; /* ─── Context ────────────────────────────────────────────────────── */ interface CollapsibleContextType { isOpen: boolean; toggle: () => void; variant: LiquidGlassCollapsibleVariant; disabled?: boolean; } const CollapsibleContext = React.createContext({ isOpen: false, toggle: () => {}, variant: "glass", }); /* ─── Main Collapsible Wrapper ───────────────────────────────────── */ export const LiquidGlassCollapsible: React.FC = ({ open, defaultOpen = false, onOpenChange, variant = "glass", disabled = false, className, children, }) => { const [internalOpen, setInternalOpen] = useState(defaultOpen); const isControlled = open !== undefined; const isOpen = isControlled ? open : internalOpen; const toggle = () => { if (disabled) return; const nextState = !isOpen; if (!isControlled) setInternalOpen(nextState); onOpenChange?.(nextState); }; const cfg = VARIANT_CONFIG[variant]; return ( {children} ); }; /* ─── Collapsible Trigger ────────────────────────────────────────── */ export interface LiquidGlassCollapsibleTriggerProps extends React.ButtonHTMLAttributes { children: React.ReactNode; } export const LiquidGlassCollapsibleTrigger = React.forwardRef< HTMLButtonElement, LiquidGlassCollapsibleTriggerProps >(({ className, children, ...props }, ref) => { const { isOpen, toggle, disabled, variant } = React.useContext(CollapsibleContext); const cfg = VARIANT_CONFIG[variant]; return ( {/* ── Top Glare confined to trigger only ── */} {/* ── Bottom prism confined to trigger only ── */} {children} ); }); LiquidGlassCollapsibleTrigger.displayName = "LiquidGlassCollapsibleTrigger"; /* ─── Collapsible Content ────────────────────────────────────────── */ export interface LiquidGlassCollapsibleContentProps extends Omit, "onDrag" | "onDragStart" | "onDragEnd" | "onAnimationStart"> { children: React.ReactNode; } export const LiquidGlassCollapsibleContent = React.forwardRef< HTMLDivElement, LiquidGlassCollapsibleContentProps >(({ className, children, ...props }, ref) => { const { isOpen, variant } = React.useContext(CollapsibleContext); const cfg = VARIANT_CONFIG[variant]; return ( {isOpen && ( {/* Crystal divider line */} {/* Content panel — liquid glass inner layer */} {/* Inner top glare for depth */} {/* Content body */} {children} {/* Inner bottom prism refraction */} )} ); }); LiquidGlassCollapsibleContent.displayName = "LiquidGlassCollapsibleContent"; export default LiquidGlassCollapsible; ``` ## Usage ```tsx import LiquidGlassCollapsible, { LiquidGlassCollapsibleTrigger, LiquidGlassCollapsibleContent } from "@/components/lightswind/liquid-glass-collapsible" ``` ```tsx import LiquidGlassCollapsible, { LiquidGlassCollapsibleTrigger, LiquidGlassCollapsibleContent, } from "@/components/lightswind/liquid-glass-collapsible"; export function Demo() { return ( Workspace Configuration Configure your liquid glass runtime settings and node parameters. ); } ```
Configure your liquid glass runtime settings and node parameters.