Vue Integration
BaroCSS integrates seamlessly with Vue 3, enabling reactive AI-driven component generation with build-free styling. Perfect for dynamic applications where styles need to update in real-time based on AI responses.
Setup
1. Installation
bash
npm install barocss
# or
yarn add barocss
# or
pnpm add barocss@latest
2. Vue Plugin Setup
javascript
// plugins/barocss.js
import { BrowserRuntime } from 'barocss/runtime/browser';
export default {
install(app, options) {
const runtime = new BrowserRuntime(options);
app.config.globalProperties.$barocss = runtime;
app.mixin({
mounted() {
if (!window.__barocss_initialized) {
runtime.init();
window.__barocss_initialized = true;
}
}
});
}
};
javascript
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import BaroCSSPlugin from './plugins/barocss.js';
const app = createApp(App);
app.use(BaroCSSPlugin);
app.mount('#app');
Reactive AI Component System
AI Component Generator Composable
vue
<script setup>
import { ref, reactive, computed, nextTick } from 'vue';
// Composable for AI component generation
function useAIComponentGenerator() {
const components = ref([]);
const isGenerating = ref(false);
const cache = reactive(new Map());
// Simulate AI component generation
const generateComponent = async (prompt, config = {}) => {
isGenerating.value = true;
try {
const cacheKey = JSON.stringify({ prompt, config });
if (cache.has(cacheKey)) {
const cached = cache.get(cacheKey);
components.value.push({ ...cached, id: Date.now() });
return;
}
// Simulate AI API call
const aiResponse = await simulateAIGeneration(prompt, config);
const newComponent = {
id: Date.now(),
type: aiResponse.type,
styles: reactive(aiResponse.styles),
content: reactive(aiResponse.content),
props: reactive(aiResponse.props || {})
};
cache.set(cacheKey, newComponent);
components.value.push(newComponent);
} finally {
isGenerating.value = false;
}
};
const updateComponent = (id, updates) => {
const component = components.value.find(c => c.id === id);
if (component) {
Object.assign(component, updates);
}
};
const removeComponent = (id) => {
const index = components.value.findIndex(c => c.id === id);
if (index > -1) {
components.value.splice(index, 1);
}
};
return {
components,
isGenerating,
generateComponent,
updateComponent,
removeComponent
};
}
// AI simulation function
async function simulateAIGeneration(prompt, config) {
await new Promise(resolve => setTimeout(resolve, 800));
const templates = {
'modern card': {
type: 'card',
styles: {
container: `w-[${config.width || 320}px] h-[${config.height || 200}px] bg-gradient-to-br from-[#667eea] to-[#764ba2] rounded-[16px] shadow-[0_8px_32px_rgba(0,0,0,0.3)] p-[24px] m-[12px] transition-all duration-[300ms] hover:scale-[1.02]`,
title: 'text-[24px] font-[600] text-white mb-[12px] leading-[1.2]',
content: 'text-[16px] text-[rgba(255,255,255,0.9)] leading-[1.5] mb-[16px]',
button: 'px-[20px] py-[8px] bg-[rgba(255,255,255,0.2)] hover:bg-[rgba(255,255,255,0.3)] text-white rounded-[8px] transition-all duration-[200ms] cursor-pointer border-none'
},
content: {
title: 'Vue AI Card',
description: 'Generated with Vue 3 reactivity and BaroCSS',
buttonText: 'Interact'
}
},
'dashboard widget': {
type: 'widget',
styles: {
container: `w-[${config.width || 280}px] h-[${config.height || 160}px] bg-white rounded-[12px] shadow-[0_4px_20px_rgba(0,0,0,0.08)] border-[1px] border-[#e2e8f0] p-[20px] hover:shadow-[0_8px_40px_rgba(0,0,0,0.12)] transition-all duration-[300ms]`,
header: 'flex items-center justify-between mb-[16px]',
title: 'text-[14px] font-[500] text-[#64748b] uppercase tracking-[0.5px]',
value: 'text-[28px] font-[700] text-[#1e293b] mb-[8px]',
trend: 'text-[12px] font-[500] text-[#10b981] flex items-center'
},
content: {
title: 'Vue Metrics',
value: '2,847',
trend: '+15.3%',
period: 'vs last month'
}
}
};
const matched = Object.keys(templates).find(key =>
prompt.toLowerCase().includes(key)
);
return templates[matched] || templates['modern card'];
}
// Main component logic
const { components, isGenerating, generateComponent, updateComponent, removeComponent } = useAIComponentGenerator();
const selectedTemplate = ref('card');
const customPrompt = ref('');
const templates = {
card: 'modern card',
widget: 'dashboard widget'
};
const handleGenerate = () => {
const prompt = templates[selectedTemplate.value];
generateComponent(prompt, { width: 320, height: 200 });
};
const handleCustomGenerate = () => {
if (customPrompt.value.trim()) {
generateComponent(customPrompt.value.trim());
customPrompt.value = '';
}
};
</script>
<template>
<div class="min-h-screen bg-[#f8fafc] p-[20px]">
<!-- AI Control Panel -->
<div class="mb-[30px] bg-white rounded-[12px] shadow-[0_4px_20px_rgba(0,0,0,0.08)] p-[24px]">
<h1 class="text-[24px] font-[700] text-[#1e293b] mb-[20px]">
Vue + BaroCSS AI Generator
</h1>
<div class="flex gap-[16px] mb-[20px]">
<label class="flex items-center gap-[8px] cursor-pointer">
<input
v-model="selectedTemplate"
type="radio"
value="card"
class="w-[16px] h-[16px] text-[#3b82f6]"
>
<span class="text-[14px] text-[#374151]">Modern Card</span>
</label>
<label class="flex items-center gap-[8px] cursor-pointer">
<input
v-model="selectedTemplate"
type="radio"
value="widget"
class="w-[16px] h-[16px] text-[#3b82f6]"
>
<span class="text-[14px] text-[#374151]">Dashboard Widget</span>
</label>
</div>
<div class="flex gap-[12px] mb-[16px]">
<button
@click="handleGenerate"
:disabled="isGenerating"
class="px-[20px] py-[10px] bg-[#10b981] text-white rounded-[8px] cursor-pointer border-none hover:bg-[#059669] disabled:opacity-[0.5] disabled:cursor-not-allowed transition-all duration-[200ms]"
>
{{ isGenerating ? 'Generating...' : 'Generate Template' }}
</button>
</div>
<div class="flex gap-[8px]">
<input
v-model="customPrompt"
@keyup.enter="handleCustomGenerate"
type="text"
placeholder="Describe your component..."
class="flex-1 px-[12px] py-[10px] border-[1px] border-[#d1d5db] rounded-[8px] text-[14px] focus:border-[#3b82f6] focus:ring-[2px] focus:ring-[rgba(59,130,246,0.2)] outline-none transition-all duration-[200ms]"
:disabled="isGenerating"
>
<button
@click="handleCustomGenerate"
:disabled="isGenerating || !customPrompt.trim()"
class="px-[20px] py-[10px] bg-[#3b82f6] text-white rounded-[8px] cursor-pointer border-none hover:bg-[#2563eb] disabled:opacity-[0.5] disabled:cursor-not-allowed transition-all duration-[200ms]"
>
Generate
</button>
</div>
</div>
<!-- Generated Components -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(300px,1fr))] gap-[20px]">
<AIComponent
v-for="component in components"
:key="component.id"
:component="component"
@update="updateComponent"
@remove="removeComponent"
/>
</div>
<!-- Empty State -->
<div
v-if="components.length === 0 && !isGenerating"
class="text-center py-[60px]"
>
<div class="text-[48px] mb-[16px]">🚀</div>
<h3 class="text-[18px] font-[600] text-[#1e293b] mb-[8px]">
Ready for AI Magic
</h3>
<p class="text-[14px] text-[#64748b]">
Generate your first Vue component with AI styling
</p>
</div>
</div>
</template>
Reactive AI Component
vue
<!-- AIComponent.vue -->
<script setup>
import { ref, reactive, computed, watch } from 'vue';
const props = defineProps({
component: {
type: Object,
required: true
}
});
const emit = defineEmits(['update', 'remove']);
const isEditing = ref(false);
const styleInstructions = ref('');
// Reactive style editor
const useStyleEditor = () => {
const updateStyle = async (instruction) => {
// Simulate AI style processing
const updates = await processStyleInstruction(instruction);
if (updates.styles) {
Object.assign(props.component.styles, updates.styles);
}
if (updates.content) {
Object.assign(props.component.content, updates.content);
}
};
return { updateStyle };
};
const { updateStyle } = useStyleEditor();
// Quick style actions
const quickActions = [
{ label: 'Make Vibrant', instruction: 'use vibrant colors' },
{ label: 'Add Shadow', instruction: 'add dramatic shadow' },
{ label: 'Round Corners', instruction: 'make more rounded' },
{ label: 'Animate', instruction: 'add hover animations' }
];
const handleQuickAction = (instruction) => {
updateStyle(instruction);
};
const handleCustomInstruction = () => {
if (styleInstructions.value.trim()) {
updateStyle(styleInstructions.value.trim());
styleInstructions.value = '';
}
};
// Process style instructions (AI simulation)
async function processStyleInstruction(instruction) {
const updates = {};
if (instruction.includes('vibrant')) {
updates.styles = {
container: props.component.styles.container.replace(
'from-[#667eea] to-[#764ba2]',
'from-[#ff6b6b] to-[#4ecdc4]'
)
};
}
if (instruction.includes('shadow')) {
updates.styles = {
container: props.component.styles.container.replace(
'shadow-[0_8px_32px_rgba(0,0,0,0.3)]',
'shadow-[0_20px_60px_rgba(0,0,0,0.4)]'
)
};
}
if (instruction.includes('rounded')) {
updates.styles = {
container: props.component.styles.container.replace(
'rounded-[16px]',
'rounded-[24px]'
)
};
}
if (instruction.includes('animate')) {
updates.styles = {
container: props.component.styles.container.replace(
'hover:scale-[1.02]',
'hover:scale-[1.05] hover:rotate-[1deg]'
)
};
}
return updates;
}
</script>
<template>
<div
class="relative group"
@mouseenter="isEditing = true"
@mouseleave="isEditing = false"
>
<!-- Card Component -->
<div
v-if="component.type === 'card'"
:class="component.styles.container"
>
<h3 :class="component.styles.title">
{{ component.content.title }}
</h3>
<p :class="component.styles.content">
{{ component.content.description }}
</p>
<button
:class="component.styles.button"
@click="updateStyle('refresh colors')"
>
{{ component.content.buttonText }}
</button>
</div>
<!-- Widget Component -->
<div
v-else-if="component.type === 'widget'"
:class="component.styles.container"
>
<div :class="component.styles.header">
<h4 :class="component.styles.title">
{{ component.content.title }}
</h4>
<div class="w-[32px] h-[32px] rounded-[8px] bg-[#3b82f6] bg-opacity-[0.1] flex items-center justify-center text-[16px]">
📊
</div>
</div>
<div :class="component.styles.value">
{{ component.content.value }}
</div>
<div :class="component.styles.trend">
<span class="mr-[4px]">↗</span>
{{ component.content.trend }} {{ component.content.period }}
</div>
</div>
<!-- Edit Controls Overlay -->
<div
v-show="isEditing"
class="absolute top-[8px] right-[8px] flex flex-col gap-[4px] bg-[rgba(0,0,0,0.8)] rounded-[6px] p-[8px] opacity-0 group-hover:opacity-100 transition-all duration-[200ms]"
>
<div class="flex gap-[4px] mb-[8px]">
<button
v-for="action in quickActions"
:key="action.label"
@click="handleQuickAction(action.instruction)"
class="px-[8px] py-[4px] bg-[#3b82f6] text-white text-[10px] rounded-[4px] cursor-pointer border-none hover:bg-[#2563eb] transition-all duration-[200ms]"
:title="action.label"
>
{{ action.label }}
</button>
</div>
<div class="flex gap-[4px]">
<input
v-model="styleInstructions"
@keyup.enter="handleCustomInstruction"
type="text"
placeholder="Custom style..."
class="w-[120px] px-[6px] py-[4px] text-[10px] border-[1px] border-[#d1d5db] rounded-[4px] bg-white"
>
<button
@click="handleCustomInstruction"
class="px-[6px] py-[4px] bg-[#10b981] text-white text-[10px] rounded-[4px] cursor-pointer border-none hover:bg-[#059669]"
>
Apply
</button>
</div>
<button
@click="$emit('remove', component.id)"
class="w-full px-[6px] py-[4px] bg-[#ef4444] text-white text-[10px] rounded-[4px] cursor-pointer border-none hover:bg-[#dc2626] mt-[4px]"
>
Remove
</button>
</div>
</div>
</template>
Advanced Reactive Patterns
Reactive Style Store with Watchers
vue
<script setup>
import { ref, reactive, watch, computed, provide, inject } from 'vue';
// Global style store for AI-generated styles
const createStyleStore = () => {
const styles = reactive(new Map());
const activeComponent = ref(null);
const history = reactive([]);
const addStyle = (id, styleObj) => {
styles.set(id, reactive(styleObj));
history.push({ action: 'add', id, timestamp: Date.now() });
};
const updateStyle = (id, updates) => {
const existing = styles.get(id);
if (existing) {
Object.assign(existing, updates);
history.push({ action: 'update', id, updates, timestamp: Date.now() });
}
};
const removeStyle = (id) => {
styles.delete(id);
history.push({ action: 'remove', id, timestamp: Date.now() });
};
// Watch for style changes and trigger BaroCSS updates
watch(
() => Array.from(styles.entries()),
(newStyles) => {
// BaroCSS automatically handles the DOM changes
console.log('Styles updated:', newStyles.length, 'components');
},
{ deep: true }
);
return {
styles,
activeComponent,
history,
addStyle,
updateStyle,
removeStyle
};
};
// Provide the store
const styleStore = createStyleStore();
provide('styleStore', styleStore);
// Component that uses the store
const AIStyleConsumer = {
setup() {
const styleStore = inject('styleStore');
const componentId = ref(`component-${Date.now()}`);
// Initialize component styles
styleStore.addStyle(componentId.value, {
container: 'w-[300px] h-[200px] bg-[#ffffff] rounded-[8px] p-[16px]',
title: 'text-[18px] font-[600] text-[#1e293b] mb-[8px]',
content: 'text-[14px] text-[#64748b] leading-[1.5]'
});
// Computed style getter
const componentStyles = computed(() =>
styleStore.styles.get(componentId.value) || {}
);
// AI style updater
const updateWithAI = async (instruction) => {
const updates = await processAIInstruction(instruction);
styleStore.updateStyle(componentId.value, updates);
};
return {
componentId,
componentStyles,
updateWithAI
};
}
};
async function processAIInstruction(instruction) {
// Simulate AI processing
const updates = {};
if (instruction.includes('blue')) {
updates.container = updates.container?.replace(/bg-\[#[a-f0-9]{6}\]/, 'bg-[#3b82f6]') || 'bg-[#3b82f6]';
}
if (instruction.includes('larger')) {
updates.container = updates.container?.replace(/w-\[\d+px\]/, 'w-[400px]') || 'w-[400px]';
}
return updates;
}
</script>
<template>
<div class="vue-ai-app">
<h1 class="text-[32px] font-[700] text-[#1e293b] mb-[24px]">
Vue + BaroCSS Reactive AI Styling
</h1>
<!-- Style store debug info -->
<div class="mb-[20px] p-[16px] bg-[#f1f5f9] rounded-[8px]">
<h3 class="text-[14px] font-[600] text-[#374151] mb-[8px]">
Active Styles: {{ styleStore.styles.size }}
</h3>
<p class="text-[12px] text-[#6b7280]">
Last action: {{ styleStore.history[styleStore.history.length - 1]?.action || 'None' }}
</p>
</div>
<!-- Component instances -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(320px,1fr))] gap-[20px]">
<component
v-for="n in 3"
:key="n"
:is="AIStyleConsumer"
/>
</div>
</div>
</template>
Performance Optimization
Lazy Loading and Virtual Scrolling
vue
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
const useVirtualAIComponents = (itemHeight = 200) => {
const containerRef = ref(null);
const scrollTop = ref(0);
const containerHeight = ref(800);
const allComponents = ref([]);
// Calculate visible range
const visibleRange = computed(() => {
const start = Math.floor(scrollTop.value / itemHeight);
const visibleCount = Math.ceil(containerHeight.value / itemHeight);
const end = Math.min(start + visibleCount + 2, allComponents.value.length);
return { start: Math.max(0, start - 1), end };
});
// Get visible components
const visibleComponents = computed(() => {
const { start, end } = visibleRange.value;
return allComponents.value.slice(start, end).map((component, index) => ({
...component,
virtualIndex: start + index,
top: (start + index) * itemHeight
}));
});
// Total height for scrollbar
const totalHeight = computed(() => allComponents.value.length * itemHeight);
const handleScroll = (e) => {
scrollTop.value = e.target.scrollTop;
};
const addAIComponent = async (prompt) => {
const component = await generateComponent(prompt);
allComponents.value.push(component);
};
onMounted(() => {
if (containerRef.value) {
containerHeight.value = containerRef.value.clientHeight;
}
});
return {
containerRef,
visibleComponents,
totalHeight,
handleScroll,
addAIComponent
};
};
const { containerRef, visibleComponents, totalHeight, handleScroll, addAIComponent } = useVirtualAIComponents();
// Generate initial components
onMounted(async () => {
for (let i = 0; i < 100; i++) {
await addAIComponent(`AI component ${i + 1}`);
}
});
</script>
<template>
<div class="h-screen flex flex-col">
<div class="p-[20px] bg-white border-b-[1px] border-[#e2e8f0]">
<h1 class="text-[24px] font-[700] text-[#1e293b] mb-[16px]">
Virtual AI Components
</h1>
<button
@click="addAIComponent('New AI component')"
class="px-[16px] py-[8px] bg-[#3b82f6] text-white rounded-[8px] cursor-pointer border-none hover:bg-[#2563eb]"
>
Add AI Component
</button>
</div>
<div
ref="containerRef"
class="flex-1 overflow-auto"
@scroll="handleScroll"
>
<div
class="relative"
:style="{ height: `${totalHeight}px` }"
>
<div
v-for="component in visibleComponents"
:key="component.id"
class="absolute w-full"
:style="{
top: `${component.top}px`,
height: '200px'
}"
>
<div :class="component.styles.container">
<h3 :class="component.styles.title">
Component {{ component.virtualIndex + 1 }}
</h3>
<p :class="component.styles.content">
{{ component.content.description }}
</p>
</div>
</div>
</div>
</div>
</div>
</template>
This Vue integration demonstrates the power of reactive AI component generation with BaroCSS, showing how Vue's reactivity system perfectly complements build-free styling for dynamic, AI-driven user interfaces.