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

@hanzo/theming

v7.3.1

Published

Theme engine, palette generation, and themed components for Hanzo-powered apps

Readme

@hanzo/theming

A theming and component toolkit for building white-label apps using hanzogui.

It also provides generation of Tailwind color tokens expected by shadcn for compatibility with systems that use @hanzo/ui.

How it works

Every color in the system — backgrounds, text, borders, button states — is derived from a 12-step palette, and sometimes an additional neutral palette. These can be configured with a simple one-color "seed." Doing so generates both light and dark versions of the palette. Hanzogui calls these palettes "themes," and our system uses 7 of them: neutral, primary, secondary, info, success, warning, and danger. These themes can be used with all @hanzogui components, as well as all componenents in @hanzo/theming/themed-components.

They are also translated into a Tailwind CSS theme that can be used with systems based on @hanzo/ui (which uses shadcn).

Typically, org-specific brand packages (@my-org/brand) just provide seeds (or 12-step palettes) and assets, and then invoke the generation logic that lives here.

Install

pnpm add @hanzo/theming

Quick start

Hanzogui config and org's brand module

In the org's brand module — assemble and export a Hanzogui config from your seeds:

// @my-org/brand/src/hanzogui-config.ts
import { createHanzoguiConfig } from '@hanzo/theming/hanzogui-config'
import brandJson from './brand.json'

export const hanzoguiConfig = createHanzoguiConfig({
  themes: brandJson.themes,
  // omitted fields (fonts, size, space) fall back to defaults
})

In the org's apps — wrap the app with HanzoguiProvider:

// @my-org/<app>/src/main.tsx
import { hanzoguiConfig } from '@my-org/brand'
import { HanzoguiProvider } from 'hanzogui'

<HanzoguiProvider config={hanzoguiConfig}>
  <App />
</HanzoguiProvider>

Tailwind / shadcn

For use with shadcn-based code, token mappings from Hanzogui 'themes' (palettes) are provided by @hanzo/theming. Only the palettes are org-specific, and they are generated at build time by the generate-palettes CLI (see For org brand packages).

For convenience, an org's brand package should likely export a single CSS file that assembles the pieces in the right order.

// App.tsx
@import '@my-org/brand/tw-my-org.css';

This file would contain...

@import 'tailwindcss';
@import './my-org-tw-additions.css';            /* (optional) org-specific non-color tokens */
@import './brand-palettes.css';                 /* --color-{theme}-{1..12}, generated by prebuild*/
@import '@hanzo/theming/shadcn-semantic-tw-colors.css';  /* shadcn semantic refs derived from the above file */

NOTE: Order matters here. brand-palettes.css define the vars, shadcn-semantic-tw-colors.css references and maps them for shadcn.

Palette system

Follows the Radix Colors semantic scale. Every seed produces a 12-step palette with fixed semantic roles:

| Step | Role | Token | |------|------|-------| | 1 | App background | $color1 / $grey1 | | 2 | Subtle background | $color2 / $grey2 | | 3 | UI element background | $color3 / $grey3 | | 4 | Hovered UI element bg | $color4 / $grey4 | | 5 | Active / pressed UI bg | $color5 / $grey5 | | 6 | Subtle borders | $color6 / $grey6 | | 7 | UI borders / focus rings | $color7 / $grey7 | | 8 | Hovered borders | $color8 / $grey8 | | 9 | Solid background (= seed) | $color9 / $grey9 | | 10 | Hovered solid | $color10 / $grey10 | | 11 | Low-contrast text | $color11 / $grey11 | | 12 | High-contrast text | $color12 / $grey12 |

Two palette types

Neutral palette — a grey ramp generated from the neutral seed. Injected into every Hanzogui theme as $grey1$grey12. Used for surfaces, borders, and body text regardless of which accent theme is active.

Accent palette — a full 12-step scale from an accent seed (primary, danger, etc.). Steps 1–8 use the accent hue at progressively increasing saturation; step 9 is the literal seed; steps 10–12 blend toward the scheme foreground. Mapped to $color1$color12 by Hanzogui's default template.

An additional $solidText token is computed per-theme based on the luminance of step 9 — white text for dark fills, dark text for light fills.

Component convention

Within any <Theme> wrapper, both palettes are available:

  • $grey — surfaces, borders, body text (neutral)
  • $color — accent fills, hover, press, accent-colored text
  • $solidText — text on solid action surfaces (auto-contrasted)
<Theme name="primary">
  {/* Card surface — grey */}
  <YStack bg="$grey2" bc="$grey7">
    <Text color="$grey12">Body text</Text>
    {/* Button — accent */}
    <XStack bg="$color9" hoverStyle={{ bg: '$color10' }}>
      <Text color="$solidText">Submit</Text>
    </XStack>
  </YStack>
</Theme>

This means a single <Theme name="primary"> gives components access to both neutral surfaces and vivid accent fills — no nested theme wrappers needed.

Theme seeds

Seven named themes, all optional. Omitted themes get these defaults:

| Theme | Default | Purpose | |-------|---------|---------| | neutral | #808080 | Greyscale canvas — backgrounds, text, borders | | primary | #3B82F6 | Primary actions — buttons, links, focus rings | | secondary | #721be4 | Secondary accent | | info | #eab308 | Informational callouts, tips | | success | #16a34a | Success states, confirmations | | warning | #fb923c | Warning states, caution | | danger | #dc2626 | Errors, destructive actions |

Each theme can be specified as:

// A seed — generates both light and dark palettes
{ seed: '#1a2744' }

// An explicit 12-step palette — used for both schemes (dark is reversed)
['#f0f3f8', '#dfe5f0', ..., '#0c1322']

// Per-scheme — mix seeds and explicit palettes
{ light: { seed: '#1a2744' }, dark: ['#0c1322', '#131d34', ..., '#f0f3f8'] }

Hanzogui config

createHanzoguiConfig() produces a complete Hanzogui config from your supplied values:

import { createHanzoguiConfig } from '@hanzo/theming/hanzogui-config'

const config = createHanzoguiConfig({
  themes: {
    primary: { seed: '#1a2744' },
    // neutral, secondary, info, success, warning, danger — pick up defaults
  },
  fonts: {
    body:    { family: '"Inter", sans-serif', size: { ... }, ... },
    heading: { family: '"Inter", sans-serif', size: { ... }, ... },
    mono:    { family: '"JetBrains Mono", monospace', size: { ... }, ... },
  },
  // pick up defaults for 'size' and 'space'
})

| Option | Type | Default | |--------|------|---------| | themes | ThemesConfig | Default color seeds for the 7 themes specified above | | fonts.body | FontDef | System sans-serif stack | | fonts.heading | FontDef | System sans-serif stack | | fonts.mono | FontDef | System monospace stack | | size | Record<string, number> | Non-linear component size scale (20–144px) | | space | Record<string, number> | Linear 4px grid (0–96px) |

Each theme seed becomes a Hanzogui children theme with both $color (accent) and $grey (neutral) tokens available:

<Theme name="primary">
  {/* Grey for surfaces, accent for actions */}
  <YStack bg="$grey2" bc="$grey7">
    <Button bg="$color9">Submit</Button>
  </YStack>
</Theme>

<Theme name="danger">
  {/* Accent-tinted surface for alerts */}
  <StatusBox bg="$color2" bc="$color6">
    <Text color="$color11">Error occurred</Text>
  </StatusBox>
</Theme>

Light/dark is handled structurally — the active scheme is inherited from the root, and each children theme has both variants.

Tailwind / shadcn details

Brand palettes as brand-palettes.css

  • auto-generated within an org's <org>/brand from its ThemesConfig (which generally lives in brand.json)

  • generateTwThemePalettesCss(config?): Produces --color-{theme}-{1..12} for all 7 themes/palettes in :root and [data-color-scheme='dark'].

Shadcn semantic colors are derived in shadcn-semantic-tw-colors.css

  • @hanzo/theming/shadcn-semantic-tw-colors.css — Derives shadcn's expected color tokens (--background, --primary, --destructive...) from the palettes as css vars (var(--color-neutral-1), var(--color-primary-10)...) that were generated from the org's ThemesConfig. Simply maps, so does not vary. Just needs to be included in the app's css file after brand-palettes.css.

The generate-palettes CLI

@hanzo/theming includes a generate-palettes CLI that reads an org's brand.json and writes the palettes CSS file:

# Defaults: reads src/brand.json, writes src/brand-palettes.css
npx generate-palettes

# Custom paths
npx generate-palettes --brandFile src/our-brand.json --outFile src/our-brand-palettes.css

Normally this is not run manually, but as a prebuild step in an org brand package's package.json:

"prebuild": "generate-palettes"

Output: brand-palettes.css

:root {
  --color-neutral-1: #fafafa;
  --color-neutral-2: #f5f5f5;
  /* ... through 12, for all 7 themes */
}
[data-color-scheme='dark'] {
  --color-neutral-1: #0d0d0d;
  /* ... */
}

Components

Themed Hanzogui components: ThemedButton, GhostButton, OutlineButton, StatusBox, ToggleSwitch, StyledCard.

See COMPONENTS.md for usage and examples.

Package structure

src/
├── types.ts                              # Palette12, ThemeSeed, ThemeDesc, ThemesConfig
├── palette-utils.ts                      # generateNeutralPalette, generateAccentPalette, resolveThemeDesc
├── hanzogui/                             # Hanzogui-specific
│   ├── types.ts                          # FontDef, HanzoguiConfigOptions
│   ├── create-config.ts                  # createHanzoguiConfig(options?)
│   ├── index.ts                          # (internal barrel)
│   ├── defaults/
│   │   ├── themes.ts                     # default seed colors
│   │   ├── spacing.ts                    # default size + space scales
│   │   ├── fonts.ts                      # default system font definitions
│   │   └── index.ts                      # barrel (seeds, size, space, fonts)
│   └── components/                       # see COMPONENTS.md
└── hanzo-ui/                             # Tailwind / shadcn
    ├── utils.ts                          # generateTwThemePalettesCss
    ├── generate-palettes.ts              # CLI: reads brand.json, writes brand-palettes.css
    ├── index.ts                          # (internal barrel)
    └── shadcn-semantic-colors.css        # static — shadcn semantic color mappings

Export paths

| Path | What | |------|------| | @hanzo/theming/hanzogui-config | createHanzoguiConfig and types | | @hanzo/theming/themed-components | Hanzogui components | | @hanzo/theming/hanzogui-config-defaults | Defaults (seeds, size, space, fonts) | | @hanzo/theming/shadcn-semantic-tw-colors.css | Static CSS — maps 12-step palettes to semantic color values expected by shadcn |

Example use in @my-org/brand

An org brand package depends on @hanzo/theming and provides just data:

@my-org/brand/
├── src/
│   ├── brand.json                # org identity, URLs, and Hanzogui palettes in 'themes' field
│   ├── brand-palettes.css        # generated by prebuild (gitignored)
│   ├── my-org-tw-additions.css   # (optional) org-specific tw tokens
│   ├── tw-my-org.css             # bundle: tailwind + additions + palettes + semantic
│   ├── fonts.ts                  # (optional) org-specific font defs 
│   ├── hanzogui-config.ts         # calls createHanzoguiConfig
│   ├── types.ts                  # BrandIdentity, OrgConfig (define the rest of brand.json)
│   └── index.ts                  # re-exports
└── assets/                       # logos, etc

The hanzogui-config.ts calls createHanzoguiConfig with the org's seeds and fonts:

import { createHanzoguiConfig } from '@hanzo/theming/hanzogui-config'
import brandJson from './brand.json'
import { bodyFont, headingFont, monoFont } from './fonts'

export const hanzoguiConfig = createHanzoguiConfig({
  themes: brandJson.themes,
  fonts: { body: bodyFont, heading: headingFont, mono: monoFont },
})

The package.json has a prebuild script that runs the palette generator:

"scripts": {
  "prebuild": "generate-palettes",
  "build": "tsup src/index.ts --format cjs,esm --dts --clean"
}

generate-palettes is included in @hanzo/theming as a bin. By default it reads src/brand.json and writes src/brand-palettes.css.