@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,261 @@
|
|
|
1
|
+
# HCT Color System
|
|
2
|
+
|
|
3
|
+
HCT (Hue, Chroma, Tone) is Google's perceptually uniform color space, specifically designed for user interface applications. It forms the foundation of Material Design 3's dynamic color system.
|
|
4
|
+
|
|
5
|
+
## What is HCT?
|
|
6
|
+
|
|
7
|
+
HCT is a color space that combines the best aspects of LAB (perceptual uniformity) with practical considerations for UI design:
|
|
8
|
+
|
|
9
|
+
- **Hue** (0-360°): The color's position on the color wheel
|
|
10
|
+
- **Chroma** (0-120+): The color's intensity or purity
|
|
11
|
+
- **Tone** (0-100): The color's perceptual lightness
|
|
12
|
+
|
|
13
|
+
## Why HCT?
|
|
14
|
+
|
|
15
|
+
### Problem with Traditional Color Spaces
|
|
16
|
+
|
|
17
|
+
Traditional color spaces have significant limitations for UI design:
|
|
18
|
+
|
|
19
|
+
1. **RGB**: Not perceptually uniform; equal numeric changes don't produce equal visual changes
|
|
20
|
+
2. **HSL**: Lightness is not perceptually accurate; colors with same L can appear very different
|
|
21
|
+
3. **LAB**: While perceptually uniform, it's not optimized for UI contrast requirements
|
|
22
|
+
|
|
23
|
+
### HCT's Solutions
|
|
24
|
+
|
|
25
|
+
HCT addresses these issues with UI-specific optimizations:
|
|
26
|
+
|
|
27
|
+
1. **Predictable Contrast**: Tone values directly correlate with WCAG contrast ratios
|
|
28
|
+
2. **Perceptual Uniformity**: Equal changes produce equal perceptual differences
|
|
29
|
+
3. **No Impossible Colors**: All HCT values represent real, displayable colors
|
|
30
|
+
4. **Chroma Preservation**: Maintains color intensity across tone changes better than HSL
|
|
31
|
+
|
|
32
|
+
## Tone and Contrast
|
|
33
|
+
|
|
34
|
+
The most powerful feature of HCT is the predictable relationship between tone values and contrast ratios:
|
|
35
|
+
|
|
36
|
+
### Contrast Guarantees
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Tone Difference | Approximate Contrast Ratio
|
|
40
|
+
----------------|---------------------------
|
|
41
|
+
40 | 3:1 (WCAG AA large text)
|
|
42
|
+
50 | 4.5:1 (WCAG AA normal text)
|
|
43
|
+
70 | 7:1 (WCAG AAA)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Example: Accessible Color Pairs
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
// Base color
|
|
50
|
+
const primary = { h: 265, c: 50, t: 50 };
|
|
51
|
+
|
|
52
|
+
// Guaranteed accessible combinations
|
|
53
|
+
const onPrimary = { h: 265, c: 50, t: 100 }; // t: 50 → 100 = 50 diff = 4.5:1
|
|
54
|
+
const primaryContainer = { h: 265, c: 25, t: 90 }; // Lower chroma, high tone
|
|
55
|
+
const onPrimaryContainer = { h: 265, c: 50, t: 10 }; // t: 90 → 10 = 80 diff = 7:1+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Material Design Integration
|
|
59
|
+
|
|
60
|
+
HCT is the foundation of Material Design 3's color system:
|
|
61
|
+
|
|
62
|
+
### Tonal Palettes
|
|
63
|
+
|
|
64
|
+
Material Design uses 13 standard tones:
|
|
65
|
+
```
|
|
66
|
+
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Each tone has specific use cases:
|
|
70
|
+
- **0-10**: On-colors for light surfaces
|
|
71
|
+
- **20-40**: Accent and emphasis colors
|
|
72
|
+
- **50**: Medium, often primary color
|
|
73
|
+
- **60-80**: Containers and surfaces
|
|
74
|
+
- **90-100**: Backgrounds and on-colors for dark surfaces
|
|
75
|
+
|
|
76
|
+
### Color Roles
|
|
77
|
+
|
|
78
|
+
HCT enables semantic color roles with guaranteed contrast:
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
// Primary color family
|
|
82
|
+
primary: tone(40) // Main brand color
|
|
83
|
+
onPrimary: tone(100) // Text on primary
|
|
84
|
+
primaryContainer: tone(90) // Light container
|
|
85
|
+
onPrimaryContainer: tone(10) // Text on container
|
|
86
|
+
|
|
87
|
+
// Surface colors
|
|
88
|
+
surface: tone(99) // Main background
|
|
89
|
+
onSurface: tone(10) // Text on surface
|
|
90
|
+
surfaceVariant: tone(95) // Secondary background
|
|
91
|
+
onSurfaceVariant: tone(30) // Secondary text
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Chroma Behavior
|
|
95
|
+
|
|
96
|
+
Chroma in HCT represents color intensity, but unlike saturation in HSL, it maintains perceptual consistency:
|
|
97
|
+
|
|
98
|
+
### Chroma Ranges
|
|
99
|
+
|
|
100
|
+
- **0-20**: Neutral, grayscale colors
|
|
101
|
+
- **20-40**: Muted, subtle colors
|
|
102
|
+
- **40-60**: Moderate intensity (recommended for UI)
|
|
103
|
+
- **60-80**: Vibrant colors
|
|
104
|
+
- **80+**: Very vibrant (use sparingly)
|
|
105
|
+
|
|
106
|
+
### Chroma and Tone Interaction
|
|
107
|
+
|
|
108
|
+
Maximum achievable chroma varies by tone:
|
|
109
|
+
```
|
|
110
|
+
Tone 0-10: Low max chroma (dark colors can't be very colorful)
|
|
111
|
+
Tone 40-60: Highest max chroma (mid-tones most colorful)
|
|
112
|
+
Tone 90-100: Low max chroma (light colors can't be very colorful)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Practical Applications
|
|
116
|
+
|
|
117
|
+
### 1. Theme Generation
|
|
118
|
+
|
|
119
|
+
Generate a complete theme from a single color:
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
function generateTheme(sourceColor) {
|
|
123
|
+
const hct = toHct(sourceColor);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
primary: { ...hct, t: 40 },
|
|
127
|
+
secondary: { h: hct.h + 60, c: hct.c * 0.5, t: 40 },
|
|
128
|
+
tertiary: { h: hct.h + 120, c: hct.c * 0.7, t: 40 },
|
|
129
|
+
error: { h: 25, c: 84, t: 40 },
|
|
130
|
+
neutral: { h: hct.h, c: 4, t: 50 },
|
|
131
|
+
neutralVariant: { h: hct.h, c: 8, t: 50 }
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 2. Accessible Color Variations
|
|
137
|
+
|
|
138
|
+
Create accessible color variations:
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
function createAccessiblePair(baseHct, contrastRatio = 4.5) {
|
|
142
|
+
const toneDiff = contrastRatio >= 7 ? 70 :
|
|
143
|
+
contrastRatio >= 4.5 ? 50 : 40;
|
|
144
|
+
|
|
145
|
+
const lighter = { ...baseHct, t: Math.min(100, baseHct.t + toneDiff) };
|
|
146
|
+
const darker = { ...baseHct, t: Math.max(0, baseHct.t - toneDiff) };
|
|
147
|
+
|
|
148
|
+
return baseHct.t > 50 ? darker : lighter;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 3. Color Harmonization
|
|
153
|
+
|
|
154
|
+
Harmonize colors to work together:
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
function harmonize(color1Hct, color2Hct) {
|
|
158
|
+
// Adjust hues to be more harmonious
|
|
159
|
+
const hueDiff = Math.abs(color2Hct.h - color1Hct.h);
|
|
160
|
+
|
|
161
|
+
if (hueDiff > 180) {
|
|
162
|
+
// Colors are opposite, increase harmony
|
|
163
|
+
const targetHue = color1Hct.h + (hueDiff > 270 ? 60 : -60);
|
|
164
|
+
return { ...color2Hct, h: targetHue };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return color2Hct;
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## HCT vs Other Color Spaces
|
|
172
|
+
|
|
173
|
+
### HCT vs LAB
|
|
174
|
+
|
|
175
|
+
| Aspect | HCT | LAB |
|
|
176
|
+
|--------|-----|-----|
|
|
177
|
+
| Perceptual Uniformity | ✅ Optimized for UI | ✅ General purpose |
|
|
178
|
+
| Contrast Prediction | ✅ Direct tone mapping | ❌ Requires calculation |
|
|
179
|
+
| UI Optimization | ✅ Designed for screens | ❌ Designed for all media |
|
|
180
|
+
| Impossible Colors | ❌ None | ✅ Can represent |
|
|
181
|
+
|
|
182
|
+
### HCT vs HSL
|
|
183
|
+
|
|
184
|
+
| Aspect | HCT | HSL |
|
|
185
|
+
|--------|-----|-----|
|
|
186
|
+
| Perceptual Uniformity | ✅ Yes | ❌ No |
|
|
187
|
+
| Lightness Accuracy | ✅ Perceptual | ❌ Mathematical |
|
|
188
|
+
| Contrast Prediction | ✅ Built-in | ❌ Requires calculation |
|
|
189
|
+
| Designer Familiarity | 🔶 Learning curve | ✅ Well known |
|
|
190
|
+
|
|
191
|
+
## Advanced Concepts
|
|
192
|
+
|
|
193
|
+
### CAM16 Foundation
|
|
194
|
+
|
|
195
|
+
HCT is built on the CAM16 color appearance model, which models how humans perceive color under different viewing conditions.
|
|
196
|
+
|
|
197
|
+
### Viewing Conditions
|
|
198
|
+
|
|
199
|
+
HCT assumes standard viewing conditions:
|
|
200
|
+
- Average surround
|
|
201
|
+
- Adapting luminance of 200 cd/m²
|
|
202
|
+
- 20% background luminance
|
|
203
|
+
- D65 white point
|
|
204
|
+
|
|
205
|
+
### Gamut Mapping
|
|
206
|
+
|
|
207
|
+
HCT automatically maps colors to the sRGB gamut, ensuring all colors are displayable:
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
// HCT handles gamut mapping internally
|
|
211
|
+
const vibrantHct = { h: 120, c: 200, t: 50 }; // Very high chroma
|
|
212
|
+
const displayable = hctToRgb(vibrantHct); // Automatically in gamut
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Best Practices
|
|
216
|
+
|
|
217
|
+
1. **Use standard tones** for consistency with Material Design
|
|
218
|
+
2. **Maintain 50+ tone difference** for text on backgrounds
|
|
219
|
+
3. **Keep chroma between 40-60** for most UI elements
|
|
220
|
+
4. **Use lower chroma (4-16)** for neutral colors
|
|
221
|
+
5. **Test with different tones** to ensure accessibility
|
|
222
|
+
|
|
223
|
+
## Common Patterns
|
|
224
|
+
|
|
225
|
+
### Dark/Light Theme Switching
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
// Light theme
|
|
229
|
+
const lightPrimary = { h: 265, c: 50, t: 40 };
|
|
230
|
+
const lightSurface = { h: 265, c: 0, t: 99 };
|
|
231
|
+
|
|
232
|
+
// Dark theme (invert tones)
|
|
233
|
+
const darkPrimary = { h: 265, c: 50, t: 80 };
|
|
234
|
+
const darkSurface = { h: 265, c: 0, t: 10 };
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Error States
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
// Standard error colors in HCT
|
|
241
|
+
const error = { h: 25, c: 84, t: 40 }; // Red-orange, high chroma
|
|
242
|
+
const errorContainer = { h: 25, c: 30, t: 90 }; // Muted, light
|
|
243
|
+
const onError = { h: 25, c: 0, t: 100 }; // White
|
|
244
|
+
const onErrorContainer = { h: 25, c: 84, t: 10 }; // Dark red
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Implementation Details
|
|
248
|
+
|
|
249
|
+
Coolors MCP implements HCT using Google's Material Color Utilities algorithms:
|
|
250
|
+
|
|
251
|
+
1. **Color Conversion**: Accurate CAM16 to/from RGB conversion
|
|
252
|
+
2. **Gamut Mapping**: Automatic sRGB gamut clipping
|
|
253
|
+
3. **Tone Mapping**: Precise tone to luminance conversion
|
|
254
|
+
4. **Chroma Maximization**: Finding maximum chroma for any hue/tone
|
|
255
|
+
|
|
256
|
+
## See Also
|
|
257
|
+
|
|
258
|
+
- [Color Spaces](./color-spaces.md) - Comparison with other color models
|
|
259
|
+
- [Material Design](./material-design.md) - HCT in Material Design 3
|
|
260
|
+
- [Accessibility](./accessibility.md) - Using tone for contrast
|
|
261
|
+
- [Theme Matching](./theme-matching.md) - HCT-based matching algorithm
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
# Image Color Analysis
|
|
2
|
+
|
|
3
|
+
Coolors MCP provides advanced image color extraction using Google's Material Color Utilities algorithms, enabling dynamic theme generation from images.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Image color extraction involves:
|
|
8
|
+
1. **Quantization** - Reducing colors to a representative set
|
|
9
|
+
2. **Scoring** - Evaluating colors for UI suitability
|
|
10
|
+
3. **Selection** - Choosing optimal source colors
|
|
11
|
+
4. **Theme Generation** - Creating complete color schemes
|
|
12
|
+
|
|
13
|
+
## The Extraction Process
|
|
14
|
+
|
|
15
|
+
### 1. Quantization
|
|
16
|
+
|
|
17
|
+
Quantization is a lossy compression process that selects a limited number of colors that best represent the original image.
|
|
18
|
+
|
|
19
|
+
#### Celebi Algorithm
|
|
20
|
+
Coolors MCP uses the Celebi quantizer, which combines:
|
|
21
|
+
- **Wu's algorithm**: Fast color quantization
|
|
22
|
+
- **WSMeans**: Weighted spatial color clustering
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
// Extract up to 128 representative colors
|
|
26
|
+
const colors = quantize(pixels, 128);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
#### How It Works
|
|
30
|
+
1. **Spatial clustering**: Groups similar colors that appear near each other
|
|
31
|
+
2. **Color frequency**: Weights colors by how often they appear
|
|
32
|
+
3. **Perceptual grouping**: Merges perceptually similar colors
|
|
33
|
+
4. **Output**: Typically 5-128 distinct colors
|
|
34
|
+
|
|
35
|
+
### 2. Color Scoring
|
|
36
|
+
|
|
37
|
+
Not all colors are suitable for UI themes. The scoring algorithm evaluates colors based on:
|
|
38
|
+
|
|
39
|
+
#### Key Metrics
|
|
40
|
+
|
|
41
|
+
**Chroma Score**
|
|
42
|
+
Colors closer to the target chroma of 48 receive higher scores:
|
|
43
|
+
```javascript
|
|
44
|
+
chromaScore = 1 - Math.abs(chroma - 48) / 48;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Population Score**
|
|
48
|
+
More frequent colors score higher:
|
|
49
|
+
```javascript
|
|
50
|
+
populationScore = pixelCount / totalPixels;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Color Diversity**
|
|
54
|
+
Promotes visually distinct colors:
|
|
55
|
+
- Higher scores for well-represented hues (30° neighborhood)
|
|
56
|
+
- Penalizes colors too similar to already selected ones
|
|
57
|
+
- Ensures good distribution across color wheel
|
|
58
|
+
|
|
59
|
+
#### Filtering Criteria
|
|
60
|
+
Colors are filtered out if they:
|
|
61
|
+
- Have very low chroma (<15) - too close to grayscale
|
|
62
|
+
- Are extremely rare (<0.01% of pixels)
|
|
63
|
+
- Fall in the "dislike zone" (dark yellow-greens)
|
|
64
|
+
- Are too similar to already selected colors
|
|
65
|
+
|
|
66
|
+
### 3. Source Color Selection
|
|
67
|
+
|
|
68
|
+
The system selects multiple source colors for theme options:
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
// Typical selection process
|
|
72
|
+
1. Extract and score all colors
|
|
73
|
+
2. Select top-scoring distinct colors (usually 3-5)
|
|
74
|
+
3. Present as theme options to user
|
|
75
|
+
4. Use selected color as source for theme generation
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Image Sources
|
|
79
|
+
|
|
80
|
+
### Wallpapers
|
|
81
|
+
User device wallpapers provide personalized color schemes:
|
|
82
|
+
- Analyzed on device for privacy
|
|
83
|
+
- Updates dynamically when wallpaper changes
|
|
84
|
+
- Creates cohesive system-wide theming
|
|
85
|
+
|
|
86
|
+
### App Content
|
|
87
|
+
Content-based colors adapt to current context:
|
|
88
|
+
- **Album art** → Music player theme
|
|
89
|
+
- **Product images** → E-commerce theme
|
|
90
|
+
- **Video thumbnails** → Media player theme
|
|
91
|
+
- **Logos** → Brand-consistent theme
|
|
92
|
+
|
|
93
|
+
### User Photos
|
|
94
|
+
Personal photos for custom themes:
|
|
95
|
+
- Profile pictures for personal spaces
|
|
96
|
+
- Gallery images for creative apps
|
|
97
|
+
- Artwork for design applications
|
|
98
|
+
|
|
99
|
+
## Implementation
|
|
100
|
+
|
|
101
|
+
### Basic Extraction
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
{
|
|
105
|
+
"name": "extract_image_colors",
|
|
106
|
+
"arguments": {
|
|
107
|
+
"imageData": [/* RGBA pixel array */],
|
|
108
|
+
"maxColors": 5
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Returns
|
|
113
|
+
{
|
|
114
|
+
"colors": [
|
|
115
|
+
{ "hex": "#6366f1", "population": 0.23, "score": 0.89 },
|
|
116
|
+
{ "hex": "#ec4899", "population": 0.15, "score": 0.76 },
|
|
117
|
+
{ "hex": "#10b981", "population": 0.12, "score": 0.71 }
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Theme from Image
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
{
|
|
126
|
+
"name": "generate_theme_from_image",
|
|
127
|
+
"arguments": {
|
|
128
|
+
"imageData": [/* RGBA pixel array */],
|
|
129
|
+
"variant": "tonalSpot"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Returns complete Material Design theme
|
|
134
|
+
{
|
|
135
|
+
"source": "#6366f1",
|
|
136
|
+
"schemes": {
|
|
137
|
+
"light": { /* light theme colors */ },
|
|
138
|
+
"dark": { /* dark theme colors */ }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Advanced Features
|
|
144
|
+
|
|
145
|
+
### Dislike Color Detection
|
|
146
|
+
|
|
147
|
+
The system automatically detects and adjusts universally disliked colors:
|
|
148
|
+
|
|
149
|
+
#### The "Bile Zone"
|
|
150
|
+
Dark yellow-greens (reminiscent of biological waste) are universally disliked:
|
|
151
|
+
- **Hue range**: 50-120° (yellow-green)
|
|
152
|
+
- **Chroma**: 20-50
|
|
153
|
+
- **Tone**: 20-50
|
|
154
|
+
|
|
155
|
+
#### Automatic Fixing
|
|
156
|
+
```javascript
|
|
157
|
+
if (isDisliked(color)) {
|
|
158
|
+
// Shift hue away from dislike zone
|
|
159
|
+
color.hue = adjustHue(color.hue);
|
|
160
|
+
// Increase tone for lighter appearance
|
|
161
|
+
color.tone = Math.max(60, color.tone);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Multi-Region Analysis
|
|
166
|
+
|
|
167
|
+
For complex images, analyze different regions:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
// Analyze specific image regions
|
|
171
|
+
const regions = [
|
|
172
|
+
{ x: 0, y: 0, width: 100, height: 100 }, // Top-left
|
|
173
|
+
{ x: 100, y: 100, width: 200, height: 200 } // Center
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
regions.forEach(region => {
|
|
177
|
+
const colors = extractFromRegion(image, region);
|
|
178
|
+
// Process regional colors
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Temporal Consistency
|
|
183
|
+
|
|
184
|
+
For video or animated content:
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
// Extract colors from multiple frames
|
|
188
|
+
const frames = [0, 30, 60, 90, 120];
|
|
189
|
+
const frameColors = frames.map(f => extractFrame(video, f));
|
|
190
|
+
|
|
191
|
+
// Find consistent colors across frames
|
|
192
|
+
const stableColors = findStableColors(frameColors);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Color Suitability Scoring
|
|
196
|
+
|
|
197
|
+
### UI Suitability Factors
|
|
198
|
+
|
|
199
|
+
| Factor | Weight | Description |
|
|
200
|
+
|--------|--------|-------------|
|
|
201
|
+
| Chroma | 40% | Preference for moderate chroma (~48) |
|
|
202
|
+
| Population | 30% | How much of image uses this color |
|
|
203
|
+
| Diversity | 20% | Distinctness from other colors |
|
|
204
|
+
| Accessibility | 10% | Potential for good contrast |
|
|
205
|
+
|
|
206
|
+
### Scoring Algorithm
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
function scoreColor(color, population, existingColors) {
|
|
210
|
+
const chromaScore = scoreChroma(color.chroma);
|
|
211
|
+
const popScore = population / totalPopulation;
|
|
212
|
+
const diversityScore = scoreDiversity(color, existingColors);
|
|
213
|
+
const accessScore = scoreAccessibility(color);
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
chromaScore * 0.4 +
|
|
217
|
+
popScore * 0.3 +
|
|
218
|
+
diversityScore * 0.2 +
|
|
219
|
+
accessScore * 0.1
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Best Practices
|
|
225
|
+
|
|
226
|
+
### Image Preparation
|
|
227
|
+
|
|
228
|
+
#### Optimal Images
|
|
229
|
+
- **Resolution**: 200-500px wide (resize larger images)
|
|
230
|
+
- **Format**: RGB/RGBA
|
|
231
|
+
- **Quality**: Avoid heavily compressed images
|
|
232
|
+
- **Content**: Clear subject with distinct colors
|
|
233
|
+
|
|
234
|
+
#### Pre-processing
|
|
235
|
+
```javascript
|
|
236
|
+
// Resize for performance
|
|
237
|
+
const resized = resizeImage(original, 300);
|
|
238
|
+
|
|
239
|
+
// Enhance contrast if needed
|
|
240
|
+
const enhanced = enhanceContrast(resized, 1.2);
|
|
241
|
+
|
|
242
|
+
// Extract colors
|
|
243
|
+
const colors = extractColors(enhanced);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Performance Optimization
|
|
247
|
+
|
|
248
|
+
#### Sampling Strategies
|
|
249
|
+
```javascript
|
|
250
|
+
// Fast: Sample every 5th pixel
|
|
251
|
+
const fastColors = quantize(pixels, 128, { sampling: 5 });
|
|
252
|
+
|
|
253
|
+
// Quality: Full pixel analysis
|
|
254
|
+
const qualityColors = quantize(pixels, 128, { sampling: 1 });
|
|
255
|
+
|
|
256
|
+
// Adaptive: Based on image size
|
|
257
|
+
const sampling = pixels.length > 100000 ? 5 : 1;
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### Caching
|
|
261
|
+
```javascript
|
|
262
|
+
// Cache extracted colors
|
|
263
|
+
const cache = new Map();
|
|
264
|
+
const hash = hashImage(imageData);
|
|
265
|
+
|
|
266
|
+
if (cache.has(hash)) {
|
|
267
|
+
return cache.get(hash);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const colors = extractColors(imageData);
|
|
271
|
+
cache.set(hash, colors);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Use Cases
|
|
275
|
+
|
|
276
|
+
### Dynamic Theming
|
|
277
|
+
Apps that adapt to content:
|
|
278
|
+
- Music players matching album art
|
|
279
|
+
- News readers matching article images
|
|
280
|
+
- Photo galleries with ambient themes
|
|
281
|
+
|
|
282
|
+
### Brand Extraction
|
|
283
|
+
Deriving brand colors from logos:
|
|
284
|
+
```javascript
|
|
285
|
+
const logo = loadImage('logo.png');
|
|
286
|
+
const brandColors = extractColors(logo, {
|
|
287
|
+
maxColors: 3,
|
|
288
|
+
minChroma: 30 // Ensure vibrant colors
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Palette Generation
|
|
293
|
+
Creating artist palettes from artwork:
|
|
294
|
+
```javascript
|
|
295
|
+
const artwork = loadImage('painting.jpg');
|
|
296
|
+
const palette = extractColors(artwork, {
|
|
297
|
+
maxColors: 12,
|
|
298
|
+
includeNeutrals: true
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Integration Examples
|
|
303
|
+
|
|
304
|
+
### Web Applications
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
// Extract colors from uploaded image
|
|
308
|
+
async function themeFromUpload(file) {
|
|
309
|
+
const image = await loadImage(file);
|
|
310
|
+
const canvas = createCanvas(image);
|
|
311
|
+
const pixels = getPixelData(canvas);
|
|
312
|
+
|
|
313
|
+
const response = await coolorsMCP.extractColors({
|
|
314
|
+
imageData: pixels,
|
|
315
|
+
maxColors: 5
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
return response.colors[0]; // Use top color
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### React Component
|
|
323
|
+
|
|
324
|
+
```jsx
|
|
325
|
+
function ImageThemeProvider({ imageUrl, children }) {
|
|
326
|
+
const [theme, setTheme] = useState(null);
|
|
327
|
+
|
|
328
|
+
useEffect(() => {
|
|
329
|
+
extractThemeFromImage(imageUrl).then(setTheme);
|
|
330
|
+
}, [imageUrl]);
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<ThemeContext.Provider value={theme}>
|
|
334
|
+
{children}
|
|
335
|
+
</ThemeContext.Provider>
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Node.js Server
|
|
341
|
+
|
|
342
|
+
```javascript
|
|
343
|
+
const sharp = require('sharp');
|
|
344
|
+
|
|
345
|
+
async function extractFromBuffer(buffer) {
|
|
346
|
+
// Resize and convert to raw pixels
|
|
347
|
+
const { data, info } = await sharp(buffer)
|
|
348
|
+
.resize(300)
|
|
349
|
+
.raw()
|
|
350
|
+
.toBuffer({ resolveWithObject: true });
|
|
351
|
+
|
|
352
|
+
// Extract colors
|
|
353
|
+
const colors = await coolorsMCP.extractColors({
|
|
354
|
+
imageData: Array.from(data),
|
|
355
|
+
width: info.width,
|
|
356
|
+
height: info.height
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
return colors;
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Limitations
|
|
364
|
+
|
|
365
|
+
### Technical Constraints
|
|
366
|
+
- Maximum image size: ~5MB recommended
|
|
367
|
+
- Color count: 128 colors maximum per extraction
|
|
368
|
+
- Processing time: ~100-500ms for typical images
|
|
369
|
+
|
|
370
|
+
### Accuracy Considerations
|
|
371
|
+
- Compressed images may lose color fidelity
|
|
372
|
+
- Very dark/light images provide limited options
|
|
373
|
+
- Monochrome images need fallback strategies
|
|
374
|
+
|
|
375
|
+
## Troubleshooting
|
|
376
|
+
|
|
377
|
+
### Common Issues
|
|
378
|
+
|
|
379
|
+
#### No Suitable Colors Found
|
|
380
|
+
**Cause**: Image too monochrome or low contrast
|
|
381
|
+
**Solution**: Adjust scoring thresholds or provide fallback
|
|
382
|
+
|
|
383
|
+
#### Inconsistent Results
|
|
384
|
+
**Cause**: Image compression or resizing artifacts
|
|
385
|
+
**Solution**: Use higher quality source images
|
|
386
|
+
|
|
387
|
+
#### Performance Problems
|
|
388
|
+
**Cause**: Processing large images
|
|
389
|
+
**Solution**: Resize before extraction
|
|
390
|
+
|
|
391
|
+
## See Also
|
|
392
|
+
|
|
393
|
+
- [Material Design](./material-design.md) - Theme generation from colors
|
|
394
|
+
- [Color Spaces](./color-spaces.md) - Understanding color models
|
|
395
|
+
- [HCT System](./hct.md) - Perceptual color space
|
|
396
|
+
- [Accessibility](./accessibility.md) - Ensuring extracted colors are usable
|