STYLE HARMONY ARCHITECTURE
Comprehensive guidelines for AI-assisted development within the Editor platform
๐จ Style Harmony Architecture
No Competition - Perfect Integration
๐ฏ THE PROBLEM SOLVED
Before: Styles competing with each other
- Theme classes vs manual values
- Hardcoded styles vs property panel edits
- State styles overriding user changes
- Responsive styles conflicting with base styles
- Result: Unpredictable, frustrating styling
After: Harmonious style hierarchy
- Clear priority system (6 layers)
- Automatic conflict resolution
- Property changes integrate seamlessly
- Every change tracked and documented
- Result: Predictable, professional styling
๐๏ธ THE 6-LAYER HIERARCHY
Layer 6: Inline Styles (Priority 600) ๐ HIGHEST
โ Overrides
Layer 5: User Overrides (Priority 500) ๐จ
โ Overrides
Layer 4: Responsive Styles (Priority 400) ๐ฑ
โ Overrides
Layer 3: State Styles (Priority 300) โก
โ Overrides
Layer 2: Widget Base Styles (Priority 200) ๐ง
โ Overrides
Layer 1: Theme Defaults (Priority 100) ๐ญ LOWEST
How It Works:
Theme Defaults (Layer 1) - Foundation
- Uses theme tokens (primary, secondary, etc.)
- Ensures design system consistency
- Lowest priority - can be overridden by anything
Widget Base Styles (Layer 2) - Identity
- Widget's core appearance
- Can be theme-class, manual, or hybrid
- Defines what makes this widget unique
State Styles (Layer 3) - Interaction
- Hover, focus, active, disabled states
- Only active when widget is in that state
- Overrides base styles temporarily
Responsive Styles (Layer 4) - Adaptability
- Breakpoint-specific styles (mobile, tablet, desktop)
- Only active for current breakpoint
- Ensures responsive design
User Overrides (Layer 5) - Control
- Property panel edits
- User's manual customizations
- High priority - respects user intent
Inline Styles (Layer 6) - Ultimate
- Direct style attributes
- Highest priority - no questions asked
- Used for special cases only
๐ช EXAMPLE: Button Styling
Scenario: User creates a primary button
// 1. Start with theme defaults
const button: WidgetStyleConfiguration = {
widgetId: 'btn-001',
preferredMode: 'theme-class',
// Layer 1 + 2: Theme + Base
baseStyles: {
mode: 'theme-class',
className: 'btn-primary',
tokens: {
color: 'primary', // Blue from theme
spacing: 'md', // 12px padding from theme
typography: 'button', // 16px font from theme
radius: 'md', // 8px radius from theme
}
},
// Layer 3: State styles
stateStyles: {
hover: {
mode: 'manual',
effects: { opacity: 0.9 } // Slightly transparent on hover
},
focus: {
mode: 'manual',
borders: { width: 2 } // Thicker border on focus
},
disabled: {
mode: 'manual',
effects: { opacity: 0.5 } // 50% opacity when disabled
}
},
// Layer 4: Responsive styles
responsiveStyles: {
mobile: {
mode: 'manual',
sizing: { width: '100%' } // Full width on mobile
},
desktop: {
mode: 'manual',
sizing: { width: 'auto' } // Auto width on desktop
}
},
// History tracking
history: [],
// Metadata
metadata: {
createdAt: new Date(),
updatedAt: new Date(),
version: '1.0.0',
author: 'user-123'
}
};
User Opens Property Panel:
// User sees current resolved values
// StyleHierarchyResolverService resolves all layers:
const resolved = resolver.resolveStyles('btn-001', {
state: 'default',
breakpoint: 'desktop',
userEditing: false,
themeTokens: theme
});
// Result:
{
backgroundColor: '#3b82f6', // From theme token 'primary'
textColor: '#ffffff', // From theme token 'primary-contrast'
padding: 12, // From theme token 'md'
fontSize: 16, // From theme token 'button'
borderRadius: 8, // From theme token 'md'
width: 'auto' // From responsive desktop style
}
User Changes Background Color:
// User sets background to custom brand color
resolver.setPropertyValue('btn-001', 'backgroundColor', '#FF5722', {
state: 'default',
breakpoint: 'desktop',
userEditing: true, // ๐ USER OVERRIDE FLAG
themeTokens: theme
});
// What happens:
// 1. Detects userEditing: true
// 2. Upgrades from theme-class to hybrid mode
// 3. Creates user override layer (priority 500)
// 4. Preserves all other theme values
// 5. Records in history
// New configuration:
{
mode: 'hybrid',
baseTheme: {
mode: 'theme-class',
className: 'btn-primary',
tokens: { color: 'primary', spacing: 'md', ... }
},
overrides: {
mode: 'manual',
colors: { background: '#FF5722' } // โ
User's custom color
}
}
// Result: Button uses brand color BUT keeps theme spacing, typography, radius!
๐ฆ CONFLICT RESOLUTION
Example Conflict:
// Situation: Multiple layers define backgroundColor
// Layer 2 (Base): background = '#3b82f6' (theme primary)
// Layer 4 (Responsive - mobile): background = '#2563eb' (darker for mobile)
// Layer 5 (User): background = '#FF5722' (brand color)
// Resolution:
const resolved = resolver.resolveStyles('btn-001', {
state: 'default',
breakpoint: 'mobile', // ๐ฑ Mobile device
userEditing: false,
themeTokens: theme
});
// Winner: Layer 5 (User) - Priority 500
// Result: background = '#FF5722'
// Conflict detected:
{
property: 'colors.background',
layers: [
{ name: 'Widget Base', priority: 200, value: '#3b82f6' },
{ name: 'Mobile Responsive', priority: 400, value: '#2563eb' },
{ name: 'User Override', priority: 500, value: '#FF5722' }
],
resolution: 'higher-priority',
winner: { name: 'User Override', priority: 500 }
}
Rule: Highest priority always wins. No ambiguity.
๐ PROPERTY PANEL INTEGRATION
How Property Changes Flow:
// 1. User opens property panel for button
// 2. PropertiesPanelNewComponent loads properties
// 3. For each property, calls resolver:
const currentValue = resolver.getPropertyValue('btn-001', 'backgroundColor', {
state: 'default',
breakpoint: 'desktop',
userEditing: false,
themeTokens: theme
});
// Resolver:
// - Gets all active layers
// - Merges them by priority
// - Returns final resolved value
// - NO HARDCODED VALUES, NO COMPETITION
// 4. User changes value in property panel
onChange(newValue) {
resolver.setPropertyValue('btn-001', 'backgroundColor', newValue, {
state: 'default',
breakpoint: 'desktop',
userEditing: true, // ๐ Creates user override layer
themeTokens: theme
});
}
// 5. Resolver:
// - Creates/updates user override layer (priority 500)
// - Rebuilds layer stack
// - Emits StyleChangeEvent
// - Updates widget configuration
๐ PROPERTY TO STYLE MAPPING
How Properties Map to Styles:
const mappings: PropertyStyleMapping[] = [
{
propertyKey: 'backgroundColor',
stylePath: 'colors.background',
type: 'color',
isThemeToken: true,
themeTokenPath: 'tokens.color'
},
{
propertyKey: 'padding',
stylePath: 'spacing.padding',
type: 'spacing',
isThemeToken: true,
themeTokenPath: 'tokens.spacing'
},
{
propertyKey: 'fontSize',
stylePath: 'typography.fontSize',
type: 'typography',
isThemeToken: true,
themeTokenPath: 'tokens.typography'
}
];
// When user sets 'backgroundColor':
// 1. Lookup mapping
// 2. Navigate to 'colors.background' in PlatformAgnosticStyle
// 3. Set value
// 4. Rebuild layers
// 5. Emit change event
๐ญ MODE SWITCHING
Theme Mode โ Hybrid Mode:
// User starts with pure theme
{
mode: 'theme-class',
className: 'btn-primary',
tokens: { color: 'primary' }
}
// User changes ONE property
resolver.setPropertyValue('btn-001', 'backgroundColor', '#custom', {...});
// Automatically converts to hybrid
{
mode: 'hybrid',
baseTheme: {
mode: 'theme-class',
className: 'btn-primary',
tokens: { color: 'primary' } // โ
Keeps other tokens
},
overrides: {
mode: 'manual',
colors: { background: '#custom' } // โ
Only override changed property
}
}
// Result: Best of both worlds!
๐งช STATE-SPECIFIC STYLING
Example: Hover state
// User wants button to scale on hover
// Current state: default
const defaultStyle = resolver.resolveStyles('btn-001', {
state: 'default',
breakpoint: 'desktop',
...
});
// Result: Base styles only
// User hovers
const hoverStyle = resolver.resolveStyles('btn-001', {
state: 'hover', // ๐ฑ๏ธ Hover state
breakpoint: 'desktop',
...
});
// Result: Base styles + Hover layer (opacity: 0.9, transform: scale(1.05))
// User stops hovering
const backToDefault = resolver.resolveStyles('btn-001', {
state: 'default', // โฉ๏ธ Back to default
...
});
// Result: Hover layer deactivated, back to base styles
๐ฑ RESPONSIVE STYLING
Example: Mobile vs Desktop
// Desktop (large screen)
const desktopStyle = resolver.resolveStyles('btn-001', {
state: 'default',
breakpoint: 'desktop', // ๐ฅ๏ธ Desktop
...
});
// Active layers:
// - Theme (100)
// - Base (200)
// - Desktop Responsive (400)
// Result: { width: 'auto', padding: 16 }
// Mobile (small screen)
const mobileStyle = resolver.resolveStyles('btn-001', {
state: 'default',
breakpoint: 'mobile', // ๐ฑ Mobile
...
});
// Active layers:
// - Theme (100)
// - Base (200)
// - Mobile Responsive (400) โ Different layer!
// Result: { width: '100%', padding: 12 }
๐ CONFLICT DETECTION
Automatic Conflict Detection:
// Enable conflict detection
resolver.configure({
detectConflicts: true,
debugLogging: true
});
// Get conflicts
const conflicts = resolver.getConflicts('btn-001', {
state: 'default',
breakpoint: 'mobile',
...
});
// Example conflict:
{
property: 'spacing.padding',
layers: [
{ name: 'Theme Default', priority: 100, value: 16 },
{ name: 'Mobile Responsive', priority: 400, value: 12 },
{ name: 'User Override', priority: 500, value: 20 }
],
resolution: 'higher-priority',
winner: { name: 'User Override', priority: 500, value: 20 }
}
// Console output:
// [StyleHierarchyResolver] Conflict detected: spacing.padding
// [StyleHierarchyResolver] Winner: User Override (priority 500) = 20
๐ CHANGE HISTORY
Every Change Tracked:
// Widget configuration includes full history
{
history: [
{
timestamp: '2025-12-06T20:00:00Z',
userId: 'user-123',
widgetId: 'btn-001',
before: null,
after: { mode: 'theme-class', className: 'btn-primary' },
reason: 'widget-created',
userModified: false
},
{
timestamp: '2025-12-06T20:05:00Z',
userId: 'user-123',
widgetId: 'btn-001',
before: { mode: 'theme-class', className: 'btn-primary' },
after: { mode: 'hybrid', baseTheme: {...}, overrides: {...} },
reason: 'user-edit',
userModified: true // ๐ User change
}
]
}
// Can undo to any point
resolver.resetToTheme('btn-001'); // Go back to pure theme
// Can clear user overrides
resolver.clearUserOverrides('btn-001'); // Remove all user edits
๐ BENEFITS
For Users:
โ
Predictable - Always know which style wins
โ
Flexible - Mix theme and manual freely
โ
Traceable - See history of all changes
โ
Reversible - Undo to any point
โ
Responsive - Different styles per breakpoint
โ
Interactive - State-specific styles
For Developers:
โ
Clear hierarchy - 6 layers, priority-based
โ
Conflict detection - Automatic resolution
โ
Type-safe - Full TypeScript
โ
Extensible - Easy to add new layers
โ
Debuggable - Comprehensive logging
โ
Testable - Pure functions
For AI:
โ
Learns patterns - Sees which layers used most
โ
Understands intent - User vs theme vs responsive
โ
Suggests improvements - "Why not use theme?"
โ
Detects mistakes - "Conflict detected at..."
๐ USAGE EXAMPLE
import { StyleHierarchyResolverService } from '@libs-foundational/core-models';
// 1. Initialize
const resolver = new StyleHierarchyResolverService();
// 2. Configure
resolver.configure({
detectConflicts: true,
debugLogging: true,
defaultMergeStrategy: 'priority-wins'
});
// 3. Register widget
resolver.registerWidget('btn-001', buttonConfig);
// 4. Resolve styles for current context
const resolved = resolver.resolveStyles('btn-001', {
state: 'hover',
breakpoint: 'mobile',
userEditing: false,
themeTokens: theme
});
// 5. User changes property
resolver.setPropertyValue('btn-001', 'backgroundColor', '#FF5722', {
state: 'default',
breakpoint: 'mobile',
userEditing: true,
themeTokens: theme
});
// 6. Check for conflicts
const conflicts = resolver.getConflicts('btn-001', {...});
if (conflicts.length > 0) {
console.warn('Conflicts detected:', conflicts);
}
// 7. Reset if needed
resolver.resetToTheme('btn-001');
๐ API SUMMARY
| Method | Purpose |
|---|---|
configure(config) |
Set resolver configuration |
registerWidget(id, config) |
Register widget style config |
resolveStyles(id, context) |
Get final resolved styles |
setPropertyValue(id, key, value, context) |
Update property (integrates with hierarchy) |
getPropertyValue(id, key, context) |
Get property (from resolved hierarchy) |
getConflicts(id, context) |
Detect style conflicts |
resetToTheme(id) |
Reset to theme defaults |
clearUserOverrides(id) |
Remove user edits |
"No more style competition - perfect harmony through clear hierarchy!" ๐จโจ
Date: December 6, 2025 - 8:30 PM
Status: Complete - No style competition
Architecture: 6-layer hierarchy with priority resolution
Conflicts: Automatically detected and resolved
Integration: Seamless property panel integration
Next: Wire up in property editors ๐