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

@ux-builder/ui

v0.16.0

Published

Widgets for the harness and embedded products: prefixed CSS and React 19 components on @ux-builder/tokens, shaped by whichever theme is on.

Readme

@ux-builder/ui

The widgets the harness and embedded products share: prefixed CSS (.piq-*) plus a React 19 component for each, owning keyboard, focus, sorting, paging and accessibility. Every rule reads a token from @ux-builder/tokens, so the theme on the page decides how each widget looks.

| Component | What it is | |---|---| | Button | variant primary, secondary or text; size="sm"; badge for a count; busy shows a spinner over the label, sets aria-busy and ignores clicks | | TextField, TextArea | Labelled input, single or multi-line, with hint and error (sets aria-invalid, wired with aria-describedby) | | Select | Dropdown on a native select; in Chrome 135+ the open list is themed too (appearance: base-select) | | Checkbox, Switch | Native checkboxes, restyled; the switch has role="switch" | | RadioGroup | Native radios in a fieldset whose legend names the group | | ChoicePicker | One choice from a few: a radio group drawn as segments, arrow keys move the selection | | Card | Title, content and actions | | Notice | In-page message, tone info, accent, success, warning or error | | StatusPill | Availability status: live, limited, purchasing, evaluation, closed | | DataTable | Sorts (aria-sort) and pages; numbers align right | | Toast, ToastProvider, useToast | Confirmation with Undo in a polite live region; waits while hovered or focused | | ProgressBar | label and value (of max, default 100) for known progress; without value, indeterminate. role="progressbar" | | Spinner | size sm, md or lg (16, 24, 48px); label is announced (role="status"), shown with showLabel | | Skeleton | Placeholder while content loads: variant text (lines), rect or circle; hidden from assistive technology | | Logo | The active theme's logo or mark, from the brand's artwork; a brand without artwork shows its title as a wordmark | | Favicon | Keeps the page's icon on the theme's favicon, for a page without a ThemeProvider |

Using it

npm install @ux-builder/tokens @ux-builder/ui
import "@ux-builder/tokens/themes.css";   // or one theme's file
import "@ux-builder/tokens/fonts.css";    // or one theme's: fonts/<theme>.css
import "@ux-builder/ui/ui.css";            // after the theme CSS
import { Button, DataTable, ToastProvider, useToast } from "@ux-builder/ui";

React 19 is a peer dependency. An app that is not React can use the CSS on its own markup: the class names and structure are what each component renders.

Theme and appearance

import { ThemeProvider, useTheme } from "@ux-builder/ui";

<ThemeProvider theme="databricks" appearance="system">   {/* light | dark | system */}
  <App />
</ThemeProvider>

function Settings() {
  const { theme, appearance, preference, themes, setTheme, setAppearance } = useTheme();
  // appearance is always "light" or "dark"; preference may be "system"
}

The provider's element carries data-theme and data-appearance (CONTRACT-003), so the theme CSS applies inside it; the nearest provider wins. appearance="system" is resolved before first paint and follows the user's preference. An appearance the theme lacks falls back to the theme's own, with a development warning. For an embedded view (ADR-011), pass tokens={resolved.tokens} instead of theme: the values are set as custom properties and no stylesheet is needed.

The provider does not remember the user's choice. onChange({ theme, appearance }) is called on every switch (appearance is the preference, so it may be system); store it and pass it back as props. as sets the element (default div).

The top-level provider also answers a window event, so a tool (such as @ux-builder/audit) or another part of the page can switch the theme as the user would:

window.dispatchEvent(new CustomEvent("piq:set-theme", { detail: { theme: "praxis-carbon", appearance: "dark" } }));

Nested and embedded providers ignore it.

Component tests run on the built package: npm run build && npm test.

For AI coding agents

The package ships guidance an agent reads before writing UI, and a machine-readable catalogue, both generated from this package's source so they never name anything that does not exist (FEAT-004 SDK-12, SDK-13):

  • AGENTS.md: the rules, how to set up, how to do common things, and a reference of every component and prop, role token, icon and theme.
  • skill/ux-builder-ui/SKILL.md: the same, as a Claude Code skill. Install it in an app with mkdir -p .claude/skills && cp -R node_modules/@ux-builder/ui/skill/ux-builder-ui .claude/skills/.
  • catalogue.json (@ux-builder/ui/catalogue.json): every token (path, tier, purpose, 0.x alias), every export (props and defaults, purpose), themes, design systems, icons and the lint rule.

Agents can also query the SDK over MCP with @ux-builder/mcp (claude mcp add ux-builder -- npx @ux-builder/mcp): themes with every role token's value, brands, design systems, widgets with props, tokens with purposes, icons and this guidance.

Interaction rules and the blueprint

@ux-builder/ui/blueprint holds the interaction and IA rules (navigation by size, modals, safeguards, states, emphasis) and checks an app's plan against them, in Node or a browser, with no network (FEAT-008, CONTRACT-005):

import { rulesFor, checkBlueprint, findRecipes, skeleton } from "@ux-builder/ui/blueprint";
rulesFor({ context: "view", sizeClasses: ["compact", "medium"], inputs: ["coarse"] }); // the rules for an embedded view
checkBlueprint(JSON.parse(readFileSync("docs/ux-builder/blueprint/app.blueprint.json", "utf8")));              // [{ rule, severity, path, message, fix }]

The command ux-builder-blueprint does the same from a shell: ux-builder-blueprint init [--kind app|view] writes a starting docs/ux-builder/blueprint/app.blueprint.json, and ux-builder-blueprint check prints each finding and exits 1 on an error. check <file> --diff <base> also reports the complexity the change adds over a base blueprint: per journey, the screens, steps, required inputs and decisions added and removed, and a simplicity.delta-decided warning for a journey that grows with no recorded decision naming the addition (FEAT-018). With --json it prints { findings, delta }. When the app has .ux-builder/principles.json (written by ux-builder-design brief), check also applies each principle that carries a rule as a project rule, project.<PR-id>. A blueprint may hold a task model, tasks[], with journeys naming the task they realize; ux-builder-blueprint simplify proposes inputs to infer, decisions to default, steps to combine and hops to remove, and changes nothing (FEAT-018). From a blueprint's actions and journeys (FEAT-012), ux-builder-blueprint propose drafts HELIX content as patches it never applies, draw writes the IA map, flow and journey diagrams as Mermaid and SVG, and wireframes renders each screen's pattern at each supported size class as HTML and PNG (PNG needs playwright and its Chromium). Every declared state is drawn too (FEAT-015 RPT-09): draw writes states-<screen>.mmd (a Mermaid stateDiagram-v2 with each state the screen declares and the transitions between them), its SVG, and states-<screen>.md with a state table (what the user sees, the action available, the live-region announcement) and notes on the default transitions and any state the screen does not declare; wireframes writes <screen>.<state>.<size>.html and .png for each state beside the ideal <screen>.<size>.html; and propose drafts HELIX's user flow for each feature (UF-<n>), a wireframe for each of its views (UF-<n>-<nn>-<view>, with the view's states, statechart and events) in docs/helix/02-design/flows/UF-<n>-<feature>/, a user-journey-map per journey, and DESIGN.md's Modalities, Size Classes and App Frame, all as patches a person applies. Every command writes under docs/ux-builder/ (the blueprint and its alternatives in blueprint/, then prototypes/, helix-proposals/, compare/, drawings/, wireframes/); an app made before that layout keeps its old paths, each command says so, and npx ux-builder-design migrate moves it (FEAT-018 GDL-80). In a guided app, wireframes and prototype --explore first need the person's modality answer (ux-builder-design modality). See the repository README. The rules, recipes and the blueprint schema are also in catalogue.json (interaction) and in AGENTS.md.

The rules restate guidance from Apple's Human Interface Guidelines, Material Design 3, WCAG 2.2 and the WAI-ARIA Authoring Practices in this package's own words, with links to the sources. ux-builder is not affiliated with or endorsed by Apple or Google. Human Interface Guidelines is a trademark of Apple Inc., and Material Design is a trademark of Google LLC.

For other agents, point the app's own AGENTS.md or CLAUDE.md at node_modules/@ux-builder/ui/AGENTS.md. In this repository, npm run catalogue regenerates them, and npm run check fails if they are stale.

Lint

ux-builder/no-hard-coded-values reports literal colours, font families and border radii in app CSS and JSX style props, and names the role token to use when your theme has one with that value (FEAT-004 SDK-10, SDK-11).

// .stylelintrc.json
{ "plugins": ["@ux-builder/ui/stylelint"],
  "rules": { "ux-builder/no-hard-coded-values": [true, {
    "theme": "databricks",
    "allow": [{ "value": "#4E79A7", "file": "src/charts/**", "reason": "chart series colours" }]
  }] } }
// eslint.config.js
import uxBuilder from "@ux-builder/ui/eslint";
export default [{ files: ["src/**/*.jsx"], plugins: { "ux-builder": uxBuilder },
  rules: { "ux-builder/no-hard-coded-values": ["error", { theme: "databricks" }] } }];

#1B3139 under databricks reports use var(--piq-color-text). Tokens, var() fallbacks, transparent, currentColor, CSS-wide keywords, 0, generic font families and fully round radii (50%, 999px) are fine. Every allow-list entry needs a reason; one without is reported. stylelint and ESLint are optional peer dependencies.

Two more ESLint rules catch what the SDK already provides:

rules: {
  "ux-builder/no-foreign-ui": ["error", { allow: [] }],   // package names you have reviewed
  "ux-builder/no-inline-svg": ["error", { allow: ["chart"] }],   // <svg> class names or ids
}

ux-builder/no-foreign-ui reports imports of other UI kits (Material UI, Ant Design, Chakra, Carbon React, Fluent UI, Mantine, Bootstrap, Blueprint, PrimeReact, Radix Themes), icon sets (lucide-react, react-icons, Heroicons, Phosphor, Tabler, Font Awesome) and loader libraries, naming the SDK's counterpart. ux-builder/no-inline-svg reports <svg> written in JSX: use <Icon>, @ux-builder/ui/icons or registerIcons. A data graphic that scanners read (a QR code, a barcode) is not an icon and must stay black on white in every theme: mark it data-graphic (<div data-graphic="qr">, or [data-graphic] in a CSS selector), and inside it both rules accept its own <svg> and black and white as its colours, nothing else. Lint reads source; for what only the running app shows, run @ux-builder/audit.

ux-builder/ai-neutral-voice (a warning, US-081) checks the copy around AI features in JSX text and string attributes. "I think", "I feel", "I understand", "I believe" and their variants present the AI as a person (rule ai.not-a-person); a label like "Copilot, summarize" makes the AI the actor (ai.user-initiates): write "Summarize" or "Summarize with Copilot". It checks English only, and not text the model writes at run time. A brand with a deliberate persona opts a string out with // ux-builder-ai-persona (or {/* ux-builder-ai-persona */} in JSX children) on the line before it.

uxBuilder.configs.recommended turns on all four rules, with the voice rule as a warning: export default [{ files: ["src/**/*.jsx"], ...uxBuilder.configs.recommended }].

Icons

import { Icon, ICON_NAMES } from "@ux-builder/ui";

<Icon name="warning" />                         {/* decorative: aria-hidden */}
<Icon name="search" label="Search" size="1.25rem" />   {/* role="img", named */}

One component, shared names (ICON_NAMES: add, close, search, warning, …, 33 in all). The glyph follows the theme's design system: Material Symbols on Material, Fluent System Icons on Fluent, Carbon icons on Carbon, and Lucide on every theme without a set of its own (praxis, plain, synaptiq, databricks). The set comes from the nearest ThemeProvider, else the closest data-theme in the page; set="material" forces one. A name a set lacks is drawn from Lucide. Icons take the text colour and are 1em unless size says otherwise.

Every other icon

@ux-builder/ui/icons has every Lucide icon (2,130) by its Lucide name, each with Material, Fluent or Carbon's own glyph where one matches (about 420, 410 and 350 of them; Lucide's otherwise, as on any theme without a set). One export per icon, so an app's bundle carries only those it imports:

import { Icon, registerIcons } from "@ux-builder/ui";
import { iconPin, iconPlay, iconPaperclip } from "@ux-builder/ui/icons";

<Icon icon={iconPin} label="Pin" />
registerIcons(iconPlay, iconPaperclip);      // once, then:
<Icon name="play" />

The export is icon + the name in PascalCase; ICON_LIBRARY_NAMES lists them, and so do catalogue.json (iconLibrary) and the MCP tool list_icons with a query. Matches come from the name itself, word synonyms per set and hand-checked overrides (Material's own pin is a PIN keypad, so pin there is keep), so a set never draws the wrong idea. For an icon the library lacks, register the app's own, registerIcons({ name, glyphs: { lucide: { viewBox, body } } }), and it draws through Icon like the rest; markup with scripts, handlers or links is refused.

The glyphs are extracted at build time (npm run icons) from pinned icon packages, so an app installs none of them; each set's licence ships in licenses/ (Apache-2.0, MIT, Apache-2.0, ISC). A test fails if the extracted glyphs drift from the packages.

Logo

import { Logo } from "@ux-builder/ui";

<Logo />                  {/* the brand's logo at the brand's height */}
<Logo variant="mark" size="sm" />   {/* the mark, compact */}

The artwork comes from the theme's brand tokens (--piq-brand-logo-image, or a mask painted in the logo colour), so the logo changes with the theme and the appearance and the app imports no file. A brand without artwork shows its title as a wordmark in its logo colour (the mark: its initial). It is one image named by the brand's title, or by label. Brand tokens belong to the top-level app (CONTRACT-001): in an embedded view (ThemeProvider with tokens) Logo draws nothing.

Favicon

The top-level ThemeProvider keeps the page's icon on the theme's favicon (--piq-brand-favicon) as the theme and appearance change: it adds one <link rel="icon" data-piq-favicon> last in the head, so browsers prefer it to the app's own. A theme without a favicon removes it, and the app's own <link rel="icon"> shows again, so keep one in index.html. favicon={false} turns this off; an embedded view or a nested provider never touches the icon. A page that sets data-theme on <html> without a provider renders <Favicon /> once; applyFavicon(element) does the same by hand.

How a theme shapes a widget

Every rule reads CONTRACT-001 role names (--piq-color-text, --piq-button-radius), never a 0.x alias. The base tokens every theme has (colour and state roles, type, --piq-button-*, --piq-pill-*, --piq-badge-*, radius, shadows, --piq-focus-*, --piq-state-layer-*) style every widget. On top of them, a design system may set widget tokens: --piq-field-*, --piq-check-*, --piq-switch-*, --piq-menu-*, --piq-choice-picker-*, --piq-card-*, --piq-notice-*, --piq-toast-*, --piq-table-*, --piq-progress-*, --piq-spinner-*, --piq-skeleton-*, and the --piq-button-secondary-* and --piq-button-ghost-fg variants. Each has a fallback in ui.css that reproduces the treatment the platform's ui.css gave that widget, so praxis and plain look as they did, and praxis-material, praxis-fluentui and praxis-carbon set them to their system's anatomy:

| | Material 3 | Fluent 2 | Carbon | |---|---|---|---| | Button | Full pill, 40dp; outlined secondary | 4px, 32px; neutral secondary | Square, 48px; tertiary outline | | Field, dropdown | Filled, top corners, 2dp underline on focus | Outlined 4px, brand bar on focus | Bottom rule on a layer, 2px inset focus | | Open list | 48dp items, tonal selection, level 2 | 32px options, brand bar on the selected, shadow16 | Square, 40px items, overlay shadow | | Checkbox, radio | 18dp, 2dp outline, primary | 16px, 1px stroke, brand | 16px, ink fill | | Switch | 52×32dp, thumb grows when on | 40×20px | 48×24px, green when on | | Choice picker | Joined pill segments, tonal selection with a tick | Separate toggle buttons | Square content switcher, inverse selection | | Notice | Tonal container, 12dp | Tinted bar with a tinted edge | 3px stripe, square | | Toast | Inverse snackbar | Neutral with shadow8 | Striped, with overlay shadow | | Table | 52dp rows | 44px rows, semibold header | 48px rows, header on its own layer |

Hover and press on the M3, Fluent and Carbon controls are a state layer: the label's own colour mixed over the container at the system's percentage.

The selected segment's label is --piq-choice-picker-option-selected-fg on --piq-choice-picker-option-selected-bg, which the contrast gate checks, so the platform's literal #fff on --navy (1.25:1 in Praxis dark) is gone; the fallback is the primary button's pairing.

Building

npm run build at the repo root builds @ux-builder/tokens, then this package: dist/index.js (ES module, React external) and dist/ui.css. The gallery renders these built components, so it shows exactly what an app gets.

Storybook

npm run storybook          # from the repo root: builds tokens, serves on :6006
npm run build-storybook    # static build in packages/ui/storybook-static

Every story renders inside a theme picked from the toolbar (Praxis, Praxis Material, Fluent and Carbon, Synaptiq and Databricks, each dark or light; Plain) and is delivered one of two ways: the theme CSS with data-theme and data-appearance, as the apps do, or the theme's resolved JSON as inline custom properties, as the harness will hand a theme to an embedded view. A widget that looks the same both ways depends on nothing but the token contract. Themes are linkable: ?globals=theme:plain-light;delivery:resolved.

The Accessibility panel (axe) checks what the browser renders, so it catches literal colours in widget rules that the token-level contrast gate cannot. It reports rather than fails until the contrast baseline is empty (step 4).

Stories cover the buttons, fields, choice picker, table, feedback (notice, status pill, toast), loading widgets, icons and the theme provider; Foundations/Text roles draws every pairing the contrast gate checks, from the gate's own list (tools/contrast.mjs). CI builds Storybook on every pull request and uploads it as the run's storybook artifact.