aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
431 lines (428 loc) • 16.3 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
import { motion } from 'framer-motion';
import { Settings, Eye, Zap, Droplets, Layers, Sun, Sparkles, Gauge } from 'lucide-react';
import { useRef, useState } from 'react';
import { cn } from '../../lib/utilsComprehensive.js';
import { useHoudiniGlass, useGlassEffect } from './HoudiniGlassProvider.js';
function HoudiniGlassCard({
children,
className = "",
preset = "standard",
effects = ["frost"],
enableWorklets = true,
customProperties = {},
interactive = true,
showControls = false,
title,
description
}) {
const prefersReducedMotion = useReducedMotion();
const cardRef = useRef(null);
const {
isSupported,
enabledEffects,
toggleEffect,
performanceMode
} = useHoudiniGlass();
const [isHovered, setIsHovered] = useState(false);
const [showEffectControls, setShowEffectControls] = useState(false);
// Apply glass effects using the Houdini hook
const {
appliedEffects,
canUseWorklets
} = useGlassEffect(cardRef, effects, {
preset,
customProperties,
enableWorklets: enableWorklets
});
const handleMouseEnter = () => {
if (interactive) {
setIsHovered(true);
}
};
const handleMouseLeave = () => {
if (interactive) {
setIsHovered(false);
}
};
const getEffectIcon = effect => {
switch (effect) {
case "frost":
return jsx(Sparkles, {
className: 'w-4 h-4'
});
case "caustics":
return jsx(Sun, {
className: 'w-4 h-4'
});
case "border":
return jsx(Layers, {
className: 'w-4 h-4'
});
case "refraction":
return jsx(Droplets, {
className: 'w-4 h-4'
});
default:
return jsx(Zap, {
className: 'w-4 h-4'
});
}
};
const getPerformanceIndicator = () => {
useReducedMotion();
if (performanceMode) {
return jsx("span", {
title: "Performance mode active",
children: jsx(Gauge, {
className: 'w-4 h-4 text-primary'
})
});
}
return jsx("span", {
title: "Full effects active",
children: jsx(Zap, {
className: 'w-4 h-4 text-primary'
})
});
};
return jsxs(motion.div, {
ref: cardRef,
className: cn(
// Base glass foundation with design tokens
"glass-foundation-complete glass-radius-xl glass-p-lg", "relative overflow-hidden",
// Houdini-specific classes
"houdini-glass", {
"houdini-glass-fallback": !isSupported,
"houdini-glass-performance": performanceMode,
"cursor-pointer glass-press glass-magnet": interactive
},
// Glass effects
"glass-overlay-specular glass-parallax", "glass-transition glass-focus", className),
onMouseEnter: handleMouseEnter,
onMouseLeave: handleMouseLeave,
whileHover: interactive ? {
scale: 1.02
} : {},
whileTap: interactive ? {
scale: 0.98
} : {},
transition: prefersReducedMotion ? {
duration: 0
} : {
duration: performanceMode ? 0.15 : 0.3,
ease: "easeOut"
},
children: [(title || description || showControls) && jsx("div", {
className: 'mb-4',
children: jsxs("div", {
className: "glass-flex glass-items-start glass-justify-between",
children: [jsxs("div", {
className: "glass-flex-1",
children: [title && jsx("h3", {
className: 'glass-text-lg font-semibold glass-text-secondary dark:text-primary mb-1',
children: title
}), description && jsx("p", {
className: 'glass-text-sm glass-text-secondary dark:text-gray-300',
children: description
})]
}), showControls && jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-2 ml-4',
children: [getPerformanceIndicator(), jsx("button", {
onClick: () => setShowEffectControls(!showEffectControls),
className: 'glass-p-2 glass-radius-lg hover:glass-surface-subtle/10 transition-colors',
title: "Toggle effect controls",
children: jsx(Settings, {
className: 'w-4 h-4 glass-text-secondary dark:glass-text-secondary'
})
})]
})]
})
}), showEffectControls && showControls && jsxs(motion.div, {
initial: {
opacity: 0,
height: 0
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
height: "auto"
},
exit: {
opacity: 0,
height: 0
},
className: 'mb-4 glass-p-3 glass-radius-lg glass-surface-dark/5 dark:glass-surface-subtle/5',
children: [jsxs("div", {
className: 'glass-flex glass-items-center glass-justify-between mb-2',
children: [jsx("span", {
className: 'glass-text-sm font-medium glass-text-secondary dark:text-gray-300',
children: "Glass Effects"
}), jsxs("span", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary',
children: [appliedEffects.length, " active"]
})]
}), jsx("div", {
className: "glass-grid glass-grid-cols-2 glass-gap-2",
children: ["frost", "caustics", "border", "refraction"].map(effect => jsxs("button", {
onClick: () => toggleEffect(effect),
className: `
flex items-center gap-2 p-2 rounded-lg text-sm transition-all
${enabledEffects.includes(effect) ? "bg-blue-500/20 text-blue-700 dark:text-blue-300 border border-blue-500/30" : "bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"}
`,
title: `${enabledEffects.includes(effect) ? "Disable" : "Enable"} ${effect} effect`,
children: [getEffectIcon(effect), jsx("span", {
className: 'capitalize',
children: effect
}), enabledEffects.includes(effect) && jsx(Eye, {
className: 'w-3 h-3 ml-auto'
})]
}, effect))
}), jsxs("div", {
className: 'mt-2 glass-text-xs glass-text-secondary dark:glass-text-secondary',
children: ["Worklets: ", canUseWorklets ? "✅ Supported" : "❌ Fallback"]
})]
}), jsx("div", {
className: 'relative z-10',
children: children
}), interactive && jsx(motion.div, {
className: 'absolute inset-0 glass-gradient-primary glass-gradient-primary glass-gradient-primary pointer-events-none',
initial: {
opacity: 0
},
animate: prefersReducedMotion ? {} : {
opacity: isHovered ? 1 : 0
},
transition: prefersReducedMotion ? {
duration: 0
} : {
duration: performanceMode ? 0.1 : 0.2
}
}), showControls && jsx("div", {
className: 'absolute glass-top-2 right-2 glass-flex glass-gap-1',
children: appliedEffects.map(effect => jsx("div", {
className: 'w-2 h-2 glass-radius-full glass-surface-blue opacity-60',
title: `Active: ${effect}`
}, effect))
}), !isSupported && jsx("div", {
className: 'absolute bottom-2 right-2',
children: jsx("div", {
className: 'w-2 h-2 glass-radius-full bg-amber-400',
title: "Houdini not supported - using fallback styles"
})
})]
});
}
// Demo component showcasing different Houdini glass effects
function HoudiniGlassShowcase() {
const [selectedPreset, setSelectedPreset] = useState("standard");
const [selectedEffects, setSelectedEffects] = useState(["frost"]);
const {
isSupported,
hasPaintAPI,
hasPropertyAPI
} = useHoudiniGlass();
const presets = [{
id: "standard",
name: "Standard",
description: "Balanced glass effect"
}, {
id: "frosted",
name: "Frosted",
description: "Enhanced blur and opacity"
}, {
id: "minimal",
name: "Minimal",
description: "Subtle glass appearance"
}, {
id: "heavy",
name: "Heavy",
description: "Maximum glass intensity"
}, {
id: "crystal",
name: "Crystal",
description: "Ultra-clear glass effect"
}];
const availableEffects = [{
id: "frost",
name: "Frost",
description: "Icy glass texture"
}, {
id: "caustics",
name: "Caustics",
description: "Light refraction patterns"
}, {
id: "border",
name: "Border",
description: "Animated border effects"
}, {
id: "refraction",
name: "Refraction",
description: "Light bending effects"
}];
const toggleEffect = effectId => {
setSelectedEffects(prev => prev.includes(effectId) ? prev.filter(id => id !== effectId) : [...prev, effectId]);
};
return jsxs("div", {
className: 'space-y-6',
children: [jsx(HoudiniGlassCard, {
title: "Houdini Glass Support",
description: "Check browser compatibility for CSS Houdini features",
showControls: true,
children: jsxs("div", {
className: 'glass-grid glass-grid-cols-1 md:grid-cols-3 glass-gap-4',
children: [jsxs("div", {
className: 'text-center glass-p-3 glass-radius-lg glass-surface-subtle dark:glass-surface-primary',
children: [jsx("div", {
className: `text-2xl mb-2 ${isSupported ? "text-green-500" : "text-red-500"}`,
children: isSupported ? "✅" : "❌"
}), jsx("div", {
className: 'font-medium',
children: "Overall Support"
}), jsx("div", {
className: 'glass-text-sm glass-text-secondary dark:glass-text-secondary',
children: "Houdini APIs available"
})]
}), jsxs("div", {
className: 'text-center glass-p-3 glass-radius-lg glass-surface-subtle dark:glass-surface-primary',
children: [jsx("div", {
className: `text-2xl mb-2 ${hasPropertyAPI ? "text-green-500" : "text-red-500"}`,
children: hasPropertyAPI ? "✅" : "❌"
}), jsx("div", {
className: 'font-medium',
children: "Properties API"
}), jsx("div", {
className: 'glass-text-sm glass-text-secondary dark:glass-text-secondary',
children: "Custom properties support"
})]
}), jsxs("div", {
className: 'text-center glass-p-3 glass-radius-lg glass-surface-subtle dark:glass-surface-primary',
children: [jsx("div", {
className: `text-2xl mb-2 ${hasPaintAPI ? "text-green-500" : "text-red-500"}`,
children: hasPaintAPI ? "✅" : "❌"
}), jsx("div", {
className: 'font-medium',
children: "Paint API"
}), jsx("div", {
className: 'glass-text-sm glass-text-secondary dark:glass-text-secondary',
children: "Paint worklets support"
})]
})]
})
}), jsx(HoudiniGlassCard, {
title: "Glass Presets",
description: "Choose from predefined glass effect configurations",
showControls: true,
children: jsx("div", {
className: 'glass-grid glass-grid-cols-1 md:grid-cols-5 glass-gap-3',
children: presets.map(preset => jsxs("button", {
onClick: () => setSelectedPreset(preset.id),
className: `
p-3 rounded-lg text-left transition-all
${selectedPreset === preset.id ? "bg-blue-500/20 border-2 border-blue-500/50 text-blue-700 dark:text-blue-300" : "bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border-2 border-transparent"}
`,
children: [jsx("div", {
className: 'font-medium glass-text-sm',
children: preset.name
}), jsx("div", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary mt-1',
children: preset.description
})]
}, preset.id))
})
}), jsx(HoudiniGlassCard, {
title: "Glass Effects",
description: "Enable/disable individual glass effect layers",
showControls: true,
children: jsx("div", {
className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 lg:grid-cols-4 glass-gap-3',
children: availableEffects.map(effect => jsxs("button", {
onClick: () => toggleEffect(effect.id),
className: `
p-3 rounded-lg text-left transition-all
${selectedEffects.includes(effect.id) ? "bg-green-500/20 border-2 border-green-500/50 text-green-700 dark:text-green-300" : "bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border-2 border-transparent"}
`,
children: [jsxs("div", {
className: 'font-medium glass-text-sm glass-flex glass-items-center glass-gap-2',
children: [getEffectIcon(effect.id), effect.name]
}), jsx("div", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary mt-1',
children: effect.description
})]
}, effect.id))
})
}), jsx(HoudiniGlassCard, {
title: "Live Preview",
description: `Preset: ${selectedPreset} | Effects: ${selectedEffects.join(", ")}`,
preset: selectedPreset,
effects: selectedEffects,
showControls: true,
interactive: true,
children: jsxs("div", {
className: 'space-y-4',
children: [jsxs("div", {
className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 glass-gap-4',
children: [jsxs("div", {
className: 'glass-p-4 glass-radius-lg glass-gradient-primary glass-gradient-primary glass-gradient-primary dark:glass-gradient-primary dark:glass-gradient-primary',
children: [jsx("h4", {
className: 'font-medium glass-text-secondary dark:text-primary mb-2',
children: "Content Area 1"
}), jsx("p", {
className: 'glass-text-sm glass-text-secondary dark:text-gray-300',
children: "This is a preview of how the selected glass effects appear with your content. The effects are applied using CSS Houdini for maximum performance."
})]
}), jsxs("div", {
className: 'glass-p-4 glass-radius-lg glass-gradient-primary glass-gradient-primary glass-gradient-primary dark:glass-gradient-primary dark:glass-gradient-primary',
children: [jsx("h4", {
className: 'font-medium glass-text-secondary dark:text-primary mb-2',
children: "Content Area 2"
}), jsx("p", {
className: 'glass-text-sm glass-text-secondary dark:text-gray-300',
children: "Experiment with different presets and effects to find the perfect glass aesthetic for your application."
})]
})]
}), jsxs("div", {
className: 'glass-p-4 glass-radius-lg glass-gradient-primary glass-gradient-primary glass-gradient-primary dark:glass-gradient-primary dark:glass-gradient-primary',
children: [jsx("h4", {
className: 'font-medium glass-text-secondary dark:text-primary mb-2',
children: "Interactive Element"
}), jsx("p", {
className: 'glass-text-sm glass-text-secondary dark:text-gray-300 mb-3',
children: "Hover over this card to see the interactive effects in action."
}), jsx("button", {
className: 'glass-px-4 glass-py-2 glass-surface-blue text-primary glass-radius-lg hover:glass-surface-blue transition-colors',
children: "Interactive Button"
})]
})]
})
})]
});
}
// Helper function for effect icons (used in showcase)
function getEffectIcon(effect) {
switch (effect) {
case "frost":
return jsx(Sparkles, {
className: 'w-4 h-4'
});
case "caustics":
return jsx(Sun, {
className: 'w-4 h-4'
});
case "border":
return jsx(Layers, {
className: 'w-4 h-4'
});
case "refraction":
return jsx(Droplets, {
className: 'w-4 h-4'
});
default:
return jsx(Zap, {
className: 'w-4 h-4'
});
}
}
export { HoudiniGlassCard, HoudiniGlassShowcase, HoudiniGlassCard as default };
//# sourceMappingURL=HoudiniGlassCard.js.map