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

@notyet.im/ui

v0.2.1

Published

NotYet UI — a dense, dark-first React design system: UI components and data-visualisation charts.

Readme

@notyet.im/ui

npm license storybook

NotYet UI — a dense, dark-first React design system in two halves: UI components for building interfaces, and Charts for encoding data.

Browse every component →

It is deliberately opinionated:

  • Two categories, nothing else. Every component is UI or Charts.
  • Semantic tokens only. Components never reference a raw colour or a literal pixel. There is no utility-class vocabulary to learn or to fight.
  • Zero runtime dependencies. React is a peer; nothing else ships.
  • Density is the point. The default control is 34px and the body size is 14px, because this system exists for information-dense screens.
src/
  components/          the library — one .tsx + .css per component family
  styles/tokens.css    every design token, the single source of truth
  styles/base.css      reset, the one focus ring, disabled and reduced motion
  lib/                 pure layout maths for the charts, plus formatting
  tracker/             a showcase application built from the components

Quick start

npm install @notyet.im/ui
import { ThemeProvider, Container, Grid, GridItem, Panel, Button } from '@notyet.im/ui'
import '@notyet.im/ui/styles.css'
import '@notyet.im/ui/fonts.css' // optional — IBM Plex from Google Fonts

export function App() {
  return (
    <ThemeProvider theme="dark">
      <Container size="lg">
        <Grid columns={{ base: 1, md: 12 }} gap={16}>
          <GridItem span={{ base: 1, md: 8 }}>
            <Panel>
              <Button variant="primary">Rebalance</Button>
            </Panel>
          </GridItem>
        </Grid>
      </Container>
    </ThemeProvider>
  )
}

ThemeProvider is required

It defines the --ny-* custom properties, sets data-theme, and scopes the reset and the shared focus ring. Outside it, components render with transparent backgrounds and browser-default text, because every colour they reference resolves to nothing. useTheme() reads the current theme; the provider is a pure presenter, so theme state lives with you.

Fonts

styles.css @imports IBM Plex Sans and Mono from Google Fonts. To self-host, skip fonts.css and redefine --ny-font-sans / --ny-font-mono.

Tokens

All ~140 tokens live in one file: src/styles/tokens.css. Two layers — primitives (--ny-ink-*, --ny-paper-*) exist so the semantic layer has something coherent to derive from, and components may only reference the semantic layer.

| Family | Notes | |---|---| | Surface / border / text | Flip with the theme | | Accent, feedback | {tone}, {tone}-subtle (tint), {tone}-text (ink on tint), text-on-{tone} (ink on solid) | | Data | --ny-positive / --ny-negative / --ny-neutral — theme-invariant on purpose: teal always means up | | Spacing | 4px base, each token named for the pixel value it holds (--ny-space-12 is 12px) | | Spacing roles | --ny-inset, --ny-inset-compact, --ny-stack, --ny-gutter — prefer these for component chrome | | Type | 9 steps in rem, so the scale honours the reader's browser font size | | Radii, elevation, motion, controls, layering | --ny-radius-*, --ny-shadow-*, --ny-duration-*, --ny-control-height-*, --ny-z-* |

src/tokens.ts mirrors the values the charts need as JS strings — SVG stroke and fill attributes cannot read a custom property. src/tokens.parity.test.ts asserts the two never drift.

Breakpoints are 480 / 768 / 1024 / 1280. --ny-bp-* exists for JS and documentation only: a custom property inside a @media condition is invalid CSS and fails silently, so stylesheets hardcode those four numbers and a test enforces it.

Layout and responsiveness

Container, Grid, GridItem and Stack are the whole system. They write inline custom properties from their props, which four static media queries read — four media queries for the entire library, no per-breakpoint class explosion, SSR-safe.

<Grid columns={{ base: 1, md: 12 }} gap={{ base: 8, md: 16 }}>
  <GridItem span={{ base: 1, md: 8 }}>…</GridItem>
</Grid>

Components themselves adapt with container queries, not media queries: they respond to the space they are actually in, so a HeatGrid in a narrow panel on a wide monitor reflows correctly.

Components

Charts (6) — SankeyFlow, RotationRing, Sparkline, HeatGrid, RotationMatrix, BreakdownBar.

UI (43) — layout (Container, Grid, GridItem, Stack, Panel, PanelHeading, PageHeader, MacroStrip), typography (Text, Heading, VisuallyHidden, Eyebrow), controls (Button, InlineAction, Select, SegmentedControl, Tabs, ThemeToggle), forms (Input, Textarea, Checkbox, Radio, RadioGroup, Switch, Field, Label), overlays (Dialog, Tooltip, Popover, Toast), feedback (Badge, Alert, Spinner, Skeleton, Avatar) and data display (StatTile, DataRow, NarrativeItem, MomentumCard, Legend, Table, Breadcrumb, Pagination).

Storybook

The full catalogue — every component, every variant, both themes — is published at notyet-im.github.io/ui. Run npm run storybook for the same thing locally against your working tree.

It deploys from main on every push, and only from a commit that passed the gate. That means it can briefly show a component or a prop that has not been released to npm yet — the published API is whatever npm install gives you, and CHANGELOG.md is the record of what changed when.

Conventions worth knowing

  • Form controls take value or defaultValue — both work. Tabs, SegmentedControl and Select are controlled-only.
  • Wrap inputs in Field rather than wiring labels by hand; it generates the ids and the aria-describedby / aria-invalid links.
  • Components never format numbers. Pass a formatted string plus the signed raw value that picks the colour. formatDelta and formatCompact emit U+2212 MINUS so signed columns stay optically flush.
  • Charts measure themselves — do not hardcode a width. SankeyFlow and RotationRing lay out in real CSS pixels because their labels are HTML and must not scale, so they read their own container. Pass a width only to pin a chart to a fixed size, which will overflow a narrower parent.
  • Give HeatGrid and RotationMatrix a label. They render a real role="grid", and a grid with no accessible name cannot be placed.
  • Sparkline's fill follows its baseline — closed on the zero line with baseline, on the bottom of the plot without it. Pass baseline when the sign is the point.
  • StatTile only works inside MacroStrip.

Utilities

deltaColor / deltaColors · formatDelta / formatCompact / formatPercent · useMeasure / useEscapeKey / useControllableState / useRovingFocus / useAnchoredPosition / useTopLayer · sankeyLayout / ringLayout / heatStyle / walkSeries / seriesPath. walkSeries is fixture plumbing — a deterministic demo series for a chart with no real data yet — not design-system API.

Scripts

| | | |---|---| | npm run dev | showcase app on Vite | | npm run storybook | component catalogue | | npm run build-storybook | the static catalogue CI publishes to Pages | | npm run build:lib | the publishable dist/ | | npm run check | the gate — biome, tsc and vitest, all zero-error | | npm test | vitest only | | npm run format | biome autofix |

styles.css is a single stylesheet for the whole library (cssCodeSplit is off), which is what the design-sync consumer expects. It is ~55 kB raw, ~8.5 kB gzipped.

Stability

0.x — the API is not frozen. Under semver a 0.x minor bump may break you, and this system has not yet been consumed outside its own repo, so it will. Pin an exact version if that matters to you; CHANGELOG.md records what moved and why. The 1.0 line commits to the usual guarantees.

Ships ESM and CJS with types for both, so import and require are equally supported under moduleResolution: "node16".

Releasing

Releases are cut by CI, not by hand: push a v* tag matching package.json's version and .github/workflows/release.yml runs the gate and publishes with build provenance — an attestation binding the tarball to the commit and workflow that produced it. There is no publish token; CI authenticates with a short-lived OIDC credential via npm trusted publishing.

Accessibility

  • One focus ring for the system, in base.css behind --ny-focus-ring. Components never declare their own.
  • Disabled is never opacity-based — opacity compounds through nesting and destroys the contrast the palette was chosen for.
  • Composite widgets implement roving focus: Tabs, SegmentedControl and RadioGroup are one tab stop that arrow keys move within, with Home/End, per the WAI-ARIA APG. HeatGrid does the same in two dimensions whenever its cells are selectable, with arrows clamping at the edges rather than wrapping. Pagination is deliberately not in this list: it is a <nav> of independent page links, not a single composite control, so each link is its own tab stop.
  • Overlays use the browser top layer — Dialog is a native <dialog> with showModal(), so the focus trap, Escape handling and scrim are the browser's, not ours. Nothing sets z-index.
  • prefers-reduced-motion collapses every transition, globally.
  • Every component has an axe assertion in its test. Colour contrast is verified visually instead, because jsdom performs no layout and axe cannot compute it.

Licence

MIT — see LICENSE.