UNPKG

hatch-slidev-builder-mcp

Version:

A comprehensive MCP server for creating Slidev presentations with component library, interactive elements, and team collaboration features

367 lines (354 loc) 16 kB
/** * Layer 4: Style Orchestration Engine * Dynamic styling system based on design tokens and atomic design principles */ export class StyleOrchestrationEngine { static hatchBrandTokens = [ // Hatch Corporate Colors { name: '--hatch-primary-blue', value: '#095078', category: 'color', scope: 'global' }, { name: '--hatch-secondary-blue', value: '#ACBCC8', category: 'color', scope: 'global' }, { name: '--hatch-primary-orange', value: '#E84B37', category: 'color', scope: 'global' }, { name: '--hatch-secondary-orange', value: '#E75300', category: 'color', scope: 'global' }, { name: '--hatch-corporate-gray', value: '#425563', category: 'color', scope: 'global' }, { name: '--hatch-medium-gray', value: '#595959', category: 'color', scope: 'global' }, { name: '--hatch-light-gray', value: '#A0AEC0', category: 'color', scope: 'global' }, { name: '--hatch-background', value: '#f4f4f4', category: 'color', scope: 'global' }, { name: '--hatch-white', value: '#FFFFFF', category: 'color', scope: 'global' }, // Typography { name: '--font-primary', value: 'Inter, system-ui, sans-serif', category: 'typography', scope: 'global' }, { name: '--font-weight-normal', value: '400', category: 'typography', scope: 'global' }, { name: '--font-weight-medium', value: '500', category: 'typography', scope: 'global' }, { name: '--font-weight-bold', value: '700', category: 'typography', scope: 'global' }, { name: '--font-size-xs', value: '0.75rem', category: 'typography', scope: 'global' }, { name: '--font-size-sm', value: '0.875rem', category: 'typography', scope: 'global' }, { name: '--font-size-base', value: '1rem', category: 'typography', scope: 'global' }, { name: '--font-size-lg', value: '1.125rem', category: 'typography', scope: 'global' }, { name: '--font-size-xl', value: '1.25rem', category: 'typography', scope: 'global' }, { name: '--font-size-2xl', value: '1.5rem', category: 'typography', scope: 'global' }, { name: '--font-size-3xl', value: '1.875rem', category: 'typography', scope: 'global' }, { name: '--font-size-4xl', value: '2.25rem', category: 'typography', scope: 'global' }, // Spacing { name: '--spacing-1', value: '0.25rem', category: 'spacing', scope: 'global' }, { name: '--spacing-2', value: '0.5rem', category: 'spacing', scope: 'global' }, { name: '--spacing-3', value: '0.75rem', category: 'spacing', scope: 'global' }, { name: '--spacing-4', value: '1rem', category: 'spacing', scope: 'global' }, { name: '--spacing-6', value: '1.5rem', category: 'spacing', scope: 'global' }, { name: '--spacing-8', value: '2rem', category: 'spacing', scope: 'global' }, { name: '--spacing-12', value: '3rem', category: 'spacing', scope: 'global' }, { name: '--spacing-16', value: '4rem', category: 'spacing', scope: 'global' }, // Shadows { name: '--shadow-sm', value: '0 1px 2px 0 rgba(0, 0, 0, 0.05)', category: 'shadow', scope: 'global' }, { name: '--shadow-md', value: '0 4px 6px -1px rgba(0, 0, 0, 0.1)', category: 'shadow', scope: 'global' }, { name: '--shadow-lg', value: '0 10px 15px -3px rgba(0, 0, 0, 0.1)', category: 'shadow', scope: 'global' }, // Border radius { name: '--border-radius-sm', value: '0.125rem', category: 'border', scope: 'global' }, { name: '--border-radius-md', value: '0.375rem', category: 'border', scope: 'global' }, { name: '--border-radius-lg', value: '0.5rem', category: 'border', scope: 'global' }, { name: '--border-radius-xl', value: '0.75rem', category: 'border', scope: 'global' } ]; /** * Generate style recommendations based on content and layout */ static generateStyleRecommendation(slideType, audience, contentDensity, brandGuidelines) { const baseTheme = this.createHatchCorporateTheme(); const customTokens = this.generateCustomTokens(slideType, audience, contentDensity); const componentOverrides = this.generateComponentOverrides(slideType, contentDensity); const cssFramework = this.generateCSSFramework(baseTheme, customTokens); return { theme: baseTheme, custom_tokens: customTokens, component_overrides: componentOverrides, css_framework: cssFramework, reasoning: this.generateStyleReasoning(slideType, audience, contentDensity) }; } /** * Create Hatch corporate theme */ static createHatchCorporateTheme() { const components = [ { component: 'slide-header', styles: { 'background': 'linear-gradient(135deg, var(--hatch-primary-blue) 0%, var(--hatch-secondary-blue) 100%)', 'color': 'var(--hatch-white)', 'padding': 'var(--spacing-8) var(--spacing-12)', 'border-radius': 'var(--border-radius-lg)', 'margin-bottom': 'var(--spacing-6)' }, variants: { 'hero': { 'font-size': 'var(--font-size-4xl)', 'text-align': 'center', 'padding': 'var(--spacing-12) var(--spacing-16)' }, 'content': { 'font-size': 'var(--font-size-2xl)', 'text-align': 'left' } } }, { component: 'slide-content', styles: { 'color': 'var(--hatch-corporate-gray)', 'font-family': 'var(--font-primary)', 'line-height': '1.6', 'padding': 'var(--spacing-6)' }, variants: { 'high-density': { 'font-size': 'var(--font-size-sm)', 'line-height': '1.4' }, 'low-density': { 'font-size': 'var(--font-size-lg)', 'line-height': '1.8' } } }, { component: 'call-to-action', styles: { 'background': 'linear-gradient(135deg, var(--hatch-primary-orange) 0%, var(--hatch-secondary-orange) 100%)', 'color': 'var(--hatch-white)', 'padding': 'var(--spacing-4) var(--spacing-8)', 'border-radius': 'var(--border-radius-md)', 'font-weight': 'var(--font-weight-bold)', 'text-align': 'center', 'box-shadow': 'var(--shadow-md)' } }, { component: 'data-table', styles: { 'border': '2px solid var(--hatch-primary-orange)', 'border-radius': 'var(--border-radius-lg)', 'overflow': 'hidden' }, variants: { 'header': { 'background': 'var(--hatch-primary-orange)', 'color': 'var(--hatch-white)', 'font-weight': 'var(--font-weight-bold)' }, 'cell': { 'padding': 'var(--spacing-3)', 'border-bottom': '1px solid var(--hatch-light-gray)' } } } ]; return { name: 'hatch-corporate', tokens: this.hatchBrandTokens, components, brand_alignment: 1.0, accessibility_score: 0.95 }; } /** * Generate custom tokens based on context */ static generateCustomTokens(slideType, audience, contentDensity) { const tokens = []; // Audience-specific adjustments if (audience === 'executive') { tokens.push({ name: '--executive-emphasis', value: 'var(--hatch-primary-blue)', category: 'color', scope: 'component' }, { name: '--executive-font-size', value: 'var(--font-size-xl)', category: 'typography', scope: 'component' }); } else if (audience === 'technical') { tokens.push({ name: '--technical-accent', value: 'var(--hatch-medium-gray)', category: 'color', scope: 'component' }, { name: '--code-font', value: 'Monaco, Consolas, monospace', category: 'typography', scope: 'component' }); } // Content density adjustments if (contentDensity === 'high') { tokens.push({ name: '--dense-spacing', value: 'var(--spacing-2)', category: 'spacing', scope: 'component' }, { name: '--dense-font-size', value: 'var(--font-size-sm)', category: 'typography', scope: 'component' }); } else if (contentDensity === 'low') { tokens.push({ name: '--spacious-spacing', value: 'var(--spacing-8)', category: 'spacing', scope: 'component' }, { name: '--spacious-font-size', value: 'var(--font-size-lg)', category: 'typography', scope: 'component' }); } // Slide type specific tokens if (slideType === 'hero') { tokens.push({ name: '--hero-gradient', value: 'linear-gradient(135deg, var(--hatch-primary-blue) 0%, var(--hatch-secondary-blue) 100%)', category: 'color', scope: 'component' }); } else if (slideType === 'evidence') { tokens.push({ name: '--evidence-highlight', value: 'var(--hatch-primary-orange)', category: 'color', scope: 'component' }); } return tokens; } /** * Generate component style overrides */ static generateComponentOverrides(slideType, contentDensity) { const overrides = []; // Slide type specific overrides if (slideType === 'comparison') { overrides.push({ component: 'comparison-grid', styles: { 'display': 'grid', 'grid-template-columns': '1fr 1fr', 'gap': 'var(--spacing-6)', 'padding': 'var(--spacing-4)' } }); } if (slideType === 'timeline') { overrides.push({ component: 'timeline-container', styles: { 'display': 'flex', 'flex-direction': 'row', 'align-items': 'center', 'gap': 'var(--spacing-4)' }, responsive: { 'mobile': { 'flex-direction': 'column' } } }); } // Content density overrides if (contentDensity === 'high') { overrides.push({ component: 'content-container', styles: { 'font-size': 'var(--dense-font-size)', 'line-height': '1.4', 'padding': 'var(--dense-spacing)' } }); } return overrides; } /** * Generate complete CSS framework */ static generateCSSFramework(theme, customTokens) { const allTokens = [...theme.tokens, ...customTokens]; // Generate CSS custom properties const cssVariables = allTokens .map(token => ` ${token.name}: ${token.value};`) .join('\n'); // Generate component styles const componentStyles = theme.components .map(component => this.generateComponentCSS(component)) .join('\n\n'); return ` /* Hatch Corporate Design System */ :root { ${cssVariables} } /* Base styles */ .slidev-container { font-family: var(--font-primary); color: var(--hatch-corporate-gray); background: var(--hatch-background); line-height: 1.6; } .slide { padding: var(--spacing-8); min-height: 100vh; display: flex; flex-direction: column; } /* Component styles */ ${componentStyles} /* Utility classes */ .text-center { text-align: center; } .text-left { text-align: left; } .text-right { text-align: right; } .font-bold { font-weight: var(--font-weight-bold); } .font-medium { font-weight: var(--font-weight-medium); } .text-primary { color: var(--hatch-primary-blue); } .text-accent { color: var(--hatch-primary-orange); } .text-muted { color: var(--hatch-medium-gray); } .bg-primary { background: var(--hatch-primary-blue); } .bg-accent { background: var(--hatch-primary-orange); } .bg-light { background: var(--hatch-light-gray); } .shadow-sm { box-shadow: var(--shadow-sm); } .shadow-md { box-shadow: var(--shadow-md); } .shadow-lg { box-shadow: var(--shadow-lg); } .rounded-sm { border-radius: var(--border-radius-sm); } .rounded-md { border-radius: var(--border-radius-md); } .rounded-lg { border-radius: var(--border-radius-lg); } /* Responsive utilities */ @media (max-width: 768px) { .slide { padding: var(--spacing-4); } .desktop-only { display: none; } } @media (min-width: 769px) { .mobile-only { display: none; } } `; } /** * Generate CSS for individual components */ static generateComponentCSS(component) { let css = `.${component.component} {\n`; // Base styles Object.entries(component.styles).forEach(([property, value]) => { css += ` ${property}: ${value};\n`; }); css += '}\n'; // Variants if (component.variants) { Object.entries(component.variants).forEach(([variant, styles]) => { css += `.${component.component}--${variant} {\n`; Object.entries(styles).forEach(([property, value]) => { css += ` ${property}: ${value};\n`; }); css += '}\n'; }); } // Responsive styles if (component.responsive) { Object.entries(component.responsive).forEach(([breakpoint, styles]) => { const mediaQuery = breakpoint === 'mobile' ? 'max-width: 768px' : 'min-width: 769px'; css += `@media (${mediaQuery}) {\n`; css += ` .${component.component} {\n`; Object.entries(styles).forEach(([property, value]) => { css += ` ${property}: ${value};\n`; }); css += ' }\n}\n'; }); } return css; } /** * Generate reasoning for style choices */ static generateStyleReasoning(slideType, audience, contentDensity) { return `Style optimized for ${slideType} slide targeting ${audience} audience with ${contentDensity} content density. ` + `Applied Hatch corporate branding with accessibility-compliant color contrasts and responsive design patterns. ` + `Used atomic design principles with modular component system for consistency and maintainability.`; } /** * Validate accessibility compliance */ static validateAccessibility(theme) { const issues = []; let score = 1.0; // Check color contrast ratios (simplified) const primaryBlue = '#095078'; const white = '#FFFFFF'; // This would use actual contrast ratio calculation // For now, assume Hatch colors are compliant // Check font sizes const hasReadableFonts = theme.tokens.some(token => token.name.includes('font-size') && parseFloat(token.value) >= 0.875 // 14px minimum ); if (!hasReadableFonts) { issues.push('Font sizes below recommended minimum'); score -= 0.1; } return { score, issues }; } }