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

hazo_theme

v1.0.1

Published

Shared design tokens, Tailwind v4 theme, and theming runtime (dark/light + brand theme-sets + whitelabel) for all hazo_* packages and apps.

Downloads

139

Readme

hazo_theme

Shared design tokens, Tailwind v4 theme, and theming runtime (dark/light + brand theme-sets + whitelabel) for all hazo_* packages and apps.

Installation

npm install hazo_theme

Quick start

This is the pattern used by test-app/app/layout.tsx in this package — copy it into a Next.js App Router root layout.

1. Import the CSS in your global stylesheet (after Tailwind, before anything else):

/* app/globals.css */
@import "tailwindcss";
@import "hazo_theme/theme.css";
@import "hazo_theme/all-themes.css";   /* optional: bundles every shipped theme-set for runtime switching */
@source "../node_modules/hazo_theme/dist";

If you only ship one brand palette, import just that theme-set instead of the full bundle:

@import "hazo_theme/themes/indigo-sunset.css";

2. Wrap the root layout in <HazoThemeProvider> and render <ThemeScript> in <head> (the script runs before first paint so there is no flash of the wrong mode/palette):

// app/layout.tsx
import './globals.css';
import { HazoThemeProvider, ThemeScript } from 'hazo_theme/client';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <ThemeScript storageKey="theme" defaultThemeSet="default" />
      </head>
      <body className="bg-background text-foreground">
        <HazoThemeProvider defaultMode="system" defaultThemeSet="default">
          {children}
        </HazoThemeProvider>
      </body>
    </html>
  );
}

storageKey/defaultThemeSet passed to <ThemeScript> must match the values passed to <HazoThemeProvider> — they read/write the same localStorage keys.

3. Use token classes and the prebuilt controls anywhere in the tree:

import { ThemeToggle, ThemeSetPicker } from 'hazo_theme/client';

function Toolbar() {
  return (
    <header className="flex items-center justify-between border-b border-border bg-background px-4 py-2 text-foreground">
      <span>My App</span>
      <div className="flex items-center gap-2">
        <ThemeSetPicker />
        <ThemeToggle />
      </div>
    </header>
  );
}

API

Styles (CSS exports)

| Import | Contents | |---|---| | hazo_theme/tokens.css | Tier-1 primitives, Tier-2 semantic tokens, Tier-3 chrome/dialog tokens, .dark overrides. Pulled in automatically by theme.css. | | hazo_theme/theme.css | The Tailwind v4 @theme inline entrypoint — maps every token to a utility class (bg-primary, text-navbar-fg, bg-toolbar-hover, …). Import this, not tokens.css directly. | | hazo_theme/themes/<name>.css | One brand theme-set ([data-theme="<name>"] override block). Currently shipped: default (implicit, no import needed), indigo-sunset. | | hazo_theme/all-themes.css | Convenience bundle importing every shipped themes/*.css. |

Chrome tokens (footer / toolbar / dialog)

New in 0.2.0, alongside the BrandIdentity contract above:

  • footer-public-*footer-public-bg, footer-public-fg, footer-public-fg-muted, footer-public-border. For a tall, multi-column public-site footer (e.g. hazo_ui's <AppFooter variant="public">).
  • footer-app-*footer-app-bg, footer-app-fg, footer-app-fg-muted, footer-app-border. For a thin in-app footer strip (e.g. <AppFooter variant="app">). The legacy footer-* family (no -public/-app suffix) is retained unchanged for existing consumers.
  • --toolbar-hover-fg — foreground companion to the existing --toolbar-hover background token; fixes toolbar hover text that was previously unreadable (no matching foreground token existed).
  • --dialog-header-bar-bg / --dialog-header-bar-fg — the dialog header-bar background/foreground, consumed by hazo_ui's HazoUiDialog in place of a hardcoded dark-slate color pair.

hazo_theme (server-safe entry)

  • SEMANTIC_TOKENS — the frozen Tier-2 token name list ("background", "primary", "chart-1", "sidebar", …). Adding a name is a minor release; renaming/removing is a major.
  • CHROME_TOKENS — the Tier-3 layout-chrome token name list ("navbar-bg", "footer-fg", "toolbar-hover", …).
  • SHIPPED_THEME_SETS["default", "indigo-sunset"], the theme-set names shipped with the package (drives ThemeSetPicker's options).
  • BrandIdentity — the per-tenant whitelabel + chrome-identity contract: { primary?, primaryForeground?, accent?, accentForeground?, fontDisplay?, fontBody?, themeSet?, logoUrl?, logoUrlDark?, appTitle?, footer? }. Colour fields are raw HSL channels (e.g. "222 47% 11%", no hsl() wrapper). The logoUrl/logoUrlDark/appTitle/footer fields are identity data, not CSS variables — resolveTenantVars() ignores them; they're consumed directly by chrome components (e.g. hazo_ui's Logo/AppHeader/AppFooter). TenantBranding is kept as a back-compat type alias for BrandIdentity.
  • FooterConfig{ columns?: FooterColumn[]; copyright?: string; social?: FooterSocial }, the shape of BrandIdentity.footer.public / .app. FooterColumn is { heading?: string; links: FooterLink[] }; FooterLink is { label: string; href: string }; FooterSocial is { github?, twitter?, linkedin?, website? } (all optional URLs).
  • resolveTenantVars(branding: TenantBranding): string — turns a BrandIdentity/TenantBranding record's colour/font fields into an inline CSS-variable declaration string, for whitelabel overrides scoped to any element. Identity-data fields (logoUrl, appTitle, footer, …) are not emitted — pass those straight to a chrome component instead.

hazo_theme/client (React runtime; re-exports everything above too)

  • HazoThemeProvider({ children, defaultMode?, defaultThemeSet?, storageKey? }) — provides theme context.
    • defaultMode?: "light" | "dark" | "system" — default "system".
    • defaultThemeSet?: string — default "default".
    • storageKey?: stringlocalStorage key prefix, default "hazo-theme" (stores ${storageKey}-mode and ${storageKey}-set).
  • useTheme() — must be called under <HazoThemeProvider>; throws otherwise. Returns:
    • mode: "light" | "dark" | "system"
    • setMode(mode): void
    • resolvedMode: "light" | "dark"mode with "system" resolved against the OS preference.
    • toggleMode(): void — flips between light/dark (always sets an explicit mode, never "system").
    • themeSet: string — active brand palette.
    • setThemeSet(name: string): void
  • ThemeScript({ storageKey?, defaultThemeSet? }) — no-flash inline <script>. Render it in <head>, before <body>. Reads the same localStorage keys as the provider and sets .dark/data-theme on <html> before hydration. Wrapped in try/catch — never throws.
  • ThemeToggleReact.forwardRef<HTMLButtonElement, { className? } & ButtonHTMLAttributes>. Prebuilt light/dark toggle button (Sun/Moon icons via lucide-react, optional peer dep). Must be rendered under <HazoThemeProvider>.
  • ThemeSetPickerReact.forwardRef<HTMLSelectElement, { className? } & SelectHTMLAttributes>. Prebuilt <select> bound to useTheme().themeSet/setThemeSet, populated from SHIPPED_THEME_SETS (plus the current value if it's a consumer-registered custom set not in that list).
  • cn(...inputs: ClassValue[]): stringclsx + tailwind-merge, the shared class-merge helper.
  • useLocalStorage<T>(key, initialValue) / useMediaQuery(query) — the small SSR-safe hooks the provider is built on; exported for consumers who need the same primitives.

Whitelabel snippet

resolveTenantVars() returns a "--var: value; --var2: value2;" inline-declaration string. React passes hyphen-prefixed (custom-property) keys straight through the style prop without camelCasing, so split the string into a plain object and apply it to the element you want scoped — typically <html>/<body> for a whole-app tenant, or any wrapper for a scoped subtree (see test-app/app/whitelabel/page.tsx for the full working version, including a live edit form):

import { resolveTenantVars, type TenantBranding } from 'hazo_theme';

function tenantVarsToStyle(cssText: string): React.CSSProperties {
  const style: Record<string, string> = {};
  for (const decl of cssText.split(';')) {
    const [key, value] = decl.split(':').map((s) => s.trim());
    if (key && value) style[key] = value;
  }
  return style as React.CSSProperties;
}

const branding: TenantBranding = {
  primary: '204 94% 40%',
  primaryForeground: '0 0% 100%',
};

function TenantScope({ children }: { children: React.ReactNode }) {
  return <div style={tenantVarsToStyle(resolveTenantVars(branding))}>{children}</div>;
}

It sits on top of whichever themeSet/dark-mode state is already active; an empty TenantBranding resolves to "" and changes nothing.

Tailwind v4 (@source required)

If this package renders UI, add the following to your app's CSS entry:

@source "../node_modules/hazo_theme/dist";

License

MIT