npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ateliercartographie/ok-palette

v0.1.4

Published

Générateur de palettes de couleurs séquentielles, divergentes et catégorielles pour la dataviz et la cartographie

Readme

@ateliercartographie/ok-palette

Color palette generator for sequential, diverging, and categorical schemes for data visualization and cartography. Also generates pattern palettes compatible with @ateliercartographie/motif.js.

Zero dependencies. Relies entirely on modern browser CSS capabilities (oklch, color-mix()) to guarantee perceptually uniform results.

Created by: Thomas Ansart — Atelier de cartographie de Sciences Po
License: ISC

Installation

pnpm add @ateliercartographie/ok-palette

Quick start

import {
  sequential,
  divergent,
  divergentSequential,
  categorical,
  categoricalPatterns,
  sequentialPatterns,
  resolveColor,
  resolvePalette,
  presets,
  temperature,
} from "@ateliercartographie/ok-palette";

// 5-class monochrome sequential palette
const mono = sequential({ colorStart: "steelblue" });

// 7-class two-tone sequential palette, high contrast
const biTone = sequential({
  colorStart: "#f4e285",
  colorEnd: "#1d3557",
  steps: 7,
  contrast: "high",
});

// Symmetric diverging palette (3+3 classes)
const div = divergent({ colorA: "#3b4cc0" });

// Asymmetric diverging palette, oklch interpolation
const divLch = divergent({
  colorA: "#d7191c",
  colorB: "#2b83ba",
  steps: [5, 2],
  contrast: "high",
  colorSpace: "oklch",
});

// Diverging palette built as two mirrored sequential palettes
const divSeq = divergentSequential({ colorA: "steelblue" });

// 8-color vivid categorical palette
const colors = categorical(8, presets.vif);

// Warm categorical palette with hue offset
const warm = categorical(6, {
  ...presets.vif,
  ...temperature.chaude,
  hueOffset: 30,
});

// Categorical patterns for motif.js
const patterns = categoricalPatterns(6);

// Sequential patterns (size progressively increases with contrast)
const seqPatterns = sequentialPatterns(5, { shape: "line", contrast: "high" });

// Resolve CSS colors to usable values
const css = resolvePalette(mono); // string[]
const webgl = resolvePalette(mono, { format: "webgl" }); // [r,g,b,a][]

API

sequential(options)

Generates a sequential palette (monochrome or two-tone).

| Option | Type | Default | Description | | ------------ | ----------------------------- | ------------ | --------------------------------------------------- | | colorStart | string | — | CSS start color (light tones) | | colorEnd | string? | colorStart | CSS end color (dark tones). If omitted → monochrome | | steps | number | 5 | Number of classes (2–12) | | contrast | "low" \| "normal" \| "high" | "normal" | Contrast profile |

Returns string[] — CSS colors color-mix(in oklch, …).

How it works

The start color is normalized to a light tone (chroma halved to avoid a fluorescent effect), the end color to a dark tone. The lightness range is automatically adapted to the number of classes via the contrast profile, inspired by ColorBrewer palette analysis:

  • 3 classes → ~30% lightness gap
  • 9 classes → ~70% lightness gap

Interpolation is entirely delegated to the browser via color-mix(in oklch, …): no color library needed.

divergent(options)

Generates a diverging (bipolar) palette around a tinted pivot.

| Option | Type | Default | Description | | ---------------- | ----------------------------- | ---------------------- | -------------------------------- | | colorA | string | — | Left-extreme CSS color | | colorB | string? | complement of colorA | Right-extreme CSS color | | steps | [number, number] | [3, 3] | Classes per side [left, right] | | contrast | "low" \| "normal" \| "high" | "normal" | Contrast profile | | hasCenterClass | boolean | false | Add a neutral center class | | colorSpace | "oklab" \| "oklch" | "oklab" | Ramp interpolation color space |

Returns string[] — CSS colors.

How it works

Both extremes converge toward an automatically derived tinted pivot: the OkLab midpoint of light, highly-desaturated variants of A and B. Because A and B are opposite on the hue wheel, their components almost cancel in OkLab, producing a quasi-neutral, slightly-tinted tone — visually integrated rather than a foreign grey.

  • colorSpace: "oklab" (default): Cartesian path — smoother, avoids hue artifacts
  • colorSpace: "oklch": circular path on the hue wheel — more saturated at the center
// Default symmetric palette (7 classes: 3 + 3)
divergent({ colorA: "#3b4cc0" });

// Asymmetric, high contrast, oklch interpolation
divergent({
  colorA: "#d7191c",
  colorB: "#2b83ba",
  steps: [5, 2],
  contrast: "high",
  colorSpace: "oklch",
});

// With a center class (3 + 1 + 3 classes)
divergent({ colorA: "steelblue", steps: [3, 3], hasCenterClass: true });

divergentSequential(options)

Generates a diverging palette built as two mirrored sequential() calls — guarantees full consistency with the sequential palette.

Accepts the same options as divergent() except colorSpace (interpolation is always oklch, as in sequential).

| Option | Type | Default | Description | | ---------------- | ----------------------------- | ---------- | -------------------------- | | colorA | string | — | Left-extreme color | | colorB | string? | complement | Right-extreme color | | steps | [number, number] | [3, 3] | Classes per side | | contrast | "low" \| "normal" \| "high" | "normal" | Contrast profile | | hasCenterClass | boolean | false | Add a neutral center class |

Returns string[] — CSS colors.

// Rigorously identical to two mirrored `sequential` calls
divergentSequential({ colorA: "#3b4cc0" });

// Asymmetric with a center class
divergentSequential({
  colorA: "#d7191c",
  colorB: "#2b83ba",
  steps: [5, 2],
  hasCenterClass: true,
});

categorical(count, options?)

Generates a categorical color palette.

| Option | Type | Default | Description | | ---------------- | ------------------ | -------------- | ---------------------------------- | | hueRange | [number, number] | [0, 360] | Hue range in degrees | | hueOffset | number | 0 | Hue rotation applied to all colors | | chromaRange | [number, number] | [0.15, 0.25] | Oklch chroma range | | lightnessRange | [number, number] | [0.5, 0.75] | Oklch lightness range, values 0–1 |

Returns string[] — CSS colors oklch(…).

Presets

presets.vif; // Saturated, vivid colors
presets.pastel; // Soft, light colors
presets.sepia; // Warm, desaturated tones

Temperature filters

temperature.mixte; // Full hue wheel
temperature.chaude; // Reds, oranges, yellows
temperature.froide; // Greens, blues, purples

Combining: categorical(8, { ...presets.vif, ...temperature.chaude })

Van der Corput sequence

Hue distribution relies on the Van der Corput sequence in base 2. It always fills the largest gap on the hue wheel, guaranteeing maximum distance between colors regardless of count.

This approach is non iterative. Performance is independent of class count and very fast (~4ms in modern browsers).

categoricalPatterns(count, options?)

Generates categorical pattern parameters compatible with @ateliercartographie/motif.js.

| Option | Type | Default | Description | | ------------ | ------------------------------------ | ----------------------------------------- | -------------------------------------------- | | shapes | string[] | ["line", "circle", "plaid", "triangle"] | Shapes to cycle through | | angleRange | [number, number] | [0, 180] | Angle range (degrees) | | scaleRange | [number, number] | [2, 8] | Scale range | | size | number | 10 | Fixed size | | fill | string | "#000000" | Fill color | | background | string | "#ffffff" | Background color | | patchSize | boolean | true | Enable patchSize | | vdcOffset | number | 0 | Van der Corput offset | | colorize | boolean \| CategoricalColorOptions | false | Auto-colorize pattern fill via categorical |

Returns PatternParams[]

How it works

Pattern variation combines three complementary mechanisms so classes stay distinct without looking purely random:

  • Shape: forms are assigned by cycling through shapes (i % shapes.length) to guarantee balanced reuse when count exceeds the shape list.
  • Scale: values oscillate between scaleRange[0] and scaleRange[1] (triangle-wave style), creating contrast while avoiding monotonic growth that could suggest an ordered scale.
  • Angle: orientation is sampled with the Van der Corput sequence, but independently within each shape group. In practice, each shape gets its own low-discrepancy angle stream, which spreads angles evenly and prevents two identical shapes from clustering around similar directions.

sequentialPatterns(count, options?)

Generates sequential pattern parameters compatible with @ateliercartographie/motif.js.

Pattern size increases monotonically across classes, analogous to the lightness range in sequential color palettes. The size range is adapted to both the class count and the contrast profile.

The shape is fixed to preserve the perception of order.

| Option | Type | Default | Description | | ------------ | ----------------------------- | ----------- | ------------------------- | | shape | string | "line" | Pattern shape | | sizeRange | [number, number] | [4, 14] | Size range [small, large] | | angle | number | 45 | Fixed angle (degrees) | | scale | number | 4 | Fixed scale | | contrast | "low" \| "normal" \| "high" | "normal" | Contrast profile | | fill | string | "#000000" | Fill color | | background | string | "#ffffff" | Background color | | patchSize | boolean | true | Enable patchSize |

Returns PatternParams[]

resolveColor(color, options?)

Resolves a complex CSS color (color-mix(…), oklch(from …), etc.) to a usable value.

  • { format: "css" } (default) → string as computed by the browser
  • { format: "webgl" }[r, g, b, a] (0–255)

How it works

resolveColor delegates parsing and evaluation to the browser color engine: the input is applied to a temporary element/canvas context, then read back as a computed color.

This ensures full support for modern CSS color syntax (oklch, relative colors, color-mix()) without external dependencies and with behavior aligned to real rendering engines. The function then normalizes the result either as a CSS color string (css) or as a WebGL-ready numeric tuple (webgl).

resolvePalette(colors, options?)

Applies resolveColor to an entire array of colors.

Why this approach?

Problem 1 — Color space

The choice of interpolation space dramatically changes the result. Using oklch guarantees perceptually uniform steps across each component (lightness, chroma, hue). As of 2025, oklch is supported by all modern browsers.

Problem 2 — Lightness range

A sequential palette only works if the lightness gap between the first and last color is sufficient and adapted to the number of classes. This library automatically normalizes input colors to guarantee a readable result.

Solution

  • Universal: normalizes any CSS color before generating the palette
  • Modern: zero dependencies, relies on native oklch and color-mix()
  • Performant: the Van der Corput sequence generates categorical palettes in constant time, unlike iterative approaches like I Want Hue

Development

# Start the demo page
pnpm dev

# Build the library
pnpm build

License

ISC — Atelier de cartographie de Sciences Po

Resources