内容
# 可访问色板
[](https://www.npmjs.com/package/accessible-color-palette)
[](https://github.com/ceciCoding/accessible-color-palette/blob/main/LICENSE)
[](https://www.npmjs.com/package/accessible-color-palette)
[](https://www.npmjs.com/package/accessible-color-palette)
一个TypeScript库,从任何十六进制颜色生成**WCAG 2.2 AA合规的单色调色板**。调色板算法没有外部依赖——WCAG数学直接实现。
给定十六进制颜色和背景主题(`'white'` | `'black'`),它生成6种色度——**100, 300, 600, 700, 800, 900**——其中每种色度都满足与主题的对比度要求。同时返回一个使用映射,描述哪些色度对通过AA正常文本(4.5:1)和哪些通过大文本/UI组件(3:1)。
作为ESM库、**CLI**和**MCP服务器**与AI助手一起使用。
---
## 安装
```bash
npm install accessible-color-palette
```
---
## 用法
### 作为CLI
```bash
npx accessible-color-palette <hex> <theme> [options]
```
| 参数/选项 | 描述 |
|-----------|-------------|
| `hex` | 十六进制颜色——带或不带`#`,3或6个字符 |
| `theme` | 背景主题:`white` | `black` |
| `--prefix <name>` | CSS变量前缀(默认值:`color`) |
| `--json` | 输出完整的调色板+使用映射为JSON |
| `-h, --help` | 显示帮助 |
```bash
# 带有WCAG清单的CSS令牌(默认)
npx accessible-color-palette 1F7A54 white
# 自定义前缀
npx accessible-color-palette 1F7A54 white --prefix brand
# 原始JSON
npx accessible-color-palette 1F7A54 white --json
```
输出(默认):
```css
/*
* WCAG AA配对清单——来源:#1f7a54 · 主题:white
*
* 正文文本(任意字体大小,≥4.5:1):
* shade-700 (#207c55) → white, 100
* shade-800 (#124630) → white, 100, 300
* ...
*/
:root {
--color-100: #e3f8ef; /* ✅ text→900·800·700 ⚠️ lg→600 */
--color-700: #207c55; /* ✅ text→white·100 ⚠️ lg→300·900 */
...
}
```
---
### 作为库
```ts
import { generatePalette, toCSSTokens } from 'accessible-color-palette'
const result = generatePalette('#1F7A54', 'white')
// 每个色度都包含十六进制、RGB和HSL值
console.log(result.palette['700'])
{
hex: '#1f7a54',
rgb: { r: 31, g: 122, b: 84 },
hsl: { h: 152, s: 59, l: 30 }
}
// 使用映射:对于每个色度,哪些背景通过正常文本和大文本
console.log(result.usage['700'].normalText)
[
{ key: '100', hex: '#d6f5e8', ratio: 4.18 },
{ key: 'background', hex: '#ffffff', ratio: 5.10 },
...
]
console.log(result.usage['700'].largeText)
[{ key: '300', hex: '#71dbae', ratio: 3.21 }, ...]
// CSS令牌
console.log(toCSSTokens(result))
--color-100: #d6f5e8;
--color-300: #71dbae;
--color-600: #259868;
--color-700: #1f7a54;
--color-800: #10422d;
--color-900: #082116;
// 自定义前缀
console.log(toCSSTokens(result, 'brand'))
--brand-100: #d6f5e8;
// ...
// 在使用前验证前景/背景色度对
import { validatePairings } from 'accessible-color-palette'
const report = validatePairings(result, [
{ foreground: '700', background: 'white' },
{ foreground: '100', background: '900' },
])
console.log(report.allPass) // true
console.log(report.pairings[0]) // { level: 'aa-normal', message: '✓ ...', ... }
// 检查两个任意十六进制颜色之间的对比度——例如品牌强调色
// 不是从生成的调色板中
import { checkContrast } from 'accessible-color-palette'
console.log(checkContrast('#ffffff', '#c75d3a'))
{
foreground: '#ffffff',
background: '#c75d3a',
ratio: 4.15,
level: 'aa-large',
message: '⚠ #ffffff on #c75d3a: 4.15:1 — AA large text only (3:1–4.5:1) ...'
}
```
### 作为MCP服务器
还作为MCP服务器,提供`generate_palette`、`validate_pairings`、`generate_css_tokens`和`check_contrast`等工具,以及`palette://{hex}/{theme}`资源和`plan-palette-usage`引导提示。`generate_css_tokens`强制实施服务器端门控——它拒绝请求十六进制+主题,直到`validate_pairings`在同一会话中通过。
参见 **[MCP.md](./MCP.md)** 获取客户端配置、完整的工具表格以及门控和引导工作流之间的关系。
---
## 输出形状
```ts
interface PaletteResult {
palette: Record<'100' | '300' | '600' | '700' | '800' | '900', {
hex: string
rgb: { r: number; g: number; b: number } // 0–255
hsl: { h: number; s: number; l: number } // h: 0–360, s/l: 0–100
}>
usage: Record<'100' | '300' | '600' | '700' | '800' | '900', {
hex: string
normalText: Array<{ key: string; hex: string; ratio: number }>
largeText: Array<{ key: string; hex: string; ratio: number }>
}>
theme: 'white' | 'black'
sourceColor: string
}
```
- `normalText` —— 背景下该色度通过 **4.5:1** 或更好(正文,小型UI)
- `largeText` —— 背景下该色度通过 **3:1–4.49:1** (标题,大文本,图标)
- 失败的组合(< 3:1)完全省略
- `key` 是色度(`'100'`–`'900'`)或 `'background'` (白色或黑色,取决于主题)
---
## 示例输出
**`generatePalette('#1F7A54', 'white')`**
| 色度 | 十六进制 | RGB | HSL |
|-------|-----|-----|-----|
| 100 | `#d6f5e8` | `214, 245, 232` | `152°, 62%, 90%` |
| 300 | `#71dbae` | `113, 219, 174` | `152°, 59%, 65%` |
| 600 | `#259868` | `37, 152, 104` | `152°, 61%, 37%` |
| 700 | `#1f7a54` | `31, 122, 84` | `152°, 59%, 30%` |
| 800 | `#10422d` | `16, 66, 45` | `152°, 61%, 16%` |
| 900 | `#082116` | `8, 33, 22` | `152°, 61%, 8%` |
**`generatePalette('#239062', 'black')`** — 黑色主题反转亮度方向
| 色度 | 十六进制 | RGB | HSL |
|-------|-----|-----|-----|
| 100 | `#061911` | `6, 25, 17` | `152°, 61%, 6%` |
| 300 | `#0e3927` | `14, 57, 39` | `152°, 61%, 14%` |
| 600 | `#1c734e` | `28, 115, 78` | `152°, 61%, 28%` |
| 700 | `#239062` | `35, 144, 98` | `152°, 61%, 35%` |
| 800 | `#4fd498` | `79, 212, 152` | `152°, 59%, 57%` |
| 900 | `#b9eed8` | `185, 238, 216` | `152°, 62%, 83%` |
对于 `theme: 'black'`,色度100是最暗的。亮度顺序相对于 `theme: 'white'` 是反转的——这是设计如此。
---
## 算法工作原理
### 核心原始
每个色度都通过相同的迭代机制找到:从一个颜色开始,以0.5%的步长调整HSL亮度,当与参考颜色的WCAG 2.2对比度达到目标时停止。
```
stepTowardRatio(startHex, referenceHex, targetRatio, direction)
1. 将startHex转换为HSL
2. 每步调整亮度+/- 0.005
3. 每次调整后:计算contrastRatio(current, referenceHex)
4. 当比例 >= targetRatio时停止
5. 安全限制:抛出后1000步
```
对比度始终使用**精确的WCAG 2.2公式**计算——没有近似,没有HSL亮度快捷方式。
### 色度派生链
700是锚点。其他所有色度都从它派生(或从派生自它的色度)。
```
inputHex --> find700 --> shade700 --> find100 --> shade100 --> find600 --> shade600 --> find800 --> shade800
| |
|--> find300 --> shade300 +- (reference for 600 and 800)
|
+--> find900 --> shade900
```
### 决策流程图
```mermaid
flowchart TD
A["输入:颜色 + 主题(白色 | 黑色)"]
A --> B["查找 700\n4.5:1 在背景上"]
B --> C["计算对比度比率\n颜色与背景之间"]
C --> D{"比率 === 5.1:1?"}
D -->|是| Z700["色度 700 = 颜色"]
D -->|"白色 + 比率 > 5.1"| E1["增加亮度直到比率 > 5.1\n如果降低 → 删除 1 步"]
D -->|"白色 + 比率 < 5.1"| E2["减少亮度直到比率 ≥ 5.1"]
D -->|"黑色 + 比率 > 5.1"| E3["减少亮度直到比率 > 5.1\n如果降低 → 增加 1 步"]
D -->|"黑色 + 比率 < 5.1"| E4["增加亮度直到比率 ≥ 5.1"]
E1 & E2 & E3 & E4 --> Z700
Z700 --> F100["查找 100\n照亮(4.5, 700)[白色]\n变暗(4.5, 700)[黑色]"]
Z700 --> F300["查找 300\n照亮(3.1, 700)[白色]\n变暗(3.1, 700)[黑色]"]
Z700 --> F900["查找 900\n变暗(3.1, 700)[白色]\n照亮(3.1, 700)[黑色]"]
F100 --> Z100["色度 100"]
F300 --> Z300["色度 300"]
F900 --> Z900["色度 900"]
Z100 --> F600["查找 600\n变暗(3.1, 100)[白色]\n照亮(3.1, 100)[黑色]"]
F600 --> Z600["色度 600"]
Z600 --> F800["查找 800\n变暗(3.1, 600)[白色]\n照亮(3.1, 600)[黑色]"]
F800 --> Z800["色度 800"]
```
### 色度-by-色度合同
| 色度 | 来源 | 目标对比度 | 方向(白色背景) |
|-------|-------------|-----------------|----------------------|
| 700 | inputHex | 5.1:1 vs background | 变暗如果太亮,变亮如果太暗 |
| 100 | 色度700 | 4.5:1 vs 色度700 | 变亮 |
| 300 | 色度700 | 3.1:1 vs 色度700 | 变亮 |
| 600 | 色度100 | 3.1:1 vs 色度100 | 变暗 |
| 800 | 色度600 | 3.1:1 vs 色度600 | 变暗 |
| 900 | 色度700 | 3.1:1 vs 色度700 | 变暗 |
---
## 设计原则
- **无类**。纯功能——无`class`,无`new`,无`this`。
- **无突变**。每个函数都返回一个新值。
- **无外部颜色库**。WCAG数学约25行,直接实现。
- **严格TypeScript**。`strict: true`,带有`HexColor`品牌类型,以防止原始字符串通过API泄漏。
- **纯函数**。相同的输入,相同的输出。唯一的副作用在`src/mcp/server.ts`中。
- **单一责任**。每个函数只做一件事。
---
## API参考
### `generatePalette(hex: string, theme: 'white' | 'black'): PaletteResult`
主要入口点。接受原始十六进制字符串,带或不带 `#`,3或6个字符。对于无效十六进制,会抛出`Error`并附上包含无效值的消息。
### `toCSSTokens(result: PaletteResult, prefix?: string): string`
从`PaletteResult`生成CSS自定义属性声明。默认前缀为 `'color'`。不包装在 `:root {}` 中——调用者决定放置位置。
### `validatePairings(result: PaletteResult, pairings: Array<{ foreground: string; background: string }>): ValidationReport`
检查`result`的兼容性矩阵中的前景/背景色度对。`foreground`必须是色度键(`100 | 300 | 600 | 700 | 800 | 900`);`background`可以是色度键、`'white'`或 `'black'`。返回 `{ summary, allPass, pairings }` —— 每个配对都有一个 `level`(`'aa-normal' | 'aa-large' | 'fail' | 'invalid'`)和一个人类可读的 `message`。与MCP服务器的`validate_pairings`工具使用相同的纯逻辑。
### `checkContrast(foregroundHex: string, backgroundHex: string): ContrastCheckResult`
两个十六进制颜色之间的独立WCAG 2.2对比度检查。不绑定到生成的调色板。使用它用于不属于单色调尺度的强调色(品牌颜色,状态颜色)。返回 `{ foreground, background, ratio, level, message }`。
---
## 运行测试
```bash
npm test
npm run test:coverage
```
使用 [Vitest](https://vitest.dev/)。集成测试验证已知输出与原始Figma测试用例。
---
## 构建
```bash
npm run build # tsc -> dist/
npm run mcp # 启动MCP服务器(需要先构建)
```
不需要打包器——库以ESM形式提供,Node原生解析。
---
## 从v1迁移
v2是完全重写的算法相同,但其他一切都改变了——API不兼容。
**有什么变化:
- 没有类。v1使用`AccessibleColorPalette.generatePalette(...)`。v2使用命名ESM导出。
- 输出形状不同。v1返回带有`compatibilities`键的色度对象。v2返回`palette`对象和单独的`usage`映射。
- 没有外部依赖。v1使用`colorsys`和`@mdhnpm/color-contrast-ratio-calculator`。v2直接实现了WCAG数学。
- 随附MCP服务器与AI助手一起使用。
**v1(旧):**
```js
const AccessibleColorPalette = require('accessible-color-palette')
AccessibleColorPalette.generatePalette('#1c734e', 'black')
// → { "700": { compatibilities: { largeText: [...], smallText: [...] }, hex, rgb, hsl } }
```
**v2(新):
```ts
import { generatePalette } from 'accessible-color-palette'
generatePalette('#1c734e', 'black')
// → { palette: { "700": { hex, rgb, hsl } }, usage: { "700": { normalText: [...], largeText: [...] }, theme, sourceColor }
```
v1在npm上仍然可用为`accessible-color-palette@1.x`但不再维护。
---
## 致谢
算法由 **Marta Herrera Hollingsworth** 提供。库和MCP由 **Cecilia Olivera** 提供。