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

@muten/shadcn

v0.0.5

Published

shadcn-style component registry for muten - own-the-source parts you add with `muten add`.

Readme

@muten/shadcn

shadcn/ui for muten - the authentic shadcn components, ported to muten's declarative DSL. Every component ships the exact shadcn class strings as semantic classes (.card, .badge, .btn, ...), so your .muten stays readable (class("card"), not a pile of utilities) while the look is 100% shadcn (zinc dark theme, the same tokens, the same animations).

This is a muten plugin: a registry of components you either import (use as-is) or eject (muten add, copy the source and own it, the shadcn way). It is not a runtime - the components compile away with your app.

Requirements

A muten app using Tailwind CSS v4 (scaffold with npm create muten@latest and pick Tailwind, or add it). The plugin ships its theme + component classes as a Tailwind @theme / @layer, so Tailwind does the work.

Install

npm install @muten/shadcn

Then wire two things:

1. Import the styles in your src/styles.css (after Tailwind):

@import "tailwindcss";
@import "@muten/shadcn/globals.css";

globals.css brings the shadcn @theme tokens (colors + radius), the base border reset, and every class-driven component. It also @sources its own parts, so any utility used inside a component is generated.

2. Let the plugin own the theme. Empty your theme.muten colors so they do not override the shadcn @theme:

# theme.muten
theme { scheme { mode "dark" } }

Two ways to use a component

Import (use as-is)

Declare the plugin in muten.config. Its parts become available across the app, no copying:

# muten.config
plugins {
  shadcn {}
}
# any page
Card {
  CardHeader { CardTitle(label: "Create project") }
  CardContent { Span "Deploy in one click." class("text-sm text-muted-foreground") }
}

Eject (own the source)

muten add copies the component's .muten into src/parts/ so you can edit it (the shadcn philosophy):

muten add card badge dialog

The two approaches mix freely. Custom components (see below) are eject-only - their host .js must live in your src/components/, so muten add copies both files.


Components

Class-driven (in globals.css, no muten add needed)

Style any muten primitive with these - the exact shadcn utility strings:

| Component | Usage | |---|---| | Button | Button "Save" -> save class("btn btn-default") - variants btn-default\|secondary\|outline\|ghost\|destructive\|link, sizes btn-sm\|btn-lg\|btn-icon | | Input / Textarea / Label | SearchField bind(q) class("input") - also .textarea, .label, .native-select | | Skeleton | Stack class("skeleton h-4 w-32") (size it at the call site) | | Separator / Kbd | Stack class("separator") - Span "Ctrl" class("kbd") | | Typography | Title "Docs" h1 class("prose-h1") - prose-h1..h4, prose-p\|lead\|large\|small\|muted\|blockquote\|code\|list | | Button Group / Input Group | Stack class("btn-group") { … } - Stack class("input-group") { Icon … SearchField class("input-group-control") } | | Table | DataTable @rows columns(a, b) class("data-table") (styles muten's DataTable) |

Parts (import via plugins {}, or muten add <name>)

Display: card (Card / CardHeader / CardTitle / CardDescription / CardContent / CardFooter), badge, alert (Alert / AlertTitle / AlertDescription), avatar, spinner, progress, empty, breadcrumb, pagination, field, item.

Stateful (the page owns the state, the part receives it - see the pattern below): switch, checkbox, toggle, toggle-group, radio-group, collapsible, accordion, tabs.

Overlays (the page owns an open bool): dialog, alert-dialog, tooltip (pure hover, no state), dropdown-menu, popover, sheet, drawer, hover-card (hover), select, scroll-area, menubar, navigation-menu (hover), combobox, command.

Chat: message (Message / Bubble / Marker), message-scroller, attachment.

Custom (eject-only - muten add copies a host .js into src/components/)

The genuinely interactive 20%: slider, input-otp, calendar, toaster, carousel, chart, resizable, context-menu. Each is a thin muten part over a small vanilla-JS host you own and can edit.

calendar ships all the shadcn variants: mode (single / range / multiple), months (1-2 side by side), caption (label / dropdown). selected encodes the value ("yyyy-mm-dd", "from/to", or "d1,d2,..."). A date picker is just Popover { Calendar … }.


The container / presentational pattern

shadcn components in React hold their own state; in muten the page owns the state and passes it down, so the oracle can see and check it. Interactive parts take a value + an action callback:

state { dark = false : bool }
action toggle mutates dark { dark.toggle() }

Switch(on: dark, onToggle: toggle)

Single-select groups pass the current value + each item's value (the part highlights the match):

state { align = "left" : text }
action setAlign(v: text) mutates align { align.set(v) }

ToggleGroup {
  ToggleGroupItem(value: "left",  current: align, onSelect: setAlign) { Icon "lucide:align-left" }
  ToggleGroupItem(value: "right", current: align, onSelect: setAlign) { Icon "lucide:align-right" }
}

Overlays own an open bool; the backdrop click closes:

state { open = false : bool }
action show mutates open { open.set(true) }
action close mutates open { open.set(false) }

Button "Open" -> show class("btn btn-outline")
Dialog(open: open, onClose: close) {
  DialogTitle(label: "Are you sure?")
  DialogFooter { Button "Cancel" -> close class("btn btn-outline") }
}

Each component's .muten file has a usage snippet in its header comment.

Theming

Everything is driven by the shadcn @theme tokens in globals.css (--color-primary, --color-card, --radius, ...). To re-theme, edit those CSS variables - the components follow. The default is shadcn's zinc dark.

License

MIT.