@trishchuk/coolors-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +39 -0
- package/.env +2 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +73 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +71 -0
- package/.github/pull_request_template.md +97 -0
- package/.github/workflows/ci.yml +127 -0
- package/.github/workflows/deploy-docs.yml +56 -0
- package/.github/workflows/release.yml +99 -0
- package/.mcp.json +12 -0
- package/.prettierignore +1 -0
- package/CLAUDE.md +201 -0
- package/DOCUMENTATION.md +274 -0
- package/GEMINI.md +54 -0
- package/LICENSE +21 -0
- package/README.md +401 -0
- package/demo/content_based_color.png +0 -0
- package/demo/music-player.html +621 -0
- package/demo/podcast-player.html +903 -0
- package/dist/bin/coolors-mcp.d.ts +1 -0
- package/dist/bin/coolors-mcp.js +154 -0
- package/dist/bin/coolors-mcp.js.map +1 -0
- package/dist/bin/server.d.ts +1 -0
- package/dist/bin/server.js +3292 -0
- package/dist/bin/server.js.map +1 -0
- package/dist/chunk-IQ7NN26V.js +114 -0
- package/dist/chunk-IQ7NN26V.js.map +1 -0
- package/dist/chunk-P3ARRKLS.js +1214 -0
- package/dist/chunk-P3ARRKLS.js.map +1 -0
- package/dist/color/index.d.ts +716 -0
- package/dist/color/index.js +153 -0
- package/dist/color/index.js.map +1 -0
- package/dist/coolors-mcp.d.ts +136 -0
- package/dist/coolors-mcp.js +7 -0
- package/dist/coolors-mcp.js.map +1 -0
- package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js +93 -0
- package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js.map +7 -0
- package/docs/.vitepress/cache/deps/_metadata.json +127 -0
- package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -0
- package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +7 -0
- package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js +12683 -0
- package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js.map +7 -0
- package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js +9719 -0
- package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js.map +7 -0
- package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js +4710 -0
- package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +7 -0
- package/docs/.vitepress/cache/deps/cytoscape.js +30278 -0
- package/docs/.vitepress/cache/deps/cytoscape.js.map +7 -0
- package/docs/.vitepress/cache/deps/dayjs.js +285 -0
- package/docs/.vitepress/cache/deps/dayjs.js.map +7 -0
- package/docs/.vitepress/cache/deps/debug.js +468 -0
- package/docs/.vitepress/cache/deps/debug.js.map +7 -0
- package/docs/.vitepress/cache/deps/package.json +3 -0
- package/docs/.vitepress/cache/deps/prismjs.js +1466 -0
- package/docs/.vitepress/cache/deps/prismjs.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js +228 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js +142 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js +27 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js +65 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js +53 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js +73 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4507 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +584 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1146 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1667 -0
- package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___minisearch.js +1814 -0
- package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
- package/docs/.vitepress/cache/deps/vue.js +344 -0
- package/docs/.vitepress/cache/deps/vue.js.map +7 -0
- package/docs/.vitepress/components/ClientGrid.vue +125 -0
- package/docs/.vitepress/components/CodeBlock.vue +231 -0
- package/docs/.vitepress/components/ConfigModal.vue +477 -0
- package/docs/.vitepress/components/DiagramModal.vue +528 -0
- package/docs/.vitepress/components/TroubleshootingModal.vue +472 -0
- package/docs/.vitepress/config.js +162 -0
- package/docs/.vitepress/theme/FundingLayout.vue +251 -0
- package/docs/.vitepress/theme/Layout.vue +134 -0
- package/docs/.vitepress/theme/components/AdBanner.vue +317 -0
- package/docs/.vitepress/theme/components/AdPlaceholder.vue +78 -0
- package/docs/.vitepress/theme/components/FundingEffects.vue +322 -0
- package/docs/.vitepress/theme/components/FundingHero.vue +345 -0
- package/docs/.vitepress/theme/components/SupportSection.vue +511 -0
- package/docs/.vitepress/theme/custom-app.css +339 -0
- package/docs/.vitepress/theme/custom.css +699 -0
- package/docs/.vitepress/theme/index.js +25 -0
- package/docs/README.md +198 -0
- package/docs/concepts/accessibility.md +473 -0
- package/docs/concepts/color-spaces.md +222 -0
- package/docs/concepts/distance-metrics.md +384 -0
- package/docs/concepts/hct.md +261 -0
- package/docs/concepts/image-analysis.md +396 -0
- package/docs/concepts/material-design.md +306 -0
- package/docs/concepts/theme-matching.md +399 -0
- package/docs/examples/basic-colors.md +490 -0
- package/docs/examples/creating-themes.md +898 -0
- package/docs/examples/css-refactoring.md +824 -0
- package/docs/examples/image-extraction.md +882 -0
- package/docs/getting-started.md +366 -0
- package/docs/index.md +190 -0
- package/docs/installation.md +157 -0
- package/docs/tools/README.md +234 -0
- package/docs/tools/accessibility.md +614 -0
- package/docs/tools/color-operations.md +374 -0
- package/docs/tools/image-extraction.md +624 -0
- package/docs/tools/material-design.md +347 -0
- package/docs/tools/theme-matching.md +552 -0
- package/eslint.config.ts +14 -0
- package/examples/theme-matching.md +113 -0
- package/jsr.json +7 -0
- package/mcp-config.json +8 -0
- package/note.md +35 -0
- package/package.json +122 -0
- package/research_results.md +53 -0
- package/src/bin/coolors-mcp.ts +194 -0
- package/src/bin/server.ts +61 -0
- package/src/color/__tests__/conversions-argb.test.ts +198 -0
- package/src/color/__tests__/extract-colors.test.ts +360 -0
- package/src/color/__tests__/image-utils.test.ts +242 -0
- package/src/color/__tests__/reference-colors.test.ts +278 -0
- package/src/color/__tests__/round-trip.test.ts +197 -0
- package/src/color/conversions.test.ts +402 -0
- package/src/color/conversions.ts +393 -0
- package/src/color/dislike/__tests__/dislike-analyzer.test.ts +248 -0
- package/src/color/dislike/dislike-analyzer.ts +114 -0
- package/src/color/extract-colors.ts +228 -0
- package/src/color/hct/__tests__/hct-class.test.ts +232 -0
- package/src/color/hct/harmonization.ts +204 -0
- package/src/color/hct/hct-class.ts +109 -0
- package/src/color/hct/hct-solver.ts +168 -0
- package/src/color/hct/index.ts +39 -0
- package/src/color/hct/tonal-palette.ts +211 -0
- package/src/color/hct/types.ts +88 -0
- package/src/color/image-utils.ts +79 -0
- package/src/color/index.ts +87 -0
- package/src/color/material-theme.ts +157 -0
- package/src/color/metrics.test.ts +276 -0
- package/src/color/metrics.ts +281 -0
- package/src/color/quantize/__tests__/quantizer_celebi.test.ts +195 -0
- package/src/color/quantize/lab_point_provider.ts +55 -0
- package/src/color/quantize/point_provider.ts +27 -0
- package/src/color/quantize/quantizer_celebi.ts +51 -0
- package/src/color/quantize/quantizer_celebi_test.ts +71 -0
- package/src/color/quantize/quantizer_map.ts +47 -0
- package/src/color/quantize/quantizer_wsmeans.ts +232 -0
- package/src/color/quantize/quantizer_wu.ts +472 -0
- package/src/color/score/__tests__/score.test.ts +224 -0
- package/src/color/score/score.ts +175 -0
- package/src/color/types.ts +151 -0
- package/src/color/utils/color_utils.ts +292 -0
- package/src/color/utils/math_utils.ts +145 -0
- package/src/color/utils.test.ts +403 -0
- package/src/color/utils.ts +315 -0
- package/src/constants.ts +5 -0
- package/src/coolors-mcp.ts +37 -0
- package/src/examples/addition.ts +333 -0
- package/src/examples/color-demo.ts +125 -0
- package/src/examples/custom-logger.ts +201 -0
- package/src/examples/oauth-server.ts +113 -0
- package/src/examples/session-context.ts +269 -0
- package/src/session.ts +116 -0
- package/src/theme/__tests__/matcher.test.ts +180 -0
- package/src/theme/__tests__/parser.test.ts +148 -0
- package/src/theme/__tests__/refactor.test.ts +224 -0
- package/src/theme/index.ts +34 -0
- package/src/theme/matcher.ts +395 -0
- package/src/theme/parser.ts +392 -0
- package/src/theme/refactor.ts +360 -0
- package/src/theme/types.ts +152 -0
- package/src/tools/__tests__/gradient-generator.test.ts +206 -0
- package/src/tools/__tests__/palette-with-locks.test.ts +109 -0
- package/src/tools/color-conversion.tool.ts +54 -0
- package/src/tools/color-distance.tool.ts +41 -0
- package/src/tools/colors.ts +31 -0
- package/src/tools/contrast-checker.tool.ts +37 -0
- package/src/tools/dislike-analyzer.tool.ts +247 -0
- package/src/tools/gradient-generator.tool.ts +250 -0
- package/src/tools/image-extraction.tools.ts +289 -0
- package/src/tools/index.ts +39 -0
- package/src/tools/material-theme.tools.ts +250 -0
- package/src/tools/palette-generator.tool.ts +135 -0
- package/src/tools/palette-with-locks.tool.ts +221 -0
- package/src/tools/registry.ts +142 -0
- package/src/tools/simple-tools.ts +37 -0
- package/src/tools/theme-matching.tools.ts +334 -0
- package/src/types.ts +182 -0
- package/src/utils.ts +22 -0
- package/tsconfig.json +8 -0
- package/vitest.config.js +15 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
# Accessibility Tools
|
|
2
|
+
|
|
3
|
+
Tools for ensuring color choices meet WCAG accessibility standards.
|
|
4
|
+
|
|
5
|
+
## check_contrast
|
|
6
|
+
|
|
7
|
+
Check the contrast ratio between two colors and verify WCAG compliance.
|
|
8
|
+
|
|
9
|
+
### Parameters
|
|
10
|
+
|
|
11
|
+
| Parameter | Type | Required | Description |
|
|
12
|
+
|-----------|------|----------|-------------|
|
|
13
|
+
| `foreground` | string | ✅ | Foreground/text color (hex, rgb, hsl) |
|
|
14
|
+
| `background` | string | ✅ | Background color (hex, rgb, hsl) |
|
|
15
|
+
| `fontSize` | number | ❌ | Font size in pixels (for determining large text) |
|
|
16
|
+
| `fontWeight` | number | ❌ | Font weight (for determining bold text) |
|
|
17
|
+
|
|
18
|
+
### Returns
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
{
|
|
22
|
+
ratio: number; // Contrast ratio (1-21)
|
|
23
|
+
ratioString: string; // Formatted as "X.XX:1"
|
|
24
|
+
passes: {
|
|
25
|
+
AA: {
|
|
26
|
+
normal: boolean; // Passes AA for normal text (4.5:1)
|
|
27
|
+
large: boolean; // Passes AA for large text (3:1)
|
|
28
|
+
nonText: boolean; // Passes AA for UI elements (3:1)
|
|
29
|
+
};
|
|
30
|
+
AAA: {
|
|
31
|
+
normal: boolean; // Passes AAA for normal text (7:1)
|
|
32
|
+
large: boolean; // Passes AAA for large text (4.5:1)
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
recommendation: string; // Human-readable recommendation
|
|
36
|
+
luminance: {
|
|
37
|
+
foreground: number; // Relative luminance (0-1)
|
|
38
|
+
background: number; // Relative luminance (0-1)
|
|
39
|
+
};
|
|
40
|
+
isLargeText: boolean; // Whether text qualifies as large
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Examples
|
|
45
|
+
|
|
46
|
+
#### Basic Contrast Check
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
// Check black text on white background
|
|
50
|
+
{
|
|
51
|
+
"name": "check_contrast",
|
|
52
|
+
"arguments": {
|
|
53
|
+
"foreground": "#000000",
|
|
54
|
+
"background": "#ffffff"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Response
|
|
59
|
+
{
|
|
60
|
+
"ratio": 21,
|
|
61
|
+
"ratioString": "21:1",
|
|
62
|
+
"passes": {
|
|
63
|
+
"AA": {
|
|
64
|
+
"normal": true,
|
|
65
|
+
"large": true,
|
|
66
|
+
"nonText": true
|
|
67
|
+
},
|
|
68
|
+
"AAA": {
|
|
69
|
+
"normal": true,
|
|
70
|
+
"large": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"recommendation": "Excellent contrast - passes all WCAG standards",
|
|
74
|
+
"luminance": {
|
|
75
|
+
"foreground": 0,
|
|
76
|
+
"background": 1
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### With Font Size Context
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// Check specific text size
|
|
85
|
+
{
|
|
86
|
+
"name": "check_contrast",
|
|
87
|
+
"arguments": {
|
|
88
|
+
"foreground": "#6366f1",
|
|
89
|
+
"background": "#ffffff",
|
|
90
|
+
"fontSize": 24, // 24px = large text
|
|
91
|
+
"fontWeight": 400
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Response
|
|
96
|
+
{
|
|
97
|
+
"ratio": 3.03,
|
|
98
|
+
"ratioString": "3.03:1",
|
|
99
|
+
"passes": {
|
|
100
|
+
"AA": {
|
|
101
|
+
"normal": false, // Needs 4.5:1
|
|
102
|
+
"large": true, // Passes 3:1 for large
|
|
103
|
+
"nonText": true
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"isLargeText": true,
|
|
107
|
+
"recommendation": "Passes AA for large text only. Consider darker foreground for normal text."
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Check Multiple Combinations
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
// Test color pairs
|
|
115
|
+
const pairs = [
|
|
116
|
+
{ fg: "#1f2937", bg: "#ffffff" }, // Dark gray on white
|
|
117
|
+
{ fg: "#6366f1", bg: "#f3f4f6" }, // Blue on light gray
|
|
118
|
+
{ fg: "#ffffff", bg: "#dc2626" } // White on red
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
for (const pair of pairs) {
|
|
122
|
+
const result = await checkContrast(pair.fg, pair.bg);
|
|
123
|
+
console.log(`${pair.fg} on ${pair.bg}: ${result.ratioString}`);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Large Text Definition
|
|
128
|
+
|
|
129
|
+
Text is considered "large" when:
|
|
130
|
+
- Font size ≥ 18pt (24px)
|
|
131
|
+
- Font size ≥ 14pt (18.67px) AND bold (weight ≥ 700)
|
|
132
|
+
|
|
133
|
+
### Use Cases
|
|
134
|
+
|
|
135
|
+
- Validating text/background combinations
|
|
136
|
+
- Ensuring button accessibility
|
|
137
|
+
- Checking form field contrast
|
|
138
|
+
- Verifying icon visibility
|
|
139
|
+
|
|
140
|
+
## ensure_contrast
|
|
141
|
+
|
|
142
|
+
Automatically adjust a color to meet contrast requirements.
|
|
143
|
+
|
|
144
|
+
### Parameters
|
|
145
|
+
|
|
146
|
+
| Parameter | Type | Required | Description |
|
|
147
|
+
|-----------|------|----------|-------------|
|
|
148
|
+
| `foreground` | string | ✅ | Color to adjust |
|
|
149
|
+
| `background` | string | ✅ | Background color |
|
|
150
|
+
| `targetRatio` | number | ✅ | Minimum contrast ratio needed |
|
|
151
|
+
| `preferLighter` | boolean | ❌ | Prefer lightening over darkening |
|
|
152
|
+
|
|
153
|
+
### Returns
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
{
|
|
157
|
+
original: {
|
|
158
|
+
color: string;
|
|
159
|
+
ratio: number;
|
|
160
|
+
};
|
|
161
|
+
adjusted: {
|
|
162
|
+
color: string;
|
|
163
|
+
ratio: number;
|
|
164
|
+
};
|
|
165
|
+
adjustmentMade: boolean;
|
|
166
|
+
adjustmentType: 'lightened' | 'darkened' | 'none';
|
|
167
|
+
toneChange: number; // HCT tone difference
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Examples
|
|
172
|
+
|
|
173
|
+
#### Ensure AA Compliance
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
// Make color accessible
|
|
177
|
+
{
|
|
178
|
+
"name": "ensure_contrast",
|
|
179
|
+
"arguments": {
|
|
180
|
+
"foreground": "#8b92f0", // Light blue
|
|
181
|
+
"background": "#ffffff",
|
|
182
|
+
"targetRatio": 4.5 // AA for normal text
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Response
|
|
187
|
+
{
|
|
188
|
+
"original": {
|
|
189
|
+
"color": "#8b92f0",
|
|
190
|
+
"ratio": 2.8
|
|
191
|
+
},
|
|
192
|
+
"adjusted": {
|
|
193
|
+
"color": "#5256c9", // Darkened
|
|
194
|
+
"ratio": 4.51
|
|
195
|
+
},
|
|
196
|
+
"adjustmentMade": true,
|
|
197
|
+
"adjustmentType": "darkened",
|
|
198
|
+
"toneChange": -15
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Ensure AAA Compliance
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
// Maximize readability
|
|
206
|
+
{
|
|
207
|
+
"name": "ensure_contrast",
|
|
208
|
+
"arguments": {
|
|
209
|
+
"foreground": "#6366f1",
|
|
210
|
+
"background": "#ffffff",
|
|
211
|
+
"targetRatio": 7 // AAA standard
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Prefer Lighter Adjustment
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
// For dark backgrounds, prefer lightening
|
|
220
|
+
{
|
|
221
|
+
"name": "ensure_contrast",
|
|
222
|
+
"arguments": {
|
|
223
|
+
"foreground": "#6366f1",
|
|
224
|
+
"background": "#1f2937", // Dark background
|
|
225
|
+
"targetRatio": 4.5,
|
|
226
|
+
"preferLighter": true // Lighten the foreground
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Use Cases
|
|
232
|
+
|
|
233
|
+
- Auto-fixing inaccessible colors
|
|
234
|
+
- Creating accessible variations
|
|
235
|
+
- Generating readable text colors
|
|
236
|
+
- Building high-contrast modes
|
|
237
|
+
|
|
238
|
+
## get_accessible_pairs
|
|
239
|
+
|
|
240
|
+
Generate accessible color pairs for different UI contexts.
|
|
241
|
+
|
|
242
|
+
### Parameters
|
|
243
|
+
|
|
244
|
+
| Parameter | Type | Required | Description |
|
|
245
|
+
|-----------|------|----------|-------------|
|
|
246
|
+
| `baseColor` | string | ✅ | Starting color |
|
|
247
|
+
| `count` | number | ❌ | Number of pairs to generate (default: 5) |
|
|
248
|
+
| `contrastLevels` | string[] | ❌ | Required levels: AA, AAA (default: ['AA']) |
|
|
249
|
+
| `contexts` | string[] | ❌ | UI contexts: text, button, border (default: all) |
|
|
250
|
+
|
|
251
|
+
### Returns
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
{
|
|
255
|
+
baseColor: string;
|
|
256
|
+
pairs: Array<{
|
|
257
|
+
foreground: string;
|
|
258
|
+
background: string;
|
|
259
|
+
contrast: number;
|
|
260
|
+
passes: object;
|
|
261
|
+
context: string;
|
|
262
|
+
recommendation: string;
|
|
263
|
+
}>;
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Examples
|
|
268
|
+
|
|
269
|
+
#### Generate Text Color Pairs
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
// Get accessible text colors
|
|
273
|
+
{
|
|
274
|
+
"name": "get_accessible_pairs",
|
|
275
|
+
"arguments": {
|
|
276
|
+
"baseColor": "#6366f1",
|
|
277
|
+
"count": 5,
|
|
278
|
+
"contexts": ["text"]
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Returns various accessible combinations
|
|
283
|
+
{
|
|
284
|
+
"pairs": [
|
|
285
|
+
{
|
|
286
|
+
"foreground": "#6366f1",
|
|
287
|
+
"background": "#ffffff",
|
|
288
|
+
"contrast": 3.03,
|
|
289
|
+
"context": "text",
|
|
290
|
+
"recommendation": "Use for large text only"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"foreground": "#ffffff",
|
|
294
|
+
"background": "#6366f1",
|
|
295
|
+
"contrast": 3.03,
|
|
296
|
+
"context": "text",
|
|
297
|
+
"recommendation": "Inverted - use for buttons"
|
|
298
|
+
}
|
|
299
|
+
// ... more pairs
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### Generate AAA Pairs
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
// Maximum accessibility
|
|
308
|
+
{
|
|
309
|
+
"name": "get_accessible_pairs",
|
|
310
|
+
"arguments": {
|
|
311
|
+
"baseColor": "#6366f1",
|
|
312
|
+
"contrastLevels": ["AAA"],
|
|
313
|
+
"contexts": ["text", "button"]
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Use Cases
|
|
319
|
+
|
|
320
|
+
- Building accessible color systems
|
|
321
|
+
- Creating theme variations
|
|
322
|
+
- Generating style guides
|
|
323
|
+
- Testing color combinations
|
|
324
|
+
|
|
325
|
+
## validate_palette_accessibility
|
|
326
|
+
|
|
327
|
+
Check all color combinations in a palette for accessibility.
|
|
328
|
+
|
|
329
|
+
### Parameters
|
|
330
|
+
|
|
331
|
+
| Parameter | Type | Required | Description |
|
|
332
|
+
|-----------|------|----------|-------------|
|
|
333
|
+
| `palette` | object | ✅ | Color palette with role names and values |
|
|
334
|
+
| `level` | string | ❌ | WCAG level to test: AA, AAA (default: AA) |
|
|
335
|
+
| `includeReport` | boolean | ❌ | Generate detailed report (default: true) |
|
|
336
|
+
|
|
337
|
+
### Returns
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
{
|
|
341
|
+
valid: boolean; // All combinations pass
|
|
342
|
+
totalCombinations: number;
|
|
343
|
+
passingCombinations: number;
|
|
344
|
+
failingCombinations: number;
|
|
345
|
+
issues: Array<{
|
|
346
|
+
foreground: string;
|
|
347
|
+
foregroundRole: string;
|
|
348
|
+
background: string;
|
|
349
|
+
backgroundRole: string;
|
|
350
|
+
ratio: number;
|
|
351
|
+
required: number;
|
|
352
|
+
suggestion: string;
|
|
353
|
+
}>;
|
|
354
|
+
report?: {
|
|
355
|
+
byRole: object; // Issues grouped by role
|
|
356
|
+
summary: string; // Human-readable summary
|
|
357
|
+
recommendations: string[];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Examples
|
|
363
|
+
|
|
364
|
+
#### Validate Material Theme
|
|
365
|
+
|
|
366
|
+
```javascript
|
|
367
|
+
// Check Material Design theme
|
|
368
|
+
{
|
|
369
|
+
"name": "validate_palette_accessibility",
|
|
370
|
+
"arguments": {
|
|
371
|
+
"palette": {
|
|
372
|
+
"primary": "#6366f1",
|
|
373
|
+
"onPrimary": "#ffffff",
|
|
374
|
+
"primaryContainer": "#e0e0ff",
|
|
375
|
+
"onPrimaryContainer": "#070764",
|
|
376
|
+
"surface": "#fffbff",
|
|
377
|
+
"onSurface": "#1b1b1f"
|
|
378
|
+
},
|
|
379
|
+
"level": "AA"
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Response shows any issues
|
|
384
|
+
{
|
|
385
|
+
"valid": false,
|
|
386
|
+
"totalCombinations": 6,
|
|
387
|
+
"passingCombinations": 5,
|
|
388
|
+
"failingCombinations": 1,
|
|
389
|
+
"issues": [
|
|
390
|
+
{
|
|
391
|
+
"foreground": "#6366f1",
|
|
392
|
+
"foregroundRole": "primary",
|
|
393
|
+
"background": "#e0e0ff",
|
|
394
|
+
"backgroundRole": "primaryContainer",
|
|
395
|
+
"ratio": 2.1,
|
|
396
|
+
"required": 4.5,
|
|
397
|
+
"suggestion": "Darken primary or lighten primaryContainer"
|
|
398
|
+
}
|
|
399
|
+
]
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
#### Validate Custom Palette
|
|
404
|
+
|
|
405
|
+
```javascript
|
|
406
|
+
// Check brand colors
|
|
407
|
+
{
|
|
408
|
+
"name": "validate_palette_accessibility",
|
|
409
|
+
"arguments": {
|
|
410
|
+
"palette": {
|
|
411
|
+
"brand": "#FF5722",
|
|
412
|
+
"text": "#212121",
|
|
413
|
+
"background": "#FFFFFF",
|
|
414
|
+
"accent": "#00BCD4",
|
|
415
|
+
"muted": "#9E9E9E"
|
|
416
|
+
},
|
|
417
|
+
"level": "AAA",
|
|
418
|
+
"includeReport": true
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Use Cases
|
|
424
|
+
|
|
425
|
+
- Validating design systems
|
|
426
|
+
- Testing theme accessibility
|
|
427
|
+
- Generating accessibility reports
|
|
428
|
+
- Finding problem combinations
|
|
429
|
+
|
|
430
|
+
## Best Practices
|
|
431
|
+
|
|
432
|
+
### Contrast Requirements
|
|
433
|
+
|
|
434
|
+
#### Text Content
|
|
435
|
+
| Context | AA Minimum | AAA Minimum |
|
|
436
|
+
|---------|------------|-------------|
|
|
437
|
+
| Normal text | 4.5:1 | 7:1 |
|
|
438
|
+
| Large text | 3:1 | 4.5:1 |
|
|
439
|
+
| Incidental text | No requirement | No requirement |
|
|
440
|
+
| Logotypes | No requirement | No requirement |
|
|
441
|
+
|
|
442
|
+
#### Non-Text Content
|
|
443
|
+
| Context | AA Minimum | Notes |
|
|
444
|
+
|---------|------------|-------|
|
|
445
|
+
| UI components | 3:1 | Active components |
|
|
446
|
+
| Graphics | 3:1 | Essential graphics |
|
|
447
|
+
| Decorative | No requirement | Purely decorative |
|
|
448
|
+
|
|
449
|
+
### Testing Strategy
|
|
450
|
+
|
|
451
|
+
```javascript
|
|
452
|
+
// Comprehensive testing
|
|
453
|
+
async function testAccessibility(theme) {
|
|
454
|
+
// 1. Check individual pairs
|
|
455
|
+
const criticalPairs = [
|
|
456
|
+
{ fg: theme.text, bg: theme.background },
|
|
457
|
+
{ fg: theme.primary, bg: theme.surface }
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
for (const pair of criticalPairs) {
|
|
461
|
+
const result = await checkContrast(pair.fg, pair.bg);
|
|
462
|
+
if (result.ratio < 4.5) {
|
|
463
|
+
console.warn(`Low contrast: ${pair.fg} on ${pair.bg}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// 2. Validate entire palette
|
|
468
|
+
const validation = await validatePalette(theme);
|
|
469
|
+
if (!validation.valid) {
|
|
470
|
+
console.error('Palette has accessibility issues:', validation.issues);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// 3. Generate report
|
|
474
|
+
return validation.report;
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Automatic Fixing
|
|
479
|
+
|
|
480
|
+
```javascript
|
|
481
|
+
// Fix accessibility issues automatically
|
|
482
|
+
async function fixAccessibility(theme) {
|
|
483
|
+
const fixed = { ...theme };
|
|
484
|
+
|
|
485
|
+
// Ensure all text meets AA
|
|
486
|
+
fixed.text = await ensureContrast({
|
|
487
|
+
foreground: theme.text,
|
|
488
|
+
background: theme.background,
|
|
489
|
+
targetRatio: 4.5
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// Ensure buttons are accessible
|
|
493
|
+
fixed.buttonText = await ensureContrast({
|
|
494
|
+
foreground: theme.buttonText,
|
|
495
|
+
background: theme.buttonBg,
|
|
496
|
+
targetRatio: 4.5
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
return fixed;
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Common Patterns
|
|
504
|
+
|
|
505
|
+
### Dark Mode Accessibility
|
|
506
|
+
|
|
507
|
+
```javascript
|
|
508
|
+
// Ensure dark mode meets standards
|
|
509
|
+
function validateDarkMode(darkTheme) {
|
|
510
|
+
const checks = [
|
|
511
|
+
// Primary text should be readable
|
|
512
|
+
{
|
|
513
|
+
fg: darkTheme.text,
|
|
514
|
+
bg: darkTheme.background,
|
|
515
|
+
min: 4.5
|
|
516
|
+
},
|
|
517
|
+
// But not too harsh (avoid pure white on black)
|
|
518
|
+
{
|
|
519
|
+
fg: darkTheme.text,
|
|
520
|
+
bg: darkTheme.background,
|
|
521
|
+
max: 18 // Avoid excessive contrast
|
|
522
|
+
}
|
|
523
|
+
];
|
|
524
|
+
|
|
525
|
+
return checks.every(check => {
|
|
526
|
+
const ratio = getContrast(check.fg, check.bg);
|
|
527
|
+
return ratio >= (check.min || 0) && ratio <= (check.max || 21);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Progressive Enhancement
|
|
533
|
+
|
|
534
|
+
```javascript
|
|
535
|
+
// Provide multiple contrast levels
|
|
536
|
+
function generateContrastLevels(baseTheme) {
|
|
537
|
+
return {
|
|
538
|
+
// Default - AA compliance
|
|
539
|
+
default: baseTheme,
|
|
540
|
+
|
|
541
|
+
// Medium - Enhanced readability
|
|
542
|
+
medium: adjustContrast(baseTheme, 1.2),
|
|
543
|
+
|
|
544
|
+
// High - AAA compliance
|
|
545
|
+
high: adjustContrast(baseTheme, 1.5),
|
|
546
|
+
|
|
547
|
+
// Maximum - For accessibility needs
|
|
548
|
+
maximum: {
|
|
549
|
+
text: '#000000',
|
|
550
|
+
background: '#ffffff',
|
|
551
|
+
primary: '#0000ff'
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Form Validation
|
|
558
|
+
|
|
559
|
+
```javascript
|
|
560
|
+
// Ensure form fields are accessible
|
|
561
|
+
function validateFormColors(formTheme) {
|
|
562
|
+
const requirements = {
|
|
563
|
+
// Input fields need clear boundaries
|
|
564
|
+
inputBorder: { min: 3.0, against: formTheme.inputBg },
|
|
565
|
+
|
|
566
|
+
// Error text must be readable
|
|
567
|
+
errorText: { min: 4.5, against: formTheme.background },
|
|
568
|
+
|
|
569
|
+
// Focus indicators must be visible
|
|
570
|
+
focusRing: { min: 3.0, against: formTheme.inputBg },
|
|
571
|
+
|
|
572
|
+
// Placeholder text (relaxed requirement)
|
|
573
|
+
placeholder: { min: 3.0, against: formTheme.inputBg }
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
return Object.entries(requirements).map(([element, req]) => ({
|
|
577
|
+
element,
|
|
578
|
+
passes: checkContrast(formTheme[element], req.against).ratio >= req.min
|
|
579
|
+
}));
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
## Troubleshooting
|
|
584
|
+
|
|
585
|
+
### Low Contrast Issues
|
|
586
|
+
|
|
587
|
+
**Problem**: Colors don't meet minimum contrast
|
|
588
|
+
**Solution**:
|
|
589
|
+
- Use `ensure_contrast` to auto-adjust
|
|
590
|
+
- Increase tone difference between colors
|
|
591
|
+
- Consider different color roles
|
|
592
|
+
|
|
593
|
+
### Harsh Contrast
|
|
594
|
+
|
|
595
|
+
**Problem**: Maximum contrast (21:1) is too harsh
|
|
596
|
+
**Solution**:
|
|
597
|
+
- Use off-white (#fafafa) instead of pure white
|
|
598
|
+
- Use very dark gray (#0a0a0a) instead of pure black
|
|
599
|
+
- Aim for 12-15:1 for comfortable reading
|
|
600
|
+
|
|
601
|
+
### Color Meaning Lost
|
|
602
|
+
|
|
603
|
+
**Problem**: Adjusted colors lose brand identity
|
|
604
|
+
**Solution**:
|
|
605
|
+
- Adjust the background instead of foreground
|
|
606
|
+
- Use borders or icons to supplement color
|
|
607
|
+
- Provide high-contrast mode as option
|
|
608
|
+
|
|
609
|
+
## See Also
|
|
610
|
+
|
|
611
|
+
- [Accessibility Concepts](../concepts/accessibility.md) - WCAG standards explained
|
|
612
|
+
- [HCT System](../concepts/hct.md) - Tone-based contrast
|
|
613
|
+
- [Color Operations](./color-operations.md) - Color manipulation
|
|
614
|
+
- [Material Design Tools](./material-design.md) - Accessible themes
|