@trishchuk/coolors-mcp 1.0.0 → 1.1.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/.github/ISSUE_TEMPLATE/bug_report.md +20 -8
- package/.github/ISSUE_TEMPLATE/feature_request.md +22 -8
- package/.github/pull_request_template.md +33 -8
- package/.github/workflows/ci.yml +107 -104
- package/.github/workflows/deploy-docs.yml +14 -11
- package/.github/workflows/release.yml +25 -23
- package/README.md +149 -15
- package/dist/bin/server.js +997 -256
- package/dist/bin/server.js.map +1 -1
- package/dist/{chunk-P3ARRKLS.js → chunk-HOMDMKUY.js} +3 -1
- package/dist/{chunk-P3ARRKLS.js.map → chunk-HOMDMKUY.js.map} +1 -1
- package/dist/{chunk-IQ7NN26V.js → chunk-LHW2ZTOU.js} +14 -2
- package/dist/chunk-LHW2ZTOU.js.map +1 -0
- package/dist/color/index.js +1 -1
- package/dist/coolors-mcp.d.ts +4 -4
- package/dist/coolors-mcp.js +1 -1
- package/docs/.vitepress/components/ClientGrid.vue +9 -3
- package/docs/.vitepress/components/CodeBlock.vue +51 -44
- package/docs/.vitepress/components/ConfigModal.vue +151 -67
- package/docs/.vitepress/components/DiagramModal.vue +186 -154
- package/docs/.vitepress/components/TroubleshootingModal.vue +101 -96
- package/docs/.vitepress/config.js +171 -141
- package/docs/.vitepress/theme/FundingLayout.vue +65 -54
- package/docs/.vitepress/theme/Layout.vue +21 -21
- package/docs/.vitepress/theme/components/AdBanner.vue +73 -52
- package/docs/.vitepress/theme/components/AdPlaceholder.vue +3 -3
- package/docs/.vitepress/theme/components/FundingEffects.vue +77 -53
- package/docs/.vitepress/theme/components/FundingHero.vue +78 -63
- package/docs/.vitepress/theme/components/SupportSection.vue +106 -89
- package/docs/.vitepress/theme/custom-app.css +19 -12
- package/docs/.vitepress/theme/custom.css +33 -25
- package/docs/.vitepress/theme/index.js +19 -16
- package/docs/concepts/accessibility.md +59 -47
- package/docs/concepts/color-spaces.md +28 -6
- package/docs/concepts/distance-metrics.md +45 -30
- package/docs/concepts/hct.md +30 -27
- package/docs/concepts/image-analysis.md +52 -21
- package/docs/concepts/material-design.md +43 -17
- package/docs/concepts/theme-matching.md +64 -40
- package/docs/examples/basic-colors.md +92 -108
- package/docs/examples/creating-themes.md +104 -108
- package/docs/examples/css-refactoring.md +33 -29
- package/docs/examples/image-extraction.md +145 -138
- package/docs/getting-started.md +45 -34
- package/docs/index.md +5 -1
- package/docs/installation.md +15 -1
- package/docs/tools/accessibility.md +74 -68
- package/docs/tools/image-extraction.md +62 -54
- package/docs/tools/theme-matching.md +45 -42
- package/eslint.config.ts +13 -0
- package/jsr.json +1 -1
- package/package.json +17 -13
- package/src/bin/server.ts +13 -1
- package/src/color/__tests__/extract-colors.test.ts +20 -30
- package/src/color/apca.ts +105 -0
- package/src/color/color-blindness.ts +109 -0
- package/src/coolors-mcp.ts +1 -1
- package/src/session.ts +10 -2
- package/src/theme/matcher.ts +1 -1
- package/src/theme/refactor.ts +1 -1
- package/src/theme/types.ts +3 -0
- package/src/tools/__tests__/cohesion.test.ts +97 -0
- package/src/tools/__tests__/color-blindness.test.ts +45 -0
- package/src/tools/__tests__/color-conversion.test.ts +38 -0
- package/src/tools/__tests__/contrast-checker.test.ts +56 -0
- package/src/tools/__tests__/palette-export.test.ts +54 -0
- package/src/tools/adjust-color.tool.ts +80 -0
- package/src/tools/cohesion.tools.ts +380 -0
- package/src/tools/color-blindness.tool.ts +168 -0
- package/src/tools/color-conversion.tool.ts +1 -1
- package/src/tools/contrast-checker.tool.ts +53 -14
- package/src/tools/dislike-analyzer.tool.ts +41 -54
- package/src/tools/image-extraction.tools.ts +62 -115
- package/src/tools/index.ts +15 -2
- package/src/tools/palette-export.tool.ts +174 -0
- package/src/tools/palette-with-locks.tool.ts +8 -6
- package/src/types.ts +2 -3
- package/tsconfig.json +12 -2
- package/vitest.config.js +1 -3
- package/.claude/settings.local.json +0 -39
- package/.env +0 -2
- package/.mcp.json +0 -12
- package/CLAUDE.md +0 -201
- package/DOCUMENTATION.md +0 -274
- package/GEMINI.md +0 -54
- package/demo/content_based_color.png +0 -0
- package/demo/music-player.html +0 -621
- package/demo/podcast-player.html +0 -903
- package/dist/chunk-IQ7NN26V.js.map +0 -1
- package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js +0 -93
- package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js.map +0 -7
- package/docs/.vitepress/cache/deps/_metadata.json +0 -127
- package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js +0 -9
- package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +0 -7
- package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js +0 -12683
- package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js.map +0 -7
- package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js +0 -9719
- package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js.map +0 -7
- package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js +0 -4710
- package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +0 -7
- package/docs/.vitepress/cache/deps/cytoscape.js +0 -30278
- package/docs/.vitepress/cache/deps/cytoscape.js.map +0 -7
- package/docs/.vitepress/cache/deps/dayjs.js +0 -285
- package/docs/.vitepress/cache/deps/dayjs.js.map +0 -7
- package/docs/.vitepress/cache/deps/debug.js +0 -468
- package/docs/.vitepress/cache/deps/debug.js.map +0 -7
- package/docs/.vitepress/cache/deps/package.json +0 -3
- package/docs/.vitepress/cache/deps/prismjs.js +0 -1466
- package/docs/.vitepress/cache/deps/prismjs.js.map +0 -7
- package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js +0 -228
- package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js.map +0 -7
- package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js +0 -142
- package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js.map +0 -7
- package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js +0 -27
- package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js.map +0 -7
- package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js +0 -65
- package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js.map +0 -7
- package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js +0 -53
- package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js.map +0 -7
- package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js +0 -73
- package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js.map +0 -7
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +0 -4507
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +0 -7
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +0 -584
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +0 -7
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +0 -1146
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +0 -7
- package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +0 -1667
- package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +0 -7
- package/docs/.vitepress/cache/deps/vitepress___minisearch.js +0 -1814
- package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +0 -7
- package/docs/.vitepress/cache/deps/vue.js +0 -344
- package/docs/.vitepress/cache/deps/vue.js.map +0 -7
- package/examples/theme-matching.md +0 -113
- package/mcp-config.json +0 -8
- package/note.md +0 -35
- package/research_results.md +0 -53
- package/src/tools/colors.ts +0 -31
- package/src/tools/registry.ts +0 -142
- package/src/tools/simple-tools.ts +0 -37
|
@@ -32,22 +32,23 @@ The system first extracts and analyzes CSS variables:
|
|
|
32
32
|
|
|
33
33
|
Variable names are analyzed to detect semantic roles:
|
|
34
34
|
|
|
35
|
-
| Pattern
|
|
36
|
-
|
|
37
|
-
| `*primary*`
|
|
38
|
-
| `*secondary*`
|
|
39
|
-
| `*surface*`, `*background*` | Surface
|
|
40
|
-
| `*text*`, `*foreground*`
|
|
41
|
-
| `*border*`, `*outline*`
|
|
42
|
-
| `*error*`, `*danger*`
|
|
43
|
-
| `*success*`
|
|
44
|
-
| `*warning*`
|
|
35
|
+
| Pattern | Detected Role | Usage Context |
|
|
36
|
+
| --------------------------- | ------------- | ----------------- |
|
|
37
|
+
| `*primary*` | Primary | Main brand color |
|
|
38
|
+
| `*secondary*` | Secondary | Supporting color |
|
|
39
|
+
| `*surface*`, `*background*` | Surface | Backgrounds |
|
|
40
|
+
| `*text*`, `*foreground*` | Text | Typography |
|
|
41
|
+
| `*border*`, `*outline*` | Outline | Borders, dividers |
|
|
42
|
+
| `*error*`, `*danger*` | Error | Error states |
|
|
43
|
+
| `*success*` | Success | Success states |
|
|
44
|
+
| `*warning*` | Warning | Warning states |
|
|
45
45
|
|
|
46
46
|
### 3. Multi-Factor Scoring
|
|
47
47
|
|
|
48
48
|
Each potential match is scored based on multiple factors:
|
|
49
49
|
|
|
50
50
|
#### Perceptual Distance (60% weight)
|
|
51
|
+
|
|
51
52
|
Using HCT color space for accurate perception:
|
|
52
53
|
|
|
53
54
|
```javascript
|
|
@@ -58,13 +59,14 @@ function calculatePerceptualDistance(color1, color2) {
|
|
|
58
59
|
// Weighted distance (tone weighted 2x for UI importance)
|
|
59
60
|
return Math.sqrt(
|
|
60
61
|
Math.pow(hct1.hue - hct2.hue, 2) +
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
Math.pow(hct1.chroma - hct2.chroma, 2) +
|
|
63
|
+
Math.pow((hct1.tone - hct2.tone) * 2, 2),
|
|
63
64
|
);
|
|
64
65
|
}
|
|
65
66
|
```
|
|
66
67
|
|
|
67
68
|
#### Semantic Context (20% weight)
|
|
69
|
+
|
|
68
70
|
Matching semantic roles improves accuracy:
|
|
69
71
|
|
|
70
72
|
```javascript
|
|
@@ -81,6 +83,7 @@ function calculateSemanticScore(colorRole, variableRole, usage) {
|
|
|
81
83
|
```
|
|
82
84
|
|
|
83
85
|
#### Accessibility Score (20% weight)
|
|
86
|
+
|
|
84
87
|
Ensuring contrast requirements are met:
|
|
85
88
|
|
|
86
89
|
```javascript
|
|
@@ -101,35 +104,41 @@ function calculateAccessibilityScore(color, background, requiredRatio) {
|
|
|
101
104
|
Final confidence score determines replacement:
|
|
102
105
|
|
|
103
106
|
```javascript
|
|
104
|
-
confidence =
|
|
105
|
-
perceptualScore * 0.6 +
|
|
106
|
-
|
|
107
|
-
accessibilityScore * 0.2
|
|
108
|
-
) * 100;
|
|
107
|
+
confidence =
|
|
108
|
+
(perceptualScore * 0.6 + semanticScore * 0.2 + accessibilityScore * 0.2) *
|
|
109
|
+
100;
|
|
109
110
|
```
|
|
110
111
|
|
|
111
112
|
## Matching Strategies
|
|
112
113
|
|
|
113
114
|
### Exact Matching
|
|
115
|
+
|
|
114
116
|
For colors that exactly match a theme variable:
|
|
117
|
+
|
|
115
118
|
- **Confidence**: 100%
|
|
116
119
|
- **Action**: Direct replacement
|
|
117
120
|
- **Example**: `#6366f1` → `var(--color-primary-500)`
|
|
118
121
|
|
|
119
122
|
### Nearest Neighbor
|
|
123
|
+
|
|
120
124
|
For colors close to theme variables:
|
|
125
|
+
|
|
121
126
|
- **Confidence**: 70-99%
|
|
122
127
|
- **Action**: Replace with warning
|
|
123
128
|
- **Example**: `#6365f0` → `var(--color-primary-500)` (99% confidence)
|
|
124
129
|
|
|
125
130
|
### Semantic Matching
|
|
131
|
+
|
|
126
132
|
For colors matching expected roles:
|
|
133
|
+
|
|
127
134
|
- **Confidence**: 60-90%
|
|
128
135
|
- **Action**: Context-aware replacement
|
|
129
136
|
- **Example**: Border color `#e5e7eb` → `var(--color-border)`
|
|
130
137
|
|
|
131
138
|
### Fallback Matching
|
|
139
|
+
|
|
132
140
|
When no good matches exist:
|
|
141
|
+
|
|
133
142
|
- **Confidence**: <60%
|
|
134
143
|
- **Action**: Keep original or create new variable
|
|
135
144
|
- **Example**: `#123456` → `/* No match: #123456 */`
|
|
@@ -201,13 +210,13 @@ When no good matches exist:
|
|
|
201
210
|
|
|
202
211
|
Different contexts have different priorities:
|
|
203
212
|
|
|
204
|
-
| Context
|
|
205
|
-
|
|
206
|
-
| `text`
|
|
207
|
-
| `background` | Color > Semantic > Contrast | Visual accuracy
|
|
208
|
-
| `border`
|
|
209
|
-
| `accent`
|
|
210
|
-
| `decorative` | Color > Semantic > Contrast | Visual appeal
|
|
213
|
+
| Context | Priority | Factors |
|
|
214
|
+
| ------------ | --------------------------- | ------------------ |
|
|
215
|
+
| `text` | Contrast > Semantic > Color | Readability first |
|
|
216
|
+
| `background` | Color > Semantic > Contrast | Visual accuracy |
|
|
217
|
+
| `border` | Semantic > Color > Contrast | Structural meaning |
|
|
218
|
+
| `accent` | Color > Contrast > Semantic | Brand consistency |
|
|
219
|
+
| `decorative` | Color > Semantic > Contrast | Visual appeal |
|
|
211
220
|
|
|
212
221
|
### Threshold Configuration
|
|
213
222
|
|
|
@@ -290,30 +299,42 @@ Use semantic, hierarchical naming:
|
|
|
290
299
|
|
|
291
300
|
Recommended thresholds by use case:
|
|
292
301
|
|
|
293
|
-
| Use Case
|
|
294
|
-
|
|
295
|
-
| Production refactor | 85%
|
|
296
|
-
| Development assist
|
|
297
|
-
| Exploration
|
|
298
|
-
| Exact only
|
|
302
|
+
| Use Case | Min Confidence | Rationale |
|
|
303
|
+
| ------------------- | -------------- | ---------------------- |
|
|
304
|
+
| Production refactor | 85% | High accuracy needed |
|
|
305
|
+
| Development assist | 70% | Balance speed/accuracy |
|
|
306
|
+
| Exploration | 50% | See all possibilities |
|
|
307
|
+
| Exact only | 100% | No false positives |
|
|
299
308
|
|
|
300
309
|
### Handling Edge Cases
|
|
301
310
|
|
|
302
311
|
#### Very Similar Colors
|
|
312
|
+
|
|
303
313
|
```css
|
|
304
314
|
/* Original */
|
|
305
|
-
.element1 {
|
|
306
|
-
|
|
315
|
+
.element1 {
|
|
316
|
+
color: #6366f1;
|
|
317
|
+
}
|
|
318
|
+
.element2 {
|
|
319
|
+
color: #6366f0;
|
|
320
|
+
}
|
|
307
321
|
|
|
308
322
|
/* After matching - both map to same variable */
|
|
309
|
-
.element1 {
|
|
310
|
-
|
|
323
|
+
.element1 {
|
|
324
|
+
color: var(--color-primary);
|
|
325
|
+
}
|
|
326
|
+
.element2 {
|
|
327
|
+
color: var(--color-primary);
|
|
328
|
+
}
|
|
311
329
|
```
|
|
312
330
|
|
|
313
331
|
#### No Good Matches
|
|
332
|
+
|
|
314
333
|
```css
|
|
315
334
|
/* Original */
|
|
316
|
-
.special {
|
|
335
|
+
.special {
|
|
336
|
+
background: #742a2a;
|
|
337
|
+
}
|
|
317
338
|
|
|
318
339
|
/* After - preserved with comment */
|
|
319
340
|
.special {
|
|
@@ -330,11 +351,11 @@ Recommended thresholds by use case:
|
|
|
330
351
|
module.exports = {
|
|
331
352
|
plugins: [
|
|
332
353
|
new ThemeMatcherPlugin({
|
|
333
|
-
themeFile:
|
|
354
|
+
themeFile: "./theme.css",
|
|
334
355
|
minConfidence: 80,
|
|
335
|
-
generateReport: true
|
|
336
|
-
})
|
|
337
|
-
]
|
|
356
|
+
generateReport: true,
|
|
357
|
+
}),
|
|
358
|
+
],
|
|
338
359
|
};
|
|
339
360
|
```
|
|
340
361
|
|
|
@@ -368,14 +389,17 @@ module.exports = {
|
|
|
368
389
|
### Common Issues
|
|
369
390
|
|
|
370
391
|
#### Low Confidence Scores
|
|
392
|
+
|
|
371
393
|
- **Cause**: Colors too different from theme
|
|
372
394
|
- **Solution**: Lower threshold or add more theme variables
|
|
373
395
|
|
|
374
396
|
#### Wrong Semantic Matches
|
|
397
|
+
|
|
375
398
|
- **Cause**: Ambiguous variable names
|
|
376
399
|
- **Solution**: Use clearer naming conventions
|
|
377
400
|
|
|
378
401
|
#### Accessibility Failures
|
|
402
|
+
|
|
379
403
|
- **Cause**: Insufficient contrast with background
|
|
380
404
|
- **Solution**: Adjust theme colors or use different variables
|
|
381
405
|
|
|
@@ -396,4 +420,4 @@ Enable detailed logging:
|
|
|
396
420
|
- [Color Spaces](./color-spaces.md) - Understanding color models
|
|
397
421
|
- [HCT System](./hct.md) - Perceptual color space
|
|
398
422
|
- [Accessibility](./accessibility.md) - Contrast requirements
|
|
399
|
-
- [Material Design](./material-design.md) - Theme generation
|
|
423
|
+
- [Material Design](./material-design.md) - Theme generation
|
|
@@ -10,19 +10,19 @@ Convert colors between different formats:
|
|
|
10
10
|
|
|
11
11
|
```javascript
|
|
12
12
|
// Hex to RGB
|
|
13
|
-
"Convert #6366f1 to RGB"
|
|
13
|
+
"Convert #6366f1 to RGB";
|
|
14
14
|
// Result: rgb(99, 102, 241)
|
|
15
15
|
|
|
16
16
|
// RGB to HSL
|
|
17
|
-
"Convert rgb(99, 102, 241) to HSL"
|
|
17
|
+
"Convert rgb(99, 102, 241) to HSL";
|
|
18
18
|
// Result: hsl(239, 84%, 67%)
|
|
19
19
|
|
|
20
20
|
// HSL to HCT
|
|
21
|
-
"Convert hsl(239, 84%, 67%) to HCT"
|
|
21
|
+
"Convert hsl(239, 84%, 67%) to HCT";
|
|
22
22
|
// Result: hct(265.8, 87.2, 47.9)
|
|
23
23
|
|
|
24
24
|
// Any format to LAB
|
|
25
|
-
"What is #6366f1 in LAB color space?"
|
|
25
|
+
"What is #6366f1 in LAB color space?";
|
|
26
26
|
// Result: lab(47.9, 35.2, -65.7)
|
|
27
27
|
```
|
|
28
28
|
|
|
@@ -31,7 +31,7 @@ Convert colors between different formats:
|
|
|
31
31
|
Convert multiple colors at once:
|
|
32
32
|
|
|
33
33
|
```javascript
|
|
34
|
-
"Convert these colors to HSL: #6366f1, #ec4899, #10b981"
|
|
34
|
+
"Convert these colors to HSL: #6366f1, #ec4899, #10b981";
|
|
35
35
|
|
|
36
36
|
// Results:
|
|
37
37
|
// #6366f1 → hsl(239, 84%, 67%)
|
|
@@ -47,7 +47,7 @@ Verify conversion accuracy:
|
|
|
47
47
|
// Original → HCT → Back to original
|
|
48
48
|
const original = "#6366f1";
|
|
49
49
|
const hct = "hct(265.8, 87.2, 47.9)";
|
|
50
|
-
const backToHex = "#6366f1";
|
|
50
|
+
const backToHex = "#6366f1"; // Perfect round-trip
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
## Color Palettes
|
|
@@ -57,16 +57,14 @@ const backToHex = "#6366f1"; // Perfect round-trip
|
|
|
57
57
|
Generate shades of a single color:
|
|
58
58
|
|
|
59
59
|
```javascript
|
|
60
|
-
"Create a monochromatic palette from #6366f1"
|
|
61
|
-
|
|
62
|
-
//
|
|
63
|
-
[
|
|
64
|
-
"#1a1d4d", // Darkest
|
|
60
|
+
"Create a monochromatic palette from #6366f1"[
|
|
61
|
+
// Result: 5 shades
|
|
62
|
+
("#1a1d4d", // Darkest
|
|
65
63
|
"#3336a3",
|
|
66
|
-
"#6366f1",
|
|
64
|
+
"#6366f1", // Original
|
|
67
65
|
"#9598ff",
|
|
68
|
-
"#c7c8ff"
|
|
69
|
-
]
|
|
66
|
+
"#c7c8ff") // Lightest
|
|
67
|
+
];
|
|
70
68
|
```
|
|
71
69
|
|
|
72
70
|
### Complementary Colors
|
|
@@ -88,14 +86,12 @@ Find opposite colors on the color wheel:
|
|
|
88
86
|
Get neighboring colors:
|
|
89
87
|
|
|
90
88
|
```javascript
|
|
91
|
-
"Generate analogous colors for #6366f1"
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
"#
|
|
96
|
-
|
|
97
|
-
"#66c6f1" // Cyan-blue
|
|
98
|
-
]
|
|
89
|
+
"Generate analogous colors for #6366f1"[
|
|
90
|
+
// Result: Colors 30° apart
|
|
91
|
+
("#8c66f1", // Purple-blue
|
|
92
|
+
"#6366f1", // Original blue
|
|
93
|
+
"#66c6f1") // Cyan-blue
|
|
94
|
+
];
|
|
99
95
|
```
|
|
100
96
|
|
|
101
97
|
### Triadic Color Scheme
|
|
@@ -103,14 +99,12 @@ Get neighboring colors:
|
|
|
103
99
|
Three colors evenly spaced on the color wheel:
|
|
104
100
|
|
|
105
101
|
```javascript
|
|
106
|
-
"Create a triadic color scheme from #6366f1"
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
"#
|
|
111
|
-
|
|
112
|
-
"#66f163" // Green
|
|
113
|
-
]
|
|
102
|
+
"Create a triadic color scheme from #6366f1"[
|
|
103
|
+
// Result: Colors 120° apart
|
|
104
|
+
("#6366f1", // Blue (original)
|
|
105
|
+
"#f16366", // Red
|
|
106
|
+
"#66f163") // Green
|
|
107
|
+
];
|
|
114
108
|
```
|
|
115
109
|
|
|
116
110
|
### Tetradic (Square) Scheme
|
|
@@ -118,15 +112,13 @@ Three colors evenly spaced on the color wheel:
|
|
|
118
112
|
Four colors evenly spaced:
|
|
119
113
|
|
|
120
114
|
```javascript
|
|
121
|
-
"Generate a tetradic palette from #6366f1"
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
"#
|
|
126
|
-
"#
|
|
127
|
-
|
|
128
|
-
"#66f18c" // Green
|
|
129
|
-
]
|
|
115
|
+
"Generate a tetradic palette from #6366f1"[
|
|
116
|
+
// Result: Colors 90° apart
|
|
117
|
+
("#6366f1", // Blue
|
|
118
|
+
"#f166c6", // Magenta
|
|
119
|
+
"#f1ee66", // Yellow
|
|
120
|
+
"#66f18c") // Green
|
|
121
|
+
];
|
|
130
122
|
```
|
|
131
123
|
|
|
132
124
|
## Contrast Checking
|
|
@@ -166,14 +158,12 @@ Four colors evenly spaced:
|
|
|
166
158
|
### Finding Accessible Colors
|
|
167
159
|
|
|
168
160
|
```javascript
|
|
169
|
-
"What text color works on #6366f1 background?"
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
{ color: "#
|
|
174
|
-
|
|
175
|
-
{ color: "#000000", ratio: 4.5 } // Black text
|
|
176
|
-
]
|
|
161
|
+
"What text color works on #6366f1 background?"[
|
|
162
|
+
// Suggestions:
|
|
163
|
+
({ color: "#ffffff", ratio: 4.6 }, // White text
|
|
164
|
+
{ color: "#f0f0f0", ratio: 4.2 }, // Light gray
|
|
165
|
+
{ color: "#000000", ratio: 4.5 }) // Black text
|
|
166
|
+
];
|
|
177
167
|
```
|
|
178
168
|
|
|
179
169
|
## Color Distance
|
|
@@ -194,15 +184,15 @@ Four colors evenly spaced:
|
|
|
194
184
|
### Finding Similar Colors
|
|
195
185
|
|
|
196
186
|
```javascript
|
|
197
|
-
"Find colors similar to #6366f1 in this palette"
|
|
187
|
+
"Find colors similar to #6366f1 in this palette";
|
|
198
188
|
|
|
199
189
|
const palette = ["#5355d1", "#7c3aed", "#6366f1", "#3b82f6"];
|
|
200
190
|
|
|
201
191
|
// Results (ΔE < 10):
|
|
202
192
|
[
|
|
203
|
-
{ color: "#5355d1", distance: 5.2 },
|
|
204
|
-
{ color: "#3b82f6", distance: 8.7 }
|
|
205
|
-
]
|
|
193
|
+
{ color: "#5355d1", distance: 5.2 }, // Very similar
|
|
194
|
+
{ color: "#3b82f6", distance: 8.7 }, // Similar
|
|
195
|
+
];
|
|
206
196
|
```
|
|
207
197
|
|
|
208
198
|
### Color Matching Threshold
|
|
@@ -224,46 +214,36 @@ const palette = ["#5355d1", "#7c3aed", "#6366f1", "#3b82f6"];
|
|
|
224
214
|
### Two-Color Gradient
|
|
225
215
|
|
|
226
216
|
```javascript
|
|
227
|
-
"Create a gradient from #6366f1 to #ec4899"
|
|
228
|
-
|
|
229
|
-
//
|
|
230
|
-
[
|
|
231
|
-
"#6366f1", // Start
|
|
217
|
+
"Create a gradient from #6366f1 to #ec4899"[
|
|
218
|
+
// Result: 5 steps
|
|
219
|
+
("#6366f1", // Start
|
|
232
220
|
"#8757d5",
|
|
233
221
|
"#ab49b9",
|
|
234
222
|
"#ce3a9d",
|
|
235
|
-
"#ec4899"
|
|
236
|
-
]
|
|
223
|
+
"#ec4899") // End
|
|
224
|
+
];
|
|
237
225
|
```
|
|
238
226
|
|
|
239
227
|
### Multi-Stop Gradient
|
|
240
228
|
|
|
241
229
|
```javascript
|
|
242
|
-
"Create a gradient: #6366f1 → #ec4899 → #10b981"
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
"#6366f1",
|
|
247
|
-
"#a154c5",
|
|
248
|
-
"#ec4899",
|
|
249
|
-
"#a9636d",
|
|
250
|
-
"#10b981"
|
|
251
|
-
]
|
|
230
|
+
"Create a gradient: #6366f1 → #ec4899 → #10b981"[
|
|
231
|
+
// Result: Smooth transition through all colors
|
|
232
|
+
("#6366f1", "#a154c5", "#ec4899", "#a9636d", "#10b981")
|
|
233
|
+
];
|
|
252
234
|
```
|
|
253
235
|
|
|
254
236
|
### Perceptual Gradient
|
|
255
237
|
|
|
256
238
|
```javascript
|
|
257
|
-
"Create a perceptually smooth gradient from blue to yellow"
|
|
258
|
-
|
|
259
|
-
//
|
|
260
|
-
[
|
|
261
|
-
"#0000ff", // Blue
|
|
239
|
+
"Create a perceptually smooth gradient from blue to yellow"[
|
|
240
|
+
// Using LAB interpolation:
|
|
241
|
+
("#0000ff", // Blue
|
|
262
242
|
"#4040df",
|
|
263
243
|
"#8080bf",
|
|
264
244
|
"#c0c09f",
|
|
265
|
-
"#ffff00"
|
|
266
|
-
]
|
|
245
|
+
"#ffff00") // Yellow
|
|
246
|
+
];
|
|
267
247
|
// Avoids muddy colors in the middle
|
|
268
248
|
```
|
|
269
249
|
|
|
@@ -272,30 +252,30 @@ const palette = ["#5355d1", "#7c3aed", "#6366f1", "#3b82f6"];
|
|
|
272
252
|
### Lighten and Darken
|
|
273
253
|
|
|
274
254
|
```javascript
|
|
275
|
-
"Make #6366f1 20% lighter"
|
|
255
|
+
"Make #6366f1 20% lighter";
|
|
276
256
|
// Result: #8c8ff5
|
|
277
257
|
|
|
278
|
-
"Make #6366f1 30% darker"
|
|
258
|
+
"Make #6366f1 30% darker";
|
|
279
259
|
// Result: #3a3d94
|
|
280
260
|
```
|
|
281
261
|
|
|
282
262
|
### Saturation Adjustment
|
|
283
263
|
|
|
284
264
|
```javascript
|
|
285
|
-
"Desaturate #6366f1 by 50%"
|
|
265
|
+
"Desaturate #6366f1 by 50%";
|
|
286
266
|
// Result: #8a8cc4 (more gray)
|
|
287
267
|
|
|
288
|
-
"Make #6366f1 more vibrant"
|
|
268
|
+
"Make #6366f1 more vibrant";
|
|
289
269
|
// Result: #5050ff (higher chroma)
|
|
290
270
|
```
|
|
291
271
|
|
|
292
272
|
### Mix Colors
|
|
293
273
|
|
|
294
274
|
```javascript
|
|
295
|
-
"Mix #6366f1 and #ec4899"
|
|
275
|
+
"Mix #6366f1 and #ec4899";
|
|
296
276
|
// Result: #a857c5 (purple)
|
|
297
277
|
|
|
298
|
-
"Mix #ff0000 and #00ff00 and #0000ff equally"
|
|
278
|
+
"Mix #ff0000 and #00ff00 and #0000ff equally";
|
|
299
279
|
// Result: #808080 (gray)
|
|
300
280
|
```
|
|
301
281
|
|
|
@@ -309,9 +289,9 @@ const primary = "#6366f1";
|
|
|
309
289
|
|
|
310
290
|
const buttonStates = {
|
|
311
291
|
default: primary,
|
|
312
|
-
hover: lighten(primary, 10),
|
|
313
|
-
active: darken(primary, 10),
|
|
314
|
-
disabled: desaturate(primary, 60) // #9a9bc7
|
|
292
|
+
hover: lighten(primary, 10), // #797cf3
|
|
293
|
+
active: darken(primary, 10), // #5053d0
|
|
294
|
+
disabled: desaturate(primary, 60), // #9a9bc7
|
|
315
295
|
};
|
|
316
296
|
```
|
|
317
297
|
|
|
@@ -322,20 +302,20 @@ const buttonStates = {
|
|
|
322
302
|
const statusColors = {
|
|
323
303
|
success: {
|
|
324
304
|
bg: "#10b981",
|
|
325
|
-
text: "#ffffff",
|
|
305
|
+
text: "#ffffff", // Contrast: 4.5:1 ✓
|
|
326
306
|
},
|
|
327
307
|
warning: {
|
|
328
308
|
bg: "#f59e0b",
|
|
329
|
-
text: "#000000",
|
|
309
|
+
text: "#000000", // Contrast: 10.7:1 ✓
|
|
330
310
|
},
|
|
331
311
|
error: {
|
|
332
312
|
bg: "#ef4444",
|
|
333
|
-
text: "#ffffff",
|
|
313
|
+
text: "#ffffff", // Contrast: 4.5:1 ✓
|
|
334
314
|
},
|
|
335
315
|
info: {
|
|
336
316
|
bg: "#3b82f6",
|
|
337
|
-
text: "#ffffff",
|
|
338
|
-
}
|
|
317
|
+
text: "#ffffff", // Contrast: 4.6:1 ✓
|
|
318
|
+
},
|
|
339
319
|
};
|
|
340
320
|
```
|
|
341
321
|
|
|
@@ -351,7 +331,7 @@ const colorSystem = {
|
|
|
351
331
|
50: "#eef2ff",
|
|
352
332
|
100: "#e0e7ff",
|
|
353
333
|
500: brand,
|
|
354
|
-
900: "#312e81"
|
|
334
|
+
900: "#312e81",
|
|
355
335
|
},
|
|
356
336
|
|
|
357
337
|
// Grays from brand
|
|
@@ -361,8 +341,8 @@ const colorSystem = {
|
|
|
361
341
|
semantic: {
|
|
362
342
|
success: harmonize("#10b981", brand),
|
|
363
343
|
warning: harmonize("#f59e0b", brand),
|
|
364
|
-
error: harmonize("#ef4444", brand)
|
|
365
|
-
}
|
|
344
|
+
error: harmonize("#ef4444", brand),
|
|
345
|
+
},
|
|
366
346
|
};
|
|
367
347
|
```
|
|
368
348
|
|
|
@@ -372,11 +352,11 @@ const colorSystem = {
|
|
|
372
352
|
// Accessible form field colors
|
|
373
353
|
const input = {
|
|
374
354
|
background: "#ffffff",
|
|
375
|
-
border: "#d1d5db",
|
|
376
|
-
text: "#1f2937",
|
|
377
|
-
placeholder: "#9ca3af",
|
|
378
|
-
focus: "#6366f1",
|
|
379
|
-
error: "#ef4444"
|
|
355
|
+
border: "#d1d5db", // 3:1 contrast ✓
|
|
356
|
+
text: "#1f2937", // 15.7:1 contrast ✓
|
|
357
|
+
placeholder: "#9ca3af", // 4.5:1 contrast ✓
|
|
358
|
+
focus: "#6366f1", // 3:1 contrast ✓
|
|
359
|
+
error: "#ef4444", // 4.5:1 contrast ✓
|
|
380
360
|
};
|
|
381
361
|
```
|
|
382
362
|
|
|
@@ -385,24 +365,24 @@ const input = {
|
|
|
385
365
|
### Get Luminance
|
|
386
366
|
|
|
387
367
|
```javascript
|
|
388
|
-
"What is the luminance of #6366f1?"
|
|
368
|
+
"What is the luminance of #6366f1?";
|
|
389
369
|
// Result: 0.166 (scale 0-1)
|
|
390
370
|
```
|
|
391
371
|
|
|
392
372
|
### Get Dominant Channel
|
|
393
373
|
|
|
394
374
|
```javascript
|
|
395
|
-
"Which color channel is strongest in #6366f1?"
|
|
375
|
+
"Which color channel is strongest in #6366f1?";
|
|
396
376
|
// Result: Blue (241 of 255)
|
|
397
377
|
```
|
|
398
378
|
|
|
399
379
|
### Color Temperature
|
|
400
380
|
|
|
401
381
|
```javascript
|
|
402
|
-
"Is #6366f1 a warm or cool color?"
|
|
382
|
+
"Is #6366f1 a warm or cool color?";
|
|
403
383
|
// Result: Cool (blue hue at 239°)
|
|
404
384
|
|
|
405
|
-
"Is #ff6b6b warm or cool?"
|
|
385
|
+
"Is #ff6b6b warm or cool?";
|
|
406
386
|
// Result: Warm (red hue at 0°)
|
|
407
387
|
```
|
|
408
388
|
|
|
@@ -431,12 +411,12 @@ function getThemeSafeColor(color) {
|
|
|
431
411
|
// Validate color format
|
|
432
412
|
function validateColor(input) {
|
|
433
413
|
const formats = [
|
|
434
|
-
/^#[0-9a-f]{6}$/i,
|
|
435
|
-
/^rgb\(\d+,\s*\d+,\s*\d+\)$/,
|
|
436
|
-
/^hsl\(\d+,\s*\d+%,\s*\d+%\)
|
|
414
|
+
/^#[0-9a-f]{6}$/i, // Hex
|
|
415
|
+
/^rgb\(\d+,\s*\d+,\s*\d+\)$/, // RGB
|
|
416
|
+
/^hsl\(\d+,\s*\d+%,\s*\d+%\)$/, // HSL
|
|
437
417
|
];
|
|
438
418
|
|
|
439
|
-
return formats.some(regex => regex.test(input));
|
|
419
|
+
return formats.some((regex) => regex.test(input));
|
|
440
420
|
}
|
|
441
421
|
```
|
|
442
422
|
|
|
@@ -448,14 +428,14 @@ function generatePalette(baseColor, options = {}) {
|
|
|
448
428
|
const {
|
|
449
429
|
shades = 5,
|
|
450
430
|
includeComplementary = true,
|
|
451
|
-
includeAnalogous = true
|
|
431
|
+
includeAnalogous = true,
|
|
452
432
|
} = options;
|
|
453
433
|
|
|
454
434
|
return {
|
|
455
435
|
base: baseColor,
|
|
456
436
|
shades: generateShades(baseColor, shades),
|
|
457
437
|
complementary: includeComplementary ? getComplement(baseColor) : null,
|
|
458
|
-
analogous: includeAnalogous ? getAnalogous(baseColor) : null
|
|
438
|
+
analogous: includeAnalogous ? getAnalogous(baseColor) : null,
|
|
459
439
|
};
|
|
460
440
|
}
|
|
461
441
|
```
|
|
@@ -463,21 +443,25 @@ function generatePalette(baseColor, options = {}) {
|
|
|
463
443
|
## Tips and Best Practices
|
|
464
444
|
|
|
465
445
|
### Color Conversion
|
|
446
|
+
|
|
466
447
|
- ✅ Use HCT for perceptual operations
|
|
467
448
|
- ✅ Use RGB/Hex for final output
|
|
468
449
|
- ❌ Don't chain too many conversions
|
|
469
450
|
|
|
470
451
|
### Palette Generation
|
|
452
|
+
|
|
471
453
|
- ✅ Start with 5-7 colors maximum
|
|
472
454
|
- ✅ Test palettes with color blindness simulators
|
|
473
455
|
- ❌ Don't use pure black (#000000) with pure white
|
|
474
456
|
|
|
475
457
|
### Contrast Checking
|
|
458
|
+
|
|
476
459
|
- ✅ Always check against actual backgrounds
|
|
477
460
|
- ✅ Test at different font sizes
|
|
478
461
|
- ❌ Don't rely on color alone for meaning
|
|
479
462
|
|
|
480
463
|
### Gradients
|
|
464
|
+
|
|
481
465
|
- ✅ Use LAB/HCT for smooth gradients
|
|
482
466
|
- ✅ Limit to 5-10 steps for performance
|
|
483
467
|
- ❌ Don't interpolate in RGB (causes muddy colors)
|
|
@@ -487,4 +471,4 @@ function generatePalette(baseColor, options = {}) {
|
|
|
487
471
|
- Try [Creating Themes](./creating-themes.md) for complete design systems
|
|
488
472
|
- Learn [CSS Refactoring](./css-refactoring.md) to modernize stylesheets
|
|
489
473
|
- Explore [Image Extraction](./image-extraction.md) for dynamic colors
|
|
490
|
-
- Read about [Distance Metrics](../concepts/distance-metrics.md) for color matching
|
|
474
|
+
- Read about [Distance Metrics](../concepts/distance-metrics.md) for color matching
|