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

@lepsto/ui

v9.0.0

Published

Lepsto design system — shared UI primitives, tokens, and theme

Readme

@lepsto/ui

The Lepsto design system as an installable package: shared UI primitives, design tokens, theme and a Tailwind preset.

Install

Published to the public npm registry — no scope or registry configuration needed:

npm install @lepsto/ui

Contract: the package name, its exports and its peer dependencies — all declared in package.json — are a cross-project contract. Do not change them without coordinating the workspace host and every federation remote (see Federated remotes). scripts/check-package-contract.mjs refuses a change to any of them, and says the same thing when it does.

What's inside

  • Components: shadcn/radix primitives themed by Lepsto tokens (Button, Dialog, Select, Tabs, …) plus layout: Grid / Col (the adaptive 4 / 8 / 12 column grid) and GridOverlay (a design-aid overlay). The full catalog lives in Storybook at ui.lessly.com/storybook.
  • Theme: useTheme hook + ThemeToggle (toggles the light class on <html>; :root is dark, .light is light).
  • Tokens: the tailwind-preset re-exports the token vocabulary from @lepsto/tokens (generated upstream in lepsto/design-system) with darkMode: 'class' added, and styles.css bundles the package's theme.css (CSS variables, .light overrides, typography classes) plus this library's base layer, and a @theme block that declares every colour token to Tailwind v4. It declares no font face: the brand faces live in @lepsto/tokens/fonts.css, which the kit re-exports as @lepsto/ui/fonts.css, a second import beside it. styles-federated.css is styles.css without the @layer base block and without the @theme colour bridge, and with its variables declared on :where(:root) / :where(.light) so a shell's own tokens outrank the remote's whatever loaded last, for remotes that must not restyle the page they load into. The bridge ships as its own sheet, theme-federated.css, which a remote imports with theme(reference) beside it. Token values are never edited here — they change by bumping @lepsto/tokens.

Usage

import { Button, useTheme } from '@lepsto/ui';
import '@lepsto/ui/styles.css';
import '@lepsto/ui/fonts.css';

The second stylesheet is what loads the brand faces — Inter in nine weights, Fira Mono in three, Sora in two — from woff2 files inside @lepsto/tokens, at relative paths your bundler resolves out of node_modules. That package reaches you as a dependency of @lepsto/ui, and the kit resolves it, so there is nothing to install beside it and the version you get is the one the kit was built against. Miss the import and every role renders in a system fallback, with no error and no warning.

The four components that need a router — AppShell, AppSidebar, SidebarNav and ExtensionLink — come from @lepsto/ui/router instead, and only that entry imports react-router. The main entry never does, which is what makes react-router an optional peer: an app that navigates with plain hrefs can use the rest of the kit without installing one.

import { AppShell, SidebarNav } from '@lepsto/ui/router';

Tailwind is what turns the kit's class names into CSS. The package ships no compiled utility CSS — every consumer runs its own Tailwind over the token preset (tailwindcss >= 3.4, an optional peer dependency, required only if you consume the preset).

Tailwind v4 (CSS-first)

Everything is declared in your CSS entry. The minimum is four lines:

@import 'tailwindcss';
/* Token variables, `.light` overrides, base layer — and a `@theme` block
   declaring every colour token, so `bg-bg-surface`, `text-text-primary` and
   `ring-border-selected` compile from this import alone. */
@import '@lepsto/ui/styles.css';
/* The brand faces. No sheet above declares one, and nothing warns when they are
   missing — the page just renders in a system fallback. */
@import '@lepsto/ui/fonts.css';
/* Scan the library. v4 does not scan `node_modules` on its own, and the kit's
   components (Grid, Col, …) carry literal utilities (`md:col-span-5`,
   `gap-gutter`) as strings inside `dist/index.js`. The glob matters: a bare
   directory path is skipped, because Tailwind honours your `.gitignore` and
   `node_modules/` is in it. */
@source './node_modules/@lepsto/ui/dist/**/*.js';

The rest of the token vocabulary is not colour and does not travel in CSS: the spacing scale, the grid gutter (gap-gutter), radii, shadows, z-index and durations come from the JS preset, and v4 has no presets: key of its own, so a config file is what loads it:

@config './tailwind.config.ts';

with that file holding nothing but the preset:

import type { Config } from 'tailwindcss';
import { lepstoPreset } from '@lepsto/ui/tailwind-preset';
export default { presets: [lepstoPreset] } satisfies Config;

Paths in @source and @config are relative to the CSS file. If your CSS entry sits in src/, they read '../tailwind.config.ts' and '../node_modules/@lepsto/ui/dist/**/*.js'.

Tailwind v3

The preset and the library scan both go in the JS config:

import { lepstoPreset } from '@lepsto/ui/tailwind-preset';
export default {
  presets: [lepstoPreset],
  // Scan the library too — its components (Grid, Col, …) use literal token
  // utilities (`md:col-span-5`, `gap-gutter`) that Tailwind must see to generate.
  content: ['./src/**/*.{ts,tsx}', './node_modules/@lepsto/ui/dist/**/*.js'],
};

CSS variables use the upstream unprefixed names (--bg-primary, --text-primary, …). The old --color-* names still resolve through a deprecated alias layer in styles.css and will be removed in the next major — migrate any direct var(--color-*) reads.

Federated remotes

A Module Federation remote loads its CSS after the shell has painted, so it must not restyle the live page: a @layer base body rule re-applied on top of a rendered page shifts font metrics on every cold load. Import the federation-safe sheet instead — same tokens, aliases, ui variables and animation layer, without that block — paired with the colour bridge, which ships as its own sheet loaded first in reference mode:

@import '@lepsto/ui/theme-federated.css' theme(reference);
@import '@lepsto/ui/styles-federated.css';

The reference line is required: miss it and the bridge compiles to a bare :root,:host block that outranks the shell, so the palette is dropped rather than inherited. styles.css keeps its bridge inline, so a standalone consumer imports one sheet as before. The shell keeps importing @lepsto/ui/styles.css, and it is the shell that imports @lepsto/ui/fonts.css: a remote declares no face and no base layer.

The preset is not optional on this path either, and installing it is not the same as reaching it: Tailwind v4 has no presets: key, so both entries — federation and standalone — need @config, and one that lacks it compiles to Tailwind's defaults with no error anywhere. What each sheet carries, why the federated one declares its variables at zero specificity, and why theme(reference) is not the fix are in docs/federated-styles.md.

Storybook

pnpm storybook          # dev server on http://localhost:6006
pnpm build-storybook    # static build → storybook-static/

The Storybook toolbar has a global Dark/Light toggle driven by the package's own useTheme hook.

Where a component goes

This kit is not the only place in lepsto a shared component can live. design-system holds the token and asset layer this package installs as @lepsto/tokens and no component implementations at all; landing-ui is the reference catalog for the public site, before the login; and a product's own app holds what only that product uses. docs/component-home.md records what each one holds today and the test that tells them apart.

Deployment (ui.lessly.com)

ui.lessly.com is served by a Lepsto platform service that builds the repo's root Dockerfile from main. The image runs the zero-dependency site/server.mjs, which serves the landing page (site/) at /, the built Storybook (storybook-static/) at /storybook, and a health check at /healthz. The Docker build runs pnpm build, pnpm build-storybook, and pnpm build-site in turn.

The npm package build is pnpm build (tsup → dist/), which the release flow uses; the npm package published to npmjs always contains the package, never the site or Storybook.

Scripts

| Script | Description | | --- | --- | | pnpm build | tsup package build → dist/ (+ assembles styles.css from the @lepsto/tokens theme, deprecated --color-* aliases, ui-local pieces, and the animation layer, the federation-safe styles-federated.css beside it, and the theme-federated.css colour bridge a remote pairs with it) — the npm package published to npmjs | | pnpm build-storybook | Storybook static site → storybook-static/ — served at ui.lessly.com/storybook by site/server.mjs | | pnpm build-site | Landing app (site/) → site/dist/ — served at ui.lessly.com/ by site/server.mjs | | pnpm storybook | Storybook dev server on http://localhost:6006 | | pnpm check | Vitest, only what your change reaches | | pnpm test | Vitest, everything but the guard meta-tests | | pnpm test:gates | Vitest, the gates-bite-* meta-tests | | pnpm test:all | Vitest, both projects — the merge gate | | pnpm type-check | tsc --noEmit | | pnpm lint | ESLint | | pnpm changeset / pnpm release:version | Versioning via Changesets (describe change → apply bump + changelog) — see RELEASING.md | | pnpm release:publish | CI-only publish wrapper (publish to npmjs if needed + changeset tag) — used by .github/workflows/version.yml |

Releasing

Add a changeset (pnpm changeset) to each significant PR; merging the auto-generated "Release: version packages" PR publishes to the public npm registry and tags v<version>. See RELEASING.md.