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

@aria-framework/theme

v0.7.0

Published

Aria App Framework — theme module. Field Ops Console design system: tokens.json single source of record + a generator that emits a Bootstrap 5.3 web theme (theme.css + self-hosted fonts) and a React Native palette/provider/primitives. Light + dark.

Readme

@aria-framework/theme

Shared Field Ops Console design system for apps on the Bootstrap 5.3 (web) + Expo / React Native (mobile) stack. One source of record for the brand tokens, the web theme.css + shell components (sidebar), and the React Native palette / theme provider / UI primitives. Light + dark.

tokens.json is the single source of record. A tiny generator (build.js) emits BOTH web/theme.css (CSS custom properties) and mobile/tokens.ts (the RN palette) from it, so the two platforms can never drift. Edit tokens.json, run npm run build, commit the regenerated outputs, and bump the package version (semver). Consuming apps pin a version and npm update to adopt.

Editing tokens (the generator)

  1. Edit values in tokens.json (palettes.light / palettes.dark, plus status / priority / space). Hex is stored uppercase; --brand-rgb is derived automatically.
  2. Run npm run build (node build.js). It regenerates:
    • web/theme.css — the :root + [data-bs-theme="dark"] token blocks (the rest of the file — Bootstrap mapping, edge-clarity, buttons, a11y — is static template), plus every component partial in src/components/*.css appended in filename order.
    • mobile/tokens.tsPalette, palettes, colors, status, priority, space, shadow.
  3. Do not hand-edit web/theme.css or mobile/tokens.ts (both carry an AUTO-GENERATED banner). The static base CSS (fonts/mapping/buttons/a11y) lives in src/base.css; component CSS lives in src/components/*.css — build.js emits generated tokens → base → components.
  4. Commit the regenerated files (consumers don't run the generator) and bump the version.

Note: the generator covers tokens. The RN fonts.ts / ThemeContext.tsx / ui.tsx are hand-written (they're code, not values). Consuming the package's RAW .tsx on mobile out-of-tree needs Metro watchFolders (and breaks tsc unless the package ships compiled JS+.d.ts) — see the Metro note below.

Install

Published on the public npm registry:

npm install @aria-framework/theme

Web (Bootstrap 5.3)

Load order in <head> matters — Bootstrap first, then this theme (so its --bs-* overrides win), then your app-specific component CSS:

<link rel="stylesheet" href="/vendor/bootstrap.min.css">
<link rel="stylesheet" href="/theme/theme.css">   <!-- @aria-framework/theme -->
<link rel="stylesheet" href="/css/app.css">         <!-- your components -->

Fonts + CSP. theme.css ships self-hosted woff2 in web/fonts/ and references them with relative URLs (./fonts/…). Serve the package's web/ folder same-origin so they satisfy a strict Content-Security-Policy: font-src 'self' (no CDN). With Express:

const themeDir = path.dirname(require.resolve('@aria-framework/theme/theme.css'));
app.use('/theme', express.static(themeDir));   // → /theme/theme.css + /theme/fonts/*

Font licensing. This package's license field is UNLICENSED, and that covers the CODE. The fonts are not ours and carry their own terms: Hanken Grotesk and IBM Plex Mono are both SIL OFL 1.1, whose clause 2 requires the licence text to travel with the files. It now does — web/fonts/OFL-HankenGrotesk.txt, web/fonts/OFL-IBMPlexMono.txt, and web/fonts/README.md saying which applies to what.

It did not before 0.6.3: four .woff2 files shipped with no licence of any kind, in every release, to every consuming app. Serving web/ statically as above republishes those files, so the condition is met by the same line that already serves them.

Dark mode is driven by a single attribute: <html data-bs-theme="dark">.

What's in theme.css

Design tokens (Ops Light :root + Ops Dark [data-bs-theme="dark"]), the --bs-* mapping, --shadow/--shadow-lg, brand buttons (.btn-primary, .btn-outline-primary), edge-clarity (cards/inputs use --line-strong + shadow; tables keep the faint --line for row dividers), .mono helper, focus-visible and reduced-motion — plus the shell components below. Domain-specific styles do not belong here — keep dashboards, page layouts etc. in your app's own CSS. Rule of thumb: if a second app would want it unchanged except for content, it belongs here; if it knows about your domain, it stays in the app.

Component: sidebar rail

A nav rail themed per scheme — light rail in light mode, dark rail in dark mode (driven by the --side-* tokens, including --side-strong for emphasis text and --side-accent for the active bar / icon / focus ring). Mobile-first: off-canvas overlay under 768px (hamburger + backdrop + Esc/backdrop-tap close, focus trapped via inert); an in-flow rail on desktop with a persisted collapse to a 64px icon rail. The package ships the CSS (in theme.css) and the behavior (sidebar.js); the app ships the markup against this contract:

<div id="wrapper">
  <nav class="s-side">
    <div class="s-brand">
      <a class="s-brand-link" href="/">
        <span class="s-mark">S</span>
        <span><span class="s-name">MyApp</span><span class="s-ver">v1.0.0</span></span>
      </a>
      <button class="s-collapse" data-sidebar-toggle aria-label="Collapse sidebar"><i class="bi bi-chevron-double-left"></i></button>
    </div>
    <div class="s-nav">
      <div class="s-grp">Section</div>
      <a class="s-link active" href="/x"><i class="bi bi-..."></i><span>Item</span></a>
    </div>
    <div class="s-me">
      <span class="s-av">AB</span>
      <span class="s-me-who"><b>Ann Bee</b><span>ADMIN</span></span>
      <form class="s-out-form" method="post" action="/logout"><button class="s-out" aria-label="Sign out"><i class="bi bi-box-arrow-right"></i></button></form>
    </div>
  </nav>
  <div id="page-content-wrapper">
    <!-- your topbar should include the mobile hamburger: -->
    <button class="topbar-toggle" data-sidebar-toggle aria-label="Open menu"><i class="bi bi-list"></i></button>
    ...
  </div>
</div>
<script src="/theme/sidebar.js" data-storage-key="myapp-sidebar-collapsed"></script>

Notes:

  • data-storage-key namespaces the persisted collapsed state per app (default aria-sidebar-collapsed). Keep an app's historical key across migrations.
  • To avoid a flash of the expanded rail on load, load theme-init.js (below) synchronously in <head> — it pre-applies the class before first paint.
  • State classes live on <html>: .sidebar-open (mobile overlay), .sidebar-collapsed (desktop icon rail).
  • Sidebar tokens are themeable in tokens.json under "sidebar" (per-scheme light/dark sub-objects, emitted as --side-* in :root and the dark block). Icons in the examples are Bootstrap Icons, but any icon font works.
  • If your app paints something with var(--side-*) that should IGNORE the scheme (e.g. a permanently-dark login hero panel), use fixed values there — since 0.5.0 these tokens follow the active scheme.

Pre-paint init (theme-init.js, since 0.6.0)

Resolves the color mode BEFORE first paint (no FOUC) and pre-applies the collapsed-sidebar class. Load it synchronously in <head> — NOT deferred:

<html data-theme-pref="system">  <!-- server stamps light|dark|system -->
<head>
  <script src="/theme/theme-init.js" data-storage-key="myapp-sidebar-collapsed"></script>
  ...
  • data-theme-pref="system" resolves from the OS color scheme and live-updates if the OS theme changes; light/dark are forced modes. The result lands on <html data-bs-theme="..."> (Bootstrap 5.3 convention, what theme.css keys on).
  • data-storage-key MUST match the key given to sidebar.js (same default: aria-sidebar-collapsed).
  • CSP-safe: external + self-hosted, works under script-src 'self'.

Mobile (Expo / React Native)

import { ThemeProvider, useTheme, Card, Btn, Field, palettes } from '@aria-framework/theme/mobile';

export default function App() {
  return (
    <ThemeProvider storageKey="myapp.theme">{/* namespace per app */}
      <Screens />
    </ThemeProvider>
  );
}

function Screen() {
  const { colors, scheme } = useTheme();
  return <Card><Text style={{ color: colors.text }}>Hello</Text></Card>;
}

Fonts: load @expo-google-fonts/hanken-grotesk + @expo-google-fonts/ibm-plex-mono in your app entry; the family names in mobile/fonts.ts match those packages.

Peer deps (provided by the app): react, react-native, @react-native-async-storage/async-storage.

Metro note: consuming a file: dependency requires Metro to watch the package folder. Add it to watchFolders in metro.config.js, or use a published git version to avoid symlink/transpile quirks.

Versioning

Token change → edit tokens.json, run npm run build, commit the regenerated web/theme.css + mobile/tokens.ts, then bump the version in package.json.

  • v0.1.0 — initial extraction (refined light borders + defined elevation tokens), hand-maintained web/mobile.
  • v0.2.0 — added tokens.json + build.js generator (single source of record; web + mobile generated from one file). Unified the drifted dark --line-strong.
  • v0.3.0 — renamed to @aria-framework/theme and published to npm. Mobile sources now compile to dist/ (.js + .d.ts, via a prepare script) so RN apps can import the package out-of-tree; exports point at the built files.
  • v0.4.0 — first shell component: the sidebar rail (CSS + --side-* tokens + sidebar.js behavior with a per-app data-storage-key; markup contract above). Build now assembles generated tokens → src/base.csssrc/components/*.css. Sidebar carries its own focus-visible contrast rule.
  • v0.4.1sidebar.js double-include guard (a second script tag no longer registers duplicate listeners); this changelog added.
  • v0.5.0 — sidebar rail is now per-scheme (light rail in light mode). tokens.json "sidebar" gained light/dark sub-objects and two new tokens: strong (emphasis text, replaces hardcoded #fff) and accent (active bar / icon / focus ring). Apps that used --side-* for deliberately-always-dark surfaces must pin fixed colors instead.
  • v0.6.0 — added web/theme-init.js: pre-paint color-mode resolver (data-theme-pref light/dark/system + live OS-change sync) and collapsed-sidebar pre-apply, parameterized by the same data-storage-key as sidebar.js. Apps can delete their local copies.

0.x semver reminder: ^0.4.0 matches <0.5.0 only — consumers pick up patches via npm update, but every minor bump (0.4 → 0.5) requires editing the range in each consuming app.