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

@meetreeve/ui

v0.14.0

Published

Shared brand-adaptive UI primitives for MindFortress frontends — min-size tokens, a favicon→logo→monogram BrandGlyph, a responsive header that keeps the search bar usable on mobile, and AppSidebar's viewport-driven auto-collapse ladder.

Readme

@meetreeve/ui

Shared brand-adaptive UI primitives for MindFortress frontends: design tokens, a favicon→logo→monogram BrandGlyph, a ResponsiveHeader that keeps the search bar usable on mobile, and AppSidebar — a viewport-driven auto-collapse navigation ladder (DEV-4351).

"use client" components ship in one bundle (.); server-safe design tokens ship separately (./tokens, ./tokens.css) so consumers that don't need the interactive pieces aren't forced into a client boundary.

Install

pnpm add @meetreeve/ui
# peers: react >=18, react-dom >=18, next >=14, lucide-react >=0.400.0, zod >=3.24.0

AppSidebar (DEV-4351)

The sidebar for apps with more modules than a short viewport can show at once. It pre-calculates every collapse step (a ladder) rather than computing a layout on the fly: as the container shrinks, whole module groups fold into a single hover/click "flyout" row — nothing is ever hidden, just one level deeper.

import { AppSidebar, type AppSidebarModuleDef } from "@meetreeve/ui";
import { usePathname } from "next/navigation";
import ladderConfig from "@/config/sidebar-ladder.json";
import { MODULE_REGISTRY, GROUP_ORDER } from "@/app/_lib/modules";

const modules: AppSidebarModuleDef[] = MODULE_REGISTRY.map((m) => ({
  moduleId: m.name,
  label: m.label,
  icon: m.icon.displayName ?? m.icon.name, // lucide-react export name, e.g. "Mail"
  group: m.group,
  href: `/modules/${m.name}`,
}));

export function Sidebar() {
  const activePath = usePathname();
  return (
    <AppSidebar
      ladder={ladderConfig} // omit/undefined when no per-tenant config exists yet
      modules={modules}
      groupOrder={GROUP_ORDER}
      activePath={activePath}
      brand={{ label: "reeve-tenant", href: "/dashboard" }}
      topItem={{ href: "/dashboard", label: "Overview", icon: "LayoutDashboard" }}
    />
  );
}

The ladder data model

A SidebarLadder is a per-tenant JSON artifact (src/config/sidebar-ladder.json by convention) — an ordered list of rungs, most-expanded first. Each rung declares every row as either a bare app (one module) or a folded group (a parent row whose children open in a flyout). rows strictly decreases rung-over-rung; rung 0 is always the fully-expanded layout (no folded groups).

interface SidebarLadder {
  version: number;
  rungs: SidebarRung[]; // ordered, most-expanded first
}
interface SidebarRung {
  rows: number; // === items.length
  items: RungItem[]; // top-to-bottom render order
}
type RungItem =
  | { type: "app"; moduleId: string }
  | { type: "group"; id: string; label: string; icon: string; children: string[] };

icon is a lucide-react named export string (e.g. "Mail", "Layers") — the ladder is plain JSON, so it can't carry a component reference; AppSidebar resolves the name to a component at render time (falling back to Layers if unresolved).

zSidebarLadder (exported from @meetreeve/ui) enforces every invariant at load: every module appears exactly once per rung (as its own row, or inside exactly one group — never both, never twice); the module set is identical across every rung (folding can't invent or lose a module); group children is non-empty; rung 0 has no folded groups.

No config? No agent output? It still works.

If ladder is omitted, null, or fails zSidebarLadder validation, AppSidebar derives a deterministic ladder from modules + groupOrder (deriveFallbackLadder) — folding the last group in groupOrder first, walking backward. A bad agent-authored config can never brick a tenant's navigation.

A valid-but-stale ladder (modules added/removed since it was authored) is reconciled against the live modules list (reconcileLadder, exported alongside): an unknown moduleId is dropped; a moduleId the ladder doesn't reference anywhere is appended as a new top-level row on every rung.

Rung selection (useLadderRung)

AppSidebar measures its nav container with ResizeObserver and snaps to the largest rung whose rows fits floor(containerHeight / rowHeight). Stepping DOWN to a more-folded rung is immediate; stepping back UP requires an extra row of slack (hysteresisRows, default 1) so a resize sitting right at a boundary doesn't flap. The hook (and AppSidebar's ladderOptions prop) exposes both rowHeight and hysteresisRows for tuning.

Server renders rung 0; the real rung is selected client-side in useLayoutEffect, before paint — no visible snap flash on load.

Mobile

The ladder itself is desktop-only. Below the md breakpoint, AppSidebar renders a self-contained hamburger trigger + overlay drawer (no shadcn/Sheet dependency) showing rung 0 — the full, flat module list — scrollable.

Folded groups (Flyout)

A folded group's row opens a horizontal flyout on hover (desktop) or click (keyboard/touch parity): aria-expanded + menu roles, focus moves to the first child on open, Esc closes and returns focus to the trigger. If the active route's module is inside a folded group, the group's own row shows the active state and the flyout highlights the specific child. No open/close transition under prefers-reduced-motion.

ResponsiveHeader

import { ResponsiveHeader } from "@meetreeve/ui";

<ResponsiveHeader
  brand={{ name: "Cadasense", favicon: "/favicon.ico", logo: null }}
  search={<SearchInput />}
  nav={[{ label: "Docs", href: "/docs" }]}
  auth={<AuthMenu />}
  themeToggle={<ThemeToggle />}
/>;

Search is the primary slot — always inline, floored at a minimum width from @meetreeve/ui/tokens. Under width pressure everything ELSE collapses first: the wordmark drops to glyph-only, secondary controls fold into a hamburger disclosure below md.

CSS container-query collapse (DEV-4260)

The wordmark↔glyph and inline-actions↔hamburger split is pure CSS — a Tailwind v4 @container on the header plus container-query variants (@min-[768px]:...) — not JS/matchMedia. Both the desktop and mobile structures are always in the DOM; CSS alone decides which is visible at the header's current container width (not the viewport — the header can be narrower than the viewport, e.g. inside a sidebar). This means: no desktop first-paint FOUC, and a no-JS viewer gets the correct layout for their actual width instead of always falling back to the mobile/hamburger markup. JS is used only for the hamburger's open/close state and one ResizeObserver that closes an open mobile menu when the header's own container regrows past the split.

Your Tailwind build must scan this package's dist for these classes to work — same requirement as AppSidebar's md:flex (DEV-4351). Tailwind v4 ignores .gitignore'd paths (node_modules) by default, so add:

@source "../../node_modules/@meetreeve/ui/dist";

to your globals.css (adjust the relative path to your app). See reeve-tenant-frontend's src/app/globals.css for the existing precedent. Without this, ResponsiveHeader's container-variant classes (and AppSidebar's) never make it into your compiled CSS, and the header renders as if display: none applied everywhere.

BrandGlyph

import { BrandGlyph } from "@meetreeve/ui";

<BrandGlyph brand={{ name: "Cadasense", favicon: "/favicon.ico", logo: null }} size="lg" />;

Resolution order: favicon → logo → initials monogram.

ContextSwitcher (DEV-4408)

One reusable, data-driven, org-grouped dropdown for switching the active "context" — a brand, a tenant, or whatever refId-addressable entity a host app maps onto ContextSwitcherItem[]. Fully headless: no data fetching, no app-specific types. Host apps write a thin wrapper that pulls their own data source (e.g. useBrand()) and maps it to items — see reeve-frontend's BrandContextSwitcher for the pattern.

import { ContextSwitcher, type ContextSwitcherItem } from "@meetreeve/ui";

const items: ContextSwitcherItem[] = brands.map((b) => ({
  refId: b.id,
  name: b.name,
  favicon: faviconUrl(b.slug), // pre-resolve any host-specific favicon rules yourself
  logo: b.logo_url,
  orgId: b.org_id,
  orgName: b.org_name,
}));

<ContextSwitcher
  items={items}
  activeRefId={activeBrand?.id ?? null}
  onSelect={(item) => switchBrand(item.refId)}
  placeholder="Select brand"
/>;

Org headers render only when items span more than one org — the common single-org case stays a flat list. collapsed swaps the trigger for an icon-only button (56px nav rail); footer adds a slot below the list (e.g. an "Add brand" CTA).

InfoTip (DEV-11387)

A "?" circle that reveals a short explanation, so a page can keep caveats and definitions one hover away instead of as paragraphs of body copy.

import { InfoTip } from "@meetreeve/ui";

<h2>Human review required</h2>
<InfoTip label="About human review">
  These networks are withheld from buying groups until a person checks the evidence.
</InfoTip>;

Built on Base UI's Popover, not a tooltip, because a tooltip never opens on touch: hover opens it for mouse users, click/tap and Enter/Space open it everywhere, and Escape or an outside click closes it. side/align set the preferred placement and the bubble flips near a viewport edge.

The trigger keeps the 44px MIN_SIZES.touchTarget floor while its glyph is 16px. The extra height is cancelled so it sits inline in a heading without changing the line height, but the full 44px of width stays in the layout — the hit area never overlaps the text beside it, so expect the glyph to look generously spaced rather than tight against its heading. The bubble is portalled to <body> and styles from the host's theme tokens, so the host must define --color-popover, --color-popover-foreground, --color-border and --color-ring, and Tailwind consumers must include this package's dist as a @source (see ResponsiveHeader above).

Tokens

import { MIN_SIZES, BREAKPOINTS } from "@meetreeve/ui/tokens";
@import "@meetreeve/ui/tokens.css"; /* Tailwind v4 @theme custom properties */

Development

npm install       # this package uses npm, NOT the monorepo's pnpm — see repo root CLAUDE.md
npm run typecheck
npm run test
npm run build

No lint script by design — the quality gate is typecheck + test + build.