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

@thespielplatz/tsp-tools-theme

v0.1.1

Published

tsp.tools design-system theme as a Nuxt layer (Nuxt UI v4 + Tailwind v4): amber-on-anthracite tokens (light + dark), Nunito/Space Grotesk fonts, per-area colour mode, and the shared app shell. Use globally via `extends`, or scoped to a sub-area.

Readme

@thespielplatz/tsp-tools-theme

The tsp.tools design-system theme as a Nuxt layer — amber-on-anthracite tokens (light + dark), Nunito / Space Grotesk fonts, a per-area colour-mode policy, and the shared app shell (sidebar, content container, theme toggle).

Built for the platform stack: Nuxt 4 · Nuxt UI v4 · Tailwind v4. It is the extracted, generalised form of the proven piggybank admin implementation (ADR 008, step 3).

Two consumption modes:

  • Globalextends the layer and the whole app is themed (e.g. trips).
  • Scoped — apply the theme to a sub-area only, leaving the rest of the app on default Nuxt UI, with per-area colour mode (e.g. piggybank: admin is themed + toggleable, the public site stays light).

Install

npm i -D @thespielplatz/tsp-tools-theme

Peer expectations (provide them in the consuming app):

  • nuxt ^4, @nuxt/ui ^4 (Tailwind v4 comes with Nuxt UI).
  • Tabler icons — the theme uses i-tabler-* (e.g. i-tabler-sun, i-tabler-brand-github). Install @iconify-json/tabler for offline icons, or rely on Iconify's network fetch. Icons are not bundled. Keep one icon family across the platform; don't mix in a second set.

Extend by package name (@thespielplatz/tsp-tools-theme) — resolved through node_modules, this reliably registers the layer's components, composables and plugin. (A deep relative path such as extends: ['../..'] can fail to pick up the source dirs; the package name always works.)


Global usage (whole app themed)

nuxt.config.ts:

export default defineNuxtConfig({
  extends: ['@thespielplatz/tsp-tools-theme'],
  runtimeConfig: {
    public: { tspTheme: { apply: 'global' } }, // this is also the default
  },
})

That's it — the colour-mode plugin puts the .tsp-theme scope class on <html>, so every Nuxt UI component renders amber-on-anthracite, dark by default, toggleable app-wide. Drop the shell components wherever you like:

<template>
  <div class="flex min-h-svh bg-default text-default">
    <TspSidebar>
      <template #brand><TspWordmark>trips<span class="text-primary">.</span></TspWordmark></template>
      <template #nav>
        <TspNavItem to="/" icon="i-tabler-car">Fahrten</TspNavItem>
        <TspNavItem to="/settings" icon="i-tabler-settings">Einstellungen</TspNavItem>
      </template>
      <template #footer>
        <TspSidebarFooter github-link="https://github.com/…" version="v1.2.0">
          <template #user>…your user link…</template>
          <template #logout>…your logout…</template>
        </TspSidebarFooter>
      </template>
    </TspSidebar>
    <main class="flex-1 min-w-0 bg-default">
      <TspContainer><NuxtPage /></TspContainer>
    </main>
  </div>
</template>

Scoped usage (one sub-area themed)

nuxt.config.ts:

export default defineNuxtConfig({
  extends: ['@thespielplatz/tsp-tools-theme'],
  runtimeConfig: {
    public: {
      tspTheme: {
        apply: 'scoped',
        pathPrefix: '/admin',   // routes that are "themed"
        publicMode: 'light',    // colour mode forced on every other route
        defaultMode: 'dark',    // default for themed routes (remembered in a cookie)
      },
    },
  },
})

Wrap the themed area in <TspThemeProvider>; everything outside it stays default Nuxt UI:

<!-- pages/admin/index.vue -->
<template>
  <TspThemeProvider class="flex min-h-svh">
    <TspSidebar> … </TspSidebar>
    <main class="flex-1 min-w-0 bg-default">
      <TspContainer> … </TspContainer>
    </main>
  </TspThemeProvider>
</template>

The colour-mode plugin then forces publicMode on non-pathPrefix routes and uses the remembered preference inside the themed area — so the public and themed surfaces never fight over light/dark.


What's in the box

Tokens (assets/css/theme.css) — exposed as Nuxt UI CSS variables under the .tsp-theme scope:

| Group | Light | Dark | |---|---|---| | primary (amber) | #fbad18 | #fbad18 | | error (tomato) | #ff6347 | #ff6347 | | bg | #f8f9fa | #212529 | | surface (bg-elevated) | #ffffff | #2b3035 | | border | #dee2e6 | #404041 | | text | #212529 | #f0f0f0 | | on-primary (text-inverted) | #212529 | #212529 |

Brand colours (amber, tomato) are the same in both modes; only bg/surface/border/text flip. The amber ramp is also available as Tailwind utilities (*-tsp-amber-{50..950}).

Fonts — Nunito (text + headings) and Space Grotesk (logo), via @nuxt/fonts. Use the tsp-wordmark class (or <TspWordmark>) for Space Grotesk; everything else is Nunito.

ComposableuseTspColorMode(){ pref, toggle }, the remembered (cookie) preference for the themed area.

Components (auto-imported):

| Component | Purpose | |---|---| | TspThemeProvider | Scoped wrapper — marks a sub-area as themed (.tsp-theme). | | TspSidebar | App-shell sidebar frame; slots #brand #nav #footer. | | TspSidebarFooter | User → Logout → divider → theme toggle → GitHub + version. | | TspThemeToggle | Sun/moon light–dark toggle (uses useTspColorMode). | | TspNavItem | Sidebar nav link (muted → filled-amber when active). | | TspWordmark | Space Grotesk + amber brand wordmark. | | TspContainer | Centered content column (max-w-4xl, px-6 sm:px-10, pt-10 pb-16). |

App-specific content (branding, nav lists, user identity, GitHub/version values) is injected via slots/props — none of it lives in the layer.


Why primary isn't set in app.config

app.config is global. Setting ui.colors.primary = 'amber' there would make every Nuxt UI component amber across the whole app, which breaks scoped mode. Instead all brand theming lives in the .tsp-theme CSS scope, which custom properties resolve per element — so amber only applies inside the scope. For global mode the plugin simply puts .tsp-theme on <html>. The mode groups are written for both layouts: .dark .tsp-theme (scoped) and .dark.tsp-theme (global, both classes on <html>).

CSS ownership

assets/css/theme.css owns the framework import (@import "tailwindcss"; @import "@nuxt/ui";) and is added to the Nuxt css array automatically when you extend the layer. How a consumer handles its own CSS depends on whether it has its own @theme:

  • No own design tokens (global apps, or scoped apps whose non-themed area uses plain Nuxt UI): you don't need any framework import — the layer provides it. If you had your own @import "tailwindcss", drop it.

  • Has its own @theme (e.g. a scoped app with a separate public-area palette/fonts): keep your own @import "tailwindcss"; @import "@nuxt/ui"; in that stylesheet. A @theme block only compiles inside a Tailwind entry, so it can't piggyback on this layer's stylesheet — yours is a separate Tailwind graph. The duplicate import is intentional and harmless: both graphs share the same runtime CSS variables, and .tsp-theme only re-points Nuxt UI's variables inside the themed subtree.

    Proven by the piggybank adoption: the public Piggy palette stays in the app's own main.css (its own Tailwind entry); the admin tsp tokens come from this layer's theme.css.

The layer's stylesheet is also exposed for manual import if you ever need it: @import "@thespielplatz/tsp-tools-theme/assets/css/theme.css".


Develop

npm install
npm run dev          # global demo  (playground/)  → http://localhost:3000
npm run dev:scoped   # scoped demo  (scoped/)      → public vs /admin
npm run lint
npm run build:playground

The playground/ (global) and scoped/ demos are direct-child layers that extends: ['..']. They are excluded from the published package (files allowlist).

License

MIT © TheSpielplatz