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

@stll/ui

v0.9.1

Published

Stella's design system: bidi-aware React primitives built on Base UI, the dockable inspector pane, and the Tailwind v4 theme they are styled with.

Downloads

2,087

Readme

@stll/ui

Stella's design system: the React primitives the product surfaces are built from, the dockable inspector pane, and the Tailwind v4 theme they are styled with.

The package is self-contained by construction. It imports no application module, no path alias, and no other workspace package, so every component can be rendered and tested without a surrounding application — and a change to one cannot reach into product code by accident. Lint enforces that boundary; the export map below is what "part of the design system" means.

Peer dependencies: react, react-dom, @base-ui/react, tailwindcss (v4), @dnd-kit/core, @dnd-kit/sortable, and @tanstack/react-virtual.

Import

Every module has its own subpath, and the package declares sideEffects: false, so a bundler keeps only what is imported:

import { Button } from "@stll/ui/button";
import { ApplicationShell } from "@stll/ui/application-shell";
import { Dialog, DialogPopup } from "@stll/ui/dialog";
import { Inspector, InspectorDock } from "@stll/ui/inspector";
import { cn } from "@stll/ui/utils";

One flat subpath per module: @stll/ui/<name> for components, hooks, and helpers alike. @stll/ui re-exports all of them under one specifier for convenience; the subpaths are the real surface, and in-repo code uses those.

Application shell

ApplicationShell keeps the navigation, page chrome and content, and an optional inline-end inspector as sibling columns. Pass the host application's surfaces as slots; route state, navigation behavior, and inspector behavior remain in the host.

import { ApplicationShell } from "@stll/ui/application-shell";

<ApplicationShell
  header={<PageHeader />}
  inspector={<InspectorDock />}
  sidebar={<Navigation />}
>
  <Page />
</ApplicationShell>;

Deprecated grouped subpaths

@stll/ui/components/<name>, @stll/ui/hooks/<name>, and @stll/ui/lib/<name> still resolve to the same modules and are kept for one minor. They will be removed after that; the export guard checks that both spellings land on the same module for as long as they both exist.

Sortable boards

@stll/ui/kanban provides board matrices, subgroup swimlanes, bounded virtual cells, and input/accessibility primitives. The caller keeps domain identifiers, card rendering, permissions, and persisted mutations. Wrap sortable boards in KanbanSortableBoard, render items through useKanbanSortable, and attach the returned bindings to KanbanDragHandle.

import {
  KanbanDragHandle,
  KanbanSortableBoard,
  KanbanSortableList,
  useKanbanSortable,
} from "@stll/ui/kanban";

const Card = ({ id }: { id: string }) => {
  const { dragHandle, setNodeRef } = useKanbanSortable({ id });
  return (
    <article ref={setNodeRef}>
      <KanbanDragHandle bindings={dragHandle} label="Move card" />
    </article>
  );
};

<KanbanSortableBoard onDragEnd={handleDragEnd}>
  <KanbanSortableList items={cardIds}>
    {cardIds.map((id) => (
      <Card id={id} key={id} />
    ))}
  </KanbanSortableList>
</KanbanSortableBoard>;

The default sensors are mouse (8px movement), touch (150ms delay with 8px tolerance), and keyboard. Scroll containers retain touch-action: auto; only KanbanDragHandle disables touch panning. KanbanSortableBoard also accepts custom sensors, accessibility, collision detection, keyboard coordinates, auto-scroll options, and an overlay render function.

getKanbanHorizontalEdge takes input: "pointer" with a current client-x and direction: "ltr" | "rtl" for mouse and touch input. Keyboard calls use input: "keyboard" and require source and target indices, so every move has a logical edge without relying on ambiguous geometry.

For Group/Sub-group boards, build one canonical matrix and render it with the installable layout and virtual cell:

import {
  KanbanSubgroupBoard,
  KanbanVirtualCell,
  buildKanbanBoardMatrix,
} from "@stll/ui/kanban";

const matrix = buildKanbanBoardMatrix({
  group,
  subgroup,
  rows,
  resolveGroupValue,
  uncategorizedLabel: "No value",
});

<KanbanSubgroupBoard
  matrix={matrix}
  renderColumnHeader={({ column, count }) => (
    <ColumnHeader column={column} count={count} />
  )}
  renderLaneIdentity={({ group: lane }) => <Lane group={lane} />}
  renderCell={({ cell }) => (
    <KanbanVirtualCell
      getRowKey={(row) => row.id}
      pagination={{ type: "none" }}
      renderRow={(row) => <Card row={row} />}
      rows={cell.rows}
    />
  )}
/>;

Styles

No compiled CSS ships. The components carry Tailwind class names, so the Tailwind build stays with the application that uses them:

@import "tailwindcss";
@import "@stll/ui/theme.css";
@source "../node_modules/@stll/ui/dist";

theme.css carries the @theme token map, the palettes, the base layer, and the custom utilities. The @source line is what makes Tailwind scan the shipped components for the utilities they use — node_modules is outside its default source detection.

theme.css is the only stylesheet the package exports. Each application owns its own Tailwind entry (apps/web/src/styles/app.css, apps/desktop/src/mainview/index.css, apps/playground/src/styles.css), which is where the source globs for that application belong.

Layout direction

The components are bidirectional. Use logical properties (ms-*, pe-*, start-*) rather than physical ones, and let the slots that render caller-supplied values (names, identifiers, filenames) keep their dir="auto" and bidi isolation.

Development

bun run build       # tsdown, one output module per source module, with .d.ts
bun run test        # unit tests
bun run test:unit   # unit tests only
bun run test:browser # Chromium mobile-input tests
bun run typecheck
bun run lint

bun scripts/check-published-exports.ts packages/ui (from the repository root) runs the publish path end to end: build, prepare-publish, bun pm pack, then resolves and imports every declared subpath from the built dist.

bun run pack:check runs that published-package check for this package.

This package is published, so a change here needs a changeset:

bun changeset            # anything a consumer of the package can observe
bun changeset --empty    # internal refactor, no change to the public surface