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

@nomiedge/ui

v0.1.1

Published

Nomiedge UI component library — React, TypeScript, Tailwind, built on @nomiedge/tokens.

Readme

@nomiedge/ui

The Nomiedge component library, per prompts/Pack-04-UI-Library/31-ui-package.md.

Stack

  • React + TypeScript. No other framework target.

  • Tailwind, via @nomiedge/tokens' generated v4 theme (tailwind-theme.css + semantic.css) — this package doesn't run its own Tailwind build. Components author className strings using utilities that theme provides (bg-neutral-500, spacing/radius/shadow scale classes, etc.); the consuming app's own CSS entrypoint compiles the actual CSS. A consumer must @import both @nomiedge/tokens/tailwind-theme.css and @nomiedge/tokens/semantic.css and add an @source pointing at this package's dist, or the utility classes this package uses won't be generated:

    /* src/app/globals.css */
    @import "tailwindcss";
    @import "@nomiedge/tokens/tailwind-theme.css";
    @import "@nomiedge/tokens/semantic.css";
    @source "../../node_modules/@nomiedge/ui/dist";

    (adjust the @source path's relative depth to wherever your app's CSS entrypoint actually lives — see apps/brand-portal/src/app/globals.css for the real pattern in use elsewhere in this repo.)

  • class-variance-authority (CVA) for variant-based styling (size, tone, state) — declared once per component, not className={size === "lg" ? "..." : "..."} chains repeated ad hoc.

  • cn() (src/lib/cn.ts, clsx + tailwind-merge) — every component composes its final className through this, so a consumer's override always wins deterministically over the component's own defaults, not by CSS source-order luck.

  • Radix UI primitives where a component has real accessibility-critical interaction behavior to get right (focus trapping, keyboard nav, ARIA state) — Dialog and Tabs, concretely, later in this pack. Not used for presentational components (Button, Card) that don't need it — adding a Radix dependency for something with no complex interaction model would be the opposite of "one job per component."

Components

Button (31-ui-package.md32-button.md)

Three variants (primary/secondary/ghost — not four; see the comment in button.tsx for why a fourth would be redundant in a monochrome + one-accent system), three sizes (sm/md/lg), loading state (loading prop — swaps in a spinner, sets disabled and aria-busy together), leading/trailing icon slots.

<Button variant="primary" size="md">
  Continue
</Button>
<Button variant="secondary" loading>
  Saving
</Button>

Colors reference the raw neutral/accent Tailwind scale directly, with dark: variants matching the exact steps @nomiedge/tokens' colors.ts already chose for dark mode (see KNOWN_ISSUES.md — there's no CSS-variable bridge from semantic tokens to Tailwind yet, so this component's dark: classes and colors.ts's color.dark values must be kept in sync by hand until that bridge exists).

Card (33-card.md)

Card, CardHeader, CardTitle, CardContent, CardFooter — a compound API, not one component with a dozen layout props.

<Card>
  <CardHeader>
    <CardTitle>Plan</CardTitle>
  </CardHeader>
  <CardContent>Details here.</CardContent>
  <CardFooter>
    <Button size="sm">Confirm</Button>
  </CardFooter>
</Card>

No shadow by default — border alone is the separation, per 27-shadow-elevation.md's "minimal shadows and borders" taken literally. elevated (a boolean prop on Card) opts into shadow.sm for contexts that need to lift above other content (e.g. a card inside a popover).

Input, Textarea, Label (34-inputs.md)

<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="[email protected]" />

Focus states use "Focus Line language" — the requirement's own phrase, taken literally: the Focus Line mark's identity is a clean rectangular negative-space gap, not a glow, so focus here is a solid 2px ring with a visible offset (focus-visible:ring-2 ... ring-offset-2), the same treatment Button uses. One consistent "this is focused" signal, not a per-component blur.

No visual invalid/error state yet — @nomiedge/tokens has no status color (KNOWN_ISSUES.md), so there's nothing non-arbitrary to color an invalid border with. Input/Textarea pass through native aria-invalid for assistive-tech semantics; the visual treatment is blocked on that color decision, not overlooked here.

Tabs (35-tabs.md)

Built on @radix-ui/react-tabs — the first Radix-based component (keyboard navigation, roving tabindex, ARIA tablist/tab/tabpanel relationships; exactly the "real accessibility-critical interaction behavior" this README's Stack section reserves Radix for).

<Tabs defaultValue="overview">
  <TabsList>
    <TabsTrigger value="overview">Overview</TabsTrigger>
    <TabsTrigger value="settings">Settings</TabsTrigger>
  </TabsList>
  <TabsContent value="overview">...</TabsContent>
  <TabsContent value="settings">...</TabsContent>
</Tabs>

Active state uses the Focus Line concept: a solid horizontal accent-colored underline under the active tab — one line marking one selection, the same idea as the mark's single negative-space line between two blocks — not a filled pill or a box border.

Dialog (36-dialog.md)

Built on @radix-ui/react-dialog — focus trapping, Escape to close, focus return on close.

<Dialog>
  <DialogTrigger asChild>
    <Button>Open</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Confirm</DialogTitle>
      <DialogDescription>Are you sure?</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="ghost">Cancel</Button>
      </DialogClose>
      <Button>Confirm</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Calm motion, no new dependency: entrance/exit transition opacity/scale directly off Radix's data-state attribute at 200ms with a decelerate-style easing — plain transition-* utilities, not the tailwindcss-animate plugin's animate-in/fade-in-0/zoom-in-95 classes. Those were the first draft; caught during build that the plugin isn't installed or part of @nomiedge/tokens' generated Tailwind theme, so those classes would have generated no CSS at all. Fixed before it shipped, not after. motion-reduce: handles reduced-motion, same mechanism reducedMotionDuration documents.

Sidebar (37-sidebar.md)

Sidebar, SidebarHeader, SidebarNav, SidebarNavItem, SidebarFooter — minimal, dashboard-ready: a fixed-width panel, a nav list, an active-item indicator. Not a mega-nav with collapsible groups or search — extend when a real dashboard needs more, not speculatively now.

<Sidebar>
  <SidebarHeader>{/* logo */}</SidebarHeader>
  <SidebarNav>
    <SidebarNavItem href="/" active>
      Overview
    </SidebarNavItem>
    <SidebarNavItem href="/settings">Settings</SidebarNavItem>
  </SidebarNav>
  <SidebarFooter>{/* user menu */}</SidebarFooter>
</Sidebar>

The active item's indicator is a vertical accent line on its leading edge — Tabs' horizontal underline, rotated for a vertical layout, not a second unrelated "this one's active" treatment. Concretely, SidebarNavItem renders <FocusLine orientation="vertical" active={active} />, the shared primitive below, rather than its own pseudo-element.

FocusLine (38-focus-line.md)

The UI-facing counterpart to @nomiedge/logo's FocusLine (the brand mark's raw SVG geometry) — same name, same idea, different job: a reusable line element for active-state indicators, loading bars, and separators.

<FocusLine variant="separator" />
<FocusLine variant="loading" />
<FocusLine variant="indicator" active={isActive} orientation="vertical" />

SidebarNavItem uses variant="indicator" directly (its active state is a controlled prop, so FocusLine's active prop wires up cleanly). Tabs' active underline deliberately stays a data-[state=active]:after: pseudo-element instead — Radix owns "active" there via its own data-state DOM attribute, which a CSS selector reads natively; threading that into a nested FocusLine would need a fragile group-data-[state=active]: override with !important. Same visual language, different implementation for a real, documented reason (see tabs.tsx, DECISIONS.md).

The loading variant's sweep animation (animate-[focus-line-sweep-x_...]) needed real @keyframes added to tailwind-theme.ts (packages/tokens, emitted into the generated tailwind-theme.css) — an arbitrary Tailwind animation name with no matching keyframes definition compiles to CSS that animates nothing. Caught and fixed during this milestone, the same category of bug Dialog's tailwindcss-animate catch was.

Logo (39-logo-component.md)

@nomiedge/logo used inside @nomiedge/ui — a thin wrapper, not a reimplementation. This is the first real dependency from @nomiedge/ui to @nomiedge/logo (there was none before).

<Logo size="md" />
<Logo size="sm" orientation="stacked" />
<Logo size="lg" markOnly />

Three named sizes (sm/md/lg), not an arbitrary pixel prop every consumer has to guess a sensible value for. markOnly renders just LogoMark for icon-only placements (a collapsed sidebar rail) with an always-present accessible label — which required fixing a real bug: LogoMark defaults to aria-hidden="true" (correct when the mark sits next to visible text), so passing aria-label alone would still leave the element hidden from the accessibility tree. Logo's markOnly branch explicitly clears aria-hidden so the label is actually reachable — caught by reasoning through what aria-hidden="true" really does, not just adding a label and assuming it worked.

Rules

  • No component hard-codes a color, spacing, radius, shadow, duration or font value that @nomiedge/tokens already defines (docs/quality-bar.md).
  • No component ships without a size/variant story that exercises every CVA variant it defines — a variant nobody demonstrates is a variant nobody's verified works. (Concrete story wiring lands with the docs/brand-portal in Pack 05; until then, every component's own file includes the full variant matrix as a comment or type, so it's visible in code.)