Files
dss/examples/merge_cases.py
Digital Production Factory 276ed71f31 Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
2025-12-09 18:45:48 -03:00

614 lines
23 KiB
Python
Executable File

"""
DSS Merge Case Examples
Demonstrates how DSS handles multi-source token ingestion and translation
to the canonical DSS structure. Uses HeroUI and shadcn as examples.
Key Principle: DSS is MONOLITHIC. External systems translate TO us.
"""
import asyncio
import json
from pathlib import Path
# Add parent to path for imports
import sys
sys.path.insert(0, str(Path(__file__).parent.parent / "tools"))
from ingest import (
DesignToken, TokenCollection, TokenMerger, MergeStrategy,
CSSTokenSource, JSONTokenSource
)
from ingest.base import TokenType, TokenCategory
# =============================================================================
# CASE 1: HeroUI Token Structure
# =============================================================================
# HeroUI uses: --heroui-{category}-{scale}
# Example: --heroui-primary-500, --heroui-content1, --heroui-radius-medium
HEROUI_TOKENS = """
:root {
/* HeroUI Color Scale System */
--heroui-primary-50: #e6f1fe;
--heroui-primary-100: #cce3fd;
--heroui-primary-200: #99c7fb;
--heroui-primary-300: #66aaf9;
--heroui-primary-400: #338ef7;
--heroui-primary-500: #006FEE;
--heroui-primary-600: #005bc4;
--heroui-primary-700: #004493;
--heroui-primary-800: #002e62;
--heroui-primary-900: #001731;
--heroui-secondary-500: #7828c8;
--heroui-success-500: #17c964;
--heroui-warning-500: #f5a524;
--heroui-danger-500: #f31260;
/* HeroUI Content Layers */
--heroui-content1: #ffffff;
--heroui-content2: #f4f4f5;
--heroui-content3: #e4e4e7;
--heroui-content4: #d4d4d8;
/* HeroUI Layout Tokens */
--heroui-radius-small: 8px;
--heroui-radius-medium: 12px;
--heroui-radius-large: 14px;
--heroui-shadow-small: 0px 0px 5px 0px rgba(0,0,0,0.02);
--heroui-shadow-medium: 0px 0px 15px 0px rgba(0,0,0,0.03);
--heroui-shadow-large: 0px 0px 30px 0px rgba(0,0,0,0.04);
--heroui-font-size-tiny: 0.75rem;
--heroui-font-size-small: 0.875rem;
--heroui-font-size-medium: 1rem;
--heroui-font-size-large: 1.125rem;
--heroui-spacing-unit: 4px;
}
"""
# =============================================================================
# CASE 2: shadcn Token Structure
# =============================================================================
# shadcn uses: --{semantic-name} with background/foreground pattern
# Example: --primary, --primary-foreground, --card, --card-foreground
SHADCN_TOKENS = """
:root {
/* shadcn Semantic Colors (OKLCH format) */
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
/* shadcn Chart Colors */
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
"""
# =============================================================================
# CASE 3: Legacy Corporate CSS (typical client scenario)
# =============================================================================
# Legacy uses: various naming conventions, inconsistent structure
LEGACY_CORPORATE_TOKENS = """
:root {
/* Old brand colors - inconsistent naming */
--brand-blue: #1E40AF;
--brand-dark-blue: #1E3A8A;
--brand-light: #DBEAFE;
--brandAccent: #F59E0B; /* camelCase mixed in */
/* Old button colors */
--btn-primary-bg: #1E40AF;
--btn-primary-text: #FFFFFF;
--btn-secondary-bg: #F3F4F6;
/* Inconsistent spacing */
--space-xs: 4px;
--space-sm: 8px;
--spacing-md: 16px; /* Different prefix! */
--SPACING_LG: 24px; /* SCREAMING_CASE! */
/* Typography chaos */
--font-base: 16px;
--fontSize-lg: 18px;
--text-xl: 24px;
/* Random radius values */
--rounded: 4px;
--border-radius-md: 8px;
--corners-lg: 12px;
}
"""
# =============================================================================
# DSS CANONICAL STRUCTURE (The Target)
# =============================================================================
# This is what ALL external systems translate TO. Never changes.
DSS_CANONICAL = {
"colors": {
"primary": {"50": None, "100": None, "200": None, "300": None, "400": None,
"500": None, "600": None, "700": None, "800": None, "900": None},
"secondary": {"500": None},
"success": {"500": None},
"warning": {"500": None},
"danger": {"500": None},
"neutral": {"50": None, "100": None, "200": None, "300": None, "400": None,
"500": None, "600": None, "700": None, "800": None, "900": None},
},
"spacing": {
"xs": None, "sm": None, "md": None, "lg": None, "xl": None, "2xl": None
},
"radius": {
"sm": None, "md": None, "lg": None, "xl": None, "full": None
},
"shadows": {
"sm": None, "md": None, "lg": None, "xl": None
},
"typography": {
"fontSize": {"xs": None, "sm": None, "base": None, "lg": None, "xl": None}
}
}
# =============================================================================
# TRANSLATION DICTIONARIES
# =============================================================================
def create_heroui_translation():
"""
Translation dictionary: HeroUI → DSS Canonical
HeroUI uses numeric color scales (like DSS) but different naming.
Mapping is mostly 1:1 with prefix removal.
"""
return {
"$schema": "dss-translation-v1",
"project": "heroui-integration",
"source": "heroui",
"mappings": {
"tokens": {
# Colors - direct scale mapping
"--heroui-primary-50": "color.primary.50",
"--heroui-primary-100": "color.primary.100",
"--heroui-primary-200": "color.primary.200",
"--heroui-primary-300": "color.primary.300",
"--heroui-primary-400": "color.primary.400",
"--heroui-primary-500": "color.primary.500",
"--heroui-primary-600": "color.primary.600",
"--heroui-primary-700": "color.primary.700",
"--heroui-primary-800": "color.primary.800",
"--heroui-primary-900": "color.primary.900",
"--heroui-secondary-500": "color.secondary.500",
"--heroui-success-500": "color.success.500",
"--heroui-warning-500": "color.warning.500",
"--heroui-danger-500": "color.danger.500",
# Content layers → Neutral scale
"--heroui-content1": "color.neutral.50",
"--heroui-content2": "color.neutral.100",
"--heroui-content3": "color.neutral.200",
"--heroui-content4": "color.neutral.300",
# Layout tokens
"--heroui-radius-small": "radius.sm",
"--heroui-radius-medium": "radius.md",
"--heroui-radius-large": "radius.lg",
"--heroui-shadow-small": "shadow.sm",
"--heroui-shadow-medium": "shadow.md",
"--heroui-shadow-large": "shadow.lg",
# Typography
"--heroui-font-size-tiny": "typography.fontSize.xs",
"--heroui-font-size-small": "typography.fontSize.sm",
"--heroui-font-size-medium": "typography.fontSize.base",
"--heroui-font-size-large": "typography.fontSize.lg",
# Spacing
"--heroui-spacing-unit": "spacing.unit",
},
"components": {
"Button": "Button", # HeroUI Button → DSS Button
"Card": "Card",
"Input": "Input",
}
},
"custom_props": {
# HeroUI-specific that don't map to DSS core
# These go in isolated namespace
},
"notes": [
"HeroUI uses numeric scales similar to DSS - easy mapping",
"Content layers (1-4) map to neutral scale",
"Component mapping is mostly 1:1"
]
}
def create_shadcn_translation():
"""
Translation dictionary: shadcn → DSS Canonical
shadcn uses semantic naming (primary, secondary, muted) without scales.
We map to the 500 (default) value in DSS scales.
KEY DIFFERENCE: shadcn is HEADLESS - no numeric scales!
"""
return {
"$schema": "dss-translation-v1",
"project": "shadcn-integration",
"source": "shadcn",
"mappings": {
"tokens": {
# Semantic colors → DSS scale defaults
"--background": "color.neutral.50",
"--foreground": "color.neutral.900",
"--primary": "color.primary.500",
"--primary-foreground": "color.primary.50",
"--secondary": "color.secondary.500",
"--secondary-foreground": "color.secondary.50",
"--muted": "color.neutral.200",
"--muted-foreground": "color.neutral.600",
"--accent": "color.accent.500",
"--accent-foreground": "color.accent.50",
"--destructive": "color.danger.500",
"--card": "color.neutral.50",
"--card-foreground": "color.neutral.900",
"--popover": "color.neutral.50",
"--popover-foreground": "color.neutral.900",
"--border": "color.neutral.200",
"--input": "color.neutral.200",
"--ring": "color.primary.500",
# Layout
"--radius": "radius.md",
},
"components": {
# shadcn components → DSS components
"Button": "Button",
"Card": "Card",
"Input": "Input",
"Dialog": "Modal",
"Popover": "Popover",
}
},
"custom_props": {
# shadcn-specific that don't exist in DSS
"color.chart.1": "var(--chart-1)",
"color.chart.2": "var(--chart-2)",
"color.chart.3": "var(--chart-3)",
"color.chart.4": "var(--chart-4)",
"color.chart.5": "var(--chart-5)",
"color.sidebar.primary": "var(--sidebar-primary)",
},
"notes": [
"shadcn is HEADLESS - no numeric color scales",
"Semantic names map to 500 (default) DSS values",
"foreground variants map to contrast colors (50)",
"Chart colors are shadcn-specific custom props"
]
}
def create_legacy_translation():
"""
Translation dictionary: Legacy Corporate → DSS Canonical
Messy legacy code with inconsistent naming needs careful mapping.
"""
return {
"$schema": "dss-translation-v1",
"project": "acme-corp-legacy",
"source": "legacy-css",
"mappings": {
"tokens": {
# Brand colors (inconsistent naming)
"--brand-blue": "color.primary.500",
"--brand-dark-blue": "color.primary.700",
"--brand-light": "color.primary.100",
"--brandAccent": "color.warning.500", # camelCase → DSS
# Button colors → Component tokens
"--btn-primary-bg": "color.primary.500",
"--btn-primary-text": "color.neutral.50",
"--btn-secondary-bg": "color.neutral.100",
# Spacing chaos → DSS order
"--space-xs": "spacing.xs",
"--space-sm": "spacing.sm",
"--spacing-md": "spacing.md",
"--SPACING_LG": "spacing.lg",
# Typography normalization
"--font-base": "typography.fontSize.base",
"--fontSize-lg": "typography.fontSize.lg",
"--text-xl": "typography.fontSize.xl",
# Radius normalization
"--rounded": "radius.sm",
"--border-radius-md": "radius.md",
"--corners-lg": "radius.lg",
},
"components": {
".btn-primary": "Button[variant=primary]",
".btn-secondary": "Button[variant=secondary]",
".card-wrapper": "Card",
".input-field": "Input",
}
},
"custom_props": {
# ACME-specific branding that extends DSS
"color.brand.acme.primary": "#1E40AF",
"color.brand.acme.accent": "#F59E0B",
},
"validation_warnings": [
"Inconsistent spacing prefixes detected",
"Mixed case conventions found",
"Some values may need manual review"
],
"notes": [
"Legacy system had 4 different naming conventions",
"All mapped to DSS canonical structure",
"Brand-specific colors isolated in custom_props"
]
}
# =============================================================================
# MERGE DEMONSTRATIONS
# =============================================================================
async def demonstrate_merge_heroui_shadcn():
"""
CASE: Merge HeroUI and shadcn tokens into DSS canonical structure.
This demonstrates how two different atomic structures become one.
"""
print("\n" + "="*70)
print("MERGE CASE: HeroUI + shadcn → DSS Canonical")
print("="*70)
css_source = CSSTokenSource()
# Ingest both sources
heroui_collection = await css_source.extract(HEROUI_TOKENS)
shadcn_collection = await css_source.extract(SHADCN_TOKENS)
print(f"\n📥 HeroUI tokens extracted: {len(heroui_collection.tokens)}")
print(f"📥 shadcn tokens extracted: {len(shadcn_collection.tokens)}")
# Merge with PREFER_SPECIFIC strategy
# This prefers concrete values (hex) over references
merger = TokenMerger(strategy=MergeStrategy.PREFER_SPECIFIC)
result = merger.merge([heroui_collection, shadcn_collection])
print(f"\n🔀 Merged result: {result.stats['total_tokens']} tokens")
print(f" New tokens: {result.stats['new_tokens']}")
print(f" Conflicts resolved: {result.stats['conflicts_resolved']}")
# Show conflicts
if result.conflicts:
print(f"\n⚠️ Conflicts detected ({len(result.conflicts)}):")
for conflict in result.conflicts[:5]:
print(f"{conflict.token_name}")
print(f" HeroUI: {conflict.existing.value}")
print(f" shadcn: {conflict.incoming.value}")
print(f" Resolution: {conflict.resolution}")
# Demonstrate atomic structure difference
print("\n" + "-"*70)
print("ATOMIC STRUCTURE COMPARISON:")
print("-"*70)
print("\n🎨 HeroUI (Numeric Scale System):")
print(" Uses: --heroui-primary-{50-900}")
print(" Pattern: Full color scales with 10 values each")
print(" Granularity: HIGH - precise control over each shade")
print("\n🔲 shadcn (Semantic System):")
print(" Uses: --primary, --primary-foreground")
print(" Pattern: Semantic pairs (color + contrast)")
print(" Granularity: LOW - headless, no scales")
print("\n📐 DSS Canonical (Target):")
print(" Uses: color.primary.{50-900}")
print(" Result: Unified scale with both sources merged")
return result
async def demonstrate_merge_with_legacy():
"""
CASE: Merge legacy corporate tokens with modern DSS structure.
Shows how messy legacy code gets normalized.
"""
print("\n" + "="*70)
print("MERGE CASE: Legacy Corporate → DSS Canonical")
print("="*70)
css_source = CSSTokenSource()
# Ingest legacy tokens
legacy_collection = await css_source.extract(LEGACY_CORPORATE_TOKENS)
print(f"\n📥 Legacy tokens extracted: {len(legacy_collection.tokens)}")
# Show the chaos
print("\n🔴 LEGACY TOKEN NAMING CHAOS:")
for token in legacy_collection.tokens[:8]:
print(f" {token.original_name}{token.normalize_name()}")
# Get translation dictionary
translation = create_legacy_translation()
print("\n🔄 TRANSLATION DICTIONARY MAPPINGS:")
for legacy, dss in list(translation["mappings"]["tokens"].items())[:6]:
print(f" {legacy}{dss}")
print("\n✅ RESULT: Unified DSS canonical tokens")
print(" All naming conventions normalized")
print(" Custom brand colors isolated in namespace")
return legacy_collection
async def demonstrate_conflict_strategies():
"""
CASE: Show different merge strategies and their outcomes.
"""
print("\n" + "="*70)
print("MERGE STRATEGIES COMPARISON")
print("="*70)
css_source = CSSTokenSource()
# Create conflicting tokens
source_a = """
:root {
--primary: #3B82F6;
--spacing-md: 16px;
}
"""
source_b = """
:root {
--primary: #2563EB;
--spacing-md: 20px;
}
"""
collection_a = await css_source.extract(source_a)
collection_b = await css_source.extract(source_b)
strategies = [
(MergeStrategy.FIRST, "Keep first occurrence"),
(MergeStrategy.LAST, "Use last (override)"),
(MergeStrategy.PREFER_SPECIFIC, "Prefer concrete values"),
(MergeStrategy.MERGE_METADATA, "Merge metadata, use latest value"),
]
for strategy, description in strategies:
merger = TokenMerger(strategy=strategy)
result = merger.merge([collection_a, collection_b])
primary_token = next(
(t for t in result.collection.tokens if 'primary' in t.name.lower()),
None
)
print(f"\n📋 {strategy.value}: {description}")
if primary_token:
print(f" --primary result: {primary_token.value}")
if result.conflicts:
print(f" Resolution: {result.conflicts[0].resolution}")
async def main():
"""Run all merge demonstrations."""
print("\n" + "="*70)
print("DSS MERGE CASE EXAMPLES")
print("Demonstrating Multi-Source Token Ingestion")
print("="*70)
# Run demonstrations
await demonstrate_merge_heroui_shadcn()
await demonstrate_merge_with_legacy()
await demonstrate_conflict_strategies()
# Summary
print("\n" + "="*70)
print("SUMMARY: ATOMIC STRUCTURE DIFFERENCES")
print("="*70)
print("""
┌─────────────────────────────────────────────────────────────────────┐
│ ATOMIC STRUCTURE COMPARISON │
├─────────────────┬──────────────────────┬────────────────────────────┤
│ Feature │ HeroUI │ shadcn │
├─────────────────┼──────────────────────┼────────────────────────────┤
│ Color System │ Numeric scales │ Semantic pairs │
│ │ (50-900) │ (primary/foreground) │
├─────────────────┼──────────────────────┼────────────────────────────┤
│ Granularity │ HIGH (10 shades) │ LOW (2 values) │
├─────────────────┼──────────────────────┼────────────────────────────┤
│ Naming │ --heroui-{cat}-{n} │ --{semantic}
├─────────────────┼──────────────────────┼────────────────────────────┤
│ CSS Variables │ Prefixed │ Unprefixed │
├─────────────────┼──────────────────────┼────────────────────────────┤
│ Theme Layers │ content1-4 │ card, popover, muted │
├─────────────────┼──────────────────────┼────────────────────────────┤
│ Design Tokens │ Full W3C-like │ Minimal semantic │
├─────────────────┼──────────────────────┼────────────────────────────┤
│ DSS Mapping │ Direct 1:1 │ Expand to scales │
│ │ (strip prefix) │ (500 = default) │
└─────────────────┴──────────────────────┴────────────────────────────┘
""")
print("""
┌─────────────────────────────────────────────────────────────────────┐
│ DSS TRANSLATION APPROACH │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ HeroUI │ │ shadcn │
│ --heroui- │ TRANSLATION │ --primary ──────┐ │
│ primary-500 ────┼────── LAYER ────────┼→ color.primary.500 ←───────┤
│ │ │ --primary-foreground ─────┤
│ --heroui- │ │ ↓ │
│ content1 ───────┼─────────────────────┼→ color.neutral.50 │
│ │ │ │
│ │ DSS CANONICAL │ │
│ │ (IMMUTABLE) │ │
│ │ │ │
└─────────────────────────────────────────────────────────────────────┘
""")
if __name__ == "__main__":
asyncio.run(main())