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

@aircall/ds

v0.6.0

Published

Aircall Design System - Modern UI Component Library

Readme

@aircall/ds - Aircall Design System

Modern React component library built with TailwindCSS 4, Radix UI primitives, and TypeScript.

Tech Stack

  • React 19 - Latest React with compiler optimizations
  • TailwindCSS 4 - Utility-first CSS with OKLch color space
  • Radix UI - Unstyled, accessible component primitives
  • TypeScript 5.7 - Type-safe development
  • Class Variance Authority (CVA) - Variant-based component styling
  • Storybook 10 - Interactive component documentation

Installation

For Monorepo Apps (Hydra Workspace)

If you're developing within the Hydra monorepo (e.g., apps/aw-web, apps/hubspot-cti):

# In your app's package.json
{
  "dependencies": {
    "@aircall/ds": "workspace:*"
  }
}

For External Apps

If you're using the design system in an external project or consuming the published NPM package:

pnpm add @aircall/ds

Required peer dependencies:

pnpm add react react-dom @aircall/icons @aircall/numbers

Optional peer dependencies (install only if you use these components):

| Component | Packages to install | |-----------|-------------------| | Calendar | react-day-picker, date-fns | | DataTable | @tanstack/react-table |

# If using Calendar
pnpm add react-day-picker date-fns

# If using DataTable
pnpm add @tanstack/react-table

Usage

Import Components

Components work the same way in both monorepo and external apps:

import { Button } from '@aircall/ds';

function App() {
  return (
    <Button variant="default" color="primary" size="default">
      Click me
    </Button>
  );
}

Import CSS Styles

The CSS import differs depending on your environment:

Monorepo Apps

In monorepo apps, import the source CSS for optimal build integration:

 /* In your app's entry point or globals.css */
import '@aircall/ds/globals.css';

/* Scan your own source files for Tailwind classes */
@source "your_own_source_file";

Benefits:

  • Source-level integration with your app's Tailwind build
  • Shared Tailwind context for better optimization
  • Access to theme variables and utilities

External Apps

In external apps, use the pre-compiled CSS bundle:

@import 'tailwindcss';
@import '@aircall/ds/globals.css';

/* Scan your own source files for Tailwind classes */
@source "your_own_source_file";

Or in TypeScript/JavaScript entry point:

// main.tsx
import '@aircall/ds/styles.css';

Benefits:

  • No need to configure Tailwind to scan node_modules
  • Smaller bundle size (pre-compiled and minified)
  • Faster build times
  • Simpler setup

Dark Mode

Dark mode is controlled via the data-theme attribute:

// Light mode (default)
<html>

// Dark mode
<html data-theme="dark">

The design system uses CSS variables that automatically adjust based on this attribute.

Components

Button

Versatile button component with multiple variants, colors, sizes, and shapes.

Props:

  • variant: 'default' | 'outline' | 'ghost' | 'link'
  • color: 'primary' | 'secondary' | 'brand-primary' | 'brand-secondary' | 'brand-destructive' | 'info' | 'success' | 'warning' | 'destructive'
  • size: 'xs' | 'sm' | 'default' | 'lg'
  • shape: 'default' | 'square' | 'circle'
  • readOnly: boolean
  • block: boolean (full width)
  • render: ReactElement — render Button as a different element (e.g. <a>). Replaces the former asChild / Slot pattern.

Examples:

// Primary button
<Button variant="default" color="primary">Primary</Button>

// Outline button
<Button variant="outline" color="secondary">Outline</Button>

// Icon button
<Button shape="circle" size="sm">
  <Phone className="size-4" />
</Button>

// Button with icon
<Button>
  <Mail className="size-4" />
  Send Email
</Button>

// Full width button
<Button block>Full Width</Button>

// Destructive button
<Button color="destructive">Delete</Button>

// Link rendered as a button — use the `render` prop
<Button render={<a href="/profile" />}>Go to Profile</Button>

Storybook

Interactive component documentation with live examples, controls, and accessibility testing.

Quick Start

cd packages/ds
pnpm sb:dev

View at: http://localhost:6008

Features

  • 🎨 Interactive component playground with live editing
  • 🌓 Light/dark mode testing via toolbar switcher
  • ♿ Accessibility testing with a11y addon
  • 📱 Responsive viewport testing
  • 🎭 Pseudo-state simulation (hover, focus, active)
  • 📐 Figma design integration
  • 📚 Auto-generated documentation from TypeScript types

Building Storybook

pnpm sb:build            # plain build — no recipe registry
pnpm sb:build:deploy     # full build including the recipe registry (what CI deploys)

Static site output: storybook-static/. See Recipes (shadcn Registry) below for when to use each.

Development

Adding Components

This package uses shadcn/ui patterns for component development:

# Add a new component from shadcn
pnpm add <component-name>

File Structure

src/
├── components/        # React components (published to npm)
│   └── button/
│       ├── __stories__/
│       │   └── Button.stories.tsx
│       └── button.tsx
├── styles/           # Global styles
│   ├── globals.css   # TailwindCSS + theme variables
│   └── brand.css     # Aircall brand colors
├── lib/              # Utilities
│   └── utils.ts      # cn() helper for class merging
├── hooks/            # Custom React hooks
└── fonts/            # Fellix Aircall font files
├── recipes/          # shadcn registry recipes (copy-paste patterns)
│   ├── combobox-searchable-select.tsx
│   ├── combobox-dropdown-search.tsx
│   └── combobox-multi-select.tsx

Styling System

Components use Class Variance Authority (CVA) for variant-based styling:

const buttonVariants = cva('base-classes', {
  variants: {
    variant: {
      default: 'variant-specific-classes',
      outline: 'outline-specific-classes'
    }
  },
  defaultVariants: {
    variant: 'default'
  }
});

This provides:

  • Type-safe variant props
  • Automatic TypeScript inference
  • Composable class generation
  • TailwindCSS class merging via tailwind-merge

Theme System

The design system uses CSS variables with OKLch color space for better color perception:

/* Light mode (default) */
:root {
  --primary: oklch(52.42% 0.201 192.01);
}

/* Dark mode */
[data-theme='dark'] {
  --primary: oklch(78.09% 0.128 192.01);
}

Colors automatically adapt when data-theme="dark" is set on the root element.

Recipes (shadcn Registry)

Recipes are pre-built, copy-paste component patterns built on top of @aircall/ds primitives. They are distributed via the shadcn registry — consumers run shadcn add to install a recipe into their project, and they own the code afterwards. They can customize it freely.

Note: in the shadcn registry schema these items are typed registry:block — that's a shadcn enum value, not our terminology. Internally we call them "recipes".

What is the Registry?

The registry is a set of JSON files that describe each recipe — its name, description, npm dependencies, and full source code. These JSON files are generated by shadcn build and served as static files alongside the DS Storybook.

How it works:

  1. Recipe source files live in src/recipes/ using relative imports (../components/combobox)
  2. pnpm run registry:build copies the recipes, rewrites imports to @aircall/ds, runs shadcn build, and outputs JSON files to public/r/
  3. Storybook serves public/ as static files via staticDirs config
  4. A consumer runs shadcn add <url> — the CLI fetches the JSON, extracts the source code, and writes it into their project with @aircall/ds imports

Local Development

Run pnpm run registry:build before starting Storybook to generate the JSON files. Then start Storybook:

pnpm run registry:build
pnpm run sb:dev

The recipe JSON files are served at http://localhost:6008/r/<name>.json. You can test the full consumer flow locally:

pnpm dlx shadcn@latest add http://localhost:6008/r/combobox-searchable-select.json

Production (Published Storybook)

When the DS Storybook is deployed to ds.aircall.io, the same JSON files are served at the public URL. Consumers install recipes with:

pnpm dlx shadcn@latest add https://ds.aircall.io/r/combobox-searchable-select.json

The deployment CI job runs pnpm sb:build:ds at the repo root, which maps to sb:build:deploy inside this package. That script runs registry:build first, then storybook build — Storybook's staticDirs: ['../public'] config picks up public/r/ and copies it into storybook-static/r/ so the JSON files ship alongside the deployed Storybook. sb:build on its own does NOT bundle the registry — use sb:build:deploy (or pnpm sb:build:ds at the root) whenever you need the registry included.

Available Recipes

| Recipe | Description | |---|---| | combobox-searchable-select | Single-select combobox — input acts as trigger and search filter | | combobox-dropdown-search | Button-triggered combobox with search inside dropdown | | combobox-multi-select | Multi-select with inline chips and type-ahead filtering |

Adding a New Recipe

  1. Create src/recipes/<name>.tsx — import primitives from ../components/
  2. Add an entry to registry.json at the package root
  3. Add a story in src/stories/ that imports from the recipe
  4. Run pnpm run registry:build to generate JSON in public/r/
  5. Verify at http://localhost:6008/r/<name>.json after starting Storybook

Scripts

# Development
pnpm sb:dev              # Start Storybook development server
pnpm lint                # Run ESLint

# Building for Distribution
pnpm build:package       # Build both JS and CSS for publishing
pnpm build:js            # Build JavaScript bundle only
pnpm build:css           # Build pre-compiled CSS only

# Registry
pnpm registry:build      # Generate recipe JSON files in public/r/
                         # (rewrites relative imports to @aircall/ds)

# Storybook — three distinct builds
pnpm sb:build            # Plain Storybook build. NO registry JSON.
pnpm sb:build:chromatic  # Storybook build for Chromatic (--test --stats-json). NO registry JSON.
pnpm sb:build:deploy     # Full deployment build: runs registry:build, builds Storybook,
                         # and copies public/r into storybook-static/r/.
                         # This is what CI runs (via `pnpm sb:build:ds` at the repo root).

# Testing Package Locally
pnpm pack                # Create tarball for local testing

# Component Management
pnpm add <component>     # Add new shadcn component

# Maintenance
pnpm clean               # Remove build artifacts