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

@boostdev/design-system-components

v2.8.0

Published

BoostDev React component library: accessible, token-driven components built on @boostdev/design-system-foundation

Readme

@boostdev/design-system-components

Accessible, token-driven React components built on @boostdev/design-system-foundation. Zero extra runtime — bring your own CSS reset and token layer, then import the components.

Installation

pnpm add @boostdev/design-system-components @boostdev/design-system-foundation
npm install @boostdev/design-system-components @boostdev/design-system-foundation
yarn add @boostdev/design-system-components @boostdev/design-system-foundation

@boostdev/design-system-foundation is a required peer dependency. The components rely on its CSS custom properties for all colour, spacing, and typography values.

Setup

Import the design system tokens and the component styles once at your app root:

/* app.css */
@import "@boostdev/design-system-foundation/css";
@import "@boostdev/design-system-components/css";

Or with a JS bundler (Vite, Next.js, etc.):

// main.ts / layout.tsx
import '@boostdev/design-system-foundation/css';
import '@boostdev/design-system-components/css';

All component styles live inside @layer boostdev.component, which sits above the token layers in the cascade. You can override any component from outside without specificity fights.


Next.js / React Server Components

The main @boostdev/design-system-components entry has no 'use client' directive. When Next.js evaluates the bundle in a React Server Component context, components that call createContext (e.g. Toast) will crash at module evaluation time.

Use the /client subpath instead — it is identical in API but has 'use client' prepended to the bundle, which tells Next.js to treat it as client-only code:

// Plain React / Vite — unchanged
import { Button, Card } from '@boostdev/design-system-components';

// Next.js / RSC — use the /client subpath
import { Button, Card } from '@boostdev/design-system-components/client';

// Server-safe utilities (cn, WithClassName) — works in Server Components, layouts, middleware
import { cn } from '@boostdev/design-system-components/utils';

Styles won't apply? Import the CSS file

The most common cause of unstyled components in a Next.js app is forgetting to import the CSS bundle. This package ships pre-built, already-scoped CSS (class names are renamed at build time to bds{version}{Component}-*), so Next.js does not need to process it — you just need to load it once at the root:

// app/layout.tsx (App Router)  or  pages/_app.tsx (Pages Router)
import '@boostdev/design-system-foundation/css';
import '@boostdev/design-system-components/css';

The ./css subpath is exposed in the package's exports map and points at the pre-built dist/index.css. You do not need transpilePackages for this library — classes are already scoped at publish time and the JS bundle contains no runtime CSS Module imports. Add transpilePackages only if another dependency in your tree genuinely requires it.


Usage

UI components

Badge

import { Badge } from '@boostdev/design-system-components';

<Badge>New</Badge>
<Badge variant="success">Active</Badge>
<Badge variant="error">Failed</Badge>
<Badge variant="warning">Pending</Badge>
<Badge variant="secondary">Draft</Badge>

Variants: primary (default) · secondary · success · error · warning


Collapsible

import { Collapsible } from '@boostdev/design-system-components';

<Collapsible summary="What is the return policy?" defaultOpen>
  You can return any item within 30 days of purchase.
</Collapsible>

{/* Group: only one open at a time */}
<Collapsible name="faq" summary="Question 1">Answer 1</Collapsible>
<Collapsible name="faq" summary="Question 2">Answer 2</Collapsible>

Built on native <details>/<summary>. Use name to group multiple Collapsibles so only one stays open at a time.


Typography

import { Typography } from '@boostdev/design-system-components';

<Typography variant="h1">Page title</Typography>
<Typography variant="h2">Section title</Typography>
<Typography variant="body">Paragraph text</Typography>
<Typography variant="body_s">Small print</Typography>

Pass component to override the rendered element without changing the visual style:

<Typography variant="h2" component="h3">Visually h2, semantically h3</Typography>

Variants: h1 · h2 · h3 · body (default) · body_s


Loading

import { Loading } from '@boostdev/design-system-components';

<Loading />
<Loading size="small" />
<Loading size="large" />

Sizes: small · medium (default) · large


Skeleton

import { Skeleton } from '@boostdev/design-system-components';

<Skeleton className="w-48 h-4" />

Renders an aria-hidden shimmer block. Size it with a className or inline style.


Interaction components

Button

import { Button } from '@boostdev/design-system-components';

<Button>Click me</Button>
<Button variant="outline">Cancel</Button>
<Button size="small">Small</Button>
<Button href="/dashboard">Link button</Button>
<Button isIconOnly aria-label="Delete"><Icon /></Button>
<Button hasPulse>Call to action</Button>

| Prop | Type | Default | |---|---|---| | variant | 'default' \| 'outline' | 'default' | | size | 'small' \| 'medium' \| 'large' | 'medium' | | href | string | — renders an <a> | | iconStart | ReactNode | — | | iconEnd | ReactNode | — | | isIconOnly | boolean | false — forces 1:1 aspect ratio with minimal padding. Requires aria-label. | | hasPulse | boolean | false | | disabled | boolean | false |

Variants:

  • default — filled with --button_color. Hover transitions to an outlined state.
  • outline — transparent background, border and text use --button_color. Hover fills. (ghost is a deprecated alias.)

Re-theming — set two CSS custom properties to change the colour of both variants:

.danger-zone .my-button {
  --button_color:    var(--bds-error);
  --button_on-color: var(--bds-on-error);
}

Dialog

import { Dialog } from '@boostdev/design-system-components';

<Dialog isOpen={open} onClose={() => setOpen(false)}>
  <h2>Confirm</h2>
  <p>Are you sure?</p>
</Dialog>

Renders a native <dialog> element with a dimmed, blurred backdrop. isOpen controls open/close; onClose is called when the close button is pressed or the backdrop is clicked. Dialog traps focus while open and restores focus to the triggering element on close.

Override the backdrop colour via --color_backdrop (default: rgb(0 0 0 / 50%)).


Rating

import { Rating } from '@boostdev/design-system-components';

<Rating value={3} />
<Rating value={4} max={10} />

max defaults to 5. Stars are rendered as SVG icons; unfilled stars use the current background colour.


Toast

import { ToastProvider, useToast } from '@boostdev/design-system-components';

// Wrap your app (or layout) once:
<ToastProvider>
  <App />
</ToastProvider>

// Inside any component:
function SaveButton() {
  const { showToast } = useToast();
  return (
    <button onClick={() => showToast('Saved!', 'success')}>
      Save
    </button>
  );
}

showToast(message, variant) — variant: 'success' · 'error' · 'info'

Toasts auto-dismiss after 5 seconds.


Form components

All components accept native HTML attributes for their root element via ...rest spread in addition to their own props. This means you can pass id, data-*, aria-*, style, event handlers, and any other valid HTML attribute directly.

FormInput

import { FormInput } from '@boostdev/design-system-components';

<FormInput
  label="Email"
  name="email"
  type="email"
  hint="We'll never share your email"
  error={errors.email}
/>

Checkbox

import { Checkbox, CheckboxGroup } from '@boostdev/design-system-components';

<Checkbox
  label="I agree to the terms"
  name="agree"
  error={errors.agree}
/>

Group multiple checkboxes with a shared label using CheckboxGroup:

<CheckboxGroup legend="Notifications" required error={errors.notifications}>
  <Checkbox label="Email" name="notify_email" />
  <Checkbox label="SMS" name="notify_sms" />
  <Checkbox label="Push" name="notify_push" />
</CheckboxGroup>

Radio

import { Radio, RadioGroup } from '@boostdev/design-system-components';

<RadioGroup legend="Preferred contact" required>
  <Radio label="Option A" name="choice" value="a" description="Optional supporting text." />
  <Radio label="Option B" name="choice" value="b" />
</RadioGroup>

Switch

import { Switch } from '@boostdev/design-system-components';

<Switch label="Enable notifications" name="notifications" />
<Switch label="Dark mode" name="dark-mode" defaultChecked />
<Switch label="On" name="theme" prefix="Off" size="small" />

Sizes: small · medium (default) · large

SegmentedControl

import { SegmentedControl } from '@boostdev/design-system-components';

<SegmentedControl aria-label="Calendar view" selectedIndex={1}>
  <button>Day</button>
  <button>Week</button>
  <button>Month</button>
</SegmentedControl>

{/* Navigation variant — active item auto-detected from aria-current */}
<SegmentedControl aria-label="Section">
  <a href="/overview" aria-current="page">Overview</a>
  <a href="/details">Details</a>
</SegmentedControl>

Children-based API — pass <button>, <a>, or router <Link> elements directly. Active item is set via selectedIndex or auto-detected from aria-current="page" / aria-pressed={true} / aria-selected={true}. Variants: outline (default) · filled. Sizes: small · medium (default) · large.


Layout components

ButtonGroup

import { ButtonGroup, Button } from '@boostdev/design-system-components';

<ButtonGroup variant="flow" aria-label="Page navigation">
  <Button>Back</Button>
  <Button>Next</Button>
</ButtonGroup>

| Prop | Type | Default | |---|---|---| | variant | 'flow' \| 'card' \| 'modal' \| 'content' | — | | aria-label | string | — recommended when group purpose isn't clear from context |


Card

import { Card } from '@boostdev/design-system-components';

<Card>Basic card</Card>
<Card variant="elevated" padding="large">Elevated</Card>
<Card variant="outlined" textAlign="center">Outlined</Card>
<Card onClick={() => navigate('/detail')}>Clickable card</Card>

| Prop | Type | Default | |---|---|---| | variant | 'default' \| 'elevated' \| 'outlined' | 'default' | | padding | 'none' \| 'small' \| 'medium' \| 'large' | 'medium' | | textAlign | 'start' \| 'center' \| 'end' | 'start' | | onClick | MouseEventHandler<HTMLElement> | — renders a <button> |


Grid / GridItem

import { Grid, GridItem, GridItemSpan } from '@boostdev/design-system-components';

<Grid variant="main">
  <GridItem columnSpan="half">Left</GridItem>
  <GridItem columnSpan="half">Right</GridItem>
</Grid>

<Grid variant="custom" columns={5}>
  {items.map((item) => <GridItem key={item.id} columnSpan={1}>{item.label}</GridItem>)}
</Grid>

Named columnSpan values are responsive via the foundation breakpoint ladder (4 / 8 / 12 columns across mobile / tablet / desktop). Also available as a web component: <bds-grid> / <bds-grid-item>.

Grid props

| Prop | Type | Default | |--------------|--------------------------------------------------|----------| | variant | 'main' \| 'page' \| 'funnel' \| 'custom' | 'main' | | columns | number | — | | isCentered | boolean | false | | isMasonry | boolean — CSS Grid Level 3 masonry layout (native + JS polyfill) | false | | autoSpanMedia | boolean — children without columnSpan auto-span by media aspect ratio | false | | masonryPreferredColumnSpan | 'full' \| 'three-quarters' \| 'two-thirds' \| 'half' \| 'one-third' \| 'one-quarter' \| number — auto items default to this span; demoted to fill the trailing row (masonry only) | — | | masonryRowUnitPx | number — manual override for the polyfill's row-track unit (px). Auto-tuned by default to keep projected implicit-grid rows under the browser cap (masonry only) | — | | as | ElementType | 'div' |

GridItem props

| Prop | Type | Default | |---------------|--------------------------------------------------------------------------------------------------|----------| | columnSpan | 'full' \| 'three-quarters' \| 'two-thirds' \| 'half' \| 'one-third' \| 'one-quarter' \| 'auto' \| number'auto' derives span from the first <img>/<video> descendant's aspect ratio | 'full' | | rowSpan | number | — | | startColumn | number — escape hatch | — | | endColumn | number — escape hatch | — | | as | ElementType | 'div' |


SectionHeader

import { SectionHeader } from '@boostdev/design-system-components';

<SectionHeader title="Our services" />
<SectionHeader
  title="Welcome"
  subtitle="Everything you need in one place"
  size="large"
  alignment="center"
/>

| Prop | Type | Default | |---|---|---| | title | string | required | | subtitle | string | — | | size | 'small' \| 'medium' \| 'large' | 'medium' | | alignment | 'start' \| 'center' \| 'end' | 'start' |


IconWrapper

import { IconWrapper } from '@boostdev/design-system-components';

<IconWrapper>
  <svg>...</svg>
</IconWrapper>

{/* Mark as decorative when accompanied by a visible label */}
<IconWrapper aria-hidden>
  <svg>...</svg>
</IconWrapper>

Renders a circular container sized to its own font-size. Override the background colour via the --icon-wrapper-color CSS custom property. Pass aria-hidden when the icon is decorative.


Utilities

cn

Composes class name strings, filtering out falsy values:

// Server-safe — works in Server Components, layouts, middleware
import { cn } from '@boostdev/design-system-components/utils';

// Also available from root or /client (but these are client-only in Next.js)
import { cn } from '@boostdev/design-system-components';

cn('card', isActive && 'card--active', [extraClass])
// → "card card--active extraClass"

Overriding component styles

Every component exposes CSS custom properties as a styling API. Set them on a parent or via style to adjust the component without writing extra CSS:

/* Override the button colour for a specific context */
.hero .button {
  --button_color:    var(--bds-brand);
  --button_on-color: var(--bds-on-brand);
}

All component styles sit in @layer boostdev.component. You can also write rules in a higher layer:

@layer my-overrides {
  .my-button {
    border-radius: 0;
  }
}

Overriding border-radius

Components default to pill (--bds-border_radius--full) or circle (--bds-border_radius--circle) shapes from the foundation. Override the component-scoped variable to change individual components:

/* Make all buttons square-cornered */
.my-button {
  --button_radius: var(--bds-border_radius--s);
}

/* Make avatars rounded-square instead of circular */
.my-avatar {
  --avatar_radius: var(--bds-border_radius--m);
}

Overriding active/checked state colour

Components with a checked or filled state (Checkbox, Radio, Slider, Progress, ProgressCircle) expose a component-scoped variable that falls back to the global --bds-active token:

/* Override the active colour for a single checkbox */
.my-checkbox {
  --checkbox_color-active:    var(--bds-brand);
  --checkbox_color-on-active: var(--bds-on-brand);
}

/* Override for all sliders in a section */
.settings-panel {
  --slider_color-active: var(--bds-important);
}

The two-level chain means you can also override at the global token level — see Theming below.


Theming

Components inherit from @boostdev/design-system-foundation tokens. Override semantic tokens to retheme all components at once:

/* tokens.config.css — import after the design system CSS */
@layer boostdev.tokens.override {
  :root {
    --bds-cta:        var(--bds-BASE__color--orange);
    --bds-on-cta:     var(--bds-BASE__color--white);
    --bds-interactive: var(--bds-BASE__color--orange);
  }
}

Active state tokens

The --bds-active family controls the checked/filled/selected colour across all form controls and progress indicators. Override it once to retheme checkboxes, radio buttons, sliders, progress bars, and the switch thumb simultaneously:

@layer boostdev.tokens.override {
  :root {
    --bds-active:            var(--bds-BASE__color--blue);
    --bds-on-active:         var(--bds-BASE__color--white);
    --bds-active--subtle:    var(--bds-BASE__color--blue--100);  /* Switch track */
    --bds-on-active--subtle: var(--bds-BASE__color--blue--900);
    --bds-active--strong:    var(--bds-BASE__color--blue--700);  /* Switch thumb */
    --bds-on-active--strong: var(--bds-BASE__color--white);
  }
}

Dark mode is handled automatically via [data-theme="dark"] or prefers-color-scheme.


Development

pnpm build        # compile to dist/
pnpm typecheck    # TypeScript check (library code only)
pnpm lint         # ESLint
pnpm lint:css     # Stylelint
pnpm test         # Vitest — 1122 tests
pnpm mcp:start    # Start the MCP server locally (http://localhost:3001/api/mcp)
pnpm storybook    # Storybook dev server on port 6006

Adding a new component

  1. Create src/components/{category}/{Name}/
  2. Add Name.tsx, Name.module.css, index.ts
  3. Wrap all CSS in @layer boostdev.component { ... }
  4. Use only design token custom properties — no hard-coded colours, spacing, or font sizes
  5. Re-export from src/index.ts
  6. Run pnpm build && pnpm typecheck && pnpm lint && pnpm lint:css && pnpm test

Design system reference (MCP server)

A live MCP server exposing this design system's components and tokens is deployed at:

https://ds.boostdev.nl/api/mcp

AI coding agents can connect to it to look up component props and design tokens without reading the source code directly. It exposes five tools:

| Tool | Description | |---|---| | ds_list_components | All components grouped by category | | ds_get_component | Full props interface for a named component | | ds_list_tokens | Design tokens, filterable by group | | ds_search | Keyword search across components and tokens | | ds_get_guide | Returns the AGENTS.md usage guide |

To run the MCP server locally:

pnpm mcp:start   # listens on http://localhost:3001/api/mcp

React Native

React Native components have moved to @boostdev/design-system-react-native.


Publishing a new version

npm version patch   # or minor / major
git push --follow-tags

GitLab CI will build and publish to npm automatically on version tags.