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

@docyrus/theme-provider

v0.1.0

Published

React theme provider with system mode support and embedded Docyrus theme presets

Downloads

416

Readme

@docyrus/theme-provider

React theme provider for Docyrus applications. It combines light / dark / system mode handling with a second theme layer for branded preset themes, so applications can switch both color mode and design preset without reimplementing the runtime.

The package ships with five built-in presets taken from examples-web-app-react-spa:

  • docyrus-default
  • shadcn-default
  • astrovista
  • darkforge
  • attornaid

Applications can register additional themes or override built-in themes by passing their own theme definitions to the provider.

Features

  • light, dark, and system mode support
  • Separate preset-theme selection via data-theme
  • Inline boot script to prevent theme flicker on initial load
  • Built-in preset CSS injection at runtime
  • App-level custom themes with typed theme definitions
  • Persistent storage for both mode and preset theme
  • React hook for reading and updating theme state
  • Publishable client wrapper for Next.js / RSC-friendly consumption

Installation

npm install @docyrus/theme-provider react
pnpm add @docyrus/theme-provider react

Peer Dependencies

  • react >= 18.0.0

Quick Start

import { ThemeProvider } from '@docyrus/theme-provider';

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider
      defaultTheme="system"
      defaultColorTheme="docyrus-default"
      disableTransitionOnChange
    >
      {children}
    </ThemeProvider>
  );
}

With the default configuration, the provider:

  • stores the mode in localStorage under theme
  • stores the preset theme in localStorage under docyrus-color-theme
  • toggles class="light|dark" on document.documentElement
  • toggles data-theme="<preset-id>" on document.documentElement

Using the Hook

import { useTheme } from '@docyrus/theme-provider';

export function ThemeSwitcher() {
  const {
    theme,
    setTheme,
    resolvedTheme,
    colorTheme,
    setColorTheme,
    availableThemes
  } = useTheme();

  return (
    <div>
      <p>Resolved mode: {resolvedTheme}</p>

      <button onClick={() => setTheme('light')}>Light</button>
      <button onClick={() => setTheme('dark')}>Dark</button>
      <button onClick={() => setTheme('system')}>System</button>

      <select
        value={colorTheme}
        onChange={event => setColorTheme(event.target.value)}
      >
        {availableThemes.map(themeDefinition => (
          <option key={themeDefinition.id} value={themeDefinition.id}>
            {themeDefinition.name}
          </option>
        ))}
      </select>
    </div>
  );
}

Adding Custom Themes

Applications can extend the built-in presets by passing additional theme definitions.

import {
  ThemeProvider,
  defaultThemeDefinitions,
  type ThemeDefinition
} from '@docyrus/theme-provider';

const baseTheme = defaultThemeDefinitions.find(theme => theme.id === 'docyrus-default');

if (!baseTheme || !baseTheme.dark) {
  throw new Error('docyrus-default theme is required');
}

const signalGridTheme: ThemeDefinition = {
  id: 'signal-grid',
  name: 'Signal Grid',
  light: {
    ...baseTheme.light,
    '--background': 'oklch(0.984 0.008 215)',
    '--primary': 'oklch(0.59 0.19 252)',
    '--font-sans': "'Space Grotesk', Inter, sans-serif"
  },
  dark: {
    ...baseTheme.dark,
    '--background': 'oklch(0.16 0.03 250)',
    '--primary': 'oklch(0.73 0.14 245)',
    '--font-sans': "'Space Grotesk', Inter, sans-serif"
  }
};

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider themes={[signalGridTheme]}>
      {children}
    </ThemeProvider>
  );
}

Override Behavior

Theme merging is keyed by theme.id.

  • new id values append themes
  • existing id values replace the built-in definition

That means a project can keep the shipped theme catalog and selectively override only one preset.

Styling Model

The provider separates two concerns:

1. Theme mode

Mode is applied through the attribute prop, which defaults to class.

By default:

  • light adds .light
  • dark adds .dark
  • system resolves to either .light or .dark

2. Preset theme

Preset themes are applied through the themeAttribute prop, which defaults to data-theme.

By default:

  • docyrus-default becomes data-theme="docyrus-default"
  • astrovista becomes data-theme="astrovista"

This allows CSS rules like:

[data-theme='astrovista'] {
  --background: oklch(0.9383 0.0042 236.4993);
}

[data-theme='astrovista'].dark {
  --background: oklch(0.2178 0 0);
}

Provider Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | themes | ThemeDefinition[] | [] | Additional or overriding preset themes | | includeDefaultThemes | boolean | true | Include the five built-in presets | | defaultTheme | 'light' \| 'dark' \| 'system' | 'system' | Initial mode | | defaultColorTheme | string | 'docyrus-default' | Initial preset theme | | forcedTheme | 'light' \| 'dark' \| 'system' | undefined | Force the active mode | | forcedColorTheme | string | undefined | Force the active preset theme | | attribute | Attribute \| Attribute[] | 'class' | HTML attribute(s) used for mode | | themeAttribute | Attribute | 'data-theme' | HTML attribute used for preset themes | | modeStorageKey | string | 'theme' | Local storage key for mode | | colorThemeStorageKey | string | 'docyrus-color-theme' | Local storage key for preset theme | | enableSystem | boolean | true | Resolve system from prefers-color-scheme | | enableColorScheme | boolean | true | Set document.documentElement.style.colorScheme | | disableTransitionOnChange | boolean | false | Temporarily disable transitions during switches | | modeValue | ModeValueObject | undefined | Custom DOM values for light / dark | | themeValue | ThemeValueObject | undefined | Custom DOM values for preset themes | | nonce | string | undefined | CSP nonce for injected script and style | | scriptProps | ScriptProps | undefined | Extra props for the boot script |

Exported Types and Helpers

import {
  ThemeProvider,
  useTheme,
  DEFAULT_COLOR_THEME,
  defaultThemeDefinitions,
  mergeThemeDefinitions,
  serializeThemeDefinitions,
  type ThemeDefinition,
  type ThemeMode,
  type ThemeProviderProps,
  type ThemeVariables
} from '@docyrus/theme-provider';

defaultThemeDefinitions

The built-in preset catalog as typed ThemeDefinition[].

mergeThemeDefinitions(baseThemes, additionalThemes)

Returns a merged theme array keyed by theme.id.

serializeThemeDefinitions(themes, attribute, valueMap?)

Serializes theme definitions into CSS text. Useful if an application wants to inspect or reuse the generated CSS.

Recommended CSS Token Setup

The provider only manages DOM state and preset CSS variables. Applications should still map those variables into their own design system tokens. With Tailwind v4, a common pattern is:

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-card: var(--card);
  --color-card-foreground: var(--card-foreground);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
  --color-border: var(--border);
  --font-sans: var(--font-sans);
}

Notes

  • The package is intentionally React-only. It does not ship a framework-agnostic core.
  • The provider injects the preset theme CSS so consuming projects do not need to copy the built-in definitions into app stylesheets.
  • If a project wants only custom themes, set includeDefaultThemes={false}.

Development

pnpm -C packages/theme-provider dev
pnpm -C packages/theme-provider build
pnpm -C packages/theme-provider lint
pnpm -C packages/theme-provider typecheck