#!/usr/bin/env node /** * Color Update Utility for Inogen Project * * This script helps update all color values across the project * when new colors are extracted from Figma designs. */ const fs = require("fs"); const path = require("path"); // Figma color configuration // Replace these values with actual colors from Figma const FIGMA_COLORS = { // Primary Brand Colors (Teal) primary: { 50: "#f0fdfa", 100: "#ccfbf1", 200: "#99f6e4", 300: "#5eead4", 400: "#2dd4bf", 500: "#48D1CC", // Main brand color from current design 600: "#40C4C4", // Hover state from current design 700: "#0f766e", 800: "#115e59", 900: "#134e4a", 950: "#042f2e", }, // Dark Theme Colors dark: { 50: "#f8fafc", 100: "#f1f5f9", 200: "#e2e8f0", 300: "#cbd5e1", 400: "#94a3b8", 500: "#64748b", 600: "#475569", 700: "#334155", 800: "#1A202C", // Login background from current design 900: "#0f172a", 950: "#020617", }, // Neutral Colors neutral: { 50: "#fafafa", 100: "#f5f5f5", 200: "#e5e5e5", 300: "#d4d4d4", 400: "#a3a3a3", 500: "#737373", 600: "#525252", 700: "#404040", 800: "#262626", 900: "#171717", 950: "#0a0a0a", }, // Status Colors success: { 50: "#f0fdf4", 100: "#dcfce7", 200: "#bbf7d0", 300: "#86efac", 400: "#4ade80", 500: "#22c55e", 600: "#16a34a", 700: "#15803d", 800: "#166534", 900: "#14532d", }, error: { 50: "#fef2f2", 100: "#fee2e2", 200: "#fecaca", 300: "#fca5a5", 400: "#f87171", 500: "#ef4444", 600: "#dc2626", 700: "#b91c1c", 800: "#991b1b", 900: "#7f1d1d", }, warning: { 50: "#fffbeb", 100: "#fef3c7", 200: "#fde68a", 300: "#fcd34d", 400: "#fbbf24", 500: "#f59e0b", 600: "#d97706", 700: "#b45309", 800: "#92400e", 900: "#78350f", }, info: { 50: "#eff6ff", 100: "#dbeafe", 200: "#bfdbfe", 300: "#93c5fd", 400: "#60a5fa", 500: "#3b82f6", 600: "#2563eb", 700: "#1d4ed8", 800: "#1e40af", 900: "#1e3a8a", }, // Login specific colors login: { primary: "#3aea83", darkStart: "#464861", darkEnd: "#111628", }, }; // Semantic color mappings const SEMANTIC_COLORS = { light: { background: "#ffffff", foreground: "#0a0a0a", card: "#ffffff", cardForeground: "#0a0a0a", popover: "#ffffff", popoverForeground: "#0a0a0a", primary: FIGMA_COLORS.primary[500], primaryForeground: FIGMA_COLORS.dark[800], secondary: FIGMA_COLORS.neutral[100], secondaryForeground: FIGMA_COLORS.neutral[900], muted: FIGMA_COLORS.neutral[100], mutedForeground: FIGMA_COLORS.neutral[500], accent: FIGMA_COLORS.neutral[100], accentForeground: FIGMA_COLORS.neutral[900], destructive: FIGMA_COLORS.error[500], destructiveForeground: "#ffffff", border: FIGMA_COLORS.neutral[200], input: FIGMA_COLORS.neutral[200], ring: FIGMA_COLORS.primary[500], loginPrimary: FIGMA_COLORS.login.primary, loginDarkStart: FIGMA_COLORS.login.darkStart, loginDarkEnd: FIGMA_COLORS.login.darkEnd, }, dark: { background: FIGMA_COLORS.dark[950], foreground: FIGMA_COLORS.neutral[50], card: FIGMA_COLORS.dark[900], cardForeground: FIGMA_COLORS.neutral[50], popover: FIGMA_COLORS.dark[900], popoverForeground: FIGMA_COLORS.neutral[50], primary: FIGMA_COLORS.primary[500], primaryForeground: FIGMA_COLORS.dark[800], secondary: FIGMA_COLORS.dark[800], secondaryForeground: FIGMA_COLORS.neutral[50], muted: FIGMA_COLORS.dark[800], mutedForeground: FIGMA_COLORS.neutral[400], accent: FIGMA_COLORS.dark[800], accentForeground: FIGMA_COLORS.neutral[50], destructive: FIGMA_COLORS.error[500], destructiveForeground: FIGMA_COLORS.neutral[50], border: FIGMA_COLORS.dark[800], input: FIGMA_COLORS.dark[800], ring: FIGMA_COLORS.primary[400], loginPrimary: FIGMA_COLORS.login.primary, loginDarkStart: FIGMA_COLORS.login.darkStart, loginDarkEnd: FIGMA_COLORS.login.darkEnd, }, }; /** * Update CSS custom properties in app.css */ function updateAppCSS() { const cssPath = path.join(__dirname, "../app/app.css"); let cssContent = fs.readFileSync(cssPath, "utf8"); // Generate CSS custom properties let newProperties = ""; // Add color scales Object.entries(FIGMA_COLORS).forEach(([colorName, colorScale]) => { newProperties += `\n /* ${colorName.charAt(0).toUpperCase() + colorName.slice(1)} color scale */\n`; if (typeof colorScale === "object" && colorScale !== null) { Object.entries(colorScale).forEach(([shade, value]) => { newProperties += ` --color-${colorName}-${shade}: ${value};\n`; }); } }); // Update semantic colors for light theme const lightThemeStart = ":root {"; const lightThemeEnd = "}"; let lightThemeContent = `${lightThemeStart} --radius: 0.5rem; /* Light theme colors */ --background: ${SEMANTIC_COLORS.light.background}; --foreground: ${SEMANTIC_COLORS.light.foreground}; --card: ${SEMANTIC_COLORS.light.card}; --card-foreground: ${SEMANTIC_COLORS.light.cardForeground}; --popover: ${SEMANTIC_COLORS.light.popover}; --popover-foreground: ${SEMANTIC_COLORS.light.popoverForeground}; --primary: ${SEMANTIC_COLORS.light.primary}; --primary-foreground: ${SEMANTIC_COLORS.light.primaryForeground}; --secondary: ${SEMANTIC_COLORS.light.secondary}; --secondary-foreground: ${SEMANTIC_COLORS.light.secondaryForeground}; --muted: ${SEMANTIC_COLORS.light.muted}; --muted-foreground: ${SEMANTIC_COLORS.light.mutedForeground}; --accent: ${SEMANTIC_COLORS.light.accent}; --accent-foreground: ${SEMANTIC_COLORS.light.accentForeground}; --destructive: ${SEMANTIC_COLORS.light.destructive}; --destructive-foreground: ${SEMANTIC_COLORS.light.destructiveForeground}; --border: ${SEMANTIC_COLORS.light.border}; --input: ${SEMANTIC_COLORS.light.input}; --ring: ${SEMANTIC_COLORS.light.ring}; /* Login specific colors */ --color-login-primary: ${SEMANTIC_COLORS.light.loginPrimary}; --color-login-dark-start: ${SEMANTIC_COLORS.light.loginDarkStart}; --color-login-dark-end: ${SEMANTIC_COLORS.light.loginDarkEnd}; ${newProperties} ${lightThemeEnd}`; // Update dark theme let darkThemeContent = `.dark { /* Dark theme colors */ --background: ${SEMANTIC_COLORS.dark.background}; --foreground: ${SEMANTIC_COLORS.dark.foreground}; --card: ${SEMANTIC_COLORS.dark.card}; --card-foreground: ${SEMANTIC_COLORS.dark.cardForeground}; --popover: ${SEMANTIC_COLORS.dark.popover}; --popover-foreground: ${SEMANTIC_COLORS.dark.popoverForeground}; --primary: ${SEMANTIC_COLORS.dark.primary}; --primary-foreground: ${SEMANTIC_COLORS.dark.primaryForeground}; --secondary: ${SEMANTIC_COLORS.dark.secondary}; --secondary-foreground: ${SEMANTIC_COLORS.dark.secondaryForeground}; --muted: ${SEMANTIC_COLORS.dark.muted}; --muted-foreground: ${SEMANTIC_COLORS.dark.mutedForeground}; --accent: ${SEMANTIC_COLORS.dark.accent}; --accent-foreground: ${SEMANTIC_COLORS.dark.accentForeground}; --destructive: ${SEMANTIC_COLORS.dark.destructive}; --destructive-foreground: ${SEMANTIC_COLORS.dark.destructiveForeground}; --border: ${SEMANTIC_COLORS.dark.border}; --input: ${SEMANTIC_COLORS.dark.input}; --ring: ${SEMANTIC_COLORS.dark.ring}; /* Login specific colors */ --color-login-primary: ${SEMANTIC_COLORS.dark.loginPrimary}; --color-login-dark-start: ${SEMANTIC_COLORS.dark.loginDarkStart}; --color-login-dark-end: ${SEMANTIC_COLORS.dark.loginDarkEnd}; }`; // Replace existing color definitions cssContent = cssContent.replace(/:root\s*{[^}]*}/s, lightThemeContent); cssContent = cssContent.replace(/\.dark\s*{[^}]*}/s, darkThemeContent); fs.writeFileSync(cssPath, cssContent); console.log("āœ… Updated app.css with new colors"); } /** * Update TypeScript design tokens */ function updateDesignTokens() { const tokensPath = path.join(__dirname, "../app/lib/design-tokens.ts"); const designTokensContent = `export const colors = { // Primary Colors primary: ${JSON.stringify(FIGMA_COLORS.primary, null, 4).replace(/"/g, '"')}, // Secondary Colors (Info/Blue) secondary: ${JSON.stringify(FIGMA_COLORS.info, null, 4).replace(/"/g, '"')}, // Dark Colors (Brand dark) dark: ${JSON.stringify(FIGMA_COLORS.dark, null, 4).replace(/"/g, '"')}, // Neutral Colors neutral: ${JSON.stringify(FIGMA_COLORS.neutral, null, 4).replace(/"/g, '"')}, // Status Colors success: ${JSON.stringify(FIGMA_COLORS.success, null, 4).replace(/"/g, '"')}, error: ${JSON.stringify(FIGMA_COLORS.error, null, 4).replace(/"/g, '"')}, warning: ${JSON.stringify(FIGMA_COLORS.warning, null, 4).replace(/"/g, '"')}, info: ${JSON.stringify(FIGMA_COLORS.info, null, 4).replace(/"/g, '"')}, }; export const typography = { fontFamily: { sans: ["Vazirmatn", "Inter", "ui-sans-serif", "system-ui", "sans-serif"], mono: ["ui-monospace", "SFMono-Regular", "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" }], "5xl": ["3rem", { lineHeight: "1" }], "6xl": ["3.75rem", { lineHeight: "1" }], }, fontWeight: { thin: "100", extralight: "200", light: "300", normal: "400", medium: "500", semibold: "600", bold: "700", extrabold: "800", black: "900", }, }; export const spacing = { px: "1px", 0: "0px", 0.5: "0.125rem", 1: "0.25rem", 1.5: "0.375rem", 2: "0.5rem", 2.5: "0.625rem", 3: "0.75rem", 3.5: "0.875rem", 4: "1rem", 5: "1.25rem", 6: "1.5rem", 7: "1.75rem", 8: "2rem", 9: "2.25rem", 10: "2.5rem", 11: "2.75rem", 12: "3rem", 14: "3.5rem", 16: "4rem", 20: "5rem", 24: "6rem", 28: "7rem", 32: "8rem", 36: "9rem", 40: "10rem", 44: "11rem", 48: "12rem", 52: "13rem", 56: "14rem", 60: "15rem", 64: "16rem", 72: "18rem", 80: "20rem", 96: "24rem", }; export const borderRadius = { none: "0px", sm: "0.125rem", DEFAULT: "0.25rem", md: "0.375rem", lg: "0.5rem", xl: "0.75rem", "2xl": "1rem", "3xl": "1.5rem", full: "9999px", }; export const shadows = { sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)", DEFAULT: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)", md: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)", lg: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)", xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)", "2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)", inner: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)", none: "0 0 #0000", }; // Theme variants export const themes = { light: ${JSON.stringify(SEMANTIC_COLORS.light, null, 4).replace(/"/g, '"')}, dark: ${JSON.stringify(SEMANTIC_COLORS.dark, null, 4).replace(/"/g, '"')}, }; `; fs.writeFileSync(tokensPath, designTokensContent); console.log("āœ… Updated design-tokens.ts with new colors"); } /** * Main execution function */ function updateColors() { console.log("šŸŽØ Updating colors from Figma design...\n"); try { updateAppCSS(); updateDesignTokens(); console.log("\n✨ Color update completed successfully!"); console.log("\nUpdated files:"); console.log("- app/app.css"); console.log("- app/lib/design-tokens.ts"); console.log("\nNext steps:"); console.log("1. Review the changes"); console.log("2. Test the application"); console.log("3. Verify colors match Figma design"); console.log("4. Update any hardcoded colors in components"); } catch (error) { console.error("āŒ Error updating colors:", error.message); process.exit(1); } } // Run the script if called directly if (require.main === module) { updateColors(); } module.exports = { FIGMA_COLORS, SEMANTIC_COLORS, updateColors, };