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

randoma11y

v1.0.0

Published

Generate accessible color combinations

Readme

randoma11y

Generate accessible background/foreground color pairs for apps, using vendored contrast math aligned with colorjs.io and no runtime dependencies.

Install

npm install randoma11y

Usage

import { randoma11y, contrast } from "randoma11y";

const pair = randoma11y({
  algorithm: "APCA",
  threshold: 75,
});

console.log(pair.colors); // ['#1d5ec9', '#f5f7cf']
console.log(pair.contrast);

const locked = randoma11y({
  color: "#336699",
  role: "background",
  algorithm: "WCAG21",
  threshold: 4.5,
});

console.log(locked.colors); // ['#336699', '#f0f4c5']
console.log(contrast(locked.colors[0], locked.colors[1], "WCAG21"));

React

randoma11y is plain JavaScript: import it and call it from event handlers, effects, or server code. colors[0] is background, colors[1] is foreground — use them for backgroundColor / color, CSS variables, or theme objects.

Random pair in component state

import { useCallback, useState } from "react";
import { randoma11y } from "randoma11y";

export function DuoPreview() {
  const [bg, setBg] = useState("#ffffff");
  const [fg, setFg] = useState("#000000");

  const regenerate = useCallback(() => {
    const { colors } = randoma11y({ algorithm: "APCA", threshold: 75 });
    setBg(colors[0]);
    setFg(colors[1]);
  }, []);

  return (
    <section
      style={{
        backgroundColor: bg,
        color: fg,
        padding: "2rem",
        minHeight: "100vh",
      }}
    >
      <p>Background {bg} · Foreground {fg}</p>
      <button type="button" onClick={regenerate} style={{ color: fg, borderColor: fg }}>
        New accessible pair
      </button>
    </section>
  );
}

Write theme colors to :root (CSS custom properties)

Use this when the rest of the app reads var(--app-bg) / var(--app-fg) in CSS. Runs in the browser only (document must exist).

import { useCallback } from "react";
import { randoma11y } from "randoma11y";

export function ThemeRefreshButton() {
  const refresh = useCallback(() => {
    randoma11y({
      algorithm: "APCA",
      threshold: 75,
      cssVariables: {
        names: ["--app-bg", "--app-fg"],
      },
    });
    // Optional: dispatch a custom event so other components re-read CSS vars if needed
    document.dispatchEvent(new CustomEvent("theme:updated"));
  }, []);

  return (
    <button type="button" onClick={refresh}>
      New theme
    </button>
  );
}

Example CSS elsewhere:

body {
  background: var(--app-bg, #fff);
  color: var(--app-fg, #000);
}

Lock a brand color (generate the other slot)

Pick a fixed background once (lazy state so you do not call randoma11y on every render).

import { useState } from "react";
import { randoma11y } from "randoma11y";

const BRAND_BG = "#0f172a";

export function BrandedHero() {
  const [{ colors, meetsThreshold }] = useState(() =>
    randoma11y({
      color: BRAND_BG,
      role: "background",
      algorithm: "APCA",
      threshold: 75,
    })
  );

  return (
    <header style={{ backgroundColor: colors[0], color: colors[1] }}>
      <h1>Uses your brand background and an accessible foreground</h1>
      {!meetsThreshold && <p>Consider lowering threshold or changing algorithm.</p>}
    </header>
  );
}

For SSR, avoid cssVariables on the server; either call randoma11y without cssVariables and pass colors into props, or run the cssVariables branch only inside useEffect / on click after mount.

API

randoma11y(options?) returns:

{
  colors: [backgroundHex, foregroundHex];
  contrast: number;
  algorithm: ContrastAlgorithm;
  threshold: number;
  meetsThreshold: boolean;
  iterations: number;
  cssVariables?: {
    applied: boolean;
    names: [string, string];
    target: { style: { setProperty(name: string, value: string): void } } | null;
  };
}

Options:

  • algorithm: 'APCA' | 'WCAG21' | 'Michelson' | 'Weber' | 'Lstar' | 'deltaPhi'
  • threshold: minimum accepted contrast for the chosen algorithm
  • color: lock one side of the pair to a specific input color
  • role: whether color is the 'background' or 'foreground'
  • maxIterations: positive integer search budget
  • cssVariables: true or an object describing where to write CSS custom properties

Notes

  • Output colors are always normalized hex strings.
  • colors[0] is background, colors[1] is foreground. This order matters for APCA, which is asymmetric.
  • The exported contrast(background, foreground, algorithm) function follows the same background-first convention. Swapping the arguments produces a different APCA value.
  • WCAG21 thresholds are ratio-based. Typical values are 3, 4.5, or 7.
  • The default threshold: 75 is intended for APCA.
  • Supported input syntax: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(), rgba(), hsl(), hsla(), black, white, transparent.
  • The package is ESM-only.
  • TypeScript declarations are included.
  • engines.node: >=14. For local development, run npm install and npm test from the package directory (tests use colorjs.io only as a devDependency to verify vendored contrast math).

CSS Variables

const result = randoma11y({
  cssVariables: {
    names: ["--app-bg", "--app-fg"],
  },
});

That writes the generated colors to the provided variable names on document.documentElement by default.