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

@pckgs/ccs

v1.0.3

Published

Common Components — shared browser modules.

Readme

ccs

ccs - short for Common Components — shared browser modules, and more.

Small, framework-free, jsDelivr-friendly browser modules (button state machines, modals, drawers, popovers, toasts, i18n, theming) plus a handful of server-side helpers for Cloudflare Workers. Each browser module is one self-contained IIFE / CSS file. No bundler, no module loader; just <script> / <link> tags.

Install

Via npm (resolves through jsDelivr — no npm install needed):

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/overlay/style.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/toast/style.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/spinner/style.min.css">

<script src="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/i18n-engine/client.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/action/client.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/overlay/client.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/popover/client.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/toast/client.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/footer-brand/client.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@pckgs/[email protected]/theme/client.min.js"></script>

Pin a specific version (recommended) or drop @<version> for "latest".

<script> tags are parser-blocking and run in document order — load i18n-engine and action before overlay (overlay reads window.tr and window.Action).

Modules

action — async-button state machine

Wraps a <button> into idle → (validate) → pending → success / error → idle states. Use it for any button that triggers async work (form submit, fetch, multi-step flow).

const btn = Action.create({
  text: 'Save',
  pendingText: 'Saving…',
  successText: 'Saved',
  retryText: 'Retry',
  validate: () => emailIsValid() || 'Email looks wrong',
  asyncFn:  async () => { await fetch('/api/save', { method: 'POST' }); },
  onSuccess: () => location.reload(),
});
container.appendChild(btn);

// Or wrap an existing button:
Action.wrap(document.querySelector('#submit'), {
  asyncFn: async () => { /* ... */ },
});

window.Action.create(opts) returns the button element (controller on btn._ctl); Action.wrap(btn, opts) returns the controller directly. The controller exposes getState(), setEnabled(), setText(), reset(), trigger(), detach().

overlay — Modal, Drawer, inline overlay

A unified covering-layer primitive. Three variants cover the common cases:

  • box — page-fixed centered box (Modal-style)
  • edge-right — page-fixed slide-in panel (Drawer-style)
  • flat — container-absolute overlay (in-section loading / busy state)

Two scopes: 'page' (full-screen, with focus-return / scroll lock / Esc / click-outside) or any HTMLElement (scoped, no page-level side effects).

// Modal sugar — back-compat aliases on window.Modal:
Modal.confirm('Delete this item?', { onOk: () => doDelete() });
Modal.alert('Saved successfully.');
Modal.input('Project name?', { onOk: (val) => createProject(val) });

// Drawer (slide from right):
const drawer = Overlay.show({
  variant: 'edge-right',
  title: 'Settings',
  body: settingsPanelEl,
});
// later: drawer.close()

// Loading overlay scoped to a section:
await Overlay.run({
  variant: 'flat',
  scope: tableContainerEl,
  asyncFn: async () => { await reloadTable(); },
});

Requires window.tr (a (key) => string lookup, caller-provided) and window.Action (load action/client.min.js first). If tr isn't set, falls back to English defaults for built-in labels.

popover — non-modal click-outside lifecycle

For caller-built popups (context menus, dropdowns) where you want click-outside / Esc to close, but don't want the modal-style focus trap or scroll lock.

triggerBtn.addEventListener('click', (e) => {
  e.stopPropagation();   // prevent the click-outside detector from firing immediately
  myDropdownEl.style.display = 'block';
  Popover.show({
    el: myDropdownEl,
    closable: { escape: true, clickOutside: true },
    onClose: () => { myDropdownEl.style.display = 'none'; },
  });
});

You manage markup, positioning, and show/hide; Popover only manages when to close.

toast — bottom-right notifications

Top-down stacked, auto-dismissed.

Toast.ok('Saved');                        // green, ~2s
Toast.err('Failed: ' + err.message);      // red, ~4.5s
Toast.show('Hello', 'ok');                // explicit kind

Container <div id="toasts"></div> is auto-created if missing. Drop <div id="toasts"></div> in your markup if you want to control its position. RTL pages get bottom-left placement automatically.

spinner — CSS-only loading circle

<div class="spinner"></div>

20×20 px ring, @keyframes spin rotation, no JS. Good as a button label, inline-with-text indicator, or inside other components.

footer-brand — branded <footer> IIFE

Drops a small, lang-aware footer at the bottom of the page. After your i18n setup runs, call:

FooterBrand.applyLang(currentLang);

Override --footer-color / --footer-border CSS vars if you want it tinted differently.

i18n-engine — language detection + DOM translation + LangSelect

20-language detection (including RTL: ar, he) with sensible Chinese fallback (zh-{hant,tw,hk,mo}zh-tw, other zh*zh-cn).

const SUPPORTED = ['en','fr','de','zh-cn','zh-tw','ja','ar', /* ... */];
const lang = detectLang(SUPPORTED);     // walks navigator.languages
applyLocaleAttrs(lang);                  // sets <html lang> + <html dir>

const TRANSLATIONS = {
  en: { hello: 'Hello', save: 'Save' },
  fr: { hello: 'Bonjour', save: 'Sauvegarder' },
  // ...
};
applyI18nAttrs(TRANSLATIONS[lang]);      // walks [data-i18n] / [data-i18n-ph] / [data-i18n-title]

// Optional <select> for switching:
//   <select id="lang-select">...</select>
LangSelect.init(lang, (newLang) => {
  localStorage.setItem('lang', newLang);
  location.reload();
});

The engine functions (detectLang, isRTL, applyLocaleAttrs, applyI18nAttrs) plus constants (SUPPORTED_LANGS, RTL_LANGS) are exposed at script global scope. LangSelect is on window.LangSelect.

theme — dark/light toggle button

Drop in a button, load the IIFE, the rest is automatic — reads localStorage('theme'), applies data-theme to <html>, wires the click handler:

<button id="themeToggle">🌓</button>
<script src=".../theme/client.min.js"></script>

Define --surface / --text / etc. with two values gated on html[data-theme="dark"] in your CSS.

Caveat: theme reads localStorage. In strict tracking-prevention browsers (Edge), cross-origin scripts may be blocked from accessing localStorage, breaking persistence. If you need theme persistence in Edge, vendor this module into your own first-party bundle instead of loading it from jsDelivr.

Required CSS variables

Modules read CSS custom properties on :root. Provide:

  • All modules: --surface, --border, --text, --text-muted, --accent, --err
  • toast: also --ok
  • spinner: uses --border and --accent
  • footer-brand: optionally --footer-color, --footer-border

License

MIT — see LICENSE.