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

@adea-ai/themes

v0.9.5

Published

The Adea theme catalogue: first-party and imported semantic OKLCH themes, with Base24, terminal, CSS and syntax adapters.

Readme

@adea-ai/themes

The Adea theme catalogue. Thirty-four themes — every one an OKLCH theme with the full set of semantic surface roles, sixteen ANSI colours, a cursor and a selection — including first-party Adea palettes and mature upstream palettes normalized through the same adapters.

bun add @adea-ai/themes

The catalogue

| Family | Variants | | --------------------------------------------------- | --------------------------------------------------------------------------------------- | | Adea | Dark, Dark Colorblind, Dark High Contrast, Light, Light Colorblind, Light High Contrast | | Aardvark | Ink, Blue | | Catppuccin | Latte, Frappé, Macchiato, Mocha | | Tokyo Night | Day, Storm, Night | | Rosé Pine | Dawn, Moon, Main | | Gruvbox | Light, Dark | | Everforest | Light, Dark | | Ayu | Light, Mirage, Dark | | Solarized | Light, Dark | | Monokai | Classic | | Nord | Nord, Light | | Dracula, One Dark, Kanagawa, Vesper | one each |

The six Adea variants are the managed family — two appearances × three accessibility variants — and all of them are composed from upstream palettes rather than authored or copied:

| Variant | canvas | hues | accent | | ------------------------ | ------------------------------------ | ------------------------------------- | ---------- | | Adea Dark | GitHub Dark Default, re-greyed | GitHub Dark Default | its violet | | Adea Dark Colorblind | same re-greyed canvas | GitHub Dark Colorblind | its violet | | Adea Dark High Contrast | GitHub Dark High Contrast, re-greyed | GitHub Dark High Contrast | its violet | | Adea Light | GitHub Light Default #ffffff | GitHub Dark Default, transposed | its violet | | Adea Light Colorblind | GitHub Light Colorblind #ffffff | GitHub Dark Colorblind, transposed | its violet | | Adea Light High Contrast | GitHub Light High Contrast | GitHub Dark High Contrast, transposed | its violet |

The re-greying is the one family-wide customisation: GitHub's dark greyscale is cut blue (h≈258), so its chroma is halved and the hue rotated to violet-grey — the ground the family's violet accent is meant to read on. The hue slots are untouched (GitHub's dark palette stays GitHub's), the accessibility variants are compositions of GitHub's own accessibility variants, and every light variant is its dark counterpart's hues transposed onto paper, so a red is the same red in both modes of a variant and switching appearance changes lightness and nothing else. tests/provenance.test.ts asserts the partnership — identical hue and chroma for every chromatic role across a variant's two appearances, the re-tinted canvases, and the violet accent on all six.

The other themes reproduce somebody else's palette from a named revision and are credited in NOTICE.

Using it

For an application that needs one palette, import its generated module directly:

import theme from '@adea-ai/themes/themes/adea-dark'
import { toXtermTheme } from '@adea-ai/themes/adapters/xterm'

terminal.options.theme = toXtermTheme(theme)

This entry point does not load the catalogue, other palettes, or normalization. @adea-ai/themes/metadata exports themeMetadata, a picker catalogue containing identity, family, appearance, description, tags, and provenance, without palette values. Load a selected palette through a static map of dynamic imports so the application bundler can create separate chunks. The full catalogue API below remains available when every palette is needed.

All JavaScript exports use native ESM with explicit relative file extensions. The shadcn adapter is available at @adea-ai/themes/adapters/shadcn. tests/package.test.ts builds and packs the actual archive, resolves its public exports in Node, and checks that a single-theme browser bundle excludes other palettes and catalogue tooling. Its 16 KiB uncompressed ceiling leaves headroom for one record and the terminal adapter while detecting catalogue inclusion. This package still has no runtime dependencies.

import { getTheme, themeFamilies, resolveTheme } from '@adea-ai/themes'
import { themeCssVariables } from '@adea-ai/themes/adapters/css'
import { toXtermTheme } from '@adea-ai/themes/adapters/xterm'
import { toShikiTheme } from '@adea-ai/themes/adapters/shiki'

// Apply a theme to the document.
const theme = resolveTheme('catppuccin-mocha', 'dark')
for (const [name, value] of Object.entries(themeCssVariables(theme))) {
  document.documentElement.style.setProperty(name, value)
}

// Colour a terminal.
terminal.options.theme = toXtermTheme(theme)

// Register a syntax theme.
const highlighter = await createHighlighter({ themes: [toShikiTheme(theme)], langs: [...] })

syntaxRolesHex(theme) keeps comments at the catalogue's intentionally quiet default. For editor palettes where comments and diff markers are small content text, editorRolesHex(theme) returns the editor roles with every rounded hex value checked against a 4.5:1 contrast floor. The source syntax palette remains unchanged.

Every exported theme value is an oklch() string, which means the browser does the colour work and a consumer can compose — color-mix(in oklch, var(--adea-accent), transparent 20%) — and get a predictable result. The adapters that need hex (xterm, Shiki) convert, gamut-mapping rather than clipping.

The design, in four decisions

Adea owns the schema and the transformation. The seventeen surface roles in src/schema.ts are the contract. No upstream project is asked to satisfy it. palettes/ holds Base24 schemes reproduced from a pinned revision; and src/normalize.ts is the single place an input becomes a canonical application theme. Adding a theme is a line in src/sources.ts plus a rebuild.

A theme may also be composed from two donors — one supplying the structure, another the hues — which is how all six of Adea's own themes are built. The composition is a slot map in COMPOSED_SOURCES, so both parents stay named and freshening either one is a one-line diff rather than a re-transcription. A composition can also declare hueTranspose, which moves a borrowed hue set onto a different kind of canvas as a group: hue and chroma intact, every relative brightness preserved.

OKLCH is the representation. Perceptually uniform lightness is what makes a surface ladder buildable by adding fixed steps, a contrast failure repairable by moving one axis, and a theme re-hueable without re-deriving its ramp by eye.

Contrast is measured, not assumed. Nothing enters the catalogue without clearing the floors in src/validate.ts, and bun run catalogue:build fails if a theme cannot be repaired within budget. The pipeline caught real defects that a hand-authored catalogue would have shipped:

  • Solarized Light's body text measures 4.28:1 on its own background — under WCAG AA.
  • Tokyo Night's black is 1.17:1 against its canvas, and Gruvbox Light's white was resolving to a near-black, both from light schemes that invert Base24's greyscale ramp.
  • Everforest Light contains no colour above 2.6:1 on its own canvas, so its status and accent roles have to be deepened rather than used as published.

Derived colours are derived. Syntax roles, chart series and status fills are functions of the roles a theme does hold (src/derive.ts), so two consumers cannot disagree about what colour a keyword is.

Base24 is a bridge, not the schema

Base24 is an excellent interchange format and a poor application schema: it has a slot for the colour of a deprecated API and nothing that means "the surface one step above the card". So Base24 is how values get in and how they get out:

import { toBase24, formatBase24Scheme, parseBase24Scheme } from '@adea-ai/themes'
import { getBase24Scheme } from '@adea-ai/themes'

formatBase24Scheme(toBase24(getTheme('nord')!)) // a Base24 scheme
getBase24Scheme('nord') // the vendored original, untouched

toBase24 re-derives Base24's orange and brown slots, because Adea's schema has no role for them; getBase24Scheme returns the artefact as reproduced, for callers that need it byte-for-byte. The mapping is documented in src/adapters/base24.ts.

Adapters

| Import | For | Output | | ------------------- | --------------------- | --------------------------------------------------- | | adapters/css | the application | CSS custom properties in OKLCH | | adapters/tailwind | the application | a Tailwind v4 @theme inline block | | adapters/shadcn | this org's components | the shadcn vocabulary, bridged from the same source | | adapters/xterm | shells | xterm.js's hex-only ITheme | | adapters/shiki | code views | a Shiki theme registration | | adapters/base24 | interop | Base24, both directions |

The shadcn bridge is worth knowing about before you read the code: Adea's accent becomes shadcn's primary, because in shadcn --primary is the action colour and --accent is a hover wash. Mapping it the other way turns every primary button grey. The mapping is in src/adapters/shadcn.ts.

Working on it

bun install
bun run verify          # typecheck, lint, catalogue freshness, tests, build
bun run catalogue:build # regenerate src/generated from palettes/ and sources.ts
bun run catalogue:check # fail if the committed catalogue is stale
bun run vendor          # refresh palettes/ from the pinned upstream revision

The generated catalogue is committed, and catalogue:check fails when it and its inputs disagree. A palette change is therefore a reviewable diff rather than an invisible drift.

palettes/ is regenerated by bun run vendor, which reproduces Base24 schemes from a revision pinned in src/sources.ts. To adopt a newer upstream, change CATALOGUE_REVISION, run vendor, then catalogue:build and read the diff — the build prints every value it repaired and why.

Composing a theme from two palettes

Add an entry to COMPOSED_SOURCES in src/sources.ts naming both donor slugs, the slot map, and the slots to synthesise; list the greyscale roles to pin to the structure donor; set hueTranspose if the hues are being borrowed for a different kind of canvas; then bun run vendor && bun run catalogue:build. The vendor step asserts the two maps cover every Base24 slot, so a composition cannot be half-specified. Assert each half against its donor in tests/provenance.test.ts.

Adding a theme

  1. Add its Base24 scheme to palettes/ (or let vendor fetch it: add the slug to VENDORED_SOURCES in src/sources.ts first).
  2. Record its family's project, URL and licence in FAMILY_PROVENANCE, and its entry in VENDORED_SOURCES — id, labels, description, appearance, tags.
  3. Declare accentSlot if the family's identity is not in its blue slot. Everforest is green, Rosé Pine is iris, Monokai is magenta.
  4. Add a fidelity assertion to tests/provenance.test.ts. The suite fails without one — a theme cannot be added without recording where its colours come from.
  5. bun run catalogue:build && bun run verify.

If the build reports a role it could not repair within budget, the answer is a decision rather than a tweak: either the palette genuinely cannot express that role, in which case say so in the entry's description, or the floor is wrong, in which case change it in CONTRAST_FLOORS and justify it there.

Licence

Apache-2.0. The palettes are not: see NOTICE for each project and its terms.