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

weird-ui

v0.1.4

Published

A shadcn/ui-style component library. Copy components into your project, not dependencies.

Readme

weird-ui

A shadcn/ui-style component library for React. Components are copied into your project, not imported as dependencies. This means:

Tailwind styles always work - No node_modules scanning issues
Full ownership - Modify components to match your design
Zero runtime dependencies - Components are just files
Type-safe - Full TypeScript support
Pre-built sections - Ready-to-use page sections

This is NOT a npm package to install. It's a component scaffold you copy into your project.

Quick Start

1. Initialize weird-ui in your project

npx weird-ui init

This will:

  • ✓ Install Tailwind CSS (if not present)
  • ✓ Create src/lib/utils.ts with the cn() utility
  • ✓ Install clsx and tailwind-merge dependencies

2. Add components

npx weird-ui add button
npx weird-ui add card
npx weird-ui add input

Components are copied to src/components/ui/.

3. Start using

import { Button } from "@/components/ui/button"
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"

export default function App() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Welcome</CardTitle>
      </CardHeader>
      <CardContent>
        <Button>Click me</Button>
      </CardContent>
    </Card>
  )
}

Components Overview

Atomic UI Components

Simple, reusable building blocks for your UI.

| Component | Command | Description | |-----------|---------|-------------| | Button | npx weird-ui add button | Flexible button with variants | | Card | npx weird-ui add card | Container with sub-components (Header, Content, Footer) | | Input | npx weird-ui add input | Form input with error states | | Badge | npx weird-ui add badge | Label component with variants | | Textarea | npx weird-ui add textarea | Multi-line text input with error states | | Accordion | npx weird-ui add accordion | Expandable/collapsible content | | Tabs | npx weird-ui add tabs | Tabbed interface for organizing content | | Modal | npx weird-ui add modal | Dialog/modal component with backdrop |

Section Components

Full-featured, drop-in page sections that combine multiple UI elements.

| Component | Command | Description | |-----------|---------|-------------| | Hero Section | npx weird-ui add hero-section | Full-width hero with title, subtitle, and CTAs | | FAQ Section | npx weird-ui add faq-section | Interactive FAQ with expandable items | | Gallery Section | npx weird-ui add gallery-section | Responsive image gallery | | Feature Section | npx weird-ui add feature-section | Features showcase with icons | | CTA Section | npx weird-ui add cta-section | Call-to-action banner |

Component Structure

After running npx weird-ui add button, you get:

src/
├── components/
│   ├── ui/
│   │   ├── button.tsx          ← Your copy
│   │   ├── card.tsx
│   │   ├── input.tsx
│   │   ├── badge.tsx
│   │   ├── textarea.tsx
│   │   ├── accordion.tsx
│   │   ├── tabs.tsx
│   │   └── modal.tsx
│   └── sections/               ← Page-level components
│       ├── hero-section.tsx
│       ├── faq-section.tsx
│       ├── gallery-section.tsx
│       ├── feature-section.tsx
│       └── cta-section.tsx
├── lib/
│   └── utils.ts                ← cn() utility
└── tailwind.config.js

How It Works

weird-ui ships source files, not compiled code. The CLI copies .tsx files into your project where:

  1. You own the source code
  2. Tailwind CSS scans your local src/ directory
  3. No build-time or runtime magic
  4. Full control over styling and behavior

Atomic UI Components

Button

Flexible button with multiple variants.

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

export default function App() {
  return (
    <>
      <Button variant="primary">Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="outline">Outline</Button>
      <Button disabled>Disabled</Button>
    </>
  )
}

Card

Container component with sub-components.

import {
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
} from "@/components/ui/card"

export default function App() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Card Title</CardTitle>
        <CardDescription>Card description goes here</CardDescription>
      </CardHeader>
      <CardContent>Content here</CardContent>
      <CardFooter>Footer content</CardFooter>
    </Card>
  )
}

Input

Form input with error states.

import { Input } from "@/components/ui/input"
import { useState } from "react"

export default function App() {
  const [value, setValue] = useState("")
  const [error, setError] = useState(false)

  return (
    <Input
      placeholder="Enter email"
      value={value}
      onChange={(e) => setValue(e.target.value)}
      error={error}
      helperText={error ? "Invalid email" : ""}
    />
  )
}

Badge

Label component with multiple variants.

import { Badge } from "@/components/ui/badge"

export default function App() {
  return (
    <>
      <Badge variant="default">Default</Badge>
      <Badge variant="secondary">Secondary</Badge>
      <Badge variant="destructive">Error</Badge>
      <Badge variant="outline">Outline</Badge>
    </>
  )
}

Textarea

Multi-line text input.

import { Textarea } from "@/components/ui/textarea"

export default function App() {
  return (
    <Textarea
      placeholder="Enter message"
      helperText="Max 500 characters"
    />
  )
}

Accordion

Expandable/collapsible content.

import { Accordion } from "@/components/ui/accordion"

export default function App() {
  const items = [
    { id: "1", title: "Item 1", content: "Content 1" },
    { id: "2", title: "Item 2", content: "Content 2" },
  ]

  return <Accordion items={items} allowMultiple={false} />
}

Tabs

Tabbed interface.

import { Tabs } from "@/components/ui/tabs"

export default function App() {
  const tabs = [
    { id: "tab1", label: "Tab 1", content: "Content 1" },
    { id: "tab2", label: "Tab 2", content: "Content 2" },
  ]

  return <Tabs tabs={tabs} defaultTabId="tab1" />
}

Modal

Dialog/modal component.

import { Modal } from "@/components/ui/modal"
import { Button } from "@/components/ui/button"
import { useState } from "react"

export default function App() {
  const [open, setOpen] = useState(false)

  return (
    <>
      <Button onClick={() => setOpen(true)}>Open Modal</Button>
      <Modal
        open={open}
        onOpenChange={setOpen}
        title="Modal Title"
        footer={
          <>
            <Button onClick={() => setOpen(false)}>Cancel</Button>
            <Button variant="primary">Confirm</Button>
          </>
        }
      >
        <p>Modal content goes here</p>
      </Modal>
    </>
  )
}

Section Components

Hero Section

Full-width hero banner.

import { HeroSection } from "@/components/sections/hero-section"

export default function App() {
  return (
    <HeroSection
      title="Build Something Amazing"
      subtitle="Create beautiful interfaces with weird-ui"
      primaryCTA={{
        label: "Get Started",
        onClick: () => console.log("clicked"),
      }}
      secondaryCTA={{
        label: "Learn More",
        onClick: () => console.log("learn more"),
      }}
      backgroundImage="https://example.com/image.jpg"
    />
  )
}

FAQ Section

Interactive FAQ with expand/collapse.

import { FAQSection } from "@/components/sections/faq-section"

export default function App() {
  const faqs = [
    { question: "How does it work?", answer: "Components are copied to your project..." },
    { question: "Can I modify components?", answer: "Yes, you own the source code!" },
    { question: "Do I need to install weird-ui?", answer: "No, it's a scaffolding tool." },
  ]

  return (
    <FAQSection
      title="Frequently Asked Questions"
      subtitle="Get answers to common questions"
      items={faqs}
    />
  )
}

Gallery Section

Responsive image gallery.

import { GallerySection } from "@/components/sections/gallery-section"

export default function App() {
  const images = [
    { src: "image1.jpg", alt: "Image 1", caption: "Caption 1" },
    { src: "image2.jpg", alt: "Image 2", caption: "Caption 2" },
    { src: "image3.jpg", alt: "Image 3", caption: "Caption 3" },
  ]

  return (
    <GallerySection
      title="Our Gallery"
      subtitle="Beautiful images"
      images={images}
      columns={3}
    />
  )
}

Feature Section

Features showcase.

import { FeatureSection } from "@/components/sections/feature-section"

export default function App() {
  const features = [
    {
      title: "Feature 1",
      description: "Description of feature 1",
      icon: "🚀",
    },
    {
      title: "Feature 2",
      description: "Description of feature 2",
      icon: "⚡",
    },
    {
      title: "Feature 3",
      description: "Description of feature 3",
      icon: "✨",
    },
  ]

  return (
    <FeatureSection
      title="Why Choose Us"
      subtitle="Here's what makes us different"
      features={features}
      columns={3}
    />
  )
}

CTA Section

Call-to-action banner.

import { CTASection } from "@/components/sections/cta-section"

export default function App() {
  return (
    <CTASection
      headline="Ready to get started?"
      description="Join thousands of developers using weird-ui"
      buttonLabel="Start Now"
      onClick={() => console.log("clicked")}
      variant="default"
    />
  )
}

The cn() Utility

All components use the cn() utility to merge Tailwind classes safely:

import { cn } from "@/lib/utils"

cn(
  "px-4 py-2",
  active && "bg-blue-500",
  disabled && "opacity-50"
)
// → "px-4 py-2 bg-blue-500 opacity-50"

The cn() function uses clsx and tailwind-merge to:

  • Combine multiple classes
  • Remove conflicting Tailwind utilities
  • Support conditional classes

TypeScript Support

All components are fully typed. TypeScript support is built-in across all atomic UI and section components.

Customizing Components

After copying, edit components directly in your project:

// src/components/ui/button.tsx
export const Button = ({ className, variant = "primary", ...props }: ButtonProps) => {
  return (
    <button
      className={cn(
        // Modify Tailwind classes here
        "px-4 py-3 rounded-lg font-bold",
        // ...
      )}
      {...props}
    />
  )
}

Tailwind CSS Configuration

Make sure your tailwind.config.js includes the content paths:

module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

CLI Commands

# Initialize project with utils and Tailwind
npx weird-ui init

# Add UI components
npx weird-ui add button
npx weird-ui add card
npx weird-ui add input
npx weird-ui add badge
npx weird-ui add textarea
npx weird-ui add accordion
npx weird-ui add tabs
npx weird-ui add modal

# Add section components
npx weird-ui add hero-section
npx weird-ui add faq-section
npx weird-ui add gallery-section
npx weird-ui add feature-section
npx weird-ui add cta-section

# Overwrite existing component
npx weird-ui add button --overwrite

FAQ

Q: Do I need to install weird-ui as a dependency?
A: No. You only use npx weird-ui to scaffold components. The package itself is never imported.

Q: What if Tailwind styles aren't working?
A: Make sure your tailwind.config.js includes:

content: [
  "./src/**/*.{js,ts,jsx,tsx}",
]

Q: Can I modify copied components?
A: Yes! That's the whole point. Edit src/components/ui/*.tsx directly.

Q: How do I update components?
A: Run npx weird-ui add <component> --overwrite to replace your copy.

Q: What's the difference between UI and Section components?
A: UI components are atomic building blocks (Button, Input, Badge, etc.). Section components are pre-built page sections that combine multiple UI elements and often include state management (Hero, FAQ, Gallery, Feature, CTA).

Q: Can I use sections without the UI components?
A: Yes! Section components are self-contained. You can add just the sections you need.

Q: Are there runtime dependencies?
A: Components only use clsx and tailwind-merge (installed by npx weird-ui init). No other dependencies are required.

Q: Can I customize section components?
A: Absolutely! After copying, edit sections directly. Add your own styles, change layouts, or add new props.

License

MIT

Contributing

Found a bug? Have an idea? Open an issue or submit a PR!


weird-ui - Copy components, own the code.