|
| 1 | +/** |
| 2 | + * Brand configuration for dashboard customization |
| 3 | + */ |
| 4 | + |
| 5 | +const HEX_COLOR_RE = /^#?[0-9a-fA-F]{6}$/ |
| 6 | + |
| 7 | +/** |
| 8 | + * Environment variables that control branding |
| 9 | + */ |
| 10 | +export interface BrandEnv { |
| 11 | + BRAND_NAME?: string |
| 12 | + BRAND_ACCENT?: string |
| 13 | + BRAND_CDN_URL?: string |
| 14 | + BRAND_FONT_NAME?: string |
| 15 | + BRAND_LOGO_URL?: string |
| 16 | + BRAND_FAVICON_URL?: string |
| 17 | + BRAND_FONT_REGULAR_URL?: string |
| 18 | + BRAND_FONT_MEDIUM_URL?: string |
| 19 | + BRAND_PATTERN_URL?: string |
| 20 | +} |
| 21 | + |
| 22 | +/** |
| 23 | + * Complete brand configuration interface |
| 24 | + */ |
| 25 | +export interface BrandConfig { |
| 26 | + name: string |
| 27 | + accentColor: string |
| 28 | + accentHoverColor: string |
| 29 | + accentLightColor: string |
| 30 | + accentDimColor: string |
| 31 | + accentGlowColor: string |
| 32 | + accentBorderColor: string |
| 33 | + fontName: string |
| 34 | + fontRegularUrl: string |
| 35 | + fontMediumUrl: string |
| 36 | + logoUrl: string |
| 37 | + faviconUrl: string |
| 38 | + patternImageUrl: string |
| 39 | + cdnHost: string |
| 40 | +} |
| 41 | + |
| 42 | +/** |
| 43 | + * Default AIBTC brand configuration |
| 44 | + */ |
| 45 | +export const DEFAULT_BRAND_CONFIG: BrandConfig = { |
| 46 | + name: 'AIBTC', |
| 47 | + accentColor: '#FF4F03', |
| 48 | + accentHoverColor: '#e54400', |
| 49 | + accentLightColor: '#ff7033', |
| 50 | + accentDimColor: 'rgba(255, 79, 3, 0.12)', |
| 51 | + accentGlowColor: 'rgba(255, 79, 3, 0.08)', |
| 52 | + accentBorderColor: 'rgba(255, 79, 3, 0.3)', |
| 53 | + fontName: 'Roc Grotesk', |
| 54 | + fontRegularUrl: 'https://aibtc.com/fonts/RocGrotesk-Regular.woff2', |
| 55 | + fontMediumUrl: 'https://aibtc.com/fonts/RocGrotesk-WideMedium.woff2', |
| 56 | + logoUrl: 'https://aibtc.com/Primary_Logo/SVG/AIBTC_PrimaryLogo_KO.svg', |
| 57 | + faviconUrl: 'https://aibtc.com/favicon-32x32.png', |
| 58 | + patternImageUrl: 'https://aibtc.com/Artwork/AIBTC_Pattern1_optimized.jpg', |
| 59 | + cdnHost: 'https://aibtc.com', |
| 60 | +} |
| 61 | + |
| 62 | +/** |
| 63 | + * Convert hex color to rgba string |
| 64 | + */ |
| 65 | +export function hexToRgba(hex: string, alpha: number): string { |
| 66 | + if (!HEX_COLOR_RE.test(hex)) { |
| 67 | + return `rgba(0, 0, 0, ${alpha})` |
| 68 | + } |
| 69 | + |
| 70 | + const cleanHex = hex.replace(/^#/, '') |
| 71 | + const r = parseInt(cleanHex.substring(0, 2), 16) |
| 72 | + const g = parseInt(cleanHex.substring(2, 4), 16) |
| 73 | + const b = parseInt(cleanHex.substring(4, 6), 16) |
| 74 | + |
| 75 | + return `rgba(${r}, ${g}, ${b}, ${alpha})` |
| 76 | +} |
| 77 | + |
| 78 | +/** |
| 79 | + * Get brand configuration from environment variables |
| 80 | + * |
| 81 | + * Supports: |
| 82 | + * - BRAND_NAME: Brand display name |
| 83 | + * - BRAND_ACCENT: Hex color (derives hover/dim/glow/border/light automatically) |
| 84 | + * - BRAND_CDN_URL: Base URL for assets (derives logo/favicon/font/pattern paths) |
| 85 | + * - BRAND_FONT_NAME: Font family name |
| 86 | + * - BRAND_LOGO_URL: Full logo URL override |
| 87 | + * - BRAND_FAVICON_URL: Full favicon URL override |
| 88 | + * - BRAND_FONT_REGULAR_URL: Full font regular URL override |
| 89 | + * - BRAND_FONT_MEDIUM_URL: Full font medium URL override |
| 90 | + * - BRAND_PATTERN_URL: Full pattern image URL override |
| 91 | + */ |
| 92 | +export function getBrandConfig(env: BrandEnv): BrandConfig { |
| 93 | + const cdnUrl = env.BRAND_CDN_URL || DEFAULT_BRAND_CONFIG.cdnHost |
| 94 | + const rawAccent = env.BRAND_ACCENT || DEFAULT_BRAND_CONFIG.accentColor |
| 95 | + const accentHex = HEX_COLOR_RE.test(rawAccent) ? rawAccent : DEFAULT_BRAND_CONFIG.accentColor |
| 96 | + |
| 97 | + // Derive accent variations from base accent color |
| 98 | + const r = parseInt(accentHex.replace(/^#/, '').substring(0, 2), 16) |
| 99 | + const g = parseInt(accentHex.replace(/^#/, '').substring(2, 4), 16) |
| 100 | + const b = parseInt(accentHex.replace(/^#/, '').substring(4, 6), 16) |
| 101 | + |
| 102 | + // Generate hover color (darken by ~10%) |
| 103 | + const hoverR = Math.max(0, Math.floor(r * 0.9)) |
| 104 | + const hoverG = Math.max(0, Math.floor(g * 0.9)) |
| 105 | + const hoverB = Math.max(0, Math.floor(b * 0.9)) |
| 106 | + const accentHoverColor = `#${hoverR.toString(16).padStart(2, '0')}${hoverG.toString(16).padStart(2, '0')}${hoverB.toString(16).padStart(2, '0')}` |
| 107 | + |
| 108 | + // Generate light color (lighten by ~15%) |
| 109 | + const lightR = Math.min(255, Math.floor(r + (255 - r) * 0.15)) |
| 110 | + const lightG = Math.min(255, Math.floor(g + (255 - g) * 0.15)) |
| 111 | + const lightB = Math.min(255, Math.floor(b + (255 - b) * 0.15)) |
| 112 | + const accentLightColor = `#${lightR.toString(16).padStart(2, '0')}${lightG.toString(16).padStart(2, '0')}${lightB.toString(16).padStart(2, '0')}` |
| 113 | + |
| 114 | + return { |
| 115 | + name: env.BRAND_NAME || DEFAULT_BRAND_CONFIG.name, |
| 116 | + accentColor: accentHex, |
| 117 | + accentHoverColor: accentHoverColor, |
| 118 | + accentLightColor: accentLightColor, |
| 119 | + accentDimColor: hexToRgba(accentHex, 0.12), |
| 120 | + accentGlowColor: hexToRgba(accentHex, 0.08), |
| 121 | + accentBorderColor: hexToRgba(accentHex, 0.3), |
| 122 | + fontName: env.BRAND_FONT_NAME || DEFAULT_BRAND_CONFIG.fontName, |
| 123 | + fontRegularUrl: env.BRAND_FONT_REGULAR_URL || `${cdnUrl}/fonts/${env.BRAND_FONT_NAME || 'RocGrotesk'}-Regular.woff2`, |
| 124 | + fontMediumUrl: env.BRAND_FONT_MEDIUM_URL || `${cdnUrl}/fonts/${env.BRAND_FONT_NAME || 'RocGrotesk'}-WideMedium.woff2`, |
| 125 | + logoUrl: env.BRAND_LOGO_URL || `${cdnUrl}/Primary_Logo/SVG/${env.BRAND_NAME || 'AIBTC'}_PrimaryLogo_KO.svg`, |
| 126 | + faviconUrl: env.BRAND_FAVICON_URL || `${cdnUrl}/favicon-32x32.png`, |
| 127 | + patternImageUrl: env.BRAND_PATTERN_URL || `${cdnUrl}/Artwork/${env.BRAND_NAME || 'AIBTC'}_Pattern1_optimized.jpg`, |
| 128 | + cdnHost: cdnUrl, |
| 129 | + } |
| 130 | +} |
0 commit comments