The API documentation below will guide you in utilizing the @adobe/leonardo-contrast-colors
library within your development projects. This powerful tool allows you to effortlessly Compare Hex Colors and generate accessible and harmonious color palettes based on contrast ratios.
Quick Start
Dive straight into using Leonardo with these simple steps.
Installation
Begin by installing the package into your project using npm:
npm install @adobe/leonardo-contrast-colors
Importing the Package
Choose the import method that suits your environment:
CommonJS (CJS) – Node 12.x
const { Theme, Color, BackgroundColor } = require('@adobe/leonardo-contrast-colors');
ECMAScript Modules (ESM) – Node 13.x and later
import { Theme, Color, BackgroundColor } from '@adobe/leonardo-contrast-colors';
Creating a Theme
To start generating colors, you need to define your color scheme. This involves creating Color
and BackgroundColor
objects and passing them to a new Theme
instance.
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});
// Retrieve theme colors as JSON
let colors = theme.contrastColors;
API Reference
Explore the detailed API reference to understand the functionalities of the Leonardo library.
Theme
Class
The Theme
class is central to generating adaptive contrast-based color palettes. It accepts several parameters to customize your theme.
Parameter | Type | Description |
---|---|---|
colors |
Array | An array of Color classes that define the colors for your theme. Must include at least one BackgroundColor class. |
lightness |
Number | A value between 0 and 100 representing the desired lightness of the generated background color. Must be a whole number. |
contrast |
Number | A multiplier to adjust the overall contrast of all theme colors. The default value is 1 . |
saturation |
Number | A value between 0 and 100 to control the saturation of all theme colors. The default is 100 (no desaturation). |
output |
Enum | Specifies the desired color output format (e.g., HEX, RGB, HSL). |
Theme Setters
These setters allow you to dynamically modify properties of an existing Theme
instance.
Setter | Description |
---|---|
Theme.lightness |
Sets the lightness value for the theme’s background color. |
Theme.contrast |
Adjusts the contrast multiplier for all colors in the theme. |
Theme.saturation |
Modifies the saturation level for all colors in the theme. |
Theme.backgroundColor |
Updates the background color of the theme. Accepts either a BackgroundColor class or a hex color string (which will create a new BackgroundColor ). |
Theme.colors |
Replaces the entire array of colors in the theme. Must be an array of Color classes. |
Theme.output |
Changes the output format for all colors in the theme. |
Theme.addColor |
Adds a new Color to the existing theme. |
Theme.removeColor |
Removes a Color from the theme. |
Theme.updateColor |
Modifies properties of an existing Color within the theme using its setters. |
Theme.addColor = color
This setter allows you to add a new Color
object to your existing theme, expanding your color palette dynamically.
let red = new Color({ name: 'red', colorKeys: ['#FF9A81', '#FF0000'], ratios: [3, 4.5] });
theme.addColor = red;
Theme.removeColor = color
Remove a Color
from the theme. You can remove a color either by providing an object with the Color
‘s name or by passing the Color
class instance itself.
// Remove by color name
theme.removeColor = {name: 'Red'};
// Remove by Color class
const red = new Color({ name: 'red', colorKeys: ['#FF9A81', '#FF0000'], ratios: [3, 4.5] });
theme.removeColor = red;
Theme.updateColor = {name, property}
Modify an existing Color
within the theme by using this setter. You need to specify the name
of the color you want to update and the property
you wish to change along with its new value.
// Change the color ratios
theme.updateColor = {color: 'red', ratios: [3, 4.5, 7]};
// Change the color colorKeys
theme.updateColor = {color: 'red', colorKeys: ['#ff0000']};
// Change the color's name
theme.updateColor = {color: 'red', name: 'Crimson'};
You can also update multiple properties of a color in a single call.
// Change ratios, colorKeys, and name simultaneously
theme.updateColor = {color: 'red', ratios: [3, 4.5, 7], colorKeys: ['#ff0000'], name: 'Crimson'};
Supported Output Formats
Leonardo supports a wide range of output formats, adhering to the W3C CSS Color Module Level 4 specification.
Output option | Sample 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
The Color
class is used to define individual colors within your theme. It allows you to specify color keys, interpolation colorspaces, and target contrast ratios, enabling you to create a nuanced color scale.
Parameter | Type | Description |
---|---|---|
name |
String | A user-defined name for the color (e.g., “Blue”). This name is used to identify the color in the output. |
colorKeys |
Array of strings | An array of hex color codes that serve as key points for color interpolation. Leonardo will generate a continuous color scale between these keys. |
colorspace |
Enum | The colorspace used for interpolation between the colorKeys . See Supported Interpolation Colorspaces for options. |
ratios |
Array or Object | Defines the target contrast ratios for generating color variations. Can be an array of ratios or an object for named ratios. |
smooth |
Boolean | When set to true , applies bezier smoothing to the color interpolation for a more gradual transition. Defaults to false . |
output |
Enum | Specifies the desired color output format for this specific color, overriding the theme’s output format if set. |
Color Setters
These setters enable dynamic modification of Color
object properties.
Setter | Description |
---|---|
Color.colorKeys |
Updates the array of color keys used for interpolation. |
Color.colorspace |
Changes the interpolation colorspace. |
Color.ratios |
Modifies the target contrast ratios. |
Color.name |
Renames the color. |
Color.smooth |
Toggles bezier smoothing on or off. |
Color.output |
Sets the output format for the color. |
Supported Interpolation Colorspaces
Leonardo offers a variety of colorspaces for color interpolation, allowing you to fine-tune how colors transition within your palettes.
'CAM02'
'CAM02p'
'LAB'
'LCH'
'HSLuv'
'HSL'
'HSV'
'RGB'
Ratios as an Array
When you provide ratios as a simple array, Leonardo automatically generates names for the output colors by appending numerical increments to the base color name. Positive contrast ratios (greater than 1:1 with the background) are incremented in multiples of 100 (e.g., gray100
, gray200
).
Negative contrast ratios (less than 1:1 with the background) are also numerically incremented, but the increment is calculated based on the number of negative ratios provided. For instance, with ratios [-1.4, -1.3, -1.2, 1, 2, 3]
, negative values will be named with increments of 100 divided by the number of negative values plus one (in this case, 100/4), leading to names like gray25
, gray50
, and gray75
.
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
For more control over naming, you can define ratios as an object with key-value pairs. The keys in the object will be used as the names for the generated color variations in your Leonardo theme. This is especially useful when you need semantic names that describe the color’s purpose, such as for different text sizes or UI elements. This allows for a more intuitive way to compare hex colors assigned to specific UI roles.
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
The Theme
class provides different ways to access the generated color values, catering to various use cases.
Getter | Description of output |
---|---|
Theme.contrastColors |
Returns an array of color objects, each containing key-value pairs with color details. |
Theme.contrastColorPairs |
Provides a simplified object with key-value pairs where keys are color names and values are hex color codes. |
Theme.contrastColorValues |
Returns a flat array containing only the generated hex color values. |
Theme.contrastColors
This getter returns a structured array where each element represents a color defined in your theme. Each color object includes a values
array, listing all generated color variations with their name
, contrast ratio
, and value
(in the specified output format, HEX by default). The background color is also included as the first element in the array.
[
{ 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
For a simpler output, contrastColorPairs
returns an object where keys are the generated color names (either numeric or user-defined) and values are the corresponding hex color codes. This format is ideal for direct use in CSS or JavaScript applications where you need to quickly access colors by name. This makes it easy to compare hex colors by their assigned names.
{
"gray100": "#e0e0e0",
"gray200": "#a0a0a0",
"gray300": "#808080",
"gray400": "#646464",
"blue100": "#b18cff",
"blue200": "#8d63ff",
"blue300": "#623aff",
"blue400": "#1c0ad1",
}
Theme.contrastColorValues
If you only need a flat array of hex color values without names or contrast information, contrastColorValues
provides just that. This can be useful for quickly iterating through all generated colors or when the color order is sufficient for your needs.
[
"#e0e0e0",
"#a0a0a0",
"#808080",
"#646464",
"#b18cff",
"#8d63ff",
"#623aff",
"#1c0ad1"
]
Leonardo with CSS Variables
Leonardo can be seamlessly integrated with CSS variables, enabling dynamic theme updates in your application. Here are examples for Vanilla JS and React.
Vanilla JS
This example demonstrates how to dynamically create CSS variables from a Leonardo theme in Vanilla JavaScript.
let varPrefix = '--';
// Iterate through each color object
for (let i = 0; i < myTheme.length; i++) {
// Iterate through 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
For React applications, you can create a Theme
component and utilize the css-vars-hook
to manage CSS variables.
Create a Theme.js
component:
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;
Import the Theme
component in your main application file (e.g., index.js
):
// index.js
import Theme from './components/Theme';
import ReactDOM from 'react-dom';
import React from 'react';
ReactDOM.render(
<React.StrictMode>
<App adaptiveTheme={Theme()} />
</React.StrictMode>,
document.getElementById('root')
);
In your App.js
file, use the useTheme
hook from css-vars-hook
to apply the Leonardo theme as CSS variables.
// App.js
import {useTheme} from 'css-vars-hook';
import React, {useState} from 'react';
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);
function _updateColorVariables() {
let themeInstance = _createThemeObject();
for (const [key, value] of Object.entries( themeInstance )) {
setVariable(key, value);
}
};
// call function to set initial values
_updateColorVariables();
return (
<div className="App" ref={setRef} >
<div>
<label htmlFor="lightness">
Lightness
<input value={lightness} id="lightness" type="range" min={ 80 } max={ 100 } step="1" onChange={e => {
setLightness(e.target.value)
props.adaptiveTheme.lightness = e.target.value
_updateColorVariables()
}} />
</label>
<label htmlFor="contrast">
Contrast
<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()
}} />
</label>
</div>
</div>
)
}
To enable dynamic theme updates based on user interactions (like sliders), include the _updateColorVariables
function and call it within your event handlers.
Dark Mode Support in React
Implement dark mode support by listening to the user’s system preference and adjusting the Leonardo theme accordingly.
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 might observe that the API often outputs a contrast ratio slightly higher than the target ratio you input. This is due to the limitations of the RGB color space and the mathematical complexities of contrast ratio calculations.
For instance, consider blue and white:
- Blue: rgb(0, 0, 255)
- White: rgb(255, 255, 255)
- Contrast ratio: 8.59:1
Even a minor change in the RGB value of either color alters the contrast ratio:
- Blue: rgb(0, 1, 255)
- White: rgb(255, 255, 255)
- Contrast ratio: 8.57:1
If you input 8.58 as the target ratio with blue as the starting color, the exact ratio might not be achievable. This discrepancy is further influenced by different colorspace interpolations.
Given that WCAG defines minimum contrast requirements, generating colors that are slightly more accessible than the minimum is considered acceptable and even beneficial for ensuring accessibility.
Chroma.js
Leonardo is built upon Chroma.js, enhanced with custom extensions to support CIE CAM02. Leonardo further refines chroma scales to ensure colors are correctly ordered by lightness and adjusts the lightness of the scale based on HSLuv for improved perceptual uniformity.
Contributing
We welcome contributions to Leonardo! Please read the Contributing Guide for detailed information on how you can contribute.
Development
For local development and testing, you can use the following command to run tests and watch for file changes:
npm run dev
Licensing
Leonardo is licensed under the Apache V2 License. See the LICENSE file for more details.