Variant System Implementation Roadmap
Step-by-step guide to implement the variant management system
Variant System Implementation Roadmap
Quick implementation guide for the variant management system.
🎯 Goal
Transform the widget gallery/demo into a powerful variant creation and management system where:
- Users can customize Core UI components
- Save variants scoped to universe/space
- Reuse variants across projects
- Clear separation between GoGrab global variants and user variants
📦 Phase 1: Variant Storage Service (Foundation)
1.1 Create Variant Storage Service
File: libs/libs-widgets/widgets-manager/src/lib/services/variant-storage.service.ts
@Injectable({ providedIn: 'root' })
export class VariantStorageService {
private storage = inject(GgStorage);
private logger = inject(LoggerService);
/**
* Get all variants for a widget type (merged: global + universe + space)
*/
async getAllVariants(params: {
widgetType: string;
universeId: string;
spaceId?: string;
}): Promise<WidgetVariant[]> {
const variants: WidgetVariant[] = [];
// 1. Global variants (from registry)
const globalVariants = CORE_VARIANTS_REGISTRY[params.widgetType] || [];
variants.push(...globalVariants);
// 2. Universe variants
const universeKey = `universes/${params.universeId}/widgets/variants/${params.widgetType}`;
const universeVariants = await this.storage.get<WidgetVariant[]>(universeKey) || [];
variants.push(...universeVariants);
// 3. Space variants (if provided)
if (params.spaceId) {
const spaceKey = `spaces/${params.spaceId}/widgets/variants/${params.widgetType}`;
const spaceVariants = await this.storage.get<WidgetVariant[]>(spaceKey) || [];
variants.push(...spaceVariants);
}
return variants;
}
/**
* Save a new variant (user-created only)
*/
async saveVariant(variant: WidgetVariant): Promise<void> {
if (variant.author === 'gograb-team') {
throw new Error('Cannot modify global variants');
}
const key = variant.scope === 'universe'
? `universes/${variant.universeId}/widgets/variants/${variant.baseWidget}`
: `spaces/${variant.spaceId}/widgets/variants/${variant.baseWidget}`;
// Get existing variants
const existingVariants = await this.storage.get<WidgetVariant[]>(key) || [];
// Check for duplicate ID
const index = existingVariants.findIndex(v => v.id === variant.id);
if (index >= 0) {
// Update existing
existingVariants[index] = variant;
} else {
// Add new
existingVariants.push(variant);
}
// Save back
await this.storage.put(key, existingVariants);
this.logger.info('[VariantStorage] Variant saved', { variantId: variant.id, scope: variant.scope });
}
/**
* Delete a variant (user-created only)
*/
async deleteVariant(params: {
variantId: string;
widgetType: string;
scope: 'universe' | 'space';
universeId?: string;
spaceId?: string;
}): Promise<void> {
const key = params.scope === 'universe'
? `universes/${params.universeId}/widgets/variants/${params.widgetType}`
: `spaces/${params.spaceId}/widgets/variants/${params.widgetType}`;
const variants = await this.storage.get<WidgetVariant[]>(key) || [];
const filtered = variants.filter(v => v.id !== params.variantId);
await this.storage.put(key, filtered);
this.logger.info('[VariantStorage] Variant deleted', { variantId: params.variantId });
}
}
1.2 Define Variant Types
File: libs/libs-widgets/widgets-manager/src/lib/types/widget-variant.types.ts
export interface WidgetVariant {
// Identity
id: string;
baseWidget: string; // 'button', 'input', 'card', etc.
name: string;
description: string;
// Ownership & Scoping
author: 'gograb-team' | string; // userId
scope: 'global' | 'universe' | 'space';
universeId?: string;
spaceId?: string;
// Category
category: 'functional' | 'style' | 'layout' | 'theme';
// Configuration
config: {
properties: Record<string, any>;
styles?: Record<string, string>; // CSS variables
template?: string;
};
// Metadata
preview?: string; // HTML preview
thumbnail?: string;
tags?: string[];
isPublic: boolean;
isFavorite?: boolean;
// Analytics
usageCount?: number;
lastUsedAt?: Date;
// Timestamps
createdAt: Date;
updatedAt: Date;
}
📚 Phase 2: Global Variants Registry
2.1 Create Core Variants Registry
File: libs/libs-widgets/widgets/src/lib/registries/core-variants.registry.ts
Define all GoGrab team global variants for each Core UI widget.
import { WidgetVariant } from '@libs-widgets/widgets-manager/types';
// Button variants
const BUTTON_VARIANTS: WidgetVariant[] = [
{
id: 'primary',
baseWidget: 'button',
name: 'Primary Button',
description: 'Main call-to-action with emphasis',
author: 'gograb-team',
scope: 'global',
category: 'style',
config: {
properties: { variant: 'primary', size: 'medium' },
styles: {
'--btn-bg': 'var(--ux-primary)',
'--btn-color': 'var(--ux-on-primary)'
}
},
preview: '<button class="btn btn-primary">Click Me</button>',
tags: ['cta', 'primary'],
isPublic: true,
createdAt: new Date('2025-01-01'),
updatedAt: new Date('2025-01-01')
},
// ... more variants
];
// Input variants
const INPUT_VARIANTS: WidgetVariant[] = [
{
id: 'text',
baseWidget: 'input',
name: 'Text Input',
description: 'Standard text input',
author: 'gograb-team',
scope: 'global',
category: 'functional',
config: {
properties: { type: 'text', placeholder: 'Enter text...' }
},
preview: '<input type="text" class="form-input">',
tags: ['text'],
isPublic: true,
createdAt: new Date('2025-01-01'),
updatedAt: new Date('2025-01-01')
},
// ... email, password, number, etc.
];
// Export registry
export const CORE_VARIANTS_REGISTRY: Record<string, WidgetVariant[]> = {
button: BUTTON_VARIANTS,
input: INPUT_VARIANTS,
select: [], // TODO
checkbox: [], // TODO
card: [], // TODO
// ... etc
};
🎨 Phase 3: Variant Creator Component
3.1 Create Variant Creator Modal
File: libs/libs-widgets/widget-builder/src/lib/components/variant-creator/variant-creator.component.ts
Modal component for creating/editing variants with live preview.
Features:
- Select base variant (optional starting point)
- Property configuration panel
- Style editor (CSS variables)
- Live preview
- Scope selector (universe/space)
- Name & description
- Save/cancel
Key Methods:
loadVariant(variantId)- Load existing variant for editingselectBaseVariant(variantId)- Use variant as starting pointupdateProperty(key, value)- Update configurationupdateStyle(variable, value)- Update CSS variablesaveVariant()- Save to storagecancel()- Close modal
🎭 Phase 4: Update Core Component Detail Page
4.1 Add Variant Management to Component Detail
File: libs/libs-widgets/widget-showcase/src/lib/components/core-component-detail/core-component-detail.component.ts
Add:
- Variant storage service injection
- Load variants (global + user's) on init
- Separate sections: "GoGrab Variants" and "My Variants"
- "Create New Variant" button → Opens variant creator
- Variant actions: Edit, Duplicate, Delete, Favorite
- Click variant → Show in live preview
Template changes:
<!-- GoGrab Team Variants -->
<section class="variants-section">
<h2>🌐 GoGrab Team Variants</h2>
<div class="variants-grid">
@for (variant of globalVariants(); track variant.id) {
<div class="variant-card" (click)="selectVariant(variant.id)">
<!-- ... variant display ... -->
</div>
}
</div>
</section>
<!-- User's Custom Variants -->
<section class="variants-section">
<div class="section-header">
<h2>🎨 My Custom Variants</h2>
<button class="btn-create" (click)="createNewVariant()">
<span class="material-icons">add</span>
Create New Variant
</button>
</div>
<div class="variants-grid">
@for (variant of userVariants(); track variant.id) {
<div class="variant-card" (click)="selectVariant(variant.id)">
<!-- ... variant display ... -->
<div class="variant-actions">
<button (click)="editVariant(variant.id)">✏️</button>
<button (click)="duplicateVariant(variant.id)">📋</button>
<button (click)="deleteVariant(variant.id)">🗑️</button>
<button (click)="toggleFavorite(variant.id)">
{{ variant.isFavorite ? '⭐' : '☆' }}
</button>
</div>
</div>
}
@if (userVariants().length === 0) {
<div class="empty-state">
<p>No custom variants yet. Create your first one!</p>
</div>
}
</div>
</section>
🎮 Phase 5: Widget Gallery as Playground
5.1 Transform Widget Demo into Interactive Playground
File: libs/libs-widgets/widget-showcase/src/lib/components/widget-demo/widget-demo.component.ts
Current: Static demo showing widget examples
New: Interactive playground for testing and creating variants
Features:
- Variant selector dropdown (try different variants)
- Live property editor panel
- Style customization panel
- "Save as New Variant" button
- Real-time preview updates
- Export code for current configuration
Layout:
┌─────────────────────────────────────────────────┐
│ 🎮 Button Widget Playground │
├──────────────────┬──────────────────────────────┤
│ Configuration │ Live Preview │
│ │ │
│ Variant: │ ┌────────────────┐ │
│ [Primary ▼] │ │ Click Me │ │
│ │ └────────────────┘ │
│ Properties: │ │
│ Size: [Medium▼] │ Breakpoint: [Desktop ▼] │
│ Text: [______] │ Zoom: [100% ▼] │
│ │ │
│ Styles: │ [Copy Code] │
│ BG: [#007bff]🎨 │ [Save as Variant] │
│ Color: [#fff]🎨 │ │
│ │ │
│ [Reset] │ │
└──────────────────┴──────────────────────────────┘
🔗 Phase 6: Widget Builder Integration
6.1 Add Variant Picker to Component Palette
When adding Core UI components to widget builder, show variant picker.
File: libs/libs-widgets/widget-builder/src/lib/components/component-palette/component-palette.component.ts
Add:
- Hover over component → Show variants quick preview
- Click component → Open variant selector
- Select variant → Add to canvas with variant config
✅ Implementation Checklist
Foundation
- Create
variant-storage.service.ts - Define
widget-variant.types.ts - Create
core-variants.registry.ts - Define button variants (5-10 variants)
- Define input variants (5-10 variants)
- Export from barrel files
UI Components
- Create
variant-creator.component.ts(modal) - Create variant creator template
- Create variant creator styles
- Add form validation
Core Component Detail Updates
- Inject
VariantStorageService - Load variants on init (global + user)
- Add "Create New Variant" button
- Add variant sections (GoGrab vs My Variants)
- Implement variant actions (edit, duplicate, delete, favorite)
- Add empty state for user variants
- Update styles for variant management
Widget Gallery/Demo Updates
- Transform demo into interactive playground
- Add variant selector dropdown
- Add live property editor
- Add style customization panel
- Add "Save as Variant" functionality
- Add code export
- Update layout (split view)
Widget Builder Integration
- Add variant picker to component palette
- Show variant preview on hover
- Apply variant config when adding component
- Add "Promote to Variant" action
Testing & Polish
- Test variant creation flow
- Test variant editing flow
- Test variant deletion
- Test scope isolation (universe vs space)
- Test storage persistence
- Add loading states
- Add error handling
- Add success notifications
Documentation
- Update widget system docs
- Create user guide for variants
- Add code examples
- Create video tutorial (optional)
🎯 Priority Order
Session 1 (Foundation):
- Create variant storage service
- Define variant types
- Create core variants registry (button + input)
- Test storage/retrieval
Session 2 (Variant Creator):
- Build variant creator component
- Add to core component detail page
- Test creation flow
Session 3 (Gallery Playground):
- Transform widget demo into playground
- Add interactive controls
- Add "Save as Variant"
Session 4 (Integration & Polish):
- Widget builder integration
- Testing across all flows
- Polish UI/UX
- Documentation
🚀 Quick Start
To begin implementation:
# 1. Create variant storage service
cd libs/libs-widgets/widgets-manager/src/lib/services
# Create variant-storage.service.ts
# 2. Create variant types
cd ../types
# Create widget-variant.types.ts
# 3. Create core variants registry
cd ../../../widgets/src/lib/registries
# Create core-variants.registry.ts
# 4. Start implementing!
Ready to transform widgets into a Ferrari-level reusable component system! 🏎️✨