@adobe/leonardo-contrast-colors

中文文档
1.1.0发布于 2026/02/18Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 7.5万

Generate colors based on a desired contrast ratio

原始使用文档
文档目录20 节
  1. Using Leonardo Contrast Colors
  2. Install the package:
  3. Import the package:
  4. Create and pass colors and a background color to a new Them…
  5. API Reference
  6. Theme
  7. Color
  8. Output examples
  9. Theme.contrastColors
  10. Theme.contrastColorPairs
  11. Theme.contrastColorValues
  12. Leonardo with CSS variables
  13. Vanilla JS
  14. React
  15. Dark mode support in React
  16. Why are not all contrast ratios available?
  17. Chroma.js
  18. Contributing
  19. Development
  20. Licensing

@adobe/leonardo-contrast-colors

npm version Package size Minified size Minified and gzipped size license Libraries.io dependency status for latest release, scoped npm package license
Pull requests welcome

This package contains all the functions for generating colors by target contrast ratio.

Using Leonardo Contrast Colors

Install the package:

npm i @adobe/leonardo-contrast-colors

Import the package:

CJS (Node 12.x)

const {Theme, Color, BackgroundColor} = require('@adobe/leonardo-contrast-colors');

ESM (Node 13.x)

import {Theme, Color, BackgroundColor} from '@adobe/leonardo-contrast-colors';

Create and pass colors and a background color to a new Theme (see additional options below):

let gray = new BackgroundColor({
  name: 'gray',
  colorKeys: ['#cacaca'],
  ratios: [2, 3, 4.5, 8]
});

let blue = new Color({
  name: 'blue',
  colorKeys: ['#5CDBFF', '#0000FF'],
  ratios: [3, 4.5]
});

let red = new Color({
  name: 'red',
  colorKeys: ['#FF9A81', '#FF0000'],
  ratios: [3, 4.5]
});

let theme = new Theme({
  colors: [gray, blue, red],
  backgroundColor: gray,
  lightness: 97
});

// returns theme colors as JSON
let colors = theme.contrastColors;

API Reference

Theme

Class function used to generate adaptive contrast-based colors. Parameters are destructured and need to be explicitly called.

ParameterTypeDescription
colorsArrayList of Color classes to generate theme colors for. A single BackgroundColor class is required.
lightnessNumberValue from 0-100 for desired lightness of generated theme background color (whole number)
contrastNumberMultiplier to increase or decrease contrast for all theme colors (default is 1)
saturationNumberValue from 0-100 for decreasing saturation of all theme colors (default is 100)
outputEnumDesired color output format

Setters

SetterDescription of output
Theme.lightnessSets the theme's lightness value
Theme.contrastSets the theme's contrast value
Theme.saturationSets the theme's saturation value
Theme.backgroundColorSets the theme's background color (creates a new BackgroundColor if passing a string)
Theme.colorsSets colors for theme (must pass Color)
Theme.outputSets output format for theme
Theme.addColorAdd a Color to the theme
Theme.removeColorRemove a Color from the theme
Theme.updateColorUpdate a Color via its setters from the theme

Theme.addColor = color

Add a Color to an existing theme

const red = new Color({...})

theme.addColor = red;

Theme.removeColor = color

Remove a Color from an existing theme. Accepts an object with the Color's name and value, or by passing the Color class itself.

// Remove via color name
theme.removeColor = {name: 'Red'};

// Remove via Color class
const red = new Color({...})
theme.removeColor = red;

Theme.updateColor = {name, property}

Update a Color via its setters from the theme. Accepts an object with the name of the color you wish to modify, followed by the property and the new value you wish to modify.

const red = new Color({...})
// Change the colors ratios
theme.updateColor = {name: 'red', ratios: [3, 4.5, 7]};

// Change the colors colorKeys
theme.updateColor = {name: 'red', colorKeys: ['#ff0000']};

// Change the color's name
theme.updateColor = {name: 'red', name: 'Crimson'};

Supported output formats:

Available output formats conform to the W3C CSS Color Module Level 4 spec for the supported options, as listed below:

Output optionSample value
'HEX' (default)#RRGGBB
'RGB'rgb(255, 255, 255)
'HSL'hsl(360deg, 0%, 100%)
'HSV'hsv(360deg, 0%, 100%)
'HSLuv'hsluv(360, 0, 100)
'LAB'lab(100%, 0, 0)
'LCH'lch(100%, 0, 360deg)
'CAM02'jab(100%, 0, 0)
'CAM02p'jch(100%, 0, 360deg)

Color

Class function used to define colors for a theme. Parameters are destructured and need to be explicitly called.

ParameterTypeDescription
nameStringUser-defined name for a color, (eg "Blue"). Used to name output color values
colorKeysArray of stringsList of specific colors to interpolate between in order to generate a full lightness scale of the color.
colorspaceEnumThe colorspace in which the key colors will be interpolated within.
ratiosArray or ObjectList of target contrast ratios, or object with named keys for each value.
smoothBooleanApplies bezier smoothing to interpolation (false by default)
outputEnumDesired color output format

Setters

SetterDescription of output
Color.colorKeysSets the color keys
Color.colorspaceSets the interpolation colorspace
Color.ratiosSets the ratios
Color.nameSets the name
Color.smoothSets the smoothing option
Color.outputSets the output format

Supported interpolation colorspaces:

Below are the available options for interpolation in Leonardo:

Ratios as an array

When passing a flat array of target ratios, the output colors in your Theme will be generated by concatenating the color name (eg "Blue") with numeric increments. Colors with a positive contrast ratio with the base (ie, 2:1) will be named in increments of 100. For example, gray100, gray200.

Colors with a negative contrast ratio with the base (ie -2:1) will be named in increments less than 100 and based on the number of negative values declared. For example, if there are 3 negative values [-1.4, -1.3, -1.2, 1, 2, 3], the name for those values will be incremented by 100/4 (length plus one to avoid a 0 value), such as gray25, gray50, and gray75.

For example:

new Color({
  name: 'blue',
  colorKeys: ['#5CDBFF', '#0000FF'],
  colorSpace: 'LCH',
  ratios: [3, 4.5]
});

// Returns:
[
  {
    name: 'blue',
    values: [
      {name: 'blue100', contrast: 3, value: '#8d63ff'},
      {name: 'blue200', contrast: 4.5, value: '#623aff'}
    ]
  }
];

Ratios as an object

When defining ratios as an object with key-value pairs, you define what name will be output in your Leonardo theme.

new Color({
  name: 'blue',
  colorKeys: ['#5CDBFF', '#0000FF'],
  colorSpace: 'LCH',
  ratios: {
    'blue--largeText': 3,
    'blue--normalText': 4.5
  }
});

// Returns:
[
  {
    name: 'blue',
    values: [
      {name: 'blue--largeText', contrast: 3, value: '#8d63ff'},
      {name: 'blue--normalText', contrast: 4.5, value: '#623aff'}
    ]
  }
];

Output examples

There are two types of output you can get from the Theme class:

GetterDescription of output
Theme.contrastColorsReturns array of color objects with key-value pairs
Theme.contrastColorPairsReturns object with key-value pairs
Theme.contrastColorValuesReturns flat array of color values

Theme.contrastColors

Each color is an object named by user-defined value (eg name: 'gray'). "Values" array consists of all generated color values for the color, with properties name, contrast, and value:

[
  {background: '#e0e0e0'},
  {
    name: 'gray',
    values: [
      {name: 'gray100', contrast: 1, value: '#e0e0e0'},
      {name: 'gray200', contrast: 2, value: '#a0a0a0'},
      {name: 'gray300', contrast: 3, value: '#808080'},
      {name: 'gray400', contrast: 4.5, value: '#646464'}
    ]
  },
  {
    name: 'blue',
    values: [
      {name: 'blue100', contrast: 2, value: '#b18cff'},
      {name: 'blue200', contrast: 3, value: '#8d63ff'},
      {name: 'blue300', contrast: 4.5, value: '#623aff'},
      {name: 'blue400', contrast: 8, value: '#1c0ad1'}
    ]
  }
];

Theme.contrastColorPairs

Simplified format as an object of key-value pairs. Property is equal to the generated or user-defined name for each generated value.

{
  "gray100": "#e0e0e0";
  "gray200": "#a0a0a0";
  "gray300": "#808080";
  "gray400": "#646464";
  "blue100": "#b18cff";
  "blue200": "#8d63ff";
  "blue300": "#623aff";
  "blue400": "#1c0ad1";
}

Theme.contrastColorValues

Returns all color values in a flat array.

['#e0e0e0', '#a0a0a0', '#808080', '#646464', '#b18cff', '#8d63ff', '#623aff', '#1c0ad1'];

Leonardo with CSS variables

Here are a few examples of how you can utilize Leonardo to dynamically create or modify CSS variables for your application.

Vanilla JS

let varPrefix = '--';

// Iterate each color object
for (let i = 0; i < myTheme.length; i++) {
  // Iterate each value object within each color object
  for (let j = 0; j < myTheme[i].values.length; j++) {
    // output "name" of color and prefix
    let key = myTheme[i].values[j].name;
    let prop = varPrefix.concat(key);
    // output value of color
    let value = myTheme[i].values[j].value;
    // create CSS property with name and value
    document.documentElement.style.setProperty(prop, value);
  }
}

React

Create a new Theme component Theme.js with your parameters:

import * as Leo from '@adobe/leonardo-contrast-colors';

const Theme = () => {
  let gray = new Leo.BackgroundColor({
    name: 'gray',
    colorKeys: ['#cacaca'],
    ratios: [2, 3, 4.5, 8]
  });

  let blue = new Leo.Color({
    name: 'blue',
    colorKeys: ['#5CDBFF', '#0000FF'],
    ratios: [3, 4.5]
  });

  let red = new Leo.Color({
    name: 'red',
    colorKeys: ['#FF9A81', '#FF0000'],
    ratios: [3, 4.5]
  });

  const adaptiveTheme = new Leo.Theme({
    colors: [gray, blue, red],
    backgroundColor: gray,
    lightness: 97,
    contrast: 1
  });

  return adaptiveTheme;
};

export default Theme;

Then import your Theme component at the top level of your application, and pass the Theme as a property of your app:

// index.js
import Theme from './components/Theme';

ReactDOM.render(
  <React.StrictMode>
    <App adaptiveTheme={Theme()} />
  </React.StrictMode>,
  document.getElementById('root')
);

In your App.js file, import useTheme from css-vars-hook and provide the following within your App function in order to format Leonardo's output in the structure required for css-vars-hook.

// App.js
import {useTheme} from 'css-vars-hook';

function App(props) {
  const [lightness, setLightness] = useState(100);
  const [contrast, setContrast] = useState(1);

  const _createThemeObject = () => {
    let themeObj = {};
    props.adaptiveTheme.contrastColors.forEach((color) => {
      if (color.name) {
        let values = color.values;
        values.forEach((instance) => {
          let name = instance.name;
          let val = instance.value;
          themeObj[name] = val;
        });
      } else {
        // must be the background
        let name = 'background';
        let val = color.background;
        themeObj[name] = val;
      }
    });
    return themeObj;
  };

  const theme = useState(_createThemeObject());

  const {setRef, setVariable} = useTheme(theme);

  return <div className="App" ref={setRef}></div>;
}

To make your application adaptive, include a function for updating your theme before your return function:

function _updateColorVariables() {
  let themeInstance = _createThemeObject();

  for (const [key, value] of Object.entries(themeInstance)) {
    setVariable(key, value);
  }
}
// call function to set initial values
_updateColorVariables();

Finally, reference this function and set the theme parameters when your users interact with slider components (do the same for Contrast):

<label htmlFor="lightness">
  Lightness
</label>
<input
  value={lightness}
  id="lightness"
  type="range"
  min={ sliderMin }
  max={ sliderMax }
  step="1"
  onChange={e => {
    setLightness(e.target.value)
    props.adaptiveTheme.lightness = e.target.value
    _updateColorVariables()
  }}
/>
<label htmlFor="contrast">
  Contrast
</label>
<input
  value={contrast}
  id="contrast"
  type="range"
  min="0.25"
  max="3"
  step="0.025"
  onChange={e => {
    setContrast(e.target.value)
    props.adaptiveTheme.contrast = e.target.value
    _updateColorVariables()
  }}
/>

Dark mode support in React

Include the following in your App.js file to listen for dark mode. This will pass a different lightness value (of your choice) to Leonardo. It's recommended to restrict the lightness range based on mode in order to avoid inaccessible ranges and to provide a better overall experience

const mq = window.matchMedia('(prefers-color-scheme: dark)');
// Update lightness and slider min/max to be conditional:
const [lightness, setLightness] = useState(mq.matches ? 8 : 100);
const [sliderMin, setSliderMin] = useState(mq.matches ? 0 : 80);
const [sliderMax, setSliderMax] = useState(mq.matches ? 30 : 100);

// Listener to update when user device mode changes:
mq.addEventListener('change', function (evt) {
  props.adaptiveTheme.lightness = mq.matches ? 11 : 100;
  setLightness(mq.matches ? 11 : 100);
  setSliderMin(mq.matches ? 0 : 80);
  setSliderMax(mq.matches ? 30 : 100);
});

Why are not all contrast ratios available?

You may notice the tool takes an input (target ratio) but most often outputs a contrast ratio slightly higher. This has to do with the available colors in the RGB color space, and the math associated with calculating these ratios.

For example let's look at blue and white. Blue: rgb(0, 0, 255) White: rgb(255, 255, 255) Contrast ratio: 8.59:1

If we change any one value in the RGB channel for either color, the ratio changes: Blue: rgb(0, 1, 255) White: rgb(255, 255, 255) Contrast ratio: 8.57:1

If 8.58 is input as the target ratio with the starting color of blue, the output will not be exact. This is exaggerated by the various colorspace interpolations.

Since the WCAG requirement is defined as a minimum contrast requirement, it should be fine to generate colors that are a little more accessible than the minimum.


Chroma.js

This project is currently built using Chroma.js with custom extensions to supportCIE CAM02. Additional functionality is added in Leonardo to enhance chroma scales so that they properly order colors by lightness and correct the lightness of the scale based on HSLuv.

Contributing

Contributions are welcomed! Read the Contributing Guide for more information.

Development

You can run tests and watch for changes with:

pnpm dev

or run just tests once with:

pnpm test

Licensing

This project is licensed under the Apache V2 License. See LICENSE for more information.

版本与发布记录

@adobe/leonardo-contrast-colors 共发布 21 个版本,首次发布于 2019/12/10;过去 12 个月发布了 2 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.1.0latest2026/02/188 个月前
1.0.12026/02/188 个月前
1.0.02024/03/162 年前
1.0.0-alpha.172023/01/273 年前
1.0.0-alpha.162022/08/134 年前
1.0.0-alpha.152022/08/104 年前

关于 @adobe/leonardo-contrast-colors 的常见问题

@adobe/leonardo-contrast-colors 是什么?

Generate colors based on a desired contrast ratio

如何安装 @adobe/leonardo-contrast-colors?

在项目目录运行 npm install @adobe/leonardo-contrast-colors,或使用 pnpm add / yarn add / bun add。

@adobe/leonardo-contrast-colors 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@adobe/leonardo-contrast-colors 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@adobe/leonardo-contrast-colors 可以商用吗?

@adobe/leonardo-contrast-colors 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@adobe/leonardo-contrast-colors 的下载量有多少?

根据 npm 官方下载统计,@adobe/leonardo-contrast-colors 在 2026/10/05 – 2026/11/03 这 30 天内被下载 75,188 次,日均约 2,506 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@adobe/leonardo-contrast-colors 还在维护吗?

最新版本 1.1.0 发布于 2026/02/18(8 个月前);至今共发布 21 个版本,其中过去 12 个月发布了 2 个;首次发布于 2019/12/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。