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

@humanforest/nuxt-layer

v0.4.0

Published

Forest design system as a Nuxt layer — extend it to inherit the theme, colour roles, icons and brand fonts in one line.

Readme

@humanforest/nuxt-layer

Forest design system as a Nuxt layer. Extend it and a Nuxt app inherits the Forest theme, colour families, icons and the real brand fonts — no npm publish involved.

The layer is self-contained — no @humanforest/* imports, no climbing to sibling packages — so giget can fetch this one directory and it still works. The generated files are committed; regenerate them from the source packages with bun run build:layer (in the design-system repo root).

What the layer delivers

  • nuxt.config.ts — registers @nuxt/ui, the Forest colour families (color="forest|maple|river|warm|acid"), the i-forest-* icon set (SVGs in assets/icons/forest/), the 23 Forest components below, and the composables listed under "Composables and helpers".
  • app.config.ts — the Nuxt UI theme: per-component tailwind-variants fragments + the semantic role map (primary→forest, error→maple, …), read from theme.gen.json.
  • styles.gen.css — fonts + ramps + theme registration + type scale + the motion ladder + the Forest base layer. The @font-face rules point at ./files/, so a giget consumer renders in the brand faces offline. This file is not published to npm.
  • styles.cdn.gen.css — byte-identical from the first token onward; only the @font-face rules differ, naming assets.forest.bike instead. This is the build the npm package ships, because a public tarball may not carry licensed binaries.
  • files/ — the real brand font binaries. Giget only; excluded from the npm tarball.
  • packages/tokens/logos/ — the 15 generated brand SVGs (mark, wordmark, both lockups, the f, and eight sub-brand favicons). Nothing in the layer imports them: FLogo and FSubLogo compose their marks live. They ship for the uses a component cannot serve — a Vue component does not go in <link rel="icon">, an email template or an OG image.
  • packages/ui/src, packages/charts/src, packages/frames/src (+ their supporting trees, mirrored at repo-relative paths — see "Regenerating") — the 39 registered Forest components, auto-registered like any Nuxt component, no import needed: ForestContext; FLogo, FSubLogo; FFilterBar, FFilterChip; FKpi, FKpiGroup; six shell components (FAppShell, FAppSidebar, FAppNavbar, FPageToolbar, FScrollShadow, FSidePanel); four canvas components (FCanvasHud, FCanvasCard, FCanvasControl, FCanvasControlGroup); eight map components (FMap, FMapPopup, FMarker, FVehicleMark, and the four controls FMapZoom, FMapCompass, FMapTilt, FMapGeolocate); eleven chart components (FBoroughShape, FCalendarGrid, FCellLegend, FChartFrame, FChartFrameSingle, FChartLegend, FDistributionBar, FDottedMap, FLondonMap, FSparkline, FStatusTrack); three presentation frames (FDeviceFrame, FBrowserFrame, FFitFrame) for staging a prototype at true scale — these carry CC BY-SA 4.0 device art, which is why they ship here and not on public npm. It is the mirrored source trees that make auto-registration possible — the components aren't published separately, they ARE those trees.

Composables and helpers

Eight are auto-imported alongside the components — FMap's imperative API plus the chart palette and the KPI formatters:

// no import needed
const { flyTo } = useMapCamera()
const palette = useChartPalette()
formatMetric(4218, { format: 'compact' })   // "4.2k"

useMapReady · useMapCamera · useMapOverlay · useFeatureSelect · MAP_SLOT · useChartPalette · formatMetric · formatPercent

This is an ALLOW-LIST, not imports.dirs, and the rest is deliberate. A directory scan would take every export in the mirrored trees, and those barrels carry names like token, isSelected, nextKey and addClause — generic enough to collide with a consuming app's own code, silently and at a distance. Everything else stays reachable by path:

import { STATE_LABEL, resolveMapColor } from '#layers/forest/packages/ui/src/map'
import { distributionShares } from '#layers/forest/packages/charts/src/distribution'

Those paths are the layer's internal shape, so treat them as less stable than the auto-imported set: they move when the source tree is reorganised.

Quickstart

Three files in the consuming app. This exact setup is verified end to end: a Nuxt 4 app in a separate repo consuming this layer over giget, with the colour ramps, semantic roles, .type-* utilities, i-forest-* icons and the brand font binaries all reaching the production build. The 21 Forest components are verified too, but via a local-extends probe app rather than a giget one — see "Local development against the layer" for a dependency step that a giget consumer gets automatically and a local one does not.

nuxt.config.ts

export default defineNuxtConfig({
  // `install: true` is load-bearing — see "Staying up to date".
  // Keep Forest LAST in extends so your own layers and app can override it.
  extends: [['github:humanforest/design-system/packages/nuxt-layer#main', { install: true }]],
  modules: ['@nuxt/ui'],
  css: ['~/assets/css/main.css'],
})

app/assets/css/main.css

@import "tailwindcss";
@import "@nuxt/ui";
@import "#layers/forest/styles.gen.css";

Installed from npm instead, the same line names the CDN build — the self-hosted one is not in the tarball, so a stale copy of this snippet fails to resolve rather than quietly falling back:

@import "@humanforest/nuxt-layer/styles.cdn.gen.css";

package.json — nuxt ^4, @nuxt/ui ^4, tailwindcss ^4, plus typescript in devDependencies. Without typescript, @nuxt/ui's unresolved peer makes bun discard the lockfile and re-resolve on every build — noisy rather than fatal, but easy to avoid.

GIGET_AUTH — a GitHub PAT with read access to this private repo, present for both install and build:

GIGET_AUTH=$(gh auth token) bun install
GIGET_AUTH=$(gh auth token) bun run build

Why the CSS needs its own import line

Forest's stylesheet uses Tailwind at-rules (@utility, @custom-variant) and mode-order-sensitive :root re-points, so it must compile in-band with your app's @import "tailwindcss", after @nuxt/ui. A layer css: [] entry would be the wrong build and the wrong order, so the consumer imports it from their own entry CSS.

It does not need copying out of the cache. #layers/forest is the alias Nuxt derives from this layer's $meta.name, and it resolves straight into giget's fetched copy — including the relative url('./files/…') @font-face paths, which Vite rewrites into content-hashed font assets emitted alongside the CSS that references them.

You do not need a @source line. Other Nuxt layer setups carry one (e.g. @source ".../node_modules/.c12/*/app/**") to make Tailwind scan the fetched layer for used classes. @nuxt/ui already does this for you: it detects every extended layer — the giget-fetched one included — and emits its own @source pointing at the resolved path. Verified by removing the manual line and rebuilding: byte-identical CSS, same 3,997 selectors, including Forest-only classes such as the coarse: touch-target utilities that appear in no app template. Copying another layer's glob is duplicate work, and here it would also walk the ~27k vendored files that install: true creates inside the cache. If a future @nuxt/ui ever stops auto-detecting layers and Forest classes go missing, add a narrow @source scoped to the layer root rather than a ** glob that descends into its node_modules.

One detail bites silently: the alias is #layers/forest (from $meta.name), not the directory name.

Precedence: earlier in extends wins, and the app wins over every layer. As a foundation, keep Forest last.

Staying up to date

Push a change to main and the next build of every consuming app picks it up — no cache-busting step, no manual cache deletion.

That follows from { install: true }: Nuxt's config loader (c12) passes it to giget as force: true, which makes giget re-fetch the ref's tarball and extract it over node_modules/.c12/<hash>/ on every build. Drop install: true and giget's "destination already exists" guard re-engages, and an app can keep building against a stale layer. It is also load-bearing for the build to succeed at all: giget only installs the layer's own dependencies (mapbox-gl, @unovis/vue, @unovis/ts, tailwind-variants, @internationalized/date) when install: true runs it inside the cache — drop the flag and every map or chart component fails to resolve its imports, not just goes stale.

The trade-off: freshness and reproducibility are the same knob. There is no lockfile pin, so a push to main reaches consumers unbidden. An app that needs reproducible builds should extend a tag or commit SHA instead of #main, and will then stop receiving updates automatically. Versioning is lockstep across the design system, cut as v0.x.y git tags. Every build also reaches the GitHub API, which is why GIGET_AUTH is required at build time and not only at install time.

Because giget serves whatever is committed, run bun run build:layer and commit the regenerated files after any token, theme, preset or font change — this includes the mirrored component sources (see "What the layer delivers"), since editing packages/ui/src or packages/charts/src doesn't touch this layer's copy until you regenerate it. The layer ships generated output (theme.gen.json, styles.gen.css, the icon set, the font files) alongside 171 mirrored component-source files, not generated output alone.

Local development against the layer

When you are changing the layer itself, point the app at your local checkout so you see edits without pushing:

const useLocalLayer = process.env.USE_LOCAL_LAYER === 'true'

export default defineNuxtConfig({
  extends: [
    useLocalLayer
      ? '../design-system/packages/nuxt-layer' // adjust to wherever you cloned it
      : ['github:humanforest/design-system/packages/nuxt-layer#main', { install: true }],
  ],
})
USE_LOCAL_LAYER=true bun run dev

This path does not install the layer's dependencies. Extending a local directory skips giget entirely, so nothing installs mapbox-gl, @unovis/vue, @unovis/ts, tailwind-variants or @internationalized/date: the layer directory has no node_modules of its own, and climbing to the design-system repo's root node_modules doesn't find them either — bun links dependencies per-workspace, not at the root. Add all five to the consuming app's own package.json dependencies. Measured against a real probe app: skip this and the build fails with Failed to resolve import "mapbox-gl" from packages/ui/src/map/FMap.vue.

The CSS import is unchanged — #layers/forest is derived from $meta.name, so it resolves whether the layer came from a local path or the giget cache.

Fonts: bundled, real, offline

This layer has two channels, and the font source is the only thing that differs between them.

Via giget, from this private repo with a PAT, it carries the real brand fonts: files/ ships the actual woff/woff2 (GT Haptik, Mohr, JetBrains Mono) and styles.gen.css points at them with relative url('./files/…') paths — no CDN, no cross-origin fetch, no Access-Control-Allow-Origin requirement. Real Mohr renders offline the moment a consuming app resolves this layer. That closed, access-controlled distribution is what makes shipping the binaries defensible.

Via npm it carries none of them. styles.cdn.gen.css names the faces on assets.forest.bike, and the files array leaves both files/ and styles.gen.css out of the tarball — Mohr and GT Haptik are licensed to Forest, not to whoever runs npm add. A licence-holder who wants to self-host from npm brings their own @font-face block.

Mohr remains licensed Black + UPPERCASE only. Because distribution is limited to the private repo plus a PAT-gated giget fetch, this stays within the licence — do not copy files/ out of this channel into a publicly published package.

Regenerating

Run after any token / theme / preset / font change, then commit the generated files:

bun run build:layer   # emits theme.gen.json, styles.gen.css, assets/icons/forest, files, packages/, apps/