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

@vipengele/react-tokens

v0.3.0

Published

vipengele design tokens — seed-and-derive theming (createTheme) and the scoped ThemeProvider that applies a theme as --vpg-* CSS custom properties

Readme

@vipengele/react-tokens

Seed-and-derive theming for the vipengele design system: createTheme and ThemeProvider.

import { createTheme, ThemeProvider } from "@vipengele/react-tokens";

const theme = createTheme({ accent: "oklch(0.62 0.19 264)" });

<ThemeProvider theme={theme}>
  <App />
</ThemeProvider>;

Seeding

A ThemeSeed is the small set of values a consumer supplies — accent, danger, success, warning, info, ink, surface, radius, fontSans, fontMono. Each has a default, so createTheme() returns a complete theme. createTheme expands the seed into a frozen Theme: a flat, JSON-serializable record of --vpg-* CSS custom properties.

Ramps are CSS, not JavaScript

The hover/press/wash/visited/dark ramps are oklch() relative-colour expressions, not colours computed at build time:

--vpg-accent-hover: oklch(
  from var(--vpg-accent) calc(l + var(--vpg-state-shift)) c h
);

The browser resolves them at paint time from whatever --vpg-accent currently is, so a mode flip reassigns the mode-resolved colours and three scalars and the whole ramp follows — no second theme object, no re-render.

Status colours

danger, success, warning and info each seed a status colour (--vpg-danger, --vpg-success, --vpg-warning, --vpg-info) and a ramp derived from it exactly as the accent ramp is: -hover, -press, -wash, -ring, -contrast and -visited, plus the -light/-dark variants the mode picks between. -contrast is black or white, whichever reads on the status colour; the default warning is a light amber, so its contrast text is black.

The four base colours are mode-resolved light-dark() expressions owned by the base stylesheet, so createTheme rejects them as overrides. Reseed them, or override their -light/-dark variants.

Chart series colours

--vpg-chart-1 to --vpg-chart-6 are six series colours for categorical data. Each is an oklch() relative-colour expression off --vpg-accent: the hue steps round the wheel in 60° increments from +30° to +330°, lightness alternates between +0.05 (odd roles) and −0.05 (even roles) so neighbouring series differ in lightness as well as hue, and chroma is floored at 0.07 so a near-grey accent still yields distinguishable series.

--vpg-chart-1: oklch(
  from var(--vpg-accent) calc(l + 0.05) max(c, 0.07) calc(h + 30)
);

--vpg-accent is a light-dark() colour, so the series follow the colour mode with no second set of values. They add no seed fields and are not stylesheet-owned, so createTheme(seed, overrides) accepts them in overrides. An override is applied inline and holds in both modes, unless it is a light-dark() or var()-reading expression, which resolves per mode.

Colour mode

A provider's mode preference is a ColorModePreference: "light", "dark" or "system". "light" and "dark" write data-vpg-mode on the provider's root and always win. "system" writes nothing, and the base stylesheet lets the host page's :root[data-theme="dark"] or the OS prefers-color-scheme decide.

The provider holds the preference itself, starting from defaultColorMode (default "system"), or takes it controlled through colorMode. onColorModeChange is called with every change in either shape. The provider stores nothing; to remember a choice, seed defaultColorMode from your own storage and save from onColorModeChange.

useColorMode() reads and sets the nearest provider's preference, and throws outside one:

import { type ColorModePreference, useColorMode } from "@vipengele/react-tokens";

function ModeSwitch() {
  const { colorMode, setColorMode, resolvedColorMode } = useColorMode();
  return (
    <select value={colorMode} onChange={(event) => setColorMode(event.target.value as ColorModePreference)}>
      <option value="light">Light</option>
      <option value="dark">Dark</option>
      <option value="system">System ({resolvedColorMode ?? "…"})</option>
    </select>
  );
}

resolvedColorMode is the mode the provider paints in. Under "system" it follows <html data-theme>, then prefers-color-scheme, as the stylesheet does, and is undefined on the server and on the first hydration pass. It is for display; the mode itself is always resolved by CSS. Nested providers are independent, and the hook binds to the closest one.

Locale

LocaleProvider carries a Locale from @vipengele/ts to the components below it, and useLocale() reads it:

import { LocaleProvider, useLocale } from "@vipengele/react-tokens";
import { Locale } from "@vipengele/ts";

<LocaleProvider locale={new Locale("sv-SE")}>
  <App />
</LocaleProvider>;

function Greeting({ locale }: { locale?: Locale }) {
  const resolved = useLocale(locale);
  return <span>{resolved.tag}</span>;
}

useLocale(locale?) resolves the locale argument, else the nearest LocaleProvider, else Locale.default(). A set argument wins outright and nothing is blended. The hook never throws outside a provider. The default is read the first time a component needs it and kept for that component's lifetime, so it is stable across renders.

The provider is independent of ThemeProvider, renders no markup and adds no .vpg-root. A nested provider overrides the outer one for its subtree, and the locale follows React context through portals.

No useTheme()

There is no hook for theme values. Components read them through CSS custom properties in their own stylesheets, and useColorMode() exposes the mode preference only, never the Theme. See docs/adr/0001-theming-via-css-custom-properties-no-context-hook.md and docs/adr/0034-themeprovider-owns-the-colour-mode-preference.md.

Peer dependencies

React 19 and React DOM 19. The base stylesheet is injected through React 19's <style href precedence> de-duplication, which keeps this package "sideEffects": false.