alouette
Version:
A modern, customizable design system built on top of NativeWind v5 with configurable defaults
86 lines (73 loc) • 2.25 kB
text/typescript
/**
* Calculates relative luminance of a color
* Based on WCAG 2.0 formula: https://www.w3.org/TR/WCAG20/#relativeluminancedef
*/
const getLuminance = (r: number, g: number, b: number) => {
const values: [number, number, number] = [r, g, b].map((c) => {
const s = c / 255;
return s <= 0.039_28 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
}) as [number, number, number];
return 0.2126 * values[0] + 0.7152 * values[1] + 0.0722 * values[2];
};
/**
* Converts hex color to RGB values
*/
const hexToRgb = (hex: string) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (!result?.[1] || !result[2] || !result[3]) return null;
return {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
};
};
/**
* Calculates contrast ratio between two colors
* Returns ratio between 1 and 21
*/
export const getContrastRatio = (color1: string, color2: string) => {
const rgb1 = hexToRgb(color1);
const rgb2 = hexToRgb(color2);
if (!rgb1 || !rgb2) return 0;
const l1 = getLuminance(rgb1.r, rgb1.g, rgb1.b);
const l2 = getLuminance(rgb2.r, rgb2.g, rgb2.b);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
};
/**
* Checks if contrast ratio meets WCAG requirements
*/
export const checkContrast = (
foreground: string,
background: string,
level: "AA" | "AAA" = "AA",
) => {
const ratio = getContrastRatio(foreground, background);
const minimumRatio = level === "AA" ? 4.5 : 7;
return {
ratio,
passes: ratio >= minimumRatio,
minimumRatio,
};
};
/**
* Development warning for contrast issues
*/
export const warnOnContrastIssues = (
themeName: string,
textColor: string,
backgroundColor: string,
) => {
if (process.env.NODE_ENV !== "development") return;
const result = checkContrast(textColor, backgroundColor);
if (!result.passes) {
console.warn(
`[Alouette] Contrast warning in theme "${themeName}":
Text color ${textColor} on background ${backgroundColor}
has contrast ratio of ${result.ratio.toFixed(2)}.
Minimum required: ${result.minimumRatio}
This may cause accessibility issues.`,
);
}
};