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

@quplex/ui

v0.2.0

Published

Shared Quplex React UI primitives and semantic theme contract.

Readme

@quplex/ui

Quplex's shared React UI library: accessible, themeable primitives for React applications.

Install

npm install @quplex/ui

Quick start

Import the stylesheet once in your application's global CSS or root entry point:

import "@quplex/ui/globals.css";

Then use the primitives:

import { Button, Input, Label } from "@quplex/ui";

export function SignInForm() {
  return (
    <form>
      <Label htmlFor="email">Email</Label>
      <Input id="email" type="email" autoComplete="email" required />

      <Label htmlFor="password">Password</Label>
      <Input id="password" type="password" autoComplete="current-password" required />

      <Button type="submit">Sign in</Button>
    </form>
  );
}

Storybook

Run the component catalogue locally from the package directory:

npm run storybook

A hosted Storybook is not configured yet. npm renders this README but cannot host interactive Storybook files; once deployed to a public static host, its URL belongs in this section.

What belongs here

@quplex/ui owns small, generic primitives and their accessible behavior:

  • Button, Input, Label, Checkbox, Dialog, Tooltip, and similar building blocks
  • shared semantic CSS tokens and safe light/dark defaults
  • the cn class-merging utility

It does not own pages, product workflows, API calls, routing, permissions, tenancy rules, or application-specific layouts. For example, AdminUserSuspensionDialog belongs in admin, while a generic Dialog belongs here.

Package contract

This package uses React, TypeScript, ESM, Tailwind CSS v4, shadcn/Base UI primitives, CSS variables, and Lucide icons. It is framework-neutral: it works with Vite applications and can be consumed by Next.js.

Imports

Import the package stylesheet once in the application's global stylesheet or root entry point:

import "@quplex/ui/globals.css";

Use a root import for convenience:

import { Button } from "@quplex/ui";

export function SaveButton() {
  return <Button>Save changes</Button>;
}

For the most explicit tree-shakeable import, use the component subpath:

import { Button } from "@quplex/ui/components/button";

The #components/*, #lib/*, and #hooks/* aliases are package-internal. Consumer applications must not import them.

Component reference

All primitives are available from explicit, tree-shakeable component subpaths. Button, Input, and Label are also available from the root package. For example:

import { Dialog, DialogContent, DialogTrigger } from "@quplex/ui/components/dialog";
import { Button } from "@quplex/ui/components/button";

Import the stylesheet once before using any component:

import "@quplex/ui/globals.css";

Each primitive accepts its underlying HTML or Base UI props unless noted below, as well as className. The key component-specific props and composition exports are listed here; consult the linked source or the local Storybook catalogue for the complete TypeScript signature and interactive examples.

| Component | Import | Main exports | Key props and usage | | --- | --- | --- | --- | | Accordion | components/accordion | Accordion, AccordionItem, AccordionTrigger, AccordionContent | Set an item value; use defaultValue/value on the root. Compose an item with a trigger and matching content. | | Alert | components/alert | Alert, AlertTitle, AlertDescription, AlertAction | variant: default or destructive. Use for important, non-blocking status messages. | | Alert dialog | components/alert-dialog | AlertDialog, AlertDialogTrigger, AlertDialogContent, header/title/description/footer, AlertDialogAction, AlertDialogCancel | A confirmation modal. Always provide a clear title, description, and safe cancel action. | | Avatar | components/avatar | Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge | Use an image with meaningful alt plus initials or an icon fallback. Avatar supports size: sm, default, lg. | | Badge | components/badge | Badge, badgeVariants | variant: default, secondary, destructive, outline, ghost, link. Short status or category labels only. | | Breadcrumb | components/breadcrumb | Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis | Use BreadcrumbPage for the current location and links for ancestors. | | Button | components/button or root | Button, buttonVariants | variant: default, outline, secondary, ghost, destructive, link; size: xs, sm, default, lg, icon, icon-xs, icon-sm, icon-lg. Icon-only buttons require aria-label. | | Button group | components/button-group | ButtonGroup, ButtonGroupText, ButtonGroupSeparator | orientation: horizontal or vertical. Group adjacent buttons with related actions. | | Calendar | components/calendar | Calendar, CalendarDayButton | Uses React Day Picker props, including mode, selected, onSelect, disabled, and numberOfMonths. Compose it into a date-picker with Popover in an application. | | Card | components/card | Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter | A semantic surface for a self-contained summary, form, or setting. | | Chart | components/chart | ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle, ChartConfig | Wrap a Recharts chart in ChartContainer, passing a required config. Configure each series with label and either color or light/dark theme colors. | | Checkbox | components/checkbox | Checkbox | Base UI checkbox props including checked, defaultChecked, onCheckedChange, disabled, and name. Pair it with a visible <label>. | | Collapsible | components/collapsible | Collapsible, CollapsibleTrigger, CollapsibleContent | Supports controlled or uncontrolled open state: open, defaultOpen, onOpenChange. | | Command | components/command | Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator | Build searchable command palettes. Set a stable value for each CommandItem; use CommandDialog for modal palettes. | | Context menu | components/context-menu | ContextMenu, trigger/content, item, checkbox/radio/submenu/group/label/separator/shortcut exports | Wrap the target with ContextMenuTrigger, then provide ContextMenuContent and accessible actions. | | Dialog | components/dialog | Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose | Supports open, defaultOpen, onOpenChange; use a title and description for every modal. | | Drawer | components/drawer | Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter, DrawerClose | A touch-friendly bottom sheet. Supports controlled open state; use for compact, task-focused mobile interactions. | | Dropdown menu | components/dropdown-menu | DropdownMenu, trigger/content, item, checkbox/radio/submenu/group/label/separator/shortcut exports | Menu items accept standard event props. Use checkbox/radio items for persistent selection state. | | Empty | components/empty | Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent | EmptyMedia variant: default, icon. Use for empty lists, searches, and unavailable content. | | Field | components/field | Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldSet, FieldLegend, FieldContent, FieldTitle, FieldSeparator | Use to structure form controls. Pass errors={[{ message }]} to FieldError, or provide error children directly. | | Hover card | components/hover-card | HoverCard, HoverCardTrigger, HoverCardContent | Use concise supplemental preview content. Position with side, sideOffset, align, and alignOffset. | | Input | components/input or root | Input | Standard input props (type, value, defaultValue, disabled, aria-invalid). Always provide a visible or ARIA label. | | Input group | components/input-group | InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InputGroupInput, InputGroupTextarea | InputGroupAddon uses align: inline-start, inline-end, block-start, block-end; InputGroupButton supports button variant and compact sizes. | | Keyboard hint | components/kbd | Kbd, KbdGroup | Display a keyboard shortcut such as <Kbd>⌘ K</Kbd>; it is presentational and not an interactive control. | | Label | components/label or root | Label | Standard label props. Set htmlFor to the input id for pointer and screen-reader association. | | Menubar | components/menubar | Menubar, menu/trigger/content/item/group/checkbox/radio/submenu/label/separator/shortcut exports | Use for desktop-style application menus; keep item labels concise and actions keyboard accessible. | | Navigation menu | components/navigation-menu | NavigationMenu, list/item/trigger/content/link/indicator/positioner exports | Use links for navigation and triggers only for a disclosure containing related links. | | Pagination | components/pagination | Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis | Set PaginationLink isActive for the current page. Provide real href/routing props for each link. | | Popover | components/popover | Popover, PopoverTrigger, PopoverContent, PopoverHeader, PopoverTitle, PopoverDescription | Supports open, defaultOpen, onOpenChange; position content with side, sideOffset, align, and alignOffset. | | Progress | components/progress | Progress, ProgressTrack, ProgressIndicator, ProgressLabel, ProgressValue | Set value, min, and max on the root. Include a label when the progress purpose is not obvious. | | Radio group | components/radio-group | RadioGroup, RadioGroupItem | Use value, defaultValue, and onValueChange on the group. Each radio needs a label. | | Resizable | components/resizable | ResizablePanelGroup, ResizablePanel, ResizableHandle | ResizablePanelGroup uses orientation: horizontal or vertical; panels support defaultSize, minSize, and maxSize; set withHandle for a visible drag grip. | | Scroll area | components/scroll-area | ScrollArea, ScrollBar | Give the root a constrained height or width. The component provides its viewport and vertical scrollbar automatically. | | Select | components/select | Select, SelectTrigger, SelectValue, SelectContent, SelectGroup, SelectLabel, SelectItem, SelectSeparator, scroll controls | Use value, defaultValue, and onValueChange; each item requires a unique value. Label the trigger. | | Separator | components/separator | Separator | orientation: horizontal or vertical; decorative separators can use decorative. | | Sheet | components/sheet | Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter, SheetClose | Side-panel dialog with controlled open state. SheetContent supports side: top, right, bottom, left. | | Sidebar | components/sidebar | SidebarProvider, Sidebar, SidebarHeader, SidebarContent, SidebarFooter, groups/menu/input/trigger/rail/inset exports | Wrap an application shell in SidebarProvider; Sidebar supports side, variant, and collapsible. Use SidebarTrigger for the built-in keyboard-accessible toggle. | | Skeleton | components/skeleton | Skeleton | A presentational loading placeholder; set dimensions using Tailwind className. | | Slider | components/slider | Slider | Use value/defaultValue arrays, min, max, step, and change callbacks. Wrap in a <label> or otherwise provide an accessible name. | | Sonner | components/sonner | Toaster | Render one <Toaster /> near the app root, then call toast.success, toast.error, etc. from sonner. The consumer must also provide a next-themes ThemeProvider. | | Spinner | components/spinner | Spinner | A status icon with built-in accessible loading text. Use className for size or color. | | Switch | components/switch | Switch | Supports checked, defaultChecked, onCheckedChange, disabled, and size: sm or default. Pair it with a label. | | Table | components/table | Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption | A semantic table primitive. Sorting, filtering, pagination, and data fetching belong in an application-level composition. | | Tabs | components/tabs | Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants | Use a unique value on every trigger/content pair. Root supports value, defaultValue, and onValueChange; list variant: default or line. | | Textarea | components/textarea | Textarea | Standard textarea props. Use a visible/ARIA label and aria-invalid for invalid state. | | Toggle | components/toggle | Toggle, toggleVariants | Supports pressed state props plus variant: default or outline, and size: sm, default, lg. Icon-only toggles need aria-label. | | Toggle group | components/toggle-group | ToggleGroup, ToggleGroupItem | Set multiple for multi-select; supports value, defaultValue, onValueChange, orientation, spacing, plus toggle variant and size. | | Tooltip | components/tooltip | TooltipProvider, Tooltip, TooltipTrigger, TooltipContent | Wrap each pair in Tooltip; position content with side, sideOffset, align, and alignOffset. Use tooltips to supplement—not replace—an icon button's aria-label. |

Complete examples

Confirm a destructive action

import { Button } from "@quplex/ui/components/button";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@quplex/ui/components/alert-dialog";

export function DeleteWorkspaceDialog() {
  return (
    <AlertDialog>
      <AlertDialogTrigger render={<Button variant="destructive" />}>
        Delete workspace
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>Delete this workspace?</AlertDialogTitle>
          <AlertDialogDescription>
            This action cannot be undone.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Cancel</AlertDialogCancel>
          <AlertDialogAction>Delete</AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}

Controlled selection field

import { useState } from "react";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@quplex/ui/components/select";

export function RoleSelect() {
  const [role, setRole] = useState("member");

  return (
    <Select value={role} onValueChange={setRole}>
      <SelectTrigger aria-label="Member role">
        <SelectValue />
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="admin">Administrator</SelectItem>
        <SelectItem value="member">Member</SelectItem>
        <SelectItem value="viewer">Viewer</SelectItem>
      </SelectContent>
    </Select>
  );
}

Next.js consumers

The initial package distribution exports TypeScript/TSX source. A Next.js consumer should transpile it:

// next.config.ts
const nextConfig = {
  transpilePackages: ["@quplex/ui"],
};

export default nextConfig;

Interactive primitives contain their own "use client" boundary. Do not add that directive to static primitives unless they genuinely need client APIs.

Theming

Theme values resolve in this order:

  1. The complete defaults in @quplex/ui/globals.css
  2. The consuming application's theme CSS
  3. A component instance's className

An application imports the global defaults, then overrides only the semantic tokens it needs:

@import "@quplex/ui/globals.css";

/* admin/src/styles/theme.css */
:root {
  --primary: oklch(0.52 0.22 290);
  --primary-foreground: oklch(0.98 0 0);
}

If app or landing does not override a token, the library default remains. For a one-off visual treatment, pass standard Tailwind utilities to the component; cn merges them last:

<Button className="bg-amber-500 text-black hover:bg-amber-600">
  Special landing action
</Button>

Use semantic utilities such as bg-primary, text-foreground, border-border, and ring-ring inside shared primitives. Do not hard-code a brand colour into a primitive when a semantic token expresses its intent.

Changing the shared theme

Edit src/styles/globals.css to change a library default. Keep both :root and .dark complete, preserve foreground pairs such as --primary / --primary-foreground, and review every affected component in Storybook. Never put an admin, app, or landing brand override in this package.

The complete theme-change procedure, including contrast and release checks, is in the UI library management guide.

Development

Run these commands from packages/ui/:

npm run storybook
npm run typecheck
npm run test
npm run build-storybook

Storybook is the visual workbench. The Vitest addon runs stories in Playwright Chromium, including keyboard interactions and accessibility checks. Add a story for every exported component and keep accessibility failures blocking.

The current Storybook suite covers the complete component catalogue, including interactive overlays, navigation, form controls, data/layout primitives, keyboard interactions, and accessibility checks.

Adding a primitive

  1. Confirm that the component is generic rather than product-specific.
  2. Use the shared components.json configuration (Base UI, Nova, Neutral, Lucide, CSS variables, RSC enabled).
  3. Run the shadcn command yourself from this directory; it may install or alter package metadata, so AI agents must not run it.
  4. Keep package-internal imports on #components/*, #lib/*, or #hooks/*.
  5. Add an intentional public export and a Storybook story.
  6. Verify type checking, Chromium/a11y story tests, and the Storybook build.

Do not add the entire shadcn catalogue in advance. Add one primitive when approved product work requires it.

Managing changes

Use this change flow for every public component or token change:

  1. Decide whether it is a generic primitive change or application-specific composition. Keep the latter in its application.
  2. Update the component or global tokens, then its Storybook stories.
  3. Run the package checks and review both light and dark states.
  4. Decide the release impact: patch for a compatible fix, minor for an additive compatible API, major for a removal or breaking behavior/API change.
  5. Publish a new package version only after validating the packed artifact.

Detailed procedures—including public exports, component additions, theme work, and consumer migration—are in the UI library management guide.

Releases and consuming

@quplex/ui is a public npm package. Each consuming application installs a published version through its own lockfile; applications never import from this repository's packages/ui folder directly.

Before a new release:

  1. Increment the package version deliberately.
  2. Run npm pack --dry-run and inspect the package contents.
  3. Run the package checks and test the tarball in a disposable consumer fixture when exports or build behavior change.
  4. Publish the verified version with npm publish --access public.
  5. Upgrade each consuming application deliberately.

The person releasing the package runs the final publish command. AI agents may prepare the change and provide commands, but do not publish on the user's behalf. The detailed release checklist is in the UI library management guide.

This preserves independent lockfiles and deployments: upgrading the UI library in admin does not silently upgrade app or landing. Avoid file:, npm link, root workspaces, and Turborepo for this architecture.

Browser and bundle policy

Tailwind CSS v4 requires modern browsers. Test shared primitives in Chromium, Firefox, and WebKit before declaring a cross-browser release. The package marks only CSS as side-effectful and exposes component subpaths so consumers can tree-shake unused primitives. Keep root exports deliberate and verify them with a consumer bundle fixture before expanding the barrel.

Related documentation

The design and implementation rationale lives in global_ai/guides/custom-theme-integration.md. Package-specific contribution rules live in AGENTS.md.