0
# Accessibility and Readability
1
2
Static methods for calculating color contrast ratios and determining readability according to WCAG (Web Content Accessibility Guidelines). These functions help ensure your color choices meet accessibility standards for users with visual impairments.
3
4
## Capabilities
5
6
### Contrast Ratio Calculation
7
8
#### Calculate Readability
9
10
Calculates the contrast ratio between two colors using WCAG formulas.
11
12
```javascript { .api }
13
/**
14
* Calculates the contrast ratio between two colors
15
* Uses WCAG formula: (L1 + 0.05) / (L2 + 0.05) where L1 is lighter, L2 is darker
16
* @param color1 - First color (any valid tinycolor input)
17
* @param color2 - Second color (any valid tinycolor input)
18
* @returns number representing contrast ratio (1-21, where 21 is maximum contrast)
19
*/
20
tinycolor.readability(color1: any, color2: any): number;
21
```
22
23
**Usage Examples:**
24
25
```javascript
26
import tinycolor from "tinycolor2";
27
28
// Calculate contrast ratios
29
const whiteBlack = tinycolor.readability("white", "black");
30
const whiteGray = tinycolor.readability("#ffffff", "#808080");
31
const blueWhite = tinycolor.readability("#0066cc", "white");
32
33
console.log("White/Black contrast:", whiteBlack); // 21 (maximum contrast)
34
console.log("White/Gray contrast:", whiteGray); // ~2.28
35
console.log("Blue/White contrast:", blueWhite); // ~4.89
36
37
// Check various background/text combinations
38
const backgrounds = ["#ffffff", "#f8f9fa", "#e9ecef", "#343a40", "#000000"];
39
const textColors = ["#000000", "#495057", "#6c757d", "#ffffff"];
40
41
backgrounds.forEach(bg => {
42
console.log(`Background: ${bg}`);
43
textColors.forEach(text => {
44
const ratio = tinycolor.readability(bg, text);
45
console.log(` Text ${text}: ${ratio.toFixed(2)}`);
46
});
47
});
48
```
49
50
### WCAG Compliance Testing
51
52
#### Test Readability Standards
53
54
Tests if two colors meet WCAG readability guidelines.
55
56
```javascript { .api }
57
/**
58
* Tests if color combination meets WCAG guidelines
59
* @param color1 - First color (any valid tinycolor input)
60
* @param color2 - Second color (any valid tinycolor input)
61
* @param wcag2 - WCAG parameters object (optional)
62
* @returns boolean indicating if combination meets standards
63
*/
64
tinycolor.isReadable(color1: any, color2: any, wcag2?: {level?: "AA" | "AAA", size?: "small" | "large"}): boolean;
65
```
66
67
**WCAG Standards:**
68
- **AA Large Text**: Contrast ratio ≥ 3:1
69
- **AA Normal Text**: Contrast ratio ≥ 4.5:1
70
- **AAA Large Text**: Contrast ratio ≥ 4.5:1
71
- **AAA Normal Text**: Contrast ratio ≥ 7:1
72
73
**Usage Examples:**
74
75
```javascript
76
// Test with default settings (AA level, small text)
77
const isReadable1 = tinycolor.isReadable("#000000", "#ffffff");
78
console.log("Black on white:", isReadable1); // true
79
80
const isReadable2 = tinycolor.isReadable("#777777", "#ffffff");
81
console.log("Gray on white:", isReadable2); // false (insufficient contrast)
82
83
// Test with specific WCAG levels
84
const combinations = [
85
{ bg: "#ffffff", text: "#000000" },
86
{ bg: "#ffffff", text: "#666666" },
87
{ bg: "#ffffff", text: "#999999" },
88
{ bg: "#2c3e50", text: "#ffffff" },
89
{ bg: "#3498db", text: "#ffffff" }
90
];
91
92
combinations.forEach(combo => {
93
const aaSmall = tinycolor.isReadable(combo.bg, combo.text, {level: "AA", size: "small"});
94
const aaLarge = tinycolor.isReadable(combo.bg, combo.text, {level: "AA", size: "large"});
95
const aaaSmall = tinycolor.isReadable(combo.bg, combo.text, {level: "AAA", size: "small"});
96
const aaaLarge = tinycolor.isReadable(combo.bg, combo.text, {level: "AAA", size: "large"});
97
98
console.log(`${combo.text} on ${combo.bg}:`);
99
console.log(` AA Small: ${aaSmall}, AA Large: ${aaLarge}`);
100
console.log(` AAA Small: ${aaaSmall}, AAA Large: ${aaaLarge}`);
101
});
102
103
// Validate user interface colors
104
function validateUIColors(backgroundColor, textColor, level = "AA") {
105
const tests = {
106
smallText: tinycolor.isReadable(backgroundColor, textColor, {level, size: "small"}),
107
largeText: tinycolor.isReadable(backgroundColor, textColor, {level, size: "large"}),
108
ratio: tinycolor.readability(backgroundColor, textColor)
109
};
110
111
return {
112
passes: tests.smallText,
113
passesLarge: tests.largeText,
114
ratio: tests.ratio,
115
level: level
116
};
117
}
118
```
119
120
### Find Most Readable Color
121
122
#### Find Optimal Text Color
123
124
Finds the most readable color from a list of options for a given background.
125
126
```javascript { .api }
127
/**
128
* Finds the most readable color from a list for the given background
129
* @param baseColor - Background color (any valid tinycolor input)
130
* @param colorList - Array of potential text colors
131
* @param args - Optional arguments object
132
* @returns tinycolor instance of the most readable color
133
*/
134
tinycolor.mostReadable(baseColor: any, colorList: any[], args?: {includeFallbackColors?: boolean, level?: "AA" | "AAA", size?: "small" | "large"}): tinycolor;
135
```
136
137
**Usage Examples:**
138
139
```javascript
140
// Find best text color for various backgrounds
141
const textOptions = ["#000000", "#333333", "#666666", "#999999", "#ffffff"];
142
143
const darkBg = "#2c3e50";
144
const lightBg = "#ecf0f1";
145
const colorfulBg = "#e74c3c";
146
147
const bestForDark = tinycolor.mostReadable(darkBg, textOptions);
148
const bestForLight = tinycolor.mostReadable(lightBg, textOptions);
149
const bestForColorful = tinycolor.mostReadable(colorfulBg, textOptions);
150
151
console.log("Best text for dark background:", bestForDark.toHexString());
152
console.log("Best text for light background:", bestForLight.toHexString());
153
console.log("Best text for colorful background:", bestForColorful.toHexString());
154
155
// With WCAG level requirements
156
const bestAAA = tinycolor.mostReadable("#3498db", ["#ffffff", "#000000"], {
157
level: "AAA",
158
size: "small"
159
});
160
161
// With fallback colors (includes black and white if needed)
162
const bestWithFallback = tinycolor.mostReadable("#ff6b6b", ["#ffcc02", "#06ffa5"], {
163
includeFallbackColors: true,
164
level: "AA"
165
});
166
167
// Smart text color selection
168
function getOptimalTextColor(backgroundColor, preferredColors = ["#000000", "#ffffff"]) {
169
const bgColor = tinycolor(backgroundColor);
170
171
// Start with preferred colors
172
let bestColor = tinycolor.mostReadable(bgColor, preferredColors);
173
174
// If no preferred color works, expand options
175
if (!tinycolor.isReadable(bgColor, bestColor)) {
176
const expandedOptions = [
177
"#000000", "#333333", "#666666", "#999999", "#cccccc", "#ffffff"
178
];
179
bestColor = tinycolor.mostReadable(bgColor, expandedOptions, {
180
includeFallbackColors: true
181
});
182
}
183
184
return bestColor.toHexString();
185
}
186
```
187
188
## Accessibility Workflows
189
190
### Comprehensive Accessibility Analysis
191
192
```javascript
193
function analyzeColorAccessibility(backgroundColor, textColor) {
194
const bgColor = tinycolor(backgroundColor);
195
const txtColor = tinycolor(textColor);
196
197
if (!bgColor.isValid() || !txtColor.isValid()) {
198
return { error: "Invalid color input" };
199
}
200
201
const ratio = tinycolor.readability(bgColor, txtColor);
202
203
const compliance = {
204
"AA-small": ratio >= 4.5,
205
"AA-large": ratio >= 3.0,
206
"AAA-small": ratio >= 7.0,
207
"AAA-large": ratio >= 4.5
208
};
209
210
const suggestions = [];
211
212
if (!compliance["AA-small"]) {
213
// Suggest better alternatives
214
const alternatives = ["#000000", "#ffffff"];
215
const better = tinycolor.mostReadable(bgColor, alternatives);
216
suggestions.push(`Try ${better.toHexString()} for better contrast`);
217
}
218
219
return {
220
ratio: ratio,
221
compliance: compliance,
222
suggestions: suggestions,
223
grade: compliance["AAA-small"] ? "AAA" :
224
compliance["AA-small"] ? "AA" :
225
compliance["AA-large"] ? "AA (large text only)" : "Fail"
226
};
227
}
228
229
// Usage
230
const analysis = analyzeColorAccessibility("#3498db", "#ffffff");
231
console.log(analysis);
232
```
233
234
### Design System Validation
235
236
```javascript
237
function validateDesignSystem(colorPalette) {
238
const results = {};
239
240
// Test all background/text combinations
241
Object.keys(colorPalette.backgrounds || {}).forEach(bgName => {
242
const bgColor = colorPalette.backgrounds[bgName];
243
results[bgName] = {};
244
245
Object.keys(colorPalette.text || {}).forEach(textName => {
246
const textColor = colorPalette.text[textName];
247
const ratio = tinycolor.readability(bgColor, textColor);
248
const isAA = tinycolor.isReadable(bgColor, textColor);
249
const isAAA = tinycolor.isReadable(bgColor, textColor, {level: "AAA"});
250
251
results[bgName][textName] = {
252
ratio: ratio,
253
AA: isAA,
254
AAA: isAAA,
255
status: isAAA ? "AAA" : isAA ? "AA" : "FAIL"
256
};
257
});
258
});
259
260
return results;
261
}
262
263
// Example color palette
264
const myPalette = {
265
backgrounds: {
266
primary: "#2c3e50",
267
secondary: "#3498db",
268
light: "#ecf0f1",
269
white: "#ffffff"
270
},
271
text: {
272
primary: "#2c3e50",
273
secondary: "#7f8c8d",
274
light: "#bdc3c7",
275
white: "#ffffff"
276
}
277
};
278
279
const validation = validateDesignSystem(myPalette);
280
```
281
282
### Automatic Color Adjustment
283
284
```javascript
285
function makeReadable(backgroundColor, textColor, targetLevel = "AA", targetSize = "small") {
286
const bgColor = tinycolor(backgroundColor);
287
let txtColor = tinycolor(textColor);
288
289
// If already readable, return as-is
290
if (tinycolor.isReadable(bgColor, txtColor, {level: targetLevel, size: targetSize})) {
291
return txtColor.toHexString();
292
}
293
294
// Try to adjust the text color to meet requirements
295
const bgLuminance = bgColor.getLuminance();
296
const shouldBeDark = bgLuminance > 0.5;
297
298
// Start with pure black or white based on background
299
const startColor = shouldBeDark ? "#000000" : "#ffffff";
300
let adjustedColor = tinycolor(startColor);
301
302
// If pure black/white doesn't work, gradually adjust towards original color
303
if (!tinycolor.isReadable(bgColor, adjustedColor, {level: targetLevel, size: targetSize})) {
304
// This is rare, but handle edge cases
305
const alternatives = ["#000000", "#ffffff", "#333333", "#cccccc"];
306
adjustedColor = tinycolor.mostReadable(bgColor, alternatives, {
307
includeFallbackColors: true,
308
level: targetLevel,
309
size: targetSize
310
});
311
}
312
313
return adjustedColor.toHexString();
314
}
315
316
// Usage
317
const readableText = makeReadable("#ff6b6b", "#ffcc02", "AA", "small");
318
console.log("Adjusted text color:", readableText);
319
```
320
321
### Color Blindness Considerations
322
323
```javascript
324
function checkColorBlindAccessibility(color1, color2) {
325
const c1 = tinycolor(color1);
326
const c2 = tinycolor(color2);
327
328
// Convert to different color spaces for analysis
329
const rgb1 = c1.toRgb();
330
const rgb2 = c2.toRgb();
331
332
// Simulate common color blindness types (simplified)
333
const protanopia = {
334
// Red-blind simulation (simplified)
335
color1: tinycolor({r: rgb1.g, g: rgb1.g, b: rgb1.b}),
336
color2: tinycolor({r: rgb2.g, g: rgb2.g, b: rgb2.b})
337
};
338
339
const deuteranopia = {
340
// Green-blind simulation (simplified)
341
color1: tinycolor({r: rgb1.r, g: rgb1.r, b: rgb1.b}),
342
color2: tinycolor({r: rgb2.r, g: rgb2.r, b: rgb2.b})
343
};
344
345
return {
346
normal: tinycolor.readability(c1, c2),
347
protanopia: tinycolor.readability(protanopia.color1, protanopia.color2),
348
deuteranopia: tinycolor.readability(deuteranopia.color1, deuteranopia.color2),
349
// Add tritanopia and other types as needed
350
};
351
}
352
```