# csscolorparser-ts

> Parser for CSS colors written in TypeScript. Supports every CSS Color 4 colour function and converts to sRGB.

Latest version **1.2.0** (published 2026-09-08) · MIT license · 0 weekly downloads

## Install

```sh
npm install csscolorparser-ts
pnpm add csscolorparser-ts
yarn add csscolorparser-ts
bun add csscolorparser-ts
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 1.2.0 |
| Published | 2026-09-08 |
| First published | 2021-03-28 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=18 |
| Dependencies | 0 |
| Unpacked size | 161.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 1 |
| Author | Mikhail Khvoinitsky |
| Maintainers | m_khvoinitsky |
| Keywords | css, color, html5, parser, ts, typescript, oklch, oklab, lab, lch, hwb, css-color-4, srgb, display-p3, wide-gamut |

## Links

- npm: https://www.npmjs.com/package/csscolorparser-ts
- Repository: https://github.com/m-khvoinitsky/csscolorparser-ts
- Homepage: https://github.com/m-khvoinitsky/csscolorparser-ts#readme
- Issues: https://github.com/m-khvoinitsky/csscolorparser-ts/issues
- npm.io page: https://npm.io/package/csscolorparser-ts

## Alternatives

- [style-dictionary](https://npm.io/package/style-dictionary.md) — 2.0M weekly downloads
- [postcss-merge-idents](https://npm.io/package/postcss-merge-idents.md) — 1.7M weekly downloads
- [@fontsource/noto-sans](https://npm.io/package/@fontsource/noto-sans.md) — 93.0K weekly downloads
- [uglifycss](https://npm.io/package/uglifycss.md) — 71.6K weekly downloads
- [mat4-interpolate](https://npm.io/package/mat4-interpolate.md) — 23.3K weekly downloads

## Recent versions

- 1.2.0 (latest) — 2026-09-08
- 1.1.1 — 2021-03-29
- 1.1.0 — 2021-03-28

## README

# csscolorparser-ts

Parser for CSS color strings written in TypeScript. Zero runtime dependencies.

```javascript
parseCSSColor('oklch(0.7 0.1 200)');
// [ 64, 177, 183, 1 ]
```

# TL;DR

* Exports a single function, `parseCSSColor(string)`, returning `[R, G, B, A]` — `[0-255, 0-255, 0-255, 0.0-1.0]` — for a color it can resolve, and `null` otherwise.
* Every [CSS Color 4](https://drafts.csswg.org/css-color-4/) color function is supported, but **only static values**. `oklch(var(--theme-lightness) 0.102 42)` might be a valid color; resolving `var()` needs a stylesheet, which is outside the scope of this library.
* The returned RGB is **sRGB**. Color spaces wider than sRGB are converted with loss, and the way that loss is applied matches browser behavior as closely as possible.
* It aims to be **fast**: a single pass over the string, with no allocation beyond the array it hands back — tens of millions of parses per second on ordinary laptop hardware.

# Install

```javascript
const { parseCSSColor } = require('csscolorparser-ts');   // CommonJS
import { parseCSSColor } from 'csscolorparser-ts';        // ESM
```

TypeScript users also get the `RGBA` type, short for `[number, number, number, number]`.

# Supported syntax

```javascript
parseCSSColor('tomato');                         // [ 255, 99, 71, 1 ]
parseCSSColor('#AABBCC');                        // [ 170, 187, 204, 1 ]
parseCSSColor('#ABC');                           // [ 170, 187, 204, 1 ]
parseCSSColor('#AABBCCCC');                      // [ 170, 187, 204, 0.8 ]
parseCSSColor('#ABCC');                          // [ 170, 187, 204, 0.8 ]

parseCSSColor('rgba(255, 128, 0, 0.5)');         // [ 255, 128, 0, 0.5 ]
parseCSSColor('rgb(255 128 0 / 0.5)');           // [ 255, 128, 0, 0.5 ]
parseCSSColor('hsla(900, 15%, 90%, 0.5)');       // [ 226, 233, 233, 0.5 ]
parseCSSColor('hsla(2rad, 15%, 90%, 0.5)');      // [ 226, 233, 226, 0.5 ]
parseCSSColor('hwb(194 30% 40% / .5)');          // [ 77, 135, 153, 0.5 ]

parseCSSColor('lab(50% 40 59.5)');               // [ 191, 87, 0, 1 ]
parseCSSColor('lch(50% 70 40)');                 // [ 206, 71, 45, 1 ]
parseCSSColor('oklab(0.5 -0.1 0.1)');            // [ 60, 116, 10, 1 ]
parseCSSColor('oklch(70% 0.1 200 / 50%)');       // [ 64, 177, 183, 0.5 ]

// color() accepts srgb, srgb-linear, display-p3, a98-rgb, prophoto-rgb,
// rec2020, xyz, xyz-d50 and xyz-d65
parseCSSColor('color(srgb 0.5 0.2 0.7)');        // [ 128, 51, 179, 1 ]
parseCSSColor('color(display-p3 1 0 0)');        // [ 255, 0, 0, 1 ]
parseCSSColor('color(rec2020 0.9 0.1 0.1)');     // [ 255, 0, 22, 1 ]
parseCSSColor('color(xyz-d50 0.2 0.3 0.4)');     // [ 0, 168, 189, 1 ]
parseCSSColor('color(srgb-linear 0.5 0.5 0.5)'); // [ 188, 188, 188, 1 ]

// the none keyword, bare numbers for saturation and lightness, comments
// inside values, and every angle unit
parseCSSColor('rgb(none 128 0)');                // [ 0, 128, 0, 1 ]
parseCSSColor('hsl(50 80 35)');                  // [ 161, 137, 18, 1 ]
parseCSSColor('rgb(1 /*a comment*/ 2 3)');       // [ 1, 2, 3, 1 ]
parseCSSColor('\trgb(50 \t 100 \n 200 / 255)\t');// [ 50, 100, 200, 1 ]

// malformed
parseCSSColor('nosuchcolor');                    // null
parseCSSColor('ffffff');                         // null
parseCSSColor('rgb(50, 50)');                    // null
parseCSSColor('rgb(50, 50a, 50)');               // null
parseCSSColor('hsl(900, 0.15, 90%)');            // null  legacy hsl() needs percentages
parseCSSColor('rgb(5%, 50, 30)');                // null  legacy rgb() may not mix types
parseCSSColor('rgb(132 170 73 0.5)');            // null  alpha needs a slash
```

# What `null` means

`null` means **this library could not produce an sRGB value**. That covers strings that are not colors at all, and *also* values a browser accepts but whose color depends on context this library does not have — there is no cascade, no element, no color scheme and no OS theme to consult:

```javascript
parseCSSColor('rgb(var(--brand), 1, 2)');        // null — may be a color, needs a stylesheet
parseCSSColor('currentcolor');                   // null — needs an element
parseCSSColor('Canvas');                         // null — needs an OS theme
parseCSSColor('light-dark(red, blue)');          // null — needs the color scheme
parseCSSColor('inherit');                        // null — needs the cascade, as do initial and unset
parseCSSColor('color(--my-profile 0 0 0)');      // null — needs an @color-profile rule
```

So `!parseCSSColor(x)` answers *"can I get an sRGB value out of this?"*, which is usually the question you want — but it is **not** a general "is this valid CSS?" test, because every browser accepts all of the above.

## Static, but not implemented yet

```javascript
parseCSSColor('color-mix(in oklab, red, blue)'); // null, but resolves to [ 140, 83, 162, 1 ]
parseCSSColor('rgb(from red r g b)');            // null, but resolves to [ 255, 0, 0, 1 ]
parseCSSColor('rgb(calc(10 + 20) 30 40)');       // null, but resolves to [ 30, 30, 40, 1 ]
```

# Stability contract

Everything below is about **`parseCSSColor`** specifically, and will not change for it without a major version. Anything with different semantics would arrive as its own function rather than as a change to this one.

- The result is `[r, g, b, a]` or `null`. Nothing else, ever — no exceptions thrown, no `undefined`.
- `r`, `g` and `b` are **rounded integers in 0–255**. `a` is a float in 0–1.
- The array always has exactly four elements, so `new Color(...parsed)` is safe.
- The array is **freshly allocated on every call**, so you may mutate it freely.
- Reconstructing a color string from the result is supported: ``` `rgba(${r},${g},${b},${a})` ``` will parse back to the same values.

# Scope

## The conversion is lossy, on purpose

Every color function outside sRGB — `lab()`, `oklch()`, `color(display-p3 …)` and the rest — can name colors your screen cannot show. Converting those to an RGBA byte triple therefore **loses information, irreversibly**:

```javascript
parseCSSColor('oklch(0.7 0.25 200)');            // [ 0, 198, 219, 1 ]
parseCSSColor('oklch(0.7 0.4  200)');            // [ 0, 212, 254, 1 ]
```

Those are visibly different colors that land two steps apart. In sRGB coordinates the second is `(-0.82, 0.83, 1.00)` — the negative red means "you would have to *remove* red light", which no display can do. Reducing such a color to something displayable is called gamut mapping, and this library does it the way browsers do: **per-channel clipping**.

## Why clipping, when the spec says otherwise

CSS Color 4 §11 requires that a color destined for a display *"must be css gamut mapped"*, and §14.2 permits three algorithms, none of which is clipping — §14.1.1 calls clipping *"the simplest and least acceptable method"*.

Every engine clips anyway. This library matches them, so the RGBA you get is the color that would actually be painted, and drawing it on a canvas beside a DOM element with the same declared color produces no visible seam. That is a deliberate choice to match reality over the specification, and the upstream issues are still open:

- [csswg-drafts#9449](https://github.com/w3c/csswg-drafts/issues/9449) — the umbrella issue
- [WebKit 255939](https://bugs.webkit.org/show_bug.cgi?id=255939), [Mozilla 1847421](https://bugzilla.mozilla.org/show_bug.cgi?id=1847421), [crbug 1440069](https://crbug.com/1440069)
- [browser-compat-data#26838](https://github.com/mdn/browser-compat-data/issues/26838) — "not spec compliant"

If an engine ships real gamut mapping, this default will be revisited.

# Correctness

The test harness compares this library against real **Chromium, Firefox and WebKit**, by painting swatches and sampling the rendered pixels. The fixtures are committed, so running the tests needs no browser.

The engines disagree with each other in small ways, and each is out of spec somewhere the others are not, so no single one is authoritative: where they differ, this library follows the specification. Each engine's known deviations are listed explicitly in the test suite, with the tolerance or the reason.

To regenerate the fixtures (requires Docker):

```
npm run reference:generate
```

# Credits

- Dean McNamee, author of the original csscolorparser library ([GitHub](https://github.com/deanm/css-color-parser-js), [NPM](https://www.npmjs.com/package/csscolorparser)) which this library is forked from.
- adroitwhiz, for excellent testcases for CSS color parsers ([GitHub](https://github.com/adroitwhiz/css-color/blob/master/test/test.js)).

# Links

- [GitHub](https://github.com/m-khvoinitsky/csscolorparser-ts)
- [NPM](https://www.npmjs.com/package/csscolorparser-ts)
- [Changelog](CHANGELOG.md)

---
_Source: https://npm.io/package/csscolorparser-ts · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
