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/shiki

v0.2.1

Published

Builds a [Shiki](https://shiki.style) theme from an untheme contract, so syntax highlighting rides the same live-reference cascade as the rest of your tokens.

Readme

@untheme/shiki

Builds a Shiki theme from an untheme contract, so syntax highlighting rides the same live-reference cascade as the rest of your tokens.

defineShikiTheme(schema, map) returns a static Shiki theme whose every scope color is the var() indirection to a token — var(--code-keyword), not a baked hex. Register it with Shiki once and never regenerate it: when a modifier context or theme layer rebinds those tokens, the highlighted output re-themes through the custom-property graph, with no re-highlight.

The vocabulary is LSP

The scope packs this integration ships route TextMate scopes to the LSP SemanticTokenTypes — the standard semantic-highlighting vocabulary (namespace, type, function, macro, decorator, keyword, string, …). We adopt the standard rather than invent one. You own the interchange: a map from those roles to tokens in your contract. There's no fixed set of tokens you must define — bind the roles to whatever tokens you have, adding carrier tokens if you like.

import { defineUntheme } from "untheme";
import { defineShikiTheme } from "@untheme/shiki";
import { codeToHtml } from "shiki";

const untheme = defineUntheme(config, themes);

// Bind LSP roles to tokens in your contract. Roles autocomplete; unmapped ones
// render at the default foreground. Many-to-one is fine — reuse a token to
// collapse distinctions you don't want to surface.
const theme = defineShikiTheme(untheme.schema, {
  keyword: "code-keyword",
  string: "code-string",
  comment: "code-comment",
  function: "code-fn",
  macro: "code-fn",
  type: "code-type",
  namespace: "code-type",
});

const html = await codeToHtml(source, { lang: "ts", theme });

schema is untheme.schema; it anchors the token type and re-proves the map at call time — every bound token must exist in the contract and be a color, or a SyntaxMappingError reports every fault at once. The rendered spans carry style="color:var(--code-keyword)"; emit the matching custom properties with defineRenderer(untheme).sheet(), and a data-* context flip re-colors every block for free.

Scopes

A universal set (BASIC_SCOPES) is always applied — the scopes every grammar emits by convention, routed to LSP roles. Because TextMate matches by prefix, those base rules already reach a language's .rust/.go/… scopes, so most languages classify off the base with no extra work. You don't compose it in; you add only what you want beyond it via options.scopes, which layers on top:

// add rules; a rule that names a base scope overrides it
defineShikiTheme(untheme.schema, map, {
  scopes: [{ scope: "storage.type", role: "type" }],
});

Strict LSP, open roles

The base is always strict LSP: a scope with no semantic token type — punctuation, brackets, Markdown headings — is left out and renders at the default foreground. That keeps it standard and lean. It does not limit you: a ScopeRule's role is an open string, so you reach anything TextMate distinguishes that LSP doesn't by adding your own rule —

defineShikiTheme(
  untheme.schema,
  { ...map, punctuation: "code-punct" },
  {
    scopes: [{ scope: "punctuation", role: "punctuation" }],
  },
);

— giving TextMate's full granularity at no cost to the base. A hand-authored rule's role just has to be a key in your map (or it renders default).

Options

defineShikiTheme(schema, map, options) accepts:

  • scopes — extra ScopeRule[] layered over the always-applied universal set. Add only what you want beyond it; a rule naming a base scope overrides it.
  • fg / bg — the theme's top-level foreground and background, the <pre>/<code> colors Shiki stamps on a whole block. Set fg for the unclassified text; bg for the block behind it.
  • name — the registration name (default "untheme").
  • type"light" or "dark" (default "dark"); cosmetic, since colors are driven by custom properties.

Types

  • SemanticType — one LSP semantic token type; the shipped role vocabulary, derived from the exported SEMANTIC_TYPES.
  • SyntaxMap<T> — the interchange: role → Token<T>. LSP roles autocomplete and are optional; the map stays open for custom roles.
  • SyntaxMappingError — thrown when the runtime re-proof finds a bound token that is missing or not a color; problems lists every fault.
  • ScopeRule / FontStyle — one scope-map entry; the exported BASIC_SCOPES (the always-applied universal set) is ScopeRule[].
  • ShikiOptions<T> — the options above.

Related

  • untheme — the umbrella package this depends on.
  • @untheme/css — the renderer whose var() naming this reuses.