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

@untheme/nuxt

v0.2.1

Published

Nuxt module for runtime theming with untheme.

Readme

@untheme/nuxt

Nuxt module for runtime theming with untheme.

Give the module a base theme and a catalog of switchable variants, and it validates them at build time, derives typed token unions, and wires up a runtime service, a reactive stylesheet, and cookie-backed persistence — no other setup required.

Setup

// nuxt.config.ts
import { defineUnthemeConfig } from "untheme/config";

export default defineNuxtConfig({
  modules: ["@untheme/nuxt"],
  untheme: defineUnthemeConfig({
    base: {
      id: "alpha",
      name: "Alpha",
      tokens: {
        white: {
          $type: "color",
          $value: { colorSpace: "srgb", components: [1, 1, 1], hex: "#ffffff" },
        },
        black: {
          $type: "color",
          $value: { colorSpace: "srgb", components: [0, 0, 0], hex: "#000000" },
        },
        surface: { $type: "color", $value: "{white}" },
        "on-surface": { $type: "color", $value: "{black}" },
      },
      modifiers: {
        color: {
          light: {},
          dark: { surface: "{black}", "on-surface": "{white}" },
        },
      },
      order: ["color"],
    },
    themes: {
      bravo: {
        id: "bravo",
        name: "Bravo",
        tokens: {
          white: {
            colorSpace: "srgb",
            components: [0.98, 0.98, 0.98],
            hex: "#fafafa",
          },
        },
      },
    },
    input: { color: "light" },
  }),
});

base is a complete theme — every token carries a $type and $value, and each modifier (here, color) declares the contexts that override those tokens for that axis. themes is a catalog of layers keyed by name: each layer states only what diverges from base, so bravo above only rebinds white. input is the selection to boot with, one context per modifier. defineUnthemeConfig is an identity helper — it does nothing at runtime, but types the config and infers the token and modifier unions from base.

What it generates

At build time the module runs defineSchema against base, which validates the theme and derives its token and modifier contract. From that it writes:

  • #build/untheme.mjs — the theme, themes, and input data, plus a sibling untheme.d.mts that types them against the derived contract instead of the loose types TypeScript would otherwise infer from a .mjs file.
  • #build/types/untheme.d.ts — a Token union of every token name in base, an Overrides type for patches to those tokens, and a Mod type describing each modifier's contexts.

It also registers the runtime plugin and two auto-imports: useUntheme() and accessUntheme(), plus the type imports AppContract, AppTheme, AppThemeLayer, AppThemes, AppInput, AppConfig, and AppUntheme — all derived from the generated contract.

useUntheme()

useUntheme() returns the theme service — @untheme/core's Untheme, bound to your app's token contract. Reads and writes flow through it directly; see that package's docs for the full API (get, resolve, set, swap, apply, select, create, update, delta, dirty, reset, …).

<script setup>
const ut = useUntheme();
</script>

<template>
  <button @click="ut.swap('color', 'dark')">Dark mode</button>
  <button @click="ut.select('bravo')">Switch to Bravo</button>
</template>

accessUntheme() is the lower-level state the service is built over: the reactive config and themes containers held in useState, and the raw input/key cookie refs. Most components only need useUntheme().

CSS

The runtime plugin injects a single reactive <style> tag holding a :root block of CSS custom properties, one per active token — built with defineRenderer(untheme).root() from untheme/css. The block re-renders whenever the selection, active theme, or an override changes.

It also mirrors each modifier's selected context onto <html> as a data-<modifier> attribute (e.g. data-color="dark"), so your own stylesheets can key off the selection directly:

[data-color="dark"] .card {
  box-shadow: none;
}

Cookies and SSR

The selection and the active theme's key persist to two cookies, untheme-input and untheme-key, written automatically whenever swap, apply, or select changes them. On the server, the module reads these cookies back before rendering: a stored input is validated with schema.check.input and adopted if it matches the contract, and a stored theme key is looked up in the catalog and validated with schema.check.layer before being applied. A cookie that fails validation — for example, after a theme is removed from the catalog — is cleared instead of applied.

Hooks

The service emits three Nuxt hooks:

| Hook | Fires when | Payload | | --------------- | ------------------------------------------- | ---------------------- | | untheme:ready | The plugin finishes setting up the service | the Untheme service | | untheme:input | The selection changes (swap) | the new input | | untheme:theme | The active theme changes (apply/select) | the new resolved theme |

Related

  • untheme — umbrella package re-exporting the core service, schema, and CSS/kit helpers.
  • @untheme/core — the runtime theme service useUntheme() returns.
  • @untheme/css — the CSS renderer the plugin uses to build the stylesheet.