Skip to main content
Glama
theme-creator.ts9.25 kB
import { callGemini, isGeminiAvailable } from '../utils/gemini.js'; interface ThemeRequest { brandColor: string; style?: 'minimal' | 'modern' | 'classic' | 'bold' | 'elegant'; colorCount?: number; includeConfig?: boolean; typography?: boolean; spacing?: boolean; } interface ColorShade { [key: string]: string; } interface ThemeConfig { colors: { primary: ColorShade; secondary?: ColorShade; accent?: ColorShade; neutral?: ColorShade; }; typography?: { fontFamily: { [key: string]: string[] }; fontSize: { [key: string]: [string, { lineHeight: string }] }; }; spacing?: { [key: string]: string }; borderRadius?: { [key: string]: string }; boxShadow?: { [key: string]: string }; } export async function createTheme(args: ThemeRequest) { try { const { brandColor, style = 'modern', colorCount = 9, includeConfig = true, typography = true, spacing = true } = args; let themeConfig: ThemeConfig; let designSystemNotes = ''; if (isGeminiAvailable()) { // Use AI to generate sophisticated theme const prompt = `Create a comprehensive Tailwind CSS theme based on the following specifications: Brand Color: ${brandColor} Design Style: ${style} Color Shades: ${colorCount} (50, 100, 200, ..., 900) Include Typography: ${typography} Include Spacing: ${spacing} Requirements: 1. Generate a complete color palette with ${colorCount} shades of the brand color 2. Create complementary secondary and accent colors that work well with the brand color 3. Include neutral colors (grays) that complement the theme 4. ${typography ? 'Design a typography scale with font families, sizes, and line heights' : ''} 5. ${spacing ? 'Create a custom spacing scale that fits the design style' : ''} 6. Include border radius, shadows, and other design tokens for the ${style} style 7. Ensure accessibility with proper contrast ratios 8. Provide design system notes and usage guidelines Return a JSON response with: { "themeConfig": { "colors": { "primary": { "50": "#...", "100": "#...", ... }, "secondary": { "50": "#...", "100": "#...", ... }, "accent": { "50": "#...", "100": "#...", ... }, "neutral": { "50": "#...", "100": "#...", ... } }, ${typography ? '"typography": { "fontFamily": {...}, "fontSize": {...} },' : ''} ${spacing ? '"spacing": {...},' : ''} "borderRadius": {...}, "boxShadow": {...} }, "designSystemNotes": "Comprehensive usage guidelines and design principles" }`; const aiResponse = await callGemini(prompt); try { const jsonMatch = aiResponse.match(/\{[\s\S]*\}/); if (jsonMatch) { const parsedResponse = JSON.parse(jsonMatch[0]); themeConfig = parsedResponse.themeConfig; designSystemNotes = parsedResponse.designSystemNotes || ''; } else { throw new Error('No valid JSON found in AI response'); } } catch (parseError) { // Fallback to manual generation themeConfig = generateManualTheme(brandColor, style, colorCount, typography, spacing); designSystemNotes = `Generated ${style} theme based on ${brandColor}`; } } else { // Manual theme generation themeConfig = generateManualTheme(brandColor, style, colorCount, typography, spacing); designSystemNotes = `Generated ${style} theme based on ${brandColor}`; } // Generate Tailwind config if requested const tailwindConfig = includeConfig ? generateTailwindConfig(themeConfig) : ''; return { content: [ { type: 'text', text: `# Custom Tailwind Theme - ${style.charAt(0).toUpperCase() + style.slice(1)} Style ## Design System Overview ${designSystemNotes} ## Color Palette ### Primary Colors ${formatColorPalette(themeConfig.colors.primary)} ${themeConfig.colors.secondary ? `### Secondary Colors\n${formatColorPalette(themeConfig.colors.secondary)}` : ''} ${themeConfig.colors.accent ? `### Accent Colors\n${formatColorPalette(themeConfig.colors.accent)}` : ''} ${themeConfig.colors.neutral ? `### Neutral Colors\n${formatColorPalette(themeConfig.colors.neutral)}` : ''} ${themeConfig.typography ? `## Typography Scale\n${formatTypography(themeConfig.typography)}` : ''} ${themeConfig.spacing ? `## Spacing Scale\n${formatSpacing(themeConfig.spacing)}` : ''} ${includeConfig ? `## Tailwind Configuration\n\`\`\`javascript\n${tailwindConfig}\n\`\`\`` : ''} ## Usage Examples ### Primary Button \`\`\`html <button class="bg-primary-600 hover:bg-primary-700 text-white px-6 py-3 rounded-md transition-colors"> Primary Action </button> \`\`\` ### Card Component \`\`\`html <div class="bg-white border border-neutral-200 rounded-lg shadow-sm p-6"> <h3 class="text-xl font-semibold text-neutral-900 mb-2">Card Title</h3> <p class="text-neutral-600">Card content goes here...</p> </div> \`\`\` ### Accent Elements \`\`\`html <div class="bg-accent-50 border-l-4 border-accent-400 p-4"> <p class="text-accent-700">Highlighted information</p> </div> \`\`\`` } ] }; } catch (error) { console.error('Theme creation error:', error); throw new Error(`Failed to create theme: ${error instanceof Error ? error.message : 'Unknown error'}`); } } function generateManualTheme( brandColor: string, style: string, colorCount: number, typography: boolean, spacing: boolean ): ThemeConfig { // Convert brand color to RGB for calculations const primaryColors = generateColorShades(brandColor, colorCount); const config: ThemeConfig = { colors: { primary: primaryColors, neutral: { '50': '#f9fafb', '100': '#f3f4f6', '200': '#e5e7eb', '300': '#d1d5db', '400': '#9ca3af', '500': '#6b7280', '600': '#4b5563', '700': '#374151', '800': '#1f2937', '900': '#111827' } } }; // Add typography if requested if (typography) { config.typography = { fontFamily: { sans: ['Inter', 'system-ui', 'sans-serif'], serif: ['Georgia', 'Times', 'serif'], mono: ['Monaco', 'Consolas', 'monospace'] }, fontSize: { 'xs': ['0.75rem', { lineHeight: '1rem' }], 'sm': ['0.875rem', { lineHeight: '1.25rem' }], 'base': ['1rem', { lineHeight: '1.5rem' }], 'lg': ['1.125rem', { lineHeight: '1.75rem' }], 'xl': ['1.25rem', { lineHeight: '1.75rem' }], '2xl': ['1.5rem', { lineHeight: '2rem' }], '3xl': ['1.875rem', { lineHeight: '2.25rem' }], '4xl': ['2.25rem', { lineHeight: '2.5rem' }] } }; } // Add spacing if requested if (spacing) { config.spacing = { 'xs': '0.5rem', 'sm': '0.75rem', 'md': '1rem', 'lg': '1.5rem', 'xl': '2rem', '2xl': '3rem', '3xl': '4rem' }; } return config; } function generateColorShades(baseColor: string, count: number): ColorShade { // This is a simplified color generation - in a real implementation, // you'd want to use a proper color manipulation library const shades: ColorShade = {}; const levels = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900]; levels.slice(0, count).forEach((level, index) => { // Simple approximation - in reality you'd use HSL/Lab color space const intensity = 1 - (index / (count - 1)); shades[level.toString()] = adjustColorBrightness(baseColor, intensity); }); return shades; } function adjustColorBrightness(color: string, factor: number): string { // Simplified color adjustment - this should be replaced with proper color library if (color.startsWith('#')) { return color; // Return as-is for now } return color; } function generateTailwindConfig(themeConfig: ThemeConfig): string { return `/** @type {import('tailwindcss').Config} */ module.exports = { content: [ './src/**/*.{html,js,jsx,ts,tsx,vue,svelte}', ], theme: { extend: { colors: ${JSON.stringify(themeConfig.colors, null, 6)}, ${themeConfig.typography ? `fontFamily: ${JSON.stringify(themeConfig.typography.fontFamily, null, 6)}, fontSize: ${JSON.stringify(themeConfig.typography.fontSize, null, 6)},` : ''} ${themeConfig.spacing ? `spacing: ${JSON.stringify(themeConfig.spacing, null, 6)},` : ''} }, }, plugins: [], }`; } function formatColorPalette(colors: ColorShade): string { return Object.entries(colors) .map(([shade, color]) => `- **${shade}**: \`${color}\``) .join('\n'); } function formatTypography(typography: any): string { let output = '### Font Families\n'; Object.entries(typography.fontFamily).forEach(([name, fonts]) => { output += `- **${name}**: ${(fonts as string[]).join(', ')}\n`; }); output += '\n### Font Sizes\n'; Object.entries(typography.fontSize).forEach(([name, config]) => { const [size, props] = config as [string, { lineHeight: string }]; output += `- **${name}**: ${size} (line-height: ${props.lineHeight})\n`; }); return output; } function formatSpacing(spacing: { [key: string]: string }): string { return Object.entries(spacing) .map(([name, value]) => `- **${name}**: \`${value}\``) .join('\n'); }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Tai-DT/mcp-tailwind-gemini'

If you have feedback or need assistance with the MCP directory API, please join our Discord server