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

haus-components

v3.1.0

Published

haus design system components: 21 accessible React primitives built on haus-tokens.

Readme

haus-components

Eighteen accessible React components built on haus-tokens.

Install

npm install haus-components haus-tokens

react and react-dom are peer dependencies, ^18 || ^19, so your copy is the only copy. Nothing else is required. Every icon in the package is drawn inline, so there is no icon font to load and no stylesheet beyond haus-components/styles.css and the token layers.

Usage

Import the token layer, then the component stylesheet.

import 'haus-tokens/index.css'
import 'haus-components/styles.css'

index.css is the four token files in the right order. They are still exported individually if you want to swap one, and replacing brand.css is the documented case. The order matters, and getting it wrong fails silently: an unresolved var() drops the declaration with no warning, no build error, and a component that renders unstyled.

Both stylesheets are in cascade layers, haus.*, so your own unlayered CSS beats them without a specificity fight.

To theme a subtree, put a brand on it:

import 'haus-tokens/brands/vault.css'

<div data-haus-theme="vault">…</div>
import { Button, Card, Badge } from 'haus-components'

<Card>
  <Button variant="primary" size="lg">Save changes</Button>
  <Badge variant="success">Clean</Badge>
</Card>

Components

Forms and content, Avatar · Badge · Button · Callout · Card · Checkbox · Divider · EmptyState · Input · Radio · Select · Textarea · Toast · Toggle

Overlays and navigation, Modal · Popover · Tabs · Tooltip

The second group is new, and it exists because the first group was the whole library. haus was complete for forms and empty for overlays, which is the half a product cannot avoid writing itself and the half with the accessibility contracts worth centralising. The six added in 1.0: Callout, Divider, EmptyState, Popover, Tabs, Tooltip: were not chosen. They were measured: each one had already been built independently in more than one of the three products consuming this system, and the measurements are on the issues.

Every component forwards its ref and spreads the remaining props onto the underlying element, so anything not modelled as a prop is still reachable. That sentence is asserted over the barrel in src/api-surface.test.tsx, so a nineteenth component is held to it by existing.

The ref goes to the element a caller would want: the control on the form components, the dialog on Modal, the root elsewhere.

Modal takes either a title, which becomes the visible heading and names the dialog, or an aria-label when the design has no heading. The types require one of the two, since a dialog with no accessible name is announced as nothing.

Toast is the surface of a notice and nothing else: no provider, no queue, no positioning, no timer. That is a boundary rather than a gap. See decision 0008. You own where notices appear and for how long; it owns how one looks, and carries its own role="status" so it is announced wherever you put it.

The stylesheet is separate, on purpose

The CSS is emitted as a standalone styles.css rather than injected when you import a component. Injection would write to document at import time, which breaks server rendering, and it takes away your control over where the component styles sit relative to the token layers. One explicit import keeps both.

Class names are scoped at build time, as in haus-Button-button-2ZuB7, so they can never collide with yours.

Server rendering

Renders under react-dom/server with no DOM present. The interactive components (Modal, Toggle, Checkbox) use hooks but no layout effects at module scope, so they hydrate cleanly.

Asserted in src/ssr.test.tsx, which runs in a node environment rather than jsdom. Under jsdom a component can reach for document and get one, which is the mistake the test is looking for.

One exception, and it is asserted too. An open Modal is a portal, and a portal has nowhere to go on a server, so it throws rather than hydrating wrong. Render it closed on the server and open it on the client.

Note there is no 'use client' directive: under a React Server Components setup, import these from a client component.

Build

Vite library mode rather than the tsup used elsewhere in this workspace. tsup routes every stylesheet through esbuild's global css loader, which turns each *.module.css into plain global CSS and hands the component an empty styles object, giving className="undefined" on every element with no error. Vite's PostCSS pipeline scopes CSS modules properly, which is the entire reason this package needs a build.

Licence

MIT