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

@amsterdamdatalabs/enact-design-system

v0.2.0

Published

Enact design system — authored React + CSS-Modules components and 3-tier design tokens. Single source of truth; no local CSS/Tailwind overrides.

Downloads

101

Readme

@amsterdamdatalabs/enact-design-system

The single source of truth for Enact product UI: React components + a 3-tier design-token system. Change a token or a component once, and every product that consumes the package updates everywhere. Product code uses only the public components and semantic tokens — never raw colors, pixels, fonts, or component internals.

Ported from the proven Amsterdam Data Labs (ADL) system in net-revenue-demo.

Stack

  • Bun for install/scripts, React 19 + TypeScript, styled with CSS Modules.
  • Tokens are plain CSS custom properties (primitive --adl-* → semantic → dark mode).
  • Built with Vite (library mode, ESM) + tsc for declarations.

Install & develop

bun install
bun run dev                 # theme workbench + component catalog at localhost:5180/?p=components
bun run typecheck           # tsc --noEmit (also enforces prop/variant contracts)
bun run lint                # Biome + Ultracite + GritQL adherence (see below)
bun run format              # Biome formatter (write)
bun run test                # bun test — render/behavior + contract + styling guards
bun run test:visual         # Playwright visual regression (light + dark)
bun run test:visual:update  # refresh visual baselines after an intended change
bun run check               # typecheck + lint + test (the pre-merge gate)
bun run build               # dist/: ESM + .d.ts + design-system.css + tokens.css

Local development against a consumer app

dev-install-and-run.sh builds the library and installs the real publishable tarball (bun pm pack) into a consumer app — so it resolves exactly like a registry install (built dist/ + the exports map), not a source symlink. This mirrors what apps get from npm, for rapid local iteration.

bash dev-install-and-run.sh --target ../net-revenue-demo   # build → test → pack → install into the app
bash dev-install-and-run.sh pack                           # just build + produce the tarball
bash dev-install-and-run.sh link                           # global `bun link`; then `bun link <pkg>` in the app
bash dev-install-and-run.sh doctor                         # list exactly what the published tarball contains
bash dev-install-and-run.sh --dry-run --target ../app      # print the plan without running it

Use --skip-build to re-pack without reinstalling deps, and --docs to launch the showcase afterwards.

Usage in a product app

Import styles first, then components — all from the single public entry point:

import "@amsterdamdatalabs/enact-design-system/tokens.css";   // design tokens (incl. dark mode)
import "@amsterdamdatalabs/enact-design-system/styles.css";   // component styles
import { Button, Card, Stat } from "@amsterdamdatalabs/enact-design-system";

export function Example() {
  return (
    <Card variant="raised">
      <Stat label="Net revenue" value="$1.24M" delta="12.4%" deltaDirection="up" />
      <Button variant="primary">View report</Button>
    </Card>
  );
}

Dark mode: set data-theme="dark" on <html> (or any subtree root).

ThemeProvider and useTheme

Wrap your application root with ThemeProvider to enable theme switching. useTheme surfaces the current theme and a setter anywhere in the tree.

import {
  ThemeProvider,
  useTheme,
} from "@amsterdamdatalabs/enact-design-system";

// 1. Wrap the app root
export function App() {
  return (
    <ThemeProvider defaultTheme="system">
      <Shell />
    </ThemeProvider>
  );
}

// 2. Read and change the theme from any descendant
function ThemeToggle() {
  const { theme, resolvedTheme, setTheme } = useTheme();

  return (
    <button
      type="button"
      onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
    >
      Switch to {resolvedTheme === "dark" ? "light" : "dark"} mode
    </button>
  );
}

ThemeProvider persists the user's choice to localStorage under the key "enact-theme" (configurable via the storageKey prop). The resolved theme ("light" or "dark") is derived from the user's explicit choice or from window.matchMedia("(prefers-color-scheme: dark)") when the theme is "system".

Preventing theme flash on first paint

Without extra work the page renders in the default (light) state for one frame while React hydrates — producing a visible flash when the user prefers dark. Inline themeInitScript in <head> before your app bundle to apply the correct data-theme attribute at browser-parse time, before any paint:

import {
  themeInitScript,
} from "@amsterdamdatalabs/enact-design-system";

// In your HTML template, _document.tsx, or root layout:
export function Document() {
  return (
    <html lang="en">
      <head>
        {/* Must come before the app bundle — runs at parse time, before paint. */}
        <script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
        {/* ...other head tags, then the app bundle */}
      </head>
      <body>...</body>
    </html>
  );
}

If your app uses a non-default storage key, use getThemeInitScript to produce a matching script:

import {
  getThemeInitScript,
} from "@amsterdamdatalabs/enact-design-system";

const script = getThemeInitScript("my-app-theme");

// Pass the same key to ThemeProvider:
// <ThemeProvider storageKey="my-app-theme">...</ThemeProvider>

The script is a self-contained IIFE:

  • reads localStorage[storageKey]
  • "dark" → sets data-theme="dark" on <html>
  • "light" → ensures data-theme is absent
  • "system" or missing → falls back to window.matchMedia("(prefers-color-scheme: dark)")
  • wrapped in try/catch so storage or matchMedia failures are inert

Keying is identical to ThemeProvider so there is no conflict on hydration.

Layout primitives

Five composable, token-based layout components handle spacing, alignment, and grid structure. All accept standard HTML div attributes in addition to the props below.

import {
  Box,
  Container,
  Grid,
  Inline,
  Stack,
} from "@amsterdamdatalabs/enact-design-system";

// Box — single block with optional padding, surface tint, and radius
<Box padding="4" surface="raised" radius="md">
  content
</Box>

// Stack — vertical flex container
<Stack gap="4" align="center">
  <div>Item A</div>
  <div>Item B</div>
</Stack>

// Inline — horizontal flex container
<Inline gap="2">
  <Button variant="primary">Save</Button>
  <Button variant="ghost">Cancel</Button>
</Inline>

// Grid — CSS grid with a named column count
<Grid columns={3} gap="6">
  <Card>One</Card>
  <Card>Two</Card>
  <Card>Three</Card>
</Grid>

// Container — centred, max-width wrapper
<Container width="narrow">
  <Stack gap="4">...</Stack>
</Container>

Typography

Heading and Text map directly to the system's type scale.

import {
  Heading,
  Text,
} from "@amsterdamdatalabs/enact-design-system";

// Heading — semantic element + visual size, decoupled for accessibility
<Heading level={1} size="xl">Page title</Heading>
<Heading level={2} size="md">Section title</Heading>

Accessibility note — level vs size: level controls the rendered HTML tag (h1h6) and therefore the document outline used by screen readers. size is a purely visual override — you can render an h2 that looks like an h4 (or vice versa) without affecting semantics. Always set level to match the document hierarchy; use size only to meet visual design requirements.

// Text — inline/block body copy
<Text size="sm" tone="secondary">Last updated 3 days ago</Text>
<Text size="md" tone="primary">Main body copy</Text>

Available sizes for both: "xs" "sm" "md" "lg" "xl" "2xl". Available tones: "primary" "secondary" "muted" "inverse" "link".

Components (17)

| Group | Components | |---|---| | core | Button IconButton Badge Avatar Card Stat Tag | | feedback | Alert Progress Tooltip | | forms | Input Select Checkbox Switch Field | | navigation | Tabs Breadcrumb |

Tooling

Biome + Ultracite is the linter/formatter (bun run lint / format), with strict accessibility, correctness, and style rules. Ultracite's a11y rules are enforced — decorative SVGs are hidden, roles/labels are required, etc.

Enforcement — "no local overrides"

Four mechanisms, each independently verified to fire:

| Rule | Enforced by | |---|---| | no raw hex colors in TS/JS | GritQL plugin .grit/no-raw-hex.grit | | no raw px in TS/JS | GritQL plugin .grit/no-raw-px.grit | | only system fonts (Space Grotesk / Manrope / IBM Plex Mono) | GritQL plugin .grit/no-offsystem-font.grit | | valid component props + enum values (variant="huge" fails) | TypeScript (bun run typecheck) | | no deep imports into internals | the package exports map (physical — deep paths aren't exported) |

Biome lacks ESLint's no-restricted-syntax, so the raw-value rules are authored as GritQL plugins (engine biome(1.0), matching JsStringLiteralExpression). Biome errors on a broken plugin, so the enforcement can't silently rot. The library lints itself, and the showcase doubles as a live adherence example.

Testing — drift protection

| Layer | What it catches | Run | |---|---|---| | A. Render + behavior | a component breaks; controlled/uncontrolled state; ARIA roles | bun test | | B. Contract snapshots | a token or variant renamed/removed | bun test | | C. Styling guard | raw hex in component CSS; a var(--…) that references an undefined token | bun test | | D. Visual regression | contrast/visual drift in light or dark, every section | bun run test:visual |

After an intended visual change, refresh baselines with bun run test:visual:update.

Layout

src/
  tokens/        CSS custom properties (ported): colors typography spacing effects fonts base + index.css
  components/    core/ feedback/ forms/ navigation/ — each: Name.tsx + Name.module.css + index.ts
  index.ts       the ONLY public entry point
studio/          Theme workbench + Components tab (bun run studio)
.grit/           GritQL adherence plugins (raw hex/px/font bans)
test/            bun test (A/B/C) + visual/ Playwright specs (D)
biome.jsonc      Biome + Ultracite config (extends + GritQL plugins)

Notes

  • Components were re-authored from the ADL bundle's source (the bundle's behavior is the reference). Visual values map 1:1 to the same semantic tokens.
  • Prop contracts are TypeScript types: components extend the native HTML attribute types, so standard attributes (id, aria-*, form attrs) are allowed by design — required for accessible label association (Field htmlFor → input id).
  • Distribution model (npm publish vs. monorepo workspace) is intentionally deferred; the build artifact supports either.