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

@dennisl0731/oh-my-ui

v0.4.0

Published

Shared CSS + Tailwind preset + lucide icons (React + vanilla SVG sprite) + nav/toast/copy helpers for the oh-my-* family. Warm-paper aesthetic, earthy-brown accents, per-service colorways.

Readme

@dennisl0731/oh-my-ui

Shared CSS + Tailwind preset for the oh-my-* family.

This package is intentionally a design foundation, not a locked UI kit. It gives you:

  • design tokens via CSS variables
  • a Tailwind preset wired to those tokens
  • a small nav/theme bootstrap script that enhances existing markup
  • Lucide icon re-exports (React) and a vanilla icons.svg sprite for server-rendered apps
  • JS helpers for toast + clipboard copy (works on plain HTTP)

It does not force page structure, copy, layout, or product-specific components.

What's new in 0.4.0

  • Apps menu + host-derived cross-app links in nav.js. Supply a registry via window.__OMI_APPS__ or data-apps="<url.json>"; .omi-appsdrop is filled with links built from the current host (window.omiOrigin(sub)), so they survive reverse-proxy base/port changes. .omi-nav entries now accept sub too. No project data baked into the package. See the Apps menu section.

What's new in 0.2.4

  • New CSS primitives promoted from the oh-my-* apps (were re-implemented in several app <style> blocks):
    • .omi-switch — toggle switch (.sm variant). Wrap a checkbox + .track.
    • .omi-meter — progress / capacity bar (.lg/.sm sizes; .fill.warn, .fill.full; .indeterminate).
    • .omi-details — collapsible disclosure (<summary> + .omi-chevron + .omi-summary-note; body in .omi-details-body).
    • .omi-mini-player — fixed bottom media bar (.mp-ico/.mp-title/.mp-audio).
    • .omi-log-console — floating streaming-log panel (.lc-head/.lc-chip running|done|error / .lc-body / .lc-line step|ok|warn|err).
    • .omi-usage-tip — provider-status popover (.dot.ok/.bad, .nm).
    • .omi-hint text + pill variants .omi-pill.info, .omi-pill.accent, .omi-pill.lg.
  • Gallery: new controls page in examples/visual/ showcasing the above.

What's new in 0.2.3

  • JS helpers in nav.js: window.omiToast(...), window.omiCopy(text), and a declarative <button data-omi-copy="…"> auto-wire (with HTTPS-fallback so copy works on plain-HTTP dev servers).
  • icons.svg — vanilla SVG <symbol> sprite for non-React consumers (Flask, Jinja, any server-rendered app). Reference via <svg><use href="…/icons.svg#i-clipboard"/></svg>.
  • New CSS primitives: .omi-dropzone, .omi-asset-card (+ .omi-asset-grid), .omi-ai-badge, .omi-theme-picker.compact, .omi-scroll-clean.
  • Press feedback: .omi-btn.pressed animation + .confirmed::after floating "✓ copied" chip — automatically applied by data-omi-copy.
  • Behavior change: .omi-panel-body no longer auto-scrolls. Short static panels in flex column layouts no longer get surprise scrollbars. Add the .scroll modifier on dashboard panels that genuinely need overflow.

Install

npm install @dennisl0731/oh-my-ui

Consumer app peer deps:

npm install tailwindcss react

lucide-react and the common Tailwind plugins used by the preset are bundled through this package.

What You Get

1. CSS tokens and shared primitives

Import once:

@import '@dennisl0731/oh-my-ui/omi.css';

@tailwind components;
@tailwind utilities;

Skip @tailwind base. omi.css already ships its own base/reset styles.

The stylesheet includes:

  • warm layered surfaces
  • shared accent/border/text tokens
  • button, input, panel, dialog, card, pill, log, markdown, and editor primitives
  • stat cards, alerts, toolbars, tables, skeletons, menu/popover/drawer/toast shells
  • code and document block wrappers
  • service-specific accent overrides such as body.service-image and body.service-slide

Default service accent is the earthy tan palette. body.service-sage restores the older sage accent.

2. Built-in themes

Theme switching is driven by data-omi-theme on <html>.

Built-in themes:

  • paper
  • forest
  • ocean
  • sunset
  • grape
  • graphite
  • rose
  • dark — dark, GitHub-style
  • light — light, GitHub-style
  • catppuccin — dark, Catppuccin Mocha
  • nord — dark, Nord

Example:

<html data-omi-theme="ocean">

These themes change the global surface/text/accent tokens. Service classes like service-image still work on top by only shifting the accent family.

The seven warm-paper themes (paperrose) are light. The four ported themes add dark colorways (dark, catppuccin, nord) plus a neutral light. Dark themes re-skin the bundled components by overriding the neutral surface channels (--omi-surface, --omi-surface-2, --omi-surface-3, --omi-shadow-rgb, --omi-scrim-rgb); small semantic chips (status pills, thinking/tool blocks) keep their accent tints.

3. Tailwind preset

Use the preset in tailwind.config.js:

import omiPreset from '@dennisl0731/oh-my-ui/tailwind-preset';

/** @type {import('tailwindcss').Config} */
export default {
  presets: [omiPreset],
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
};

The preset adds:

  • token-backed colors such as bg-surface-1, text-fg, bg-accent, border-edge
  • token-backed fonts and radii
  • shadow-soft and shadow-lift
  • centered container defaults
  • prose-omi typography theme
  • @tailwindcss/forms with strategy: 'class'
  • @tailwindcss/typography
  • @tailwindcss/aspect-ratio
  • @tailwindcss/container-queries

This is the closest thing to an "industrial standard" baseline for app work: forms, prose, media ratio handling, responsive container logic, and tokenized colors all work out of the box.

4. Icons

React consumers:

import { Check, Plus, Trash2 } from '@dennisl0731/oh-my-ui/icons';

Everything from lucide-react is re-exported unchanged.

Vanilla / server-rendered consumers:

The package ships an SVG <symbol> sprite at icons.svg. Inline it once per page (or serve it as a static file and reference via <use>):

{# Jinja partial: inline once #}
{% include '_icons.html' %}                       <!-- or copy icons.svg verbatim -->

<svg class="icon" aria-hidden="true">
  <use href="#i-clipboard"/>
</svg>

Sprite IDs are i-<lucide-icon-name> — e.g. i-clipboard, i-image, i-pen-line, i-settings, i-refresh-cw, i-trash-2, i-sparkles, i-bug, i-check, i-x, i-chevron-right/down/left/up, i-arrow-left/right, i-search, i-eye, i-copy, i-file, i-folder, i-download, i-upload, i-presentation, i-brain, i-info, i-alert-circle, i-check-circle, i-x-circle, i-more-horizontal, i-menu, i-sun, i-moon, i-plus, i-edit, i-scan-text.

Recommended icon CSS:

.icon { width: 1em; height: 1em; vertical-align: -0.15em; flex-shrink: 0; display: inline-block; }

Icons use stroke="currentColor" so they inherit the surrounding text color.

5. JS helpers (nav.js)

nav.js exposes three globals:

// Toast — uses .omi-toast / .omi-toast-stack from omi.css. Creates the
// stack container on first call if not already in the DOM.
window.omiToast({ title: 'Saved', body: 'Theme updated.', kind: 'success' });
window.omiToast('Quick title', 'optional body', 'error');   // legacy positional

// Clipboard write with HTTPS-context fallback. Always returns Promise<boolean>.
const ok = await window.omiCopy('text to copy');

// Re-scan the DOM for [data-omi-copy] after dynamic inserts.
window.omiWireCopy(rootElement?);

The declarative form auto-wires on initial page load:

<button data-omi-copy="ssh user@host"
        data-omi-copy-label="ssh command">
  copy
</button>

On click: press animation → clipboard write (with fallback) → ✓ floater on success → toast (title: 'Copied' or 'Copy failed', body = the label). Buttons that already opt out via data-omi-copy-wired are skipped on rescan, so calling omiWireCopy() repeatedly is safe.

Theme Switching

nav.js no longer injects product UI. It only enhances markup you already render.

Minimal example

<div class="omi-topnav">
  <div class="omi-brand">oh-my-ui</div>
  <nav class="omi-nav"></nav>
  <label class="omi-theme-picker">
    <span>Theme</span>
    <select data-omi-theme-switch></select>
  </label>
</div>

<script>
  window.__OMI_NAV__ = [
    { id: 'image', label: 'image', href: 'http://localhost:5006/' },
    { id: 'slide', label: 'slide', href: 'http://localhost:5008/' },
    { id: 'skill', label: 'skill', href: 'http://localhost:5009/' }
  ];

  window.__OMI_THEME__ = {
    defaultTheme: 'paper'
  };
</script>
<script src="/node_modules/@dennisl0731/oh-my-ui/js/nav.js" data-service="image"></script>

Behavior

  • if window.__OMI_NAV__ exists, .omi-nav will be filled from that config
  • if a select[data-omi-theme-switch] exists, it will be wired to theme state
  • if the select has no options, built-in themes are injected automatically
  • selected theme is persisted to localStorage under omi-theme
  • theme changes emit document event omi:themechange

Optional theme config

<script>
  window.__OMI_THEME__ = {
    defaultTheme: 'forest',
    storageKey: 'my-app-theme',
    themes: [
      { id: 'paper', label: 'Paper' },
      { id: 'forest', label: 'Forest' },
      { id: 'ocean', label: 'Ocean' }
    ]
  };
</script>

Apps menu (.omi-appsdrop) — host-derived cross-app links

The package ships no app list (that's project data). Supply your own and nav.js fills any .omi-appsdrop container. Two ways:

<!-- inline -->
<script>
  window.__OMI_APPS__ = [
    { id: 'clipboard', sub: 'clipboard', path: '/',        label: 'clipboard', icon: 'clipboard' },
    { id: 'podcast',   sub: 'rss',       path: '/podcast', label: 'podcast',   icon: '<svg …>' }
  ];
</script>
<script src="…/nav.js" data-service="clipboard"></script>

<!-- or a settings file, fetched once -->
<script src="…/nav.js" data-service="clipboard" data-apps="/omi/apps.json"></script>

Each entry: {id, sub, path, label, icon}. icon is either a sprite id (clipboardicons.svg#i-clipboard) or a raw <svg …> string. No list → the container is left untouched (so hardcoded markup still works).

Hrefs are built by window.omiOrigin(sub), which swaps only the leading host label and keeps the current scheme/apex/port — so links keep working when the reverse proxy changes the base domain or port. .omi-nav entries accept the same sub (+ optional path) instead of a hardcoded href/port. Bare-IP / localhost / single-label hosts fall back to same-origin.

Tailwind Utilities

Common aliases:

| Class | Token | | --- | --- | | bg-surface-{1,2,3} | --tw-bg-{1,2,3} | | text-fg | --tw-fg | | text-fg-soft | --tw-fg-soft | | text-muted | --tw-muted | | bg-accent, bg-accent-deep | --tw-accent* | | border-edge, border-edge2 | --omi-border* | | font-sans, font-heading, font-mono | --omi-font-* | | rounded, rounded-lg, rounded-full | --omi-radius* |

Opacity modifiers work:

<div class="bg-fg/5 text-fg border border-edge"></div>

Recommended Next Components

These primitives are now available as styling building blocks:

  • tables: .omi-table-wrap, .omi-table, .omi-table-actions, .omi-table-empty
  • alerts: .omi-alert.info|success|warn|error
  • stat cards: .omi-stat-grid, .omi-stat-card, .omi-stat-value, .omi-stat-delta
  • toolbars: .omi-toolbar, .omi-toolbar-group, .omi-segmented
  • list rows: .omi-list, .omi-list-row, .omi-list-row-meta, .omi-list-row-actions
  • loading states: .omi-skeleton.line|title|block
  • overlays: .omi-menu, .omi-popover, .omi-drawer, .omi-toast-stack, .omi-toast
  • code/docs: .omi-codeblock, .omi-doc-block
  • ingestion: .omi-dropzone (0.2.3)
  • gallery: .omi-asset-grid, .omi-asset-card (+ .omi-asset-card-thumb|meta|name|submeta|actions|check) (0.2.3)
  • status: .omi-ai-badge.ok|bad (0.2.3)
  • chrome: .omi-theme-picker.compact, .omi-scroll-clean (0.2.3)
  • feedback animations: .omi-btn.pressed, .omi-btn.confirmed (0.2.3, auto-applied by data-omi-copy)

They are intentionally presentational. Behavior such as keyboard navigation, focus management, escape handling, portal mounting, and toast lifecycles should stay in the consumer app or a later headless helper layer.

Primitive Examples

Table

<div class="omi-table-wrap">
  <table class="omi-table">
    <thead>
      <tr>
        <th>Name</th>
        <th>Status</th>
        <th></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Experiment A</td>
        <td><span class="omi-pill ok">Ready</span></td>
        <td>
          <div class="omi-table-actions">
            <button class="omi-btn secondary sm">Open</button>
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</div>

Alert

<div class="omi-alert warn">
  <div class="omi-alert-body">
    <strong>Review needed</strong>
    Config changed and requires confirmation before publish.
  </div>
</div>

Toolbar

<div class="omi-toolbar">
  <div class="omi-toolbar-group">
    <input class="omi-input" placeholder="Search">
    <div class="omi-segmented">
      <button class="active">All</button>
      <button>Open</button>
      <button>Closed</button>
    </div>
  </div>
  <div class="omi-toolbar-spacer"></div>
  <div class="omi-toolbar-group">
    <button class="omi-btn secondary">Filter</button>
    <button class="omi-btn primary">New</button>
  </div>
</div>

Stat Cards

<div class="omi-stat-grid">
  <div class="omi-stat-card">
    <div class="omi-stat-label">Success Rate</div>
    <div class="omi-stat-value">93.2%</div>
    <div class="omi-stat-meta">
      <span class="omi-stat-delta up">+4.1%</span>
      vs last week
    </div>
  </div>
</div>

Skeleton

<div class="omi-card">
  <div class="omi-card-body">
    <div class="omi-skeleton title"></div>
    <div style="height:10px"></div>
    <div class="omi-skeleton line"></div>
    <div style="height:8px"></div>
    <div class="omi-skeleton line"></div>
  </div>
</div>

Drawer and Toast

<aside class="omi-drawer open">
  <div class="omi-drawer-header">
    <div class="omi-drawer-title">Details</div>
  </div>
  <div class="omi-drawer-body">...</div>
</aside>
<div class="omi-drawer-backdrop"></div>

<div class="omi-toast-stack">
  <div class="omi-toast success">
    <div class="omi-toast-title">Saved</div>
    <div class="omi-toast-body">Theme settings were updated.</div>
  </div>
</div>

Toasts are usually spawned from JS — see window.omiToast(...) above.

Drop zone (.omi-dropzone)

Paste / drag-drop / click-to-pick file ingestion:

<div class="omi-dropzone" id="dz" tabindex="0" role="button">
  <span class="omi-dropzone-icon">
    <svg class="icon"><use href="#i-clipboard"/></svg>
  </span>
  <div class="omi-dropzone-title">
    Paste an image with <kbd>Ctrl</kbd>+<kbd>V</kbd>
  </div>
  <div class="omi-dropzone-hint">
    drag &amp; drop a file<span class="sep">·</span>or click
  </div>
  <input type="file" hidden>
</div>

The app owns the JS — wire paste / dragover / drop / click handlers and toggle the .active class on dragover for the highlight pulse. Idle state has a subtle lifted shadow; hover gets an accent ring; active gets solid border + accent fill.

Asset card (.omi-asset-card + .omi-asset-grid)

Tile-based gallery for screenshots, images, or any thumbnailable asset:

<div class="omi-asset-grid">
  <div class="omi-asset-card">
    <img class="omi-asset-card-thumb" src="/thumb/1" alt="">
    <div class="omi-asset-card-meta">
      <div class="omi-asset-card-name">screenshot_2026-06-02.png</div>
      <div class="omi-asset-card-submeta">
        <span>77 KB</span>
        <span class="sep">·</span>
        <span>screenshots</span>
      </div>
    </div>
    <div class="omi-asset-card-actions">
      <button class="omi-icon-btn" data-omi-copy="/path/to/file"
              title="Copy path">
        <svg class="icon"><use href="#i-copy"/></svg>
      </button>
      <button class="omi-icon-btn" title="Edit">
        <svg class="icon"><use href="#i-pen-line"/></svg>
      </button>
      <button class="omi-icon-btn danger" title="Delete">
        <svg class="icon"><use href="#i-trash-2"/></svg>
      </button>
    </div>
    <div class="omi-asset-card-check">
      <svg class="icon"><use href="#i-check"/></svg>
    </div>
  </div>
</div>

Apply .selected to a card to mark it; apply .selecting to the grid to always-show the check overlay during multi-select. Without those, the check appears only on hover.

AI status badge (.omi-ai-badge)

<span class="omi-ai-badge ok"  title="Claude AI available"></span>
<span class="omi-ai-badge bad" title="AI not configured"></span>
<span class="omi-ai-badge"     title="AI status unknown"></span>

Pure CSS: the dot is a ::before pseudo-element. Toggle the .ok / .bad class from JS based on your AI/service health check.

Compact theme picker (.omi-theme-picker.compact)

<label class="omi-theme-picker compact" title="Choose theme">
  <span>theme</span>     <!-- hidden in compact mode; kept for a11y -->
  <select data-omi-theme-switch></select>
</label>

The compact modifier hides the label, drops the background/padding, and gives the <select> a borderless-pill style that fits cleanly in a top nav.

Node graphs (.omi-flow — React Flow + elkjs)

A theme-only, opt-in restyle of @xyflow/react to the warm-paper palette. No JS is bundled — install the libs in your app:

npm install @xyflow/react elkjs
import '@xyflow/react/dist/style.css';   // 1. React Flow base styles
import '@dennisl0731/oh-my-ui/omi.css';  // 2. then this theme

<ReactFlow className="omi-flow" nodes={nodes} edges={edges}>
  <Background />
  <Controls />
  <MiniMap />
</ReactFlow>

Adding className="omi-flow" themes nodes, edges, handles, controls, minimap, and the background pattern — mostly via React Flow's own --xy-* CSS variables, so per-service colorways (data-omi-theme) and dark mode flow through automatically.

elkjs only computes node x/y positions, so it needs no styling. Three optional accent classes are available for nodes returned from an ELK layout (set on a node's className): omi-flow__node--root, omi-flow__node--accent, omi-flow__node--muted.

Design Direction Ideas

The package can support more than one visual attitude while keeping the same API:

  • editorial workspace: denser typography, stronger document styling, better table and footnote treatments
  • builder console: more structured panels, status colors, logs, inspectors, split panes
  • gallery mode: richer cards, media-first layouts, asset metadata chips
  • research notebook: citations, callouts, figure wrappers, code-result pair layouts

That is the right level for this repo: shared language plus shared primitives, not fixed page compositions.

CSS-only Usage

<link rel="stylesheet" href="/node_modules/@dennisl0731/oh-my-ui/css/omi.css">
<script src="/node_modules/@dennisl0731/oh-my-ui/js/nav.js"></script>

If you only want theme behavior, render your own select with data-omi-theme-switch.

Testing

This repo now includes a minimal Playwright visual regression harness.

Files:

  • examples/visual/index.html: static preview shell
  • examples/visual/preview.js: deterministic scenes for each primitive group
  • tests/tokens.spec.js: CSS token contract checks
  • tests/visual.spec.js: screenshot regression checks
  • playwright.config.js: local static server + browser config

Install test deps:

npm install
npx playwright install

Run token checks:

npm run test:tokens

Generate or refresh screenshots:

npm run test:update-snapshots

Run visual regression against committed baselines:

npm run test:visual

Recommended workflow:

  • add or change a primitive in omi.css
  • update the matching scene in examples/visual/preview.js
  • regenerate snapshots intentionally
  • review image diffs before committing
  • commit snapshot baselines together with the style change

The important rule is that screenshot baselines are part of the contract. If a visual change is intended, update them in the same PR. If the change is not intended, the diff should fail review.

Publishing

npm publish --access public