aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
829 lines (809 loc) • 27 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
import { useRef, useState, useEffect, useCallback, useContext, createContext } from 'react';
import { motion } from 'framer-motion';
import { cn } from '../../lib/utilsComprehensive.js';
// AI-powered layout generator
class AILayoutGenerator {
constructor(config) {
this.config = config;
this.designTokens = this.initializeDesignTokens();
this.templateLibrary = new Map();
this.loadTemplateLibrary();
}
initializeDesignTokens() {
return {
colors: {
primary: "var(--glass-color-primary)",
secondary: "var(--glass-color-success)",
accent: "#8b5cf6",
background: '/* Use createGlassStyle({ intent: "primary", elevation: "level2" }) */',
surface: "var(--glass-bg-default)",
text: "var(--glass-white)",
textSecondary: "rgba(var(--glass-color-white) / var(--glass-opacity-70))"
},
spacing: {
xs: "0.25rem",
sm: "0.5rem",
md: "1rem",
lg: "1.5rem",
xl: "2rem",
"2xl": "3rem"
},
typography: {
fontSize: {
xs: "0.75rem",
sm: "0.875rem",
base: "1rem",
lg: "1.125rem",
xl: "1.25rem",
"2xl": "1.5rem"
},
fontWeight: {
light: 300,
normal: 400,
medium: 500,
semibold: 600,
bold: 700
}
},
shadows: {
sm: "0 1px 2px 0 rgba(var(--glass-color-black) / var(--glass-opacity-5))",
md: "0 4px 6px -1px rgba(var(--glass-color-black) / var(--glass-opacity-10))",
lg: "0 10px 15px -3px rgba(var(--glass-color-black) / var(--glass-opacity-10))",
glass: "0 8px 32px rgba(31, 38, 135, 0.37)"
},
borders: {
radius: "0.375rem"
},
animations: {
duration: {
fast: "0.15s",
normal: "0.3s",
slow: "0.5s"
},
easing: {
ease: "ease",
easeIn: "ease-in",
easeOut: "ease-out",
bounce: "cubic-bezier(0.68, -0.55, 0.265, 1.55)"
}
}
};
}
loadTemplateLibrary() {
this.templateLibrary.set("card", `
<div className="glass-surface-primary glass-elev-2 glass-radius-lg p-6">
{children}
</div>
`);
this.templateLibrary.set("button", `
<motion.button
className="glass-surface-secondary px-4 py-2 glass-radius-md text-primary"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
{children}
</motion.button>
`);
this.templateLibrary.set("input", `
<input
className="glass-surface-secondary border glass-radius-md px-3 py-2 text-primary glass-touch-target glass-contrast-guard"
placeholder={placeholder}
/>
`);
}
async generateLayout(prompt) {
const startTime = Date.now();
// For demo, we'll use rule-based generation
// In practice, this would call an LLM API
const generatedJSX = await this.generateJSXFromPrompt(prompt);
const generatedCSS = await this.generateCSSFromPrompt(prompt);
const iterations = 1;
return {
id: `layout-${Date.now()}`,
prompt,
jsx: generatedJSX,
css: generatedCSS,
tokens: this.designTokens,
confidence: 0.85,
iterations,
timestamp: startTime
};
}
async generateJSXFromPrompt(prompt) {
// Simulate AI generation with template matching
const {
description,
purpose,
style = "standard"
} = prompt;
let jsx = "";
if (description.toLowerCase().includes("dashboard")) {
jsx = this.generateDashboardLayout(prompt);
} else if (description.toLowerCase().includes("form")) {
jsx = this.generateFormLayout(prompt);
} else if (description.toLowerCase().includes("card")) {
jsx = this.generateCardLayout(prompt);
} else if (description.toLowerCase().includes("list")) {
jsx = this.generateListLayout(prompt);
} else {
jsx = this.generateGenericLayout(prompt);
}
return jsx;
}
generateDashboardLayout(prompt) {
return `
<div className="glass-min-h-screen glass-surface-primary p-6">
<div className="max-w-7xl mx-auto space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<h1 className="text-3xl font-bold text-primary">Dashboard</h1>
<div className="flex gap-4">
<motion.button className="glass-surface-secondary px-4 py-2 glass-radius-md">
Settings
</motion.button>
<motion.button className="glass-surface-accent px-4 py-2 glass-radius-md">
New Item
</motion.button>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{stats.map((stat, index) => (
<motion.div
key={index}
className="glass-surface-secondary glass-elev-2 glass-radius-lg p-6"
initial={{ opacity: 0, y: 20 }}
animate={prefersReducedMotion ? {} : { opacity: 1, y: 0 }}
transition={prefersReducedMotion ? { duration: 0 } : { duration: 0.3 }}
>
<div className="text-2xl font-bold text-primary">{stat.value}</div>
<div className="text-sm glass-text-secondary">{stat.label}</div>
</motion.div>
))}
</div>
{/* Main Content */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<div className="glass-surface-secondary glass-elev-2 glass-radius-lg p-6">
<h2 className="text-xl font-semibold text-primary mb-4">Main Chart</h2>
{/* Chart component would go here */}
</div>
</div>
<div className="space-y-6">
<div className="glass-surface-secondary glass-elev-2 glass-radius-lg p-6">
<h3 className="text-lg font-semibold text-primary mb-4">Recent Activity</h3>
{/* Activity list would go here */}
</div>
</div>
</div>
</div>
</div>`;
}
generateFormLayout(prompt) {
return `
<div className="max-w-md mx-auto glass-surface-primary glass-elev-3 glass-radius-lg p-8">
<h2 className="text-2xl font-bold text-primary mb-6 text-center">Contact Form</h2>
<form className="space-y-6">
<div>
<label className="block text-sm font-medium glass-text-secondary mb-2">
Full Name
</label>
<input
type="text"
className="w-full glass-surface-secondary border glass-radius-md px-3 py-2 text-primary placeholder-opacity-50 glass-touch-target glass-contrast-guard"
placeholder="Enter your full name"
/>
</div>
<div>
<label className="block text-sm font-medium glass-text-secondary mb-2">
Email Address
</label>
<input
type="email"
className="w-full glass-surface-secondary border glass-radius-md px-3 py-2 text-primary placeholder-opacity-50 glass-touch-target glass-contrast-guard"
placeholder="Enter your email"
/>
</div>
<div>
<label className="block text-sm font-medium glass-text-secondary mb-2">
Message
</label>
<textarea
rows={4}
className="w-full glass-surface-secondary border glass-radius-md px-3 py-2 text-primary placeholder-opacity-50 glass-touch-target glass-contrast-guard"
placeholder="Enter your message"
/>
</div>
<motion.button
type="submit"
className="w-full glass-surface-accent glass-elev-2 glass-radius-md py-3 font-semibold text-primary"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
Send Message
</motion.button>
</form>
</div>`;
}
generateCardLayout(prompt) {
return `
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 p-6">
{items.map((item, index) => (
<motion.div
key={item.id}
className="glass-surface-secondary glass-elev-2 glass-radius-lg overflow-hidden"
initial={{ opacity: 0, y: 20 }}
animate={prefersReducedMotion ? {} : { opacity: 1, y: 0 }}
transition={prefersReducedMotion ? { duration: 0 } : { duration: 0.3 }}
whileHover={{ scale: 1.02 }}
>
{item.image && (
<div className="aspect-video glass-gradient-primary glass-gradient-primary glass-gradient-primary"></div>
)}
<div className="p-6">
<h3 className="text-xl font-semibold text-primary mb-2">
{item.title}
</h3>
<p className="glass-text-secondary mb-4">
{item.description}
</p>
<div className="flex items-center justify-between">
<span className="text-sm glass-text-tertiary">
{item.date}
</span>
<motion.button
className="glass-surface-accent px-3 py-1 glass-radius-sm text-sm"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
View
</motion.button>
</div>
</div>
</motion.div>
))}
</div>`;
}
generateListLayout(prompt) {
return `
<div className="max-w-2xl mx-auto glass-surface-primary glass-elev-2 glass-radius-lg overflow-hidden">
<div className="p-6 border-b border">
<h2 className="text-2xl font-bold text-primary">List Items</h2>
<p className="glass-text-secondary glass-mt-1">Manage your items efficiently</p>
</div>
<div className="divide-y glass-divide">
{items.map((item, index) => (
<motion.div
key={item.id}
className="p-4 hover:glass-surface-secondary transition-colors"
initial={{ opacity: 0, x: -20 }}
animate={prefersReducedMotion ? {} : { opacity: 1, x: 0 }}
transition={prefersReducedMotion ? { duration: 0 } : { duration: 0.3 }}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 glass-surface-accent glass-radius-full flex items-center justify-center">
{item.icon}
</div>
<div>
<div className="font-medium text-primary">{item.title}</div>
<div className="text-sm glass-text-secondary">{item.subtitle}</div>
</div>
</div>
<div className="flex items-center gap-2">
<motion.button
className="p-2 glass-surface-secondary glass-radius-md"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Edit
</motion.button>
<motion.button
className="p-2 glass-surface-secondary glass-radius-md"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Delete
</motion.button>
</div>
</div>
</motion.div>
))}
</div>
</div>`;
}
generateGenericLayout(prompt) {
return `
<div className="glass-surface-primary glass-elev-2 glass-radius-lg p-6">
<div className="text-center gap-4">
<h1 className="text-3xl font-bold text-primary">
Generated Component
</h1>
<p className="glass-text-secondary max-w-md mx-auto">
This is a dynamically generated component based on your prompt: "${prompt.description}"
</p>
<div className="flex justify-center gap-4 pt-4">
<motion.button
className="glass-surface-accent px-6 py-3 glass-radius-md font-medium"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Primary Action
</motion.button>
<motion.button
className="glass-surface-secondary px-6 py-3 glass-radius-md font-medium"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Secondary Action
</motion.button>
</div>
</div>
</div>`;
}
async generateCSSFromPrompt(prompt) {
const {
style = "standard"
} = prompt;
const baseCSS = `
.generated-component {
/* Base glassmorphism styles */
backdrop-filter: var(--glass-backdrop-blur);
-webkit-backdrop-filter: var(--glass-backdrop-blur);
background: var(--glass-bg-default);
border: 1px solid var(--glass-border-default);
border-radius: 12px;
box-shadow: var(--glass-elev-2);
}
.generated-component .glass-surface-primary {
background: var(--glass-bg-default);
backdrop-filter: var(--glass-backdrop-blur);
}
.generated-component .glass-surface-secondary {
background: var(--glass-bg-default);
backdrop-filter: var(--glass-backdrop-blur);
}
.generated-component .glass-text-primary {
color: var(--glass-text-primary);
}
.generated-component .glass-text-secondary {
color: var(--glass-text-secondary);
}
.generated-component .glass-border {
border: 1px solid var(--glass-border-default);
}
`;
let styleSpecificCSS = "";
switch (style) {
case "minimal":
styleSpecificCSS = `
.generated-component {
--glass-blur: 5px;
--glass-opacity: 0.05;
--animation-duration: 0.2s;
}
`;
break;
case "detailed":
styleSpecificCSS = `
.generated-component {
--glass-blur: 25px;
--glass-opacity: 0.15;
--animation-duration: 0.4s;
box-shadow: var(--glass-elev-2);
}
`;
break;
case "experimental":
styleSpecificCSS = `
.generated-component {
--glass-blur: 30px;
--glass-opacity: 0.2;
--animation-duration: 0.6s;
background: var(--glass-bg-default);
animation: glowPulse 2s ease-in-out infinite alternate;
}
glowPulse {
0% { box-shadow: var(--glass-elev-2); }
100% { box-shadow: var(--glass-elev-2); }
}
`;
break;
}
return baseCSS + styleSpecificCSS;
}
}
// Layout optimizer using genetic algorithms
class LayoutOptimizer {
constructor() {
this.population = [];
this.fitnessScores = new Map();
this.generationCount = 0;
}
async optimizeLayout(baseLayout, userFeedback) {
// Initialize population if empty
if (this.population.length === 0) {
this.population = await this.createInitialPopulation(baseLayout);
}
// Calculate fitness scores
this.calculateFitnessScores(userFeedback);
// Evolve the population
this.population = await this.evolvePopulation();
this.generationCount++;
// Return the best layout
return this.getBestLayout();
}
async createInitialPopulation(baseLayout) {
const population = [baseLayout];
// Create variants
for (let i = 0; i < 9; i++) {
const variant = await this.createVariant(baseLayout);
population.push(variant);
}
return population;
}
async createVariant(layout) {
// Create variations in the layout
const mutations = ["spacing", "colors", "typography", "shadows", "animations"];
const randomMutation = mutations[Math.floor(Math.random() * mutations.length)];
const mutatedTokens = {
...layout.tokens
};
// Apply random mutations
switch (randomMutation) {
case "colors":
const hue = Math.floor(Math.random() * 360);
mutatedTokens.colors.primary = `hsl(${hue}, 70%, 60%)`;
break;
case "spacing":
const scale = 0.8 + Math.random() * 0.4; // 0.8 to 1.2
Object.keys(mutatedTokens.spacing).forEach(key => {
const value = parseFloat(mutatedTokens.spacing[key]);
mutatedTokens.spacing[key] = `${value * scale}rem`;
});
break;
}
return {
...layout,
id: `${layout.id}-variant-${Date.now()}`,
tokens: mutatedTokens,
confidence: layout.confidence * (0.8 + Math.random() * 0.4),
timestamp: Date.now()
};
}
calculateFitnessScores(feedback) {
this.population.forEach((layout, index) => {
const score = feedback[index] || 0.5; // Default neutral feedback
this.fitnessScores.set(layout.id, score);
});
}
async evolvePopulation() {
// Selection, crossover, and mutation
const selected = this.selection();
const offspring = await this.crossover(selected);
return this.mutation(offspring);
}
selection() {
// Tournament selection
const selected = [];
const tournamentSize = 3;
while (selected.length < this.population.length / 2) {
const tournament = [];
for (let i = 0; i < tournamentSize; i++) {
const randomIndex = Math.floor(Math.random() * this.population.length);
tournament.push(this.population[randomIndex]);
}
// Select the best from tournament
const winner = tournament.reduce((best, current) => {
const bestScore = this.fitnessScores.get(best.id) || 0;
const currentScore = this.fitnessScores.get(current.id) || 0;
return currentScore > bestScore ? current : best;
});
selected.push(winner);
}
return selected;
}
async crossover(parents) {
const offspring = [...parents];
// Create offspring by combining parent traits
while (offspring.length < this.population.length) {
const parent1 = parents[Math.floor(Math.random() * parents.length)];
const parent2 = parents[Math.floor(Math.random() * parents.length)];
const child = await this.combineLayouts(parent1, parent2);
offspring.push(child);
}
return offspring;
}
async combineLayouts(parent1, parent2) {
// Combine design tokens from both parents
const combinedTokens = {
colors: Math.random() > 0.5 ? parent1.tokens.colors : parent2.tokens.colors,
spacing: Math.random() > 0.5 ? parent1.tokens.spacing : parent2.tokens.spacing,
typography: Math.random() > 0.5 ? parent1.tokens.typography : parent2.tokens.typography,
shadows: Math.random() > 0.5 ? parent1.tokens.shadows : parent2.tokens.shadows,
borders: Math.random() > 0.5 ? parent1.tokens.borders : parent2.tokens.borders,
animations: Math.random() > 0.5 ? parent1.tokens.animations : parent2.tokens.animations
};
return {
id: `child-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
prompt: parent1.prompt,
jsx: Math.random() > 0.5 ? parent1.jsx : parent2.jsx,
css: parent1.css,
// Would need more sophisticated combination
tokens: combinedTokens,
confidence: (parent1.confidence + parent2.confidence) / 2,
iterations: Math.max(parent1.iterations, parent2.iterations) + 1,
timestamp: Date.now()
};
}
mutation(population) {
const mutationRate = 0.1;
return population.map(async layout => {
if (Math.random() < mutationRate) {
return await this.createVariant(layout);
}
return layout;
}); // Simplified for demo
}
getBestLayout() {
return this.population.reduce((best, current) => {
const bestScore = this.fitnessScores.get(best.id) || 0;
const currentScore = this.fitnessScores.get(current.id) || 0;
return currentScore > bestScore ? current : best;
});
}
}
// React Context
const AutoComposerContext = /*#__PURE__*/createContext({
generator: null,
optimizer: null,
generateLayout: async () => ({}),
optimizeLayout: async () => ({}),
currentLayouts: []
});
// Provider component
function GlassAutoComposerProvider({
children,
config = {
model: "claude",
temperature: 0.7,
maxTokens: 2000,
designSystem: "glass",
accessibility: true,
responsive: true
}
}) {
useReducedMotion();
const generatorRef = useRef();
const optimizerRef = useRef();
const [currentLayouts, setCurrentLayouts] = useState([]);
useEffect(() => {
const fullConfig = {
model: "claude",
temperature: 0.7,
maxTokens: 2000,
designSystem: "glass",
accessibility: true,
responsive: true,
...config
};
generatorRef.current = new AILayoutGenerator(fullConfig);
optimizerRef.current = new LayoutOptimizer();
}, [config]);
const generateLayout = useCallback(async prompt => {
if (!generatorRef.current) throw new Error("Generator not initialized");
const layout = await generatorRef.current.generateLayout(prompt);
setCurrentLayouts(prev => [...prev, layout]);
return layout;
}, []);
const optimizeLayout = useCallback(async (layout, feedback) => {
if (!optimizerRef.current) throw new Error("Optimizer not initialized");
const optimized = await optimizerRef.current.optimizeLayout(layout, feedback);
setCurrentLayouts(prev => [...prev, optimized]);
return optimized;
}, []);
const value = {
generator: generatorRef.current || null,
optimizer: optimizerRef.current || null,
generateLayout,
optimizeLayout,
currentLayouts
};
return jsx(AutoComposerContext.Provider, {
value: value,
children: children
});
}
// Hook to use auto composer
function useAutoComposer() {
const context = useContext(AutoComposerContext);
if (!context) {
throw new Error("useAutoComposer must be used within GlassAutoComposerProvider");
}
return context;
}
// Auto composer interface
function GlassAutoComposerInterface({
className
}) {
const {
generateLayout
} = useAutoComposer();
const [prompt, setPrompt] = useState("");
const [generatedLayout, setGeneratedLayout] = useState(null);
const [isGenerating, setIsGenerating] = useState(false);
const handleGenerate = async () => {
if (!prompt.trim()) return;
setIsGenerating(true);
try {
const layout = await generateLayout({
description: prompt,
purpose: "user-requested",
style: "standard"
});
setGeneratedLayout(layout);
} catch (error) {
console.error("Failed to generate layout:", error);
} finally {
setIsGenerating(false);
}
};
return jsxs("div", {
className: cn("space-y-6", className),
children: [jsxs("div", {
className: "glass-surface-primary glass-elev-2 glass-radius-lg glass-p-6",
children: [jsx("h2", {
className: 'glass-text-2xl font-bold text-primary mb-4',
children: "AI Layout Generator"
}), jsxs("div", {
className: "glass-gap-4",
children: [jsxs("div", {
children: [jsx("label", {
className: 'block glass-text-sm font-medium glass-text-secondary mb-2',
children: "Describe the layout you want"
}), jsx("textarea", {
className: 'glass-w-full glass-surface-secondary glass-border glass-radius-md glass-px-3 glass-py-2 text-primary glass-touch-target glass-contrast-guard',
rows: 3,
value: prompt,
onChange: e => setPrompt(e.target.value),
placeholder: "e.g., Create a modern dashboard with charts, stats cards, and a sidebar navigation"
})]
}), jsx(motion.button, {
className: 'glass-surface-accent glass-px-6 glass-py-3 glass-radius-md font-medium',
onClick: handleGenerate,
disabled: isGenerating || !prompt.trim(),
whileHover: {
scale: 1.02
},
whileTap: {
scale: 0.98
},
children: isGenerating ? "Generating..." : "Generate Layout"
})]
})]
}), generatedLayout && jsxs("div", {
className: "glass-surface-primary glass-elev-2 glass-radius-lg glass-p-6",
children: [jsxs("div", {
className: 'glass-flex glass-items-center glass-justify-between mb-4',
children: [jsx("h3", {
className: 'glass-text-xl font-semibold text-primary',
children: "Generated Layout"
}), jsx("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: jsxs("span", {
className: "glass-text-sm glass-text-secondary",
children: ["Confidence: ", (generatedLayout.confidence * 100).toFixed(0), "%"]
})
})]
}), jsxs("div", {
className: "glass-gap-4",
children: [jsxs("div", {
children: [jsx("h4", {
className: 'glass-text-sm font-medium glass-text-secondary mb-2',
children: "JSX Code"
}), jsx("pre", {
className: 'glass-surface-secondary glass-radius-md glass-p-4 glass-text-xs text-primary overflow-x-auto',
children: generatedLayout.jsx
})]
}), jsxs("div", {
children: [jsx("h4", {
className: 'glass-text-sm font-medium glass-text-secondary mb-2',
children: "CSS Styles"
}), jsx("pre", {
className: 'glass-surface-secondary glass-radius-md glass-p-4 glass-text-xs text-primary overflow-x-auto',
children: generatedLayout.css
})]
})]
})]
})]
});
}
// Layout renderer component
function GlassGeneratedLayoutRenderer({
layout,
data = {},
className
}) {
const renderJSX = useCallback(() => {
// In a real implementation, this would safely execute the generated JSX
// For demo purposes, we'll show a placeholder
return jsxs("div", {
className: 'generated-component glass-p-6 text-center',
children: [jsx("h3", {
className: 'glass-text-xl font-bold text-primary mb-2',
children: "Generated Component Preview"
}), jsxs("p", {
className: 'glass-text-secondary mb-4',
children: ["Based on: \"", layout.prompt.description, "\""]
}), jsxs("div", {
className: "glass-text-xs glass-text-tertiary",
children: ["Generated at: ", new Date(layout.timestamp).toLocaleString()]
})]
});
}, [layout]);
return jsxs("div", {
className: cn("generated-layout", className),
children: [jsx("style", {
children: layout.css
}), renderJSX()]
});
}
// Hook for layout generation
function useLayoutGenerator() {
const {
generateLayout,
currentLayouts
} = useAutoComposer();
const generateFromDescription = useCallback(async description => {
return generateLayout({
description,
purpose: "user-generated",
style: "standard"
});
}, [generateLayout]);
return {
generateFromDescription,
currentLayouts,
hasLayouts: currentLayouts.length > 0
};
}
// Presets for different generation modes
const autoComposerPresets = {
creative: {
temperature: 0.9,
style: "experimental",
iterations: 5
},
balanced: {
temperature: 0.7,
style: "standard",
iterations: 3
},
conservative: {
temperature: 0.3,
style: "minimal",
iterations: 1
},
accessibility: {
temperature: 0.5,
style: "standard",
accessibility: true,
iterations: 2
}
};
const GlassAutoComposer = ({
config,
...interfaceProps
}) => {
return jsx(GlassAutoComposerProvider, {
config: config,
children: jsx(GlassAutoComposerInterface, {
...interfaceProps
})
});
};
export { GlassAutoComposer, GlassAutoComposerInterface, GlassAutoComposerProvider, GlassGeneratedLayoutRenderer, autoComposerPresets, useAutoComposer, useLayoutGenerator };
//# sourceMappingURL=GlassAutoComposer.js.map