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

@wire-ui/react

v0.5.0

Published

AI-native unstyled primitives for your design system. Headless, compound components with zero CSS — style via data-* attributes.

Readme


What is Wire UI?

Wire UI is an AI-native, headless component library. Every component ships with zero CSS — style everything using your own classes by targeting data-* attributes that reflect interactive state. AI-integrated docs with llms.txt and machine-readable API references make it built for AI-assisted workflows.

  • AI-native. AI-integrated docs with llms.txt, machine-readable API references, and MCP server support. Built for AI-assisted development.
  • Unstyled primitives. No colors, spacing, or fonts baked in. You own every pixel of your design.
  • Compound components. Complex widgets follow the Component.Part pattern, giving you full control over markup structure and element nesting.
  • State via data-* attributes. Hover, focus, pressed, disabled, open — all exposed as data-hover, data-focus-visible, data-active, etc.
  • asChild polymorphism. Merge all behaviour onto your own element — perfect for router links, icon buttons, and custom wrappers.
  • Consumer-owned validation. Form components expose invalidType and errorMessage but never validate internally. Your logic, your rules.

Documentation

Full documentation with live examples is at wire-ui.com.


Installation

npm install @wire-ui/react

Peer requirements

{
  "react": ">=19.0.0",
  "react-dom": ">=19.0.0"
}

Quick start

import { Button } from '@wire-ui/react'

export default function App() {
  return (
    <Button
      className="
        px-4 py-2 rounded-lg bg-indigo-600 text-white font-medium
        [data-hover]:bg-indigo-700
        [data-active]:scale-95
        [data-focus-visible]:ring-2 [data-focus-visible]:ring-indigo-500
        [data-disabled]:opacity-40 [data-disabled]:cursor-not-allowed
      "
    >
      Save changes
    </Button>
  )
}

Components

Form inputs

| Component | Description | |---|---| | Input | Text input with label, error, and success states | | Textarea | Multi-line input with the same compound API as Input | | Password | Input with a built-in show/hide toggle | | Checkbox | Group and individual checkbox items | | Radio | Single-selection radio group | | Switch | Toggle on/off with a thumb element | | OTP | One-time password input with individual slots | | Select | Accessible select menu with groups and separators | | Search | Search input with keyboard-navigable results list |

Overlay & dialog

| Component | Description | |---|---| | Modal | Dialog with portal rendering, overlay click-to-close, and Escape key | | Drawer | Side-panel dialog — same structure as Modal | | Dropdown | Trigger + floating menu with click-outside support | | Tooltip | Hover/focus tooltip with configurable delay and side |

Layout & navigation

| Component | Description | |---|---| | Accordion | Collapsible sections — single or multiple open mode | | Divider | Horizontal or vertical separator |

Display

| Component | Description | |---|---| | Alert | Dismissible alert with optional auto-dismiss | | Avatar | Image with a text/initial fallback | | Badge | Numeric count badge, capped at 9+ | | Card | Container with optional color and size variants | | Icon | SVG renderer from a consumer-supplied icon map | | Image | Image with a loading placeholder | | List | Ordered/unordered list with optional dividers and striping | | ProgressBar | Accessible progress indicator | | Rating | Interactive or read-only star rating | | Timeago | Relative or formatted timestamp that updates live |


Key concepts

Data attributes

Attributes are present as an empty string when active, and absent when not — never "true" or "false".

| Attribute | When present | |---|---| | data-hover | Mouse is over the element | | data-focus-visible | Keyboard focus (mirrors :focus-visible) | | data-active | Element is being pressed | | data-disabled | Element is disabled | | data-state | Open/closed, checked/unchecked — varies per component | | data-invalid | Consumer-controlled via invalidType | | data-success | Consumer-controlled via isSuccess |

Style them in Tailwind:

<Button className="[data-hover]:bg-blue-700 [data-active]:scale-95 [data-disabled]:opacity-50">

Or in plain CSS:

button[data-hover]  { background: #1d4ed8; }
button[data-active] { transform: scale(0.95); }

Compound components

Complex widgets follow the Component.Part pattern so you control the structure:

<Input.Root value={email} onChange={setEmail} invalidType={error}>
  <Input.Label>Email</Input.Label>
  <Input.Field type="email" placeholder="[email protected]" />
  <Input.Error />
</Input.Root>

asChild polymorphism

Pass asChild to merge behaviour onto your own element:

// Renders as <a> but with all Button data attributes
<Button asChild>
  <a href="/dashboard">Go to dashboard</a>
</Button>

Consumer-owned validation

Set invalidType to a key and the component renders the matching error message — no internal validation ever runs:

<Input.Root
  invalidType={error}         // e.g. "required" or "email"
  errorMessage={{
    required: 'Email is required',
    email: 'Enter a valid email address',
  }}
>
  <Input.Field type="email" />
  <Input.Error />   {/* renders the matching message */}
</Input.Root>

Hooks

useInteractiveState

The same hook used internally by Button, Accordion.Trigger, and Modal.Close — exported for building your own interactive elements.

import { useInteractiveState } from '@wire-ui/react'

function MyCard({ disabled }: { disabled?: boolean }) {
  const { handlers, dataAttributes } = useInteractiveState({ disabled })

  return (
    <div
      {...handlers}
      {...dataAttributes}
      className="[data-hover]:bg-gray-100 [data-active]:scale-95"
    >
      Card content
    </div>
  )
}

useClickOutside

Fires a callback when the user clicks outside a referenced element.

import { useRef } from 'react'
import { useClickOutside } from '@wire-ui/react'

function Popover() {
  const ref = useRef<HTMLDivElement>(null)
  useClickOutside(ref, () => setOpen(false))

  return <div ref={ref}>Popover content</div>
}

TypeScript

All component props and utility types are exported:

import type {
  ButtonProps,
  InputRootProps,
  TextareaRootProps,
  PasswordRootProps,
  ModalRootProps,
  AccordionRootProps,
  SearchOption,
  IconSize,
  Size,
  Status,
  InteractiveStateOptions,
  InteractiveStateResult,
} from '@wire-ui/react'

Accessibility

Accessibility is verified in three layers, from static markup to real assistive technology:

  1. Static audit (axe-core). Every Storybook story is rendered in a real browser and run through axe-core via @storybook/addon-a11y (a11y.test = 'error'). This fails the build on any invalid ARIA, missing required parent/child roles, or other static violations. Run with npm run test:a11y. Because the library ships zero CSS, color-contrast is the consumer's responsibility and is disabled in the audit.

  2. Screen-reader semantics (*.sr.test.tsx). axe proves the markup is valid; these tests prove it says the right thing to a screen reader — the part axe cannot check. Each interactive component has a <Component>.sr.test.tsx suite asserting the things VoiceOver, NVDA, and JAWS actually convey:

    • the computed accessible name a control is announced by,
    • the role it is exposed as,
    • ARIA state (expanded / selected / checked / pressed / current / disabled) and that it transitions on interaction,
    • exposed relationships (aria-labelledby / describedby / controls / activedescendant),
    • live-region announcements when content changes without focus moving (toasts, alerts, status, async results),
    • focus management for overlays (focus moves into a dialog on open, returns to the trigger on close).

    These run in the fast jsdom unit project. Run just this layer with npm run test:sr. Shared assertions live in src/test/sr.ts (expectExposedAs, expectAnnounced, liveRegionText, accessibleNameVia).

  3. Manual screen-reader pass. Layers 1–2 make the manual pass repeatable and guard against regressions, but they are not a substitute for it. Before a release, smoke-test the primary flows with VoiceOver (Safari), NVDA (Firefox/Chrome), and JAWS — the *.sr.test.tsx files document the expected announcements to check against.


Development

# Install dependencies (from monorepo root)
npm install

# Run Storybook
npm run storybook

# Unit tests (watch mode)
npm test

# Unit tests (single run)
npm run test:run

# Unit tests with coverage
npm run test:coverage

# Screen-reader semantics tests (accessible name/role/state/live regions)
npm run test:sr

# Accessibility audit (axe-core over every story)
npm run test:a11y

# Type check + build
npm run build

# Lint
npm run lint

# Format
npm run format

Community

Authors


Contributing

See the contributing guide for local development instructions and pull request guidelines.

License

MIT License © 2025 Wire UI. See LICENSE for details.