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

crusher-ui-kit

v1.0.0

Published

Token-driven Web Components UI framework with interchangeable design dialects for modern web products.

Readme

Crusher UI Kit

A token-driven, framework-agnostic Web Components design system. 42 public custom elements, 7 visual dialects, light/dark, density, runtime + static delivery. Built with Lit. Zero framework lock-in.

npm i crusher-ui-kit

Live demo: ui.muhammadhassaanjaved.com Current release: 0.3.1


Why

You're shipping multiple surfaces — a marketing site, a portfolio, an admin tool, an AI workspace. They each want a slightly different feel but the same system underneath. Crusher gives you one token pipeline, one component library, and seven visual dialects (Glass, Brutal, NeoBrutal, Neumorph, Minimal, Futuristic, Bento) that you switch with a single root-attribute.

The components are Web Components, so they work in React, Vue, Astro, vanilla HTML, or no framework at all.


Install paths

1. Bundler app (Vite / Next / Astro / etc.)

// Register all custom elements + import the core token CSS.
import 'crusher-ui-kit';
import 'crusher-ui-kit/styles/tokens.css';
import 'crusher-ui-kit/styles/modes.css';
import 'crusher-ui-kit/styles/semantic.css';
import 'crusher-ui-kit/styles/bridge.css';
import 'crusher-ui-kit/themes/glass.css'; // pick one or more dialect overlays

import {
  setTheme, setMode, setDensity, setBrand,
  showToast, openPalette, createHashPanelController,
} from 'crusher-ui-kit/runtime';

Theme imports are extension-qualified: import 'crusher-ui-kit/themes/<name>.css'. Pick only the dialects you ship.

2. Static HTML (no bundler)

<html data-theme="minimal" data-mode="dark">
<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/crusher-ui.min.css" />
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/themes/minimal.css" />
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/static/tool-shell.css" />
  <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/static/tool-shell.js"></script>
  <script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/crusher-ui.standalone.esm.js"></script>
</head>
<body>
  <crusher-button>Hello</crusher-button>
</body>
</html>

For utility-tool sites, start from templates/static-tool-shell.html. The tool-shell.js IIFE hydrates <html data-default-*> attributes from saved prefs before paint, so themed sites don't flash.

3. Astro / SSR

Same as a bundler app — register on the client. The recommended pattern for Astro is in hassaan-site and crusher-portfolio: a small scripts/resolve-crusher-ui.mjs runs on predev/prebuild that links a sibling framework checkout for local work and installs the pinned published version for hosted builds.


Concepts

Theme (dialect) — the aesthetic. Set on the root: <html data-theme="glass">. Seven dialects ship today. Each overrides design tokens (radii, shadows, transitions, type) on top of the base palette.

Mode — light vs dark. Set on the root: <html data-mode="dark">. Independent of theme; every dialect supports both modes.

Densitycomfortable (default) or compact. Set on the root: <html data-density="compact">. Tightens spacing tokens for data-heavy surfaces.

Brand color — a single tokenized accent (--crusher-color-brand-primary). Override via CSS or via setBrand('#7c3aed'). New in 0.3.0: --crusher-brand-on-surface is auto-derived to soften brand against the dark canvas via color-mix.

Theme lock — pin a fixed dialect for product sites: <html data-theme="minimal" data-theme-lock="minimal">. Lock wins in memory only and does not write back to localStorage, so the user's saved brand color is preserved.


Component catalog (42 public elements)

Atoms (10)

| Tag | Notes | | --- | --- | | <crusher-badge> | Inline label / count | | <crusher-button> | Variants: primary (default), outline, ghost, link, destructive / danger alias. Sizes: sm / md / lg. loading boolean. Respects prefers-reduced-motion. | | <crusher-checkbox> | Accessible checkbox | | <crusher-chip> | Token-styled chip / tag. Supports color attribute. | | <crusher-code-block> | Code snippet with token-aware syntax highlighting | | <crusher-icon-link> | Anchor styled as an icon button | | <crusher-input> | Text input, validates, ARIA-wired | | <crusher-switch> | Toggle switch | | <crusher-textarea> | Multi-line input | | <crusher-tooltip> | Hover/focus tooltip |

Molecules (20)

| Tag | Notes | | --- | --- | | <crusher-alert> | Variants: info / success / warning / danger / neutral. Dismissible with persistence by id. | | <crusher-audio-player> | Ambient audio control. preload="none" by default (set preload="metadata"/"auto" to override). Album art deferred until first play. | | <crusher-banner> | Site-wide announcement strip. Variants: brand / info / success / warning / danger. Dismissible with separate-map persistence. | | <crusher-card> | Surface container. Variants: default / data / highlight / muted. Density-aware. hover opts into hover-lift. | | <crusher-card-link> | Whole-card link wrapper for <crusher-card> | | <crusher-command-palette> | ⌘K-style palette. Async loaders supported. | | <crusher-disclosure> | Lit wrapper over native <details> with two-way open reflection. Also available as the .crusher-disclosure CSS-only utility class for no-build consumers. | | <crusher-dropdown> | Menu with keyboard navigation | | <crusher-empty-state> | No-results / empty surface. Slots for icon and actions. | | <crusher-filter-bar> | Wires data-value chips to filter a target selector by attribute | | <crusher-grid> | Responsive auto-fit grid primitive. min="13rem" controls column width. | | <crusher-menubar> | Horizontal menu / toolbar | | <crusher-modal> | Focus-trap modal. close-on-overlay opt-in. Emits cancelable request-close. | | <crusher-select> | Async + type-ahead select, ARIA-announced | | <crusher-skill-bar> | Animated progress bar | | <crusher-stack> | Flex layout primitive. gap, align, justify, orientation (vertical default / horizontal), wrap. | | <crusher-stat> | Data metric tile. tone, size (sm/md/lg/xl), orientation. Slots for icon + trend. | | <crusher-style-switcher> | Theme/mode/brand control. Hide-* boolean attrs scope each section. inline boolean renders in-flow (new in 0.3.1). Use swatches attribute for consumer-defined palettes. | | <crusher-tabs> | ARIA tablist with roving focus | | <crusher-toast-center> | Global toast region. Drive via showToast(...) from crusher-ui-kit/runtime. | | <crusher-toolbar> | Section toolbar — leading / center / actions slots |

Organisms (7)

| Tag | Notes | | --- | --- | | <crusher-app-shell> | Page-level shell. Slots: nav, aside, footer, default. Composes with <crusher-page-header> + <crusher-grid> + <crusher-stack>. | | <crusher-nav-list> | Sidebar / drawer nav list | | <crusher-page-header> | Page-level header with kicker, title, lede, meta, pills | | <crusher-section-title> | Section heading with under-bar treatment | | <crusher-table> | Data table with token-driven density | | <crusher-timeline> | Vertical / branching timeline container | | <crusher-timeline-item> | Individual timeline event |

Forms (4)

| Tag | Notes | | --- | --- | | <crusher-field> | Wrapper that auto-wires aria-labelledby/aria-describedby/aria-invalid on the [slot="control"] element. label / helper / error attrs or slots. orientation vertical / horizontal. | | <crusher-label> | Standalone label primitive | | <crusher-hint> | Helper text primitive | | <crusher-error> | Error text primitive |

A new public element must appear in index.html (check:demo) and types/index.d.ts (check:types) — both gates run in CI.


Runtime helpers

import {
  setTheme, lockTheme, setMode, setDensity, setBrand,
  showToast, openPalette, createHashPanelController,
} from 'crusher-ui-kit/runtime';

setTheme('glass');
lockTheme('minimal');        // pin a fixed dialect (in-memory only — no localStorage write)
setMode('dark');
setDensity('compact');
setBrand('#7c3aed');

showToast({ title: 'Saved', variant: 'success' });
openPalette();

createHashPanelController({
  defaultId: 'home',
  panelSelector: '[data-crusher-panel]',
  linkSelector: '[data-crusher-panel-link]',
  inactiveClass: 'is-hidden',
});

All preferences persist to localStorage under crusher_prefs (schema version v: 1). Future versions up-migrate on read; unknown future versions become read-only with a one-time console.warn.


Accessibility

  • Modal traps focus, restores on close, locks body scroll, dismisses on Esc + overlay click, emits cancelable request-close.
  • Tabs follow the WAI-ARIA tablist pattern: role="tablist" / tab / tabpanel, roving focus, live announcements.
  • Select announces selections via aria-live, async data + type-ahead.
  • Field auto-generates stable IDs and wires aria-labelledby / aria-describedby / aria-invalid on the slotted control.
  • Style switcher has :focus-visible outlines, aria-pressed / aria-expanded, respects prefers-reduced-motion.
  • Contrast validated via npm run check:contrast (WCAG AA contrast on every theme/mode pair).
  • Reduced motion honored in spinners, transitions, hover lifts, and modal animations.

Browser support

  • Modern evergreen browsers (Chrome / Edge / Safari / Firefox last two versions).
  • color-mix(in srgb, ...) is used widely — requires Chrome 111+, Safari iOS 16.2+, Firefox 113+.
  • interpolate-size: allow-keywords for <crusher-disclosure> animation requires Chrome 129+ / Safari 18+; gracefully degrades to instant open/close elsewhere.
  • Node 18+ for dev / build toolchain.

Migration notes

From ^0.2.x to ^0.3.x

No breaking changes. Additive only:

  • New: <crusher-field>, <crusher-disclosure>, <crusher-stack orientation="horizontal" wrap>, <crusher-button variant="danger"> alias, --crusher-brand-on-surface per-mode token.
  • New (0.3.1): <crusher-style-switcher inline> for in-flow rendering, <crusher-audio-player preload="..."> attribute (default flipped to "none").
  • Element count: 41 → 42.

If you used <crusher-audio-player> and relied on the browser default preloading the source on page load, set preload="metadata" or "auto" explicitly to restore that behavior.

From ^0.1.x to ^0.2.x

  • data-theme-lock now wins in memory only — it does not write the locked theme back to localStorage. Smoke checks in hassaan-site / crusher-portfolio were updated to assert this contract.
  • crusher_prefs schema versioned (v: 1). Pre-0.1.6 prefs up-migrate automatically.
  • Element count: 39 → 41 (M3 added crusher-alert and crusher-banner).

Scripts

| Command | Purpose | | --- | --- | | npm run dev | Vite dev server on the framework demo site | | npm run build:tokens | Generate token + theme CSS from JSON sources | | npm run build | Build distributable bundles (ESM + standalone ESM + CSS + static assets) | | npm run check:contrast | WCAG color-contrast validation across every theme/mode pair | | npm run check:package | Verify runtime exports + package contract | | npm run check:browser | Playwright-driven browser contract assertions | | npm run check:demo | Every public custom element renders in index.html | | npm run check:types | Every public custom element typed in types/index.d.ts |

Release gate (run before tagging):

npm run build:tokens && npm run check:contrast && npm run build && \
  npm run check:package && npm run check:browser && \
  npm run check:demo && npm run check:types && npm pack --dry-run

Releases are Changesets-driven via GitHub Actions. The publish command uses npm publish --no-provenance --access public (the GitHub repo is private; the npm package is public). NPM_TOKEN is configured as a GitHub Actions secret.


Project structure

crusher-ui-kit/
├── design/tokens/          ← base token JSON (source of truth)
├── design/themes/          ← per-dialect token overlays (JSON)
├── src/
│   ├── components/         ← Lit Web Components (atoms / molecules / organisms / forms)
│   ├── css/                ← generated tokens.css, modes.css, themes/*.css (do not hand-edit)
│   ├── runtime/            ← setTheme, setMode, setDensity, setBrand, prefs, panels, toast
│   ├── static/             ← tool-shell.css + tool-shell.js (no-build CDN consumers)
│   ├── scripts/            ← framework demo site scripts
│   └── js/main.js          ← bundler entry; registers every public element
├── templates/              ← canonical static-tool-shell.html
├── scripts/                ← build / contract checks / token regeneration
├── types/index.d.ts        ← TS / VS Code IntelliSense for `<crusher-*>` tags
└── docs/                   ← internal architecture + roadmap (not published)

Public package contract is defined by the exports field in package.json. Anything outside it is internal and may change.


TypeScript

types/index.d.ts declares every public custom element in the global HTMLElementTagNameMap. With moduleResolution: "NodeNext" or "Bundler", document.createElement('crusher-button') returns the correctly typed instance. Bring-your-own framework typings (Vue / Svelte / Solid) can extend the same map.


Changelog

See CHANGELOG.md for per-release notes generated by Changesets.


License

MIT. Built and maintained by Hassaan (itxcrusher). muhammadhassaanjaved.com