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

@parasutcom/fds

v0.2.0

Published

FDS React UI components.

Readme

@parasutcom/fds — Frame Design System

A React component library built with Base UI, Tailwind CSS v4, and class-variance-authority.

Quick Start

In the monorepo (workspace)

// package.json
{ "dependencies": { "@parasutcom/fds": "workspace:*" } }
/* globals.css */
@import 'tailwindcss';
@import 'tw-animate-css';
@import '@parasutcom/fds/theme.css';

@source '../../../packages/shared/ui/src/**/*.{ts,tsx}';
@source '../**/*.{ts,tsx}';
import { Button } from '@parasutcom/fds'

export default function Page() {
  return <Button variant="outline">Click me</Button>
}

From npm (external project)

npm install @parasutcom/fds tailwindcss@^4 @tailwindcss/postcss lucide-react
/* globals.css */
@import 'tailwindcss';
@import '@parasutcom/fds/styles.css';
import { Button } from '@parasutcom/fds'

Development

pnpm dev          # Start docs site (Vite dev server)
pnpm build        # Build library to dist/
pnpm storybook    # Start Storybook on port 6006
pnpm release      # Build + publish to npm

The pnpm dev command starts a documentation site at http://localhost:5173 with live demos for every component.


Creating a New Component

1. Create the component file

src/components/ui/my-component.tsx

Every component follows the same pattern:

'use client'

import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@fds/lib/utils'

const myComponentVariants = cva(
    'base-classes-here',
    {
        variants: {
            variant: {
                default: 'default-style-classes',
                outline: 'outline-style-classes',
            },
            size: {
                sm: 'h-8 text-sm',
                default: 'h-10 text-base',
                lg: 'h-12 text-lg',
            },
        },
        defaultVariants: {
            variant: 'default',
            size: 'default',
        },
    },
)

function MyComponent({
    className,
    variant,
    size,
    ...props
}: React.ComponentProps<'div'> &
    VariantProps<typeof myComponentVariants>) {
    return (
        <div
            className={cn(myComponentVariants({ variant, size }), className)}
            {...props}
        />
    )
}

export { MyComponent, myComponentVariants }

Key rules:

  • Always start with 'use client'
  • Use cva for variants (not conditional classnames)
  • Use cn() from @fds/lib/utils for merging classnames
  • Use @fds/ path alias for internal imports
  • Accept className prop and merge it with cn()
  • Use React.ComponentProps<'element'> for typing (extends native HTML)
  • Export both the component and variants

2. For compound components (with subcomponents)

Use Base UI primitives as the headless foundation:

'use client'

import * as React from 'react'
import { mergeProps } from '@base-ui/react/merge-props'
import { useRender } from '@base-ui/react/use-render'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@fds/lib/utils'

// Root component
function MyDialog({ children, ...props }: React.ComponentProps<'div'>) {
    return <div {...props}>{children}</div>
}

// Subcomponents
function MyDialogTitle({ className, ...props }: React.ComponentProps<'h2'>) {
    return <h2 className={cn('text-lg font-semibold', className)} {...props} />
}

function MyDialogContent({ className, ...props }: React.ComponentProps<'div'>) {
    return <div className={cn('p-6', className)} {...props} />
}

export { MyDialog, MyDialogTitle, MyDialogContent }

3. Export from the library

Add the export to src/index.ts:

export * from './components/ui/my-component'

4. Add documentation

Create a doc page at src/docs/pages/my-component/:

src/docs/pages/my-component/
  my-component-page.tsx    # Documentation page
  demos/
    basic.tsx              # Basic usage demo
    variants.tsx           # Variants showcase

Demo file (demos/basic.tsx):

import { MyComponent } from '@parasutcom/fds'

export function BasicDemo() {
    return <MyComponent variant="outline">Hello</MyComponent>
}

Page file (my-component-page.tsx):

import { DemoCard } from '../../components/demo-card'
import { DemoGrid } from '../../components/demo-grid'
import { ApiTable, type PropDefinition } from '../../components/api-table'

import { BasicDemo } from './demos/basic'
import basicSource from './demos/basic.tsx?raw'

const apiProps: PropDefinition[] = [
    {
        name: 'variant',
        description: 'Visual style',
        type: '"default" | "outline"',
        default: '"default"',
    },
]

export function MyComponentPage() {
    return (
        <div className="space-y-10">
            <div>
                <h1 id="my-component" className="text-3xl font-bold">MyComponent</h1>
                <p className="mt-2 text-lg text-muted-foreground">
                    Short description.
                </p>
            </div>

            <section>
                <h2 id="when-to-use" className="text-xl font-semibold mb-3">When To Use</h2>
                <p className="text-muted-foreground">Guidance text.</p>
            </section>

            <section>
                <h2 id="examples" className="text-xl font-semibold mb-6">Examples</h2>
                <DemoGrid>
                    <DemoCard title="Basic" sourceCode={basicSource} id="demo-basic">
                        <BasicDemo />
                    </DemoCard>
                </DemoGrid>
            </section>

            <section>
                <h2 id="api" className="text-xl font-semibold mb-6">API</h2>
                <ApiTable title="MyComponent" props={apiProps} />
            </section>
        </div>
    )
}

5. Register the route

Add to src/docs/lib/nav-data.ts:

{ title: 'MyComponent', href: '/components/my-component' },

Add to src/docs/lib/routes.tsx:

import { MyComponentPage } from '../pages/my-component/my-component-page'
// ...
{ path: 'components/my-component', element: <MyComponentPage /> },

Using Components

Import

All components are exported from the package root:

import { Button, Badge, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@parasutcom/fds'

Icons

FDS uses Lucide React for icons (peer dependency):

import { Loader2Icon, MailIcon } from 'lucide-react'

<Button disabled>
    <Loader2Icon className="mr-2 size-4 animate-spin" />
    Loading...
</Button>

Variants & Sizes

Most components accept variant and size props via CVA:

<Button variant="destructive" size="lg">Delete</Button>
<Badge variant="outline">Status</Badge>
<Input size="sm" variant="outline" />

Compound Components

Complex components use the compound component pattern:

<Select>
    <SelectTrigger>
        <SelectValue placeholder="Pick one" />
    </SelectTrigger>
    <SelectContent>
        <SelectItem value="a">Option A</SelectItem>
        <SelectItem value="b">Option B</SelectItem>
    </SelectContent>
</Select>

Render Prop (polymorphic elements)

Some components support Base UI's render prop to change the underlying element:

import { Link } from 'react-router-dom'

<SidebarMenuButton render={<Link to="/about" />}>
    About
</SidebarMenuButton>

Server Components

All FDS components include 'use client'. In Next.js Server Component pages, either:

  • Import them directly (Next.js auto-creates the client boundary)
  • Or wrap in a Client Component for explicit control

Project Structure

packages/shared/ui/
├── src/
│   ├── index.ts                    # Library entry — all exports
│   ├── index.css                   # Tailwind + theme (bundled as styles.css)
│   ├── theme.css                   # Theme tokens only (for workspace consumers)
│   ├── components/
│   │   ├── ui/                     # Base components (Button, Select, Dialog, etc.)
│   │   └── customized/             # Higher-level components (TextField, DataTable, etc.)
│   ├── hooks/                      # Shared hooks (use-mobile)
│   ├── lib/                        # Utilities (cn, dictionary)
│   ├── styles/                     # Extended theme tokens (fds-theme.css, dev-fonts.css)
│   └── docs/                       # Documentation site (dev-only, excluded from build)
│       ├── app.tsx                  # Router root
│       ├── lib/                     # Routes, nav data, hooks
│       ├── layout/                  # Sidebar, header, TOC
│       ├── components/              # DemoCard, ApiTable, CodeBlock
│       └── pages/                   # One folder per component
├── public/                          # Static assets for docs site
├── scripts/
│   └── prepare-publish.mjs         # Rewrites package.json for npm publish
├── vite.config.ts                   # Build config (lib mode + dev server)
└── package.json                     # @parasutcom/fds

CSS Architecture

| File | Purpose | Who imports it | |------|---------|----------------| | styles.css | Full bundle (Tailwind + theme + component styles) | npm consumers | | theme.css | Theme tokens only (colors, fonts, radii) | Workspace apps via globals.css | | index.css | Source CSS (builds into styles.css) | Internal only |

Workspace apps use theme.css because they run their own Tailwind pipeline and only need the design tokens. npm consumers use styles.css which includes everything pre-built.