Color Palettes
Veltix provides comprehensive color palette management with predefined palettes and custom color generation tools.
Overview
Color palette features include:
- Predefined Palettes: Ready-to-use color schemes
- Custom Palettes: Create brand-specific color schemes
- Accessibility Tools: WCAG compliant color combinations
- Color Generation: Automated palette creation
- Palette Management: Import, export, and organize palettes
Predefined Palettes
Default Palettes
// Veltix default color palettes
const defaultPalettes = {
blue: {
name: 'Blue',
colors: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a'
}
},
green: {
name: 'Green',
colors: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d'
}
},
purple: {
name: 'Purple',
colors: {
50: '#faf5ff',
100: '#f3e8ff',
200: '#e9d5ff',
300: '#d8b4fe',
400: '#c084fc',
500: '#a855f7',
600: '#9333ea',
700: '#7c3aed',
800: '#6b21a8',
900: '#581c87'
}
},
red: {
name: 'Red',
colors: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d'
}
},
orange: {
name: 'Orange',
colors: {
50: '#fff7ed',
100: '#ffedd5',
200: '#fed7aa',
300: '#fdba74',
400: '#fb923c',
500: '#f97316',
600: '#ea580c',
700: '#c2410c',
800: '#9a3412',
900: '#7c2d12'
}
}
};Semantic Palettes
// Semantic color palettes for different contexts
const semanticPalettes = {
success: {
name: 'Success',
colors: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d'
}
},
warning: {
name: 'Warning',
colors: {
50: '#fffbeb',
100: '#fef3c7',
200: '#fde68a',
300: '#fcd34d',
400: '#fbbf24',
500: '#f59e0b',
600: '#d97706',
700: '#b45309',
800: '#92400e',
900: '#78350f'
}
},
error: {
name: 'Error',
colors: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d'
}
},
info: {
name: 'Info',
colors: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a'
}
}
};Custom Palette Creation
Manual Palette Creation
// Create custom palette manually
const createCustomPalette = (name, baseColor) => {
return {
name,
colors: generateColorScale(baseColor),
metadata: {
createdAt: new Date().toISOString(),
baseColor,
type: 'custom'
}
};
};
// Color scale generation
const generateColorScale = (baseColor) => {
const colors = {};
const steps = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
steps.forEach(step => {
const lightness = step === 500 ? 50 :
step < 500 ? 50 + (500 - step) * 0.1 :
step > 500 ? 50 - (step - 500) * 0.1 : 50;
colors[step] = adjustColorLightness(baseColor, lightness);
});
return colors;
};
// Color adjustment utility
const adjustColorLightness = (color, lightness) => {
// Convert hex to HSL, adjust lightness, convert back to hex
const hsl = hexToHsl(color);
hsl.l = Math.max(0, Math.min(100, lightness));
return hslToHex(hsl);
};Palette from Image
// Extract palette from image
const extractPaletteFromImage = async (imageUrl) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const colors = extractColorsFromImageData(imageData);
resolve({
name: 'Extracted Palette',
colors: generatePaletteFromColors(colors),
metadata: {
source: imageUrl,
extractedAt: new Date().toISOString()
}
});
};
img.onerror = reject;
img.src = imageUrl;
});
};
// Extract dominant colors from image data
const extractColorsFromImageData = (imageData) => {
const colors = new Map();
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const hex = rgbToHex(r, g, b);
colors.set(hex, (colors.get(hex) || 0) + 1);
}
// Sort by frequency and return top colors
return Array.from(colors.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 5)
.map(([color]) => color);
};Palette Management
Palette Registry
// Palette registry for management
class PaletteRegistry {
constructor() {
this.palettes = new Map();
this.activePalette = null;
}
register(name, palette) {
this.palettes.set(name, palette);
return true;
}
get(name) {
return this.palettes.get(name);
}
list() {
return Array.from(this.palettes.keys());
}
remove(name) {
return this.palettes.delete(name);
}
setActive(name) {
if (!this.palettes.has(name)) {
throw new Error(`Palette ${name} not found`);
}
this.activePalette = name;
return this.palettes.get(name);
}
getActive() {
return this.activePalette ? this.palettes.get(this.activePalette) : null;
}
export(name) {
const palette = this.get(name);
if (!palette) return null;
return JSON.stringify(palette, null, 2);
}
import(paletteData) {
try {
const palette = JSON.parse(paletteData);
if (!palette.name || !palette.colors) {
throw new Error('Invalid palette format');
}
this.register(palette.name, palette);
return { success: true, palette: palette.name };
} catch (error) {
return { success: false, error: error.message };
}
}
}
// Global palette registry
const paletteRegistry = new PaletteRegistry();Palette Utilities
// Palette utility functions
const paletteUtils = {
// Get color from palette
getColor: (palette, shade = 500) => {
return palette.colors[shade];
},
// Get contrasting text color
getContrastColor: (backgroundColor) => {
const brightness = getColorBrightness(backgroundColor);
return brightness > 128 ? '#000000' : '#ffffff';
},
// Check color accessibility
checkAccessibility: (foreground, background) => {
const ratio = calculateContrastRatio(foreground, background);
return {
ratio,
isAACompliant: ratio >= 4.5,
isAAACompliant: ratio >= 7,
level: ratio >= 7 ? 'AAA' : ratio >= 4.5 ? 'AA' : 'Fail'
};
},
// Generate complementary colors
getComplementary: (color) => {
const hsl = hexToHsl(color);
hsl.h = (hsl.h + 180) % 360;
return hslToHex(hsl);
},
// Generate analogous colors
getAnalogous: (color, count = 3) => {
const hsl = hexToHsl(color);
const colors = [];
for (let i = 0; i < count; i++) {
const hue = (hsl.h + (i - Math.floor(count / 2)) * 30) % 360;
colors.push(hslToHex({ ...hsl, h: hue }));
}
return colors;
}
};
// Color conversion utilities
const colorUtils = {
hexToHsl: (hex) => {
// Convert hex to HSL
const r = parseInt(hex.slice(1, 3), 16) / 255;
const g = parseInt(hex.slice(3, 5), 16) / 255;
const b = parseInt(hex.slice(5, 7), 16) / 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h: h * 360, s: s * 100, l: l * 100 };
},
hslToHex: (hsl) => {
// Convert HSL to hex
const h = hsl.h / 360;
const s = hsl.s / 100;
const l = hsl.l / 100;
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
const toHex = (c) => {
const hex = Math.round(c * 255).toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
};Accessibility Tools
Contrast Checking
// Contrast ratio calculation
const calculateContrastRatio = (color1, color2) => {
const luminance1 = getColorLuminance(color1);
const luminance2 = getColorLuminance(color2);
const lighter = Math.max(luminance1, luminance2);
const darker = Math.min(luminance1, luminance2);
return (lighter + 0.05) / (darker + 0.05);
};
// Color luminance calculation
const getColorLuminance = (color) => {
const rgb = hexToRgb(color);
const [r, g, b] = [rgb.r, rgb.g, rgb.b].map(c => {
c = c / 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
};
// Accessibility checker component
function AccessibilityChecker({ foreground, background }) {
const contrast = calculateContrastRatio(foreground, background);
const isAACompliant = contrast >= 4.5;
const isAAACompliant = contrast >= 7;
return (
<div className="accessibility-checker">
<div className="color-preview" style={{ backgroundColor: background, color: foreground }}>
Sample Text
</div>
<div className="contrast-info">
<p>Contrast Ratio: {contrast.toFixed(2)}:1</p>
<p>WCAG AA: {isAACompliant ? '✅ Pass' : '❌ Fail'}</p>
<p>WCAG AAA: {isAAACompliant ? '✅ Pass' : '❌ Fail'}</p>
</div>
</div>
);
}Color Blindness Simulation
// Color blindness simulation
const colorBlindnessTypes = {
protanopia: {
name: 'Protanopia (Red-Blind)',
matrix: [
[0.567, 0.433, 0],
[0.558, 0.442, 0],
[0, 0.242, 0.758]
]
},
deuteranopia: {
name: 'Deuteranopia (Green-Blind)',
matrix: [
[0.625, 0.375, 0],
[0.7, 0.3, 0],
[0, 0.3, 0.7]
]
},
tritanopia: {
name: 'Tritanopia (Blue-Blind)',
matrix: [
[0.95, 0.05, 0],
[0, 0.433, 0.567],
[0, 0.475, 0.525]
]
}
};
// Simulate color blindness
const simulateColorBlindness = (color, type) => {
const rgb = hexToRgb(color);
const matrix = colorBlindnessTypes[type].matrix;
const newR = Math.round(
matrix[0][0] * rgb.r + matrix[0][1] * rgb.g + matrix[0][2] * rgb.b
);
const newG = Math.round(
matrix[1][0] * rgb.r + matrix[1][1] * rgb.g + matrix[1][2] * rgb.b
);
const newB = Math.round(
matrix[2][0] * rgb.r + matrix[2][1] * rgb.g + matrix[2][2] * rgb.b
);
return rgbToHex(newR, newG, newB);
};
// Color blindness simulator component
function ColorBlindnessSimulator({ palette }) {
const [simulationType, setSimulationType] = useState('normal');
const simulatedColors = useMemo(() => {
if (simulationType === 'normal') return palette.colors;
const simulated = {};
Object.entries(palette.colors).forEach(([shade, color]) => {
simulated[shade] = simulateColorBlindness(color, simulationType);
});
return simulated;
}, [palette, simulationType]);
return (
<div className="color-blindness-simulator">
<select value={simulationType} onChange={(e) => setSimulationType(e.target.value)}>
<option value="normal">Normal Vision</option>
<option value="protanopia">Protanopia</option>
<option value="deuteranopia">Deuteranopia</option>
<option value="tritanopia">Tritanopia</option>
</select>
<div className="color-swatch">
{Object.entries(simulatedColors).map(([shade, color]) => (
<div
key={shade}
className="color-sample"
style={{ backgroundColor: color }}
title={`${shade}: ${color}`}
/>
))}
</div>
</div>
);
}Palette Export and Import
Export Formats
// Export palette in different formats
const exportPalette = (palette, format = 'json') => {
switch (format) {
case 'json':
return JSON.stringify(palette, null, 2);
case 'css':
return generateCSSVariables(palette);
case 'scss':
return generateSCSSVariables(palette);
case 'tailwind':
return generateTailwindConfig(palette);
default:
throw new Error(`Unsupported format: ${format}`);
}
};
// Generate CSS variables
const generateCSSVariables = (palette) => {
let css = `/* ${palette.name} Color Palette */\n:root {\n`;
Object.entries(palette.colors).forEach(([shade, color]) => {
css += ` --color-${palette.name.toLowerCase()}-${shade}: ${color};\n`;
});
css += '}';
return css;
};
// Generate Tailwind config
const generateTailwindConfig = (palette) => {
const colors = {};
Object.entries(palette.colors).forEach(([shade, color]) => {
colors[shade] = color;
});
return `module.exports = {
theme: {
extend: {
colors: {
'${palette.name.toLowerCase()}': ${JSON.stringify(colors, null, 2)}
}
}
}
}`;
};Import from Various Sources
// Import palette from different sources
const importPalette = async (source, type) => {
switch (type) {
case 'json':
return importFromJSON(source);
case 'image':
return await extractPaletteFromImage(source);
case 'url':
return await importFromURL(source);
case 'adobe':
return importFromAdobe(source);
default:
throw new Error(`Unsupported import type: ${type}`);
}
};
// Import from Adobe Color
const importFromAdobe = (adobeUrl) => {
// Parse Adobe Color URL and extract colors
const colors = extractColorsFromAdobeURL(adobeUrl);
return {
name: 'Adobe Color Palette',
colors: generateColorScale(colors[0]),
metadata: {
source: 'Adobe Color',
url: adobeUrl,
importedAt: new Date().toISOString()
}
};
};Best Practices
1. Color Selection
- Use semantic color names
- Maintain consistent color scales
- Consider cultural color meanings
- Test colors in different contexts
2. Accessibility
- Ensure sufficient contrast ratios
- Test with color blindness simulators
- Provide alternative color schemes
- Follow WCAG guidelines
3. Organization
- Use descriptive palette names
- Version your palettes
- Document color purposes
- Organize by project or brand
4. Performance
- Cache color calculations
- Optimize color generation
- Use efficient color formats
- Minimize palette complexity
Troubleshooting
Common Issues
Colors not displaying correctly
- Check color format validity
- Verify color space conversion
- Test in different browsers
- Validate color values
Accessibility issues
- Check contrast ratios
- Test with color blindness tools
- Provide alternative colors
- Follow accessibility guidelines
Performance problems
- Optimize color generation
- Cache color calculations
- Use efficient color formats
- Monitor memory usage
Last updated on