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

After: Harmonious style hierarchy


๐Ÿ—๏ธ 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:

  1. Theme Defaults (Layer 1) - Foundation

    • Uses theme tokens (primary, secondary, etc.)
    • Ensures design system consistency
    • Lowest priority - can be overridden by anything
  2. Widget Base Styles (Layer 2) - Identity

    • Widget's core appearance
    • Can be theme-class, manual, or hybrid
    • Defines what makes this widget unique
  3. State Styles (Layer 3) - Interaction

    • Hover, focus, active, disabled states
    • Only active when widget is in that state
    • Overrides base styles temporarily
  4. Responsive Styles (Layer 4) - Adaptability

    • Breakpoint-specific styles (mobile, tablet, desktop)
    • Only active for current breakpoint
    • Ensures responsive design
  5. User Overrides (Layer 5) - Control

    • Property panel edits
    • User's manual customizations
    • High priority - respects user intent
  6. 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 ๐ŸŽŠ