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

@roprgm/ui

v0.7.0

Published

A minimal, dark UI library for React.

Readme

@roprgm/ui

A minimal, dark UI library for React and Tailwind CSS v4.

Docs and live examples: ui.roprgm.com

Install

Add the package, then import the theme after Tailwind in your CSS:

bun add @roprgm/ui
@import "tailwindcss";
@import "@roprgm/ui/themes/default.css";

Or copy components into your app with the shadcn CLI. No package needed: each component brings the theme and the components it uses.

npx shadcn@latest add https://ui.roprgm.com/r/button.json

Use

Import each component from its own path:

import { Button } from "@roprgm/ui/button";

<Button variant="primary">Export</Button>;

Components compose from their parts. An overlay's trigger renders your own element:

<Menu>
  <MenuTrigger render={<Button />}>Edit</MenuTrigger>
  <MenuContent>
    <MenuItem>Duplicate</MenuItem>
  </MenuContent>
</Menu>

Put groups of controls on a card. Controls take their fills from the surface they sit on, so they keep the same contrast everywhere:

<Card>
  <Input placeholder="Name" />
</Card>

A card, popover, dialog, or notice pads its content as one section. To split it, put its parts in Sections: one reads the same as none, and several stack with a line between each, or, in a dialog or notice, without one. A Collapsible counts as a section. For content that runs edge to edge, such as a frame around an app, remove the padding with p-0:

<Card>
  <Section className="flex-row items-center">Layers</Section>
  <Section>
    <Input placeholder="Name" />
  </Section>
</Card>
  • Use surface-card for cards, which rise two levels over what they sit on, even another card, and surface-raised for controls, three levels up. Dock a card in your layout with surface-panel, which has no edge. What floats is surface-float, a card drawn floating: a popup opens as a card on the page, and with raised, a level up, for one that opens over a card.
  • Use the theme's colors, such as text-muted, bg-field, or bg-level-3, instead of Tailwind's palette.
  • Build your own controls from the surface-* primitives rather than shadows or borders of your own, so they follow the theme.
  • For content that may not fit, use overflow-fade-x or overflow-fade-y: it scrolls, and fades where more lies that way. For one line of text, use ScrollText rather than truncate: it fades at the end and scrolls to show the rest, without becoming a tab stop. Inside something you click, such as a button, keep truncate.

Components without JavaScript, such as Button and Input, render on the server.

Mount one <Toaster /> near the root, then show a toast from anywhere, even outside React: toast.add({ title: "Exported" }).

Components

| Section | Components | | --- | --- | | Actions | Button, IconButton, CopyButton, Chip, Kbd, Badge | | Inputs | Input, Textarea, Field, Checkbox, Radio, Switch, ToggleGroup, Select, Combobox, Slider, ScrubInput | | Containers | Card, with Section and SectionAction from section, and sections() for a panel docked at your app's side; the surface-* primitives; Collapsible, ScrollArea, CodeBlock | | Navigation | Tabs, ListItem, TreeList | | Overlays | Tooltip, Menu, ContextMenu, Popover, Dialog, Notice, Toast | | Effects | Spinner, ScrollText, and the shimmer, overflow-fade-x, and overflow-fade-y utilities |

Each one has a live demo and its install command on the docs site.

Theme

base.css holds the tokens, the primitives the components are built from, and the page's rules: 13px text on a dark background. themes/default.css imports it, draws the primitives with light and shadow, and sets Geist when your app loads it. The comments in both files describe each token.

Colors

| Token | Use | | --- | --- | | level-0 to level-12 | the backgrounds of the interface: black, then darkest first in even steps of lightness; every fill is one of them | | surface, field, raised, raised-hover, hover | fills relative to the surface they sit on: its own, a field two levels below, a raised control three above and four on hover, and a ghost's hover one above | | foreground, muted, faint, disabled | text, strongest first: faint for what should barely show, such as a disclaimer | | primary, primary-hover, on-primary | primary buttons, checked controls, and slider thumbs | | pressed | a translucent state for ghost buttons and chips | | line, separator, focus, backdrop | the lines between a card's parts, a separator's translucent dark, the focus ring, and the shade behind a dialog | | danger | errors |

Surfaces

A surface is what a box is filled with and how it stands against what's under it; the theme draws its edge. A raised surface fills itself two levels over where it sits and sets the fills of what sits on it, so everything keeps its contrast at any depth and every color is one of the palette's:

| Surface | Fill | Used by | | --- | --- | --- | | surface-card | two levels up from what it sits on | cards | | surface-panel | a card's, with no edge | a card docked in your layout, such as a sidebar | | surface-float | a card's, drawn floating; with raised, a level up | popups, dialogs, notices, anything dragged | | surface-raised | three levels up from what it sits on | buttons, selects, selected tabs and toggles | | surface-sunken | two levels down from what it sits on | fields, tracks, checkboxes, switches, groups | | surface-primary | the primary color | primary buttons, checked switches, slider thumbs | | separator | a line | a menu's separators |

Sizes

Controls are 28px tall (h-7), with 24px and 32px sizes, and list rows 40px. Heights and spacing are Tailwind's own, so a control of your own takes the same classes.

Customize

Redefine a token to restyle every component that uses it:

@theme {
  --color-primary: oklch(67% 0.16 252);
  --radius-md: 8px;
}

A theme changes what a surface is by setting its fills again, at each depth, in its own @utility surface-card. A surface of your own sets the fills of what sits on it and paints its own:

@utility surface-sidebar {
  --color-surface: var(--color-level-1);
  --color-field: var(--color-level-0);
  --color-raised: var(--color-level-4);
  --color-raised-hover: var(--color-level-5);
  --color-hover: var(--color-level-2);
  background-color: var(--color-surface);
}

Development

bun install
bun run dev

bun run check formats, lints, and type-checks. bun run build compiles the package, the registry, and the docs site.

Publishing a GitHub release publishes the package to npm. The release tag must match the version in package.json, such as v0.6.0.