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

@viax.io/uxm

v4.48.1

Published

Viax UXM — React 19 UI primitives and design tokens.

Downloads

730

Readme

@viax.io/uxm

React 19 UI primitives, design tokens, and theming previews — packaged as a standalone library.

BEM-classed components, a single design-token layer, and themable previews. Framework-agnostic React 19 — distributed as ESM + CJS with first-class .d.ts, no runtime dependencies beyond React, and sideEffects: ["**/*.css"] for full tree-shaking.

The package ships no "use client" / "use server" directives by design: consumers are client-side SPAs, and framework-specific pragmas would leak into every one of them. Under a React Server Components setup (e.g. Next.js App Router) the consuming file is responsible for its own "use client" boundary.

What's in this package

  • UI primitives (@viax.io/uxm/ui) — BEM-classed React 19 components.
  • Icon registry (@viax.io/uxm/ui) — ICONS, ICON_OPTIONS, getIcon, IconDef for tooling that enumerates the bundled icon set.
  • Design tokens (@viax.io/uxm/tokens) — the canonical themeTokens array plus helpers (findToken, resolveHex, isTokenValue) and the ThemeToken type.
  • Behaviour hooks (@viax.io/uxm/hooks) — useDismiss, useFocusTrap, useFocusOnMount, useRovingTabIndex, useScrollLock, usePortal, useToastStore for hosts composing their own floating layers or keyboard widgets.
  • Themable previews (@viax.io/uxm/previews) — preview components (one per atom, plus composite previews) used by host shells like MODO's brand-settings editor to render live, knob-driven theme exploration.
  • Studio (@viax.io/uxm/studio) — UxmApp, the full design workbench MODO serves at /uxm, backend-decoupled through the StudioPersistence contract; see Studio.
  • WCAG / contrast helpers (@viax.io/uxm) — contrastRatio, parseColor, rgbToHex, suggestAccessibleColor, suggestAccessibleToken, wcagLevel, plus RGB and TokenCandidate types.
  • Default stylesheets@viax.io/uxm/ui.css (component primitive defaults) and @viax.io/uxm/tokens.css (token declarations).

Install

npm i @viax.io/uxm

Every example in this README imports from @viax.io/uxm and its subpaths.

Tip: install under an alias. viax apps install the package as uxm so the real name appears once, in package.json, and imports stay short and rename-proof:

npm i uxm@npm:@viax.io/uxm@latest

That writes "uxm": "npm:@viax.io/uxm@^4.40.1" and lets you write import { Button } from 'uxm/ui' or import 'uxm/tokens.css'. The subpath exports are identical under either name — read @viax.io/uxm as uxm throughout.

Peer deps: react@^19, react-dom@^19. Node >=20 for local dev.

Published publicly on npm — no registry configuration, credentials or VPN required for a plain npm i @viax.io/uxm against the default npm registry.

If your project's default registry points at Nexus (registry=https://nexus.viax.tech/repository/viax-npm/ in .npmrc — the common viax-internal setup), add a scope override so this package still resolves straight from npm:

@viax.io:registry=https://registry.npmjs.org/

Nexus mirrors registry.npmjs.org with a delay, so installing right after a fresh release can 404 or silently resolve a stale version through Nexus without this override.

Quick start

Import both stylesheets once, at your application entry point:

// e.g. src/main.tsx
import '@viax.io/uxm/tokens.css';
import '@viax.io/uxm/ui.css';

Then use the primitives anywhere:

import { ButtonPrimary, Icon } from '@viax.io/uxm/ui';

export function Page() {
  return (
    <ButtonPrimary>
      <Icon glyph="sparkles" /> Hello UXM
    </ButtonPrimary>
  );
}

Consume tokens programmatically:

import { themeTokens, findToken, resolveHex } from '@viax.io/uxm/tokens';

const accent = findToken('--color-accent-bold');
const hex = resolveHex(accent?.cssVar ?? '#000');

Both primary entry points are also re-exported from the root:

import { ButtonPrimary, themeTokens } from '@viax.io/uxm';

Subpath exports

| Entry | Purpose | |-------|---------| | @viax.io/uxm | Root barrel — re-exports ui + tokens + WCAG helpers. | | @viax.io/uxm/ui | All UI primitives + icon registry. Tree-shake-friendly per-component imports. | | @viax.io/uxm/ui.css | Compiled component stylesheet — required for visual output. | | @viax.io/uxm/tokens | themeTokens array + findToken / resolveHex / isTokenValue + ThemeToken type. | | @viax.io/uxm/tokens.css | --color-* declarations on :root. | | @viax.io/uxm/hooks | The behaviour hooks the atoms are built on — useDismiss, useFocusTrap, useFocusOnMount, useRovingTabIndex, useScrollLock, usePortal, useToastStore — for hosts composing their own floating layers or keyboard widgets. Pure React, no atom imports. | | @viax.io/uxm/previews | Preview components for host shells building theme editors. No preview symbol leaks into /ui — see the tree-shake guarantee below. | | @viax.io/uxm/studio | UxmApp + UxmProvider / useUxm, the component registry, the StudioPersistence contract and its three adapters (createHttpPersistence, createClientPersistence, createReadOnlyPersistence). | | @viax.io/uxm/studio/generate-css | generateOverridesCss + the CSS sanitizers — turn saved studio overrides and a BrandConfig into a stylesheet on the server, without pulling in the workbench UI. | | @viax.io/uxm/studio.css | Tailwind utilities for the studio shell + token declarations. Does not bundle the atom CSS — a studio host imports ui.css alongside it. |

CDN usage

For pages that can't run npm at all — prototypes, CodePen repros, embeds in someone else's site — every release also publishes a handful of self-contained <script>/<link>-ready bundles, built by npm run build:cdn and uploaded automatically on release.

If your project has a bundler, this is not what you want. Use npm i @viax.io/uxm — the CDN bundles below exist specifically for pages that can't.

All files live at https://uxm.viax.io/<version>/<file> — versioned and immutable (Cache-Control: public, max-age=31536000, immutable). There is no /latest/: pin an exact version, and use the same version across every file you load on one page.

| File | Format | React | For | |---|---|---|---| | uxm.esm.js | ESM, react / react-dom / react/jsx-runtime external | the host page's, via import map | pages that already have React 19 | | uxm.standalone.js | IIFE, window.UXM, React bundled in | its own, inside the bundle | pages with no React at all | | uxm.css | flattened tokens.css + ui.css | — | both | | uxm-generate-css.esm.js | ESM, no deps | — | only if you need generateOverridesCss (the theming applier) | | uxm.esm.d.ts / uxm-generate-css.esm.d.ts | bundled .d.ts per JS artifact | needs @types/react | editor/type-check support | | cdn-manifest.json | byte size, gzip size, sha384 SRI hash per file | — | verify what you fetched |

⚠️ React 19 ships no UMD build. The classic "two <script> tags from a CDN plus our UMD on top" isn't possible — that's why there are two separate formats above instead of one.

ESM — page that already has React 19

<script type="importmap">
{
  "imports": {
    "react": "https://esm.sh/react@19",
    "react-dom/client": "https://esm.sh/react-dom@19/client",
    "@viax.io/uxm/ui": "https://uxm.viax.io/4.43.0/uxm.esm.js"
  }
}
</script>
<link rel="stylesheet" href="https://uxm.viax.io/4.43.0/uxm.css" />
<script type="module">
  import { ButtonPrimary } from '@viax.io/uxm/ui';
  import { createElement } from 'react';
  import { createRoot } from 'react-dom/client';

  createRoot(document.getElementById('root')).render(createElement(ButtonPrimary, null, 'Hello UXM'));
</script>

Standalone — page with no React

<link rel="stylesheet" href="https://uxm.viax.io/4.43.0/uxm.css" />
<script src="https://uxm.viax.io/4.43.0/uxm.standalone.js"></script>
<script>
  const { React, createRoot, ButtonPrimary } = window.UXM;
  createRoot(document.getElementById('root')).render(React.createElement(ButtonPrimary, null, 'Hello UXM'));
</script>

⚠️ Never load uxm.standalone.js on a page that already has another React instance — two copies of React break hooks and context. If the page has React, use the ESM build above instead.

Required host baseline

A CDN page gets none of the resets a bundler-based app inherits from its own global stylesheet. Add this once, after uxm.css — skip it and the app renders in Times New Roman with inputs overflowing their containers. This is the same baseline npm consumers need; see skills/viax-uxm/references/quick-recipes.md (recipe 0) for the full version and why each line is load-bearing.

*, *::before, *::after { box-sizing: border-box; }
html, body, #root { height: 100%; }
:root { --font-sans: var(--font-inter, 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif); }
html, body, #root { font-family: var(--brand-font, var(--font-sans)); }
button, input, select, textarea { font: inherit; }

Types

The .d.ts bundles need @types/react in your project — they extend React's own prop types (ButtonHTMLAttributes and friends), which aren't ours to inline. Fetch one alongside your CDN import and map it in tsconfig.json:

UXM=4.43.0
curl -fsSL -o types/uxm.esm.d.ts https://uxm.viax.io/$UXM/uxm.esm.d.ts
{ "compilerOptions": { "paths": { "@viax.io/uxm/ui": ["./types/uxm.esm.d.ts"] } } }

If your project can take even a dev-only npm dependency, npm i -D @viax.io/uxm@<version> is simpler and gives every subpath's types at once — the curl route above exists for projects that can't.

Verifying what you fetched (SRI)

cdn-manifest.json next to every version's files lists a sha384 hash per file — pass it as integrity on the <script> tag:

<script
  type="module"
  src="https://uxm.viax.io/4.43.0/uxm.esm.js"
  integrity="sha384-X5wmfaVzW+An31gN2SpznF1F6UxTbD4SsCynzec/bElec2EneSTZf2l0O81YHLVt"
  crossorigin="anonymous"
></script>

Component catalog

Every component folder ships a README.md documenting props, CSS variables, MODO-configurable design tokens, states/variants, and accessibility. Click through any name for the full reference.

The catalog below covers every folder in src/ui/. If you add a component, add it here too — and keep src/ui/ the authoritative source if the two ever drift.

Forms & inputs

calendar · checkbox · code-editor (CodeEditor + CodeBlock) · color-input (ColorInput + ColorInputPopover) · currency-input · date-input · editable-cell · field-error · file-upload · form-field · input (TextInput + Select + Textarea) · input-with-icon · number-input · number-stepper · password-input · phone-input · pill-select · radio-group · range-slider · search-dropdown · slider · time-input · toggle-switch

Buttons & actions

back-link · bulk-action-bar · button (Primary/Secondary/Tertiary/Ghost) · button-group · button-icon (deprecated → icon-button variant="filled") · button-with-icon · icon-button · inline-action · link

Navigation

app-sidebar · app-top-bar · breadcrumb · filter-tabs · menu · sidebar-nav-item · sidebar-nav-trigger · tabs · tabs-underline · view-switcher

Overlays & floating layers

dialog · hover-tooltip · listbox (Listbox + MultiListbox) · modal · option-list · popover · toast (Toast + Toaster + the toast.* API)

Feedback & status

badge · banner · chip · empty-state · error-page · loader · progress-bar · tag · tooltip (Tooltip + ContentTooltip)

Layout & structure

card · cluster · detail-section · divider · inline-filter · page-header · page-shell · responsive-grid · section-header · side-flexpane · stack

Data display

avatar · data-table · disclosure · icon · icon-tile · list (List + ListItem) · meta-row · property-field (PropertyField + PropertyGrid) · stat-card · thumbnail · timeline-entry · type-overview-card

Configuration editor

component-row · config-component-row · config-segment-item · explorer-list-item · explorer-section · segment-card · segment-row

Localisation

language-switcher · locale (UxmLocaleProvider + useUxmLocale)

Lifecycle diagrams

lifecycle-connector · lifecycle-drop-slot · lifecycle-edge-label · lifecycle-group-box · lifecycle-minimap · lifecycle-node-card · lifecycle-terminal · lifecycle-zoom-control

Design tokens & MODO theming

The library is built on a two-layer customisation model:

  1. Component-scoped CSS variables (--uxm-{component}-*) — per-instance fine-tuning set inline or via a higher CSS scope. Each component README lists its full surface.
  2. Global design tokens (--color-*) — declared on :root by tokens.css, consumed as fallbacks inside every component-scoped variable. This is the MODO-configurable layer: brand-settings UIs (like MODO's BrandSettingsPreview) edit these tokens centrally and instantly re-tint every consumer.

The canonical token catalogue lives in src/tokens/index.ts as the themeTokens array. Each entry carries:

  • name — display label shown in MODO's brand-settings editor (e.g. "Accent Bold")
  • cssVar — the --color-* identifier consumed by components
  • hex / darkHex — light/dark defaults
  • group — one of surfaces · text · borders · accent · highlights · categories · semantic
  • identity (optional) — promotes the token into the brand editor's Identity section as a top-level knob; reserved for tokens that fan out (the accent base drives the whole ramp)

tokens.css and themeTokens must stay in lockstep — npm run check:drift fails when a --color-* variable exists on one side only.

import { themeTokens, findToken } from '@viax.io/uxm/tokens';

themeTokens.forEach((t) => {
  console.log(`${t.group}/${t.name} → ${t.cssVar} = ${t.hex} (dark: ${t.darkHex})`);
});

const accent = findToken('--color-accent-bold');
//    ^ { name: 'Accent Bold', cssVar: '--color-accent-bold', hex: '#1E7150', … }

Each component README's Design tokens (MODO-configurable) section names every token the component reads, paired with its Group / Name from themeTokens — making it straightforward to look up "if I edit X in MODO, what re-tints?"

Localisation

The library formats; you translate. @viax.io/uxm ships no i18n engine — no message catalogue, no translation runtime — because a primitives library that owns translation forces its choice of engine onto every consuming app. Localisation splits in two:

Copy is yours, and arrives as props. Every user-visible string a component can render or announce has a prop with an English default — clearLabel, closeLabel, emptyState, requiredMessage, placeholder, or a grouped labels={{ … }} object where a component owns several. Translate in your app and pass the result down. A string you cannot reach from props is a bug — open an issue.

Formatting is ours, and follows one locale. Month names, decimal separators, and byte units come from Intl and can't be expressed as a prop string, so the components that need them read a locale instead. Mount UxmLocaleProvider once at the app root:

import { UxmLocaleProvider } from '@viax.io/uxm';

<UxmLocaleProvider locale="uk-UA">
  <App />
</UxmLocaleProvider>;

Calendar, DateInput, EditableCell, CurrencyInput, and FileUpload pick it up automatically. Each still takes a locale prop that wins locally, so one always-USD amount can opt out. Without a provider everything falls back to en-US, exactly as before.

To let the user change the language, use LanguageSwitcher — the one control the library owns. It takes the locale list rather than fetching it, and disappears entirely (no wrapper, no disabled control) when only one locale is configured:

<LanguageSwitcher locales={supportedLocales} value={locale} onChange={setLocale} label={t('language')} />

Labels that interpolate a value take a function, not a prefix — removeFile(name), uploadProgress(percent, size), countLabel(count) — because word order and pluralisation around the value are language-specific:

<FileUpload
  files={files}
  titleText={t('upload.title')}
  labels={{
    uploading: t('upload.busy'),
    removeFile: (name) => t('upload.remove', { name }),
  }}
/>

Previews

Previews live under @viax.io/uxm/previews and are designed for host shells building theme editors. Each preview:

  • Accepts a uniform PreviewProps = { componentId, styles, variants, shell? } signature.
  • Reads styles (slider/colorpicker knob values) and projects them as inline CSS variables on the component instance, exercising the production CSS path so what designers see is what consumers ship.
  • Optionally reads shell (host-provided context: brand, theme, uploadAsset) to integrate with the editor's broader state — e.g. LoginPagePreview and BrandSettingsPreview use shell.uploadAsset for logo uploads.
import { ButtonPreview, type PreviewShellContext } from '@viax.io/uxm/previews';

<ButtonPreview
  componentId="button-primary"
  styles={{ backgroundColor: '#1E7150', borderRadius: 8, paddingX: 20, paddingY: 10 }}
  variants={{ state: 'hover' }}
  shell={shell satisfies PreviewShellContext}
/>;

Tree-shake guarantee: no preview symbols leak into @viax.io/uxm/ui, so consumers that only import primitives never pay for preview code. ESLint enforces it (eslint.config.mjs): no-restricted-imports rejects any *-preview re-export from src/ui/index.ts or a src/ui/*/index.ts, and the import/no-restricted-paths zones pin the layer direction — ui may not import studio or the previews barrel, tokens imports nothing, and lib / hooks / helpers may not import components. After a build, grep "Preview" dist/ui/index.js still returns nothing.

Studio

@viax.io/uxm/studio ships UxmApp, the design workbench MODO serves at /uxm: sidebar, canvas, properties panel and WCAG panel over the same previews. It is decoupled from any backend through the StudioPersistence contract — pick an adapter per host:

  • createHttpPersistence() — reads and writes against a Hono API.
  • createClientPersistence() — live preview plus client-side asset uploads (what the portal/ dev shell uses).
  • createReadOnlyPersistence() — a static, view-only portal.
import '@viax.io/uxm/tokens.css';
import '@viax.io/uxm/ui.css';
import '@viax.io/uxm/studio.css';
import { UxmApp, createHttpPersistence } from '@viax.io/uxm/studio';

<UxmApp persistence={createHttpPersistence('/api/uxm')} />;

@viax.io/uxm/studio/generate-css exposes generateOverridesCss and the CSS sanitizers on their own, so a server can render the saved overrides into a stylesheet without loading the workbench. The studio is the only layer styled with Tailwind; studio.css bundles those utilities and the token declarations but not the atom CSS.

Architecture

┌─────────────────────────────────────────────────┐
│  @viax.io/uxm/studio (+ studio.css)             │  ← the design workbench (MODO /uxm)
│  UxmApp, registry, StudioPersistence adapters   │
└────────────────┬────────────────────────────────┘
                 │ renders
                 ▼
┌─────────────────────────────────────────────────┐
│  @viax.io/uxm/previews                          │  ← host editors (MODO brand-settings)
│  themable, shell-aware preview components       │
└────────────────┬────────────────────────────────┘
                 │ consumes
                 ▼
┌─────────────────────────────────────────────────┐
│  @viax.io/uxm/ui (+ ui.css)                     │  ← application code
│  BEM-classed React components                   │
│  + Icon registry (ICONS, getIcon, …)            │
│  built on @viax.io/uxm/hooks                    │
└────────────────┬────────────────────────────────┘
                 │ reads fallbacks
                 ▼
┌─────────────────────────────────────────────────┐
│  @viax.io/uxm/tokens (+ tokens.css)             │  ← MODO brand-settings edits this
│  themeTokens array + --color-* declarations     │
└─────────────────────────────────────────────────┘

The dependency direction is studio / previews → ui → tokens, enforced by the ESLint layer zones. Every layer is independently importable; every layer has its own type declarations and own CSS bundle.

Build & develop

npm install
npm run dev:modo     # Vite dev server for the studio portal — fastest loop, no build needed
npm run build:modo   # Vite production build of the portal
npm run build        # full dist/ (see the pipeline below)
npm run typecheck    # tsc --noEmit for src + portal
npm run lint         # eslint .
npm run lint:fix     # eslint . --fix
npm run check:drift  # state-var drift, type-scale and tokens.css ↔ themeTokens parity gates
npm test             # Vitest smoke suite (tests/), jsdom — `npm run test:watch` while iterating
npm run test:coverage # coverage summary for orientation only — no thresholds, not a gate
npm run dev          # tsup --watch — only for local linked dev against a consumer

npm run dev:modo is the fastest way to see a UI change. The portal pulls src/ui/**/*.scss through import.meta.glob, so components render with real CSS over HMR without building dist/ first.

Tests are a small Vitest smoke suite, not coverage. npm test (tests/*.test.tsx, jsdom + Testing Library + axe-core) pins the contracts nothing else can see: focus trap / Escape / outside-click on the floating layers, ARIA wiring on the pickers, the CSS sanitizers and the overrides generator, and an axe pass over a form and a dialog. It also pins the public surface: the export names of every subpath, every --uxm-* variable the stylesheets read, and that dist/ loads as ESM and CJS (snapshots in tests/__snapshots__/; a minus line in the diff is a removed export or knob, i.e. a MAJOR). See tests/README.md. Add a test when you touch one of those contracts or fix a behavioural bug; do not chase coverage on presentational atoms. For a visual change "tests pass" proves nothing — the verification is typecheck, lint, build and the portal.

CI (.github/workflows/ci.yml) runs lint, typecheck, check:drift, build and test:ci on every pull request, plus an npm audit job that fails at critical.

npm run build chains five steps, and the order is deliberate:

  1. tsup — ESM + CJS JS with bundle: false and dts: false (one output per source file). Its onSuccess hook compiles src/ui/**/*.scss → sibling dist/ui/**/*.css, then copies styles.css and tokens/index.css.
  2. build:studio-css — Tailwind CLI compiles src/studio/studio.css.
  3. tsc -p tsconfig.build.json — a separate declaration pass. DTS is off in tsup because its single-worker DTS pass runs out of memory with 100+ unbundled entries.
  4. tsc-alias — rewrites @/* path aliases to relative paths across dist (JS + CJS + .d.ts).
  5. scripts/fix-cjs-requires.mjs — post-processes CJS require() calls.

Skipping the separate tsc pass or the alias rewrite yields a dist with either no types or unresolved @/ imports.

Output layout under dist/:

dist/
├── index.{js,cjs,d.ts}
├── ui/
│   ├── index.{js,cjs,d.ts}
│   ├── styles.css
│   └── …per-component files…
├── tokens/
│   ├── index.{js,cjs,d.ts}
│   └── index.css
├── hooks/
│   └── index.{js,cjs,d.ts}
├── previews/
│   ├── index.{js,cjs,d.ts}
│   └── …per-preview files…
└── studio/
    ├── index.{js,cjs,d.ts}
    ├── studio.css
    └── persistence/generate-css.{js,cjs,d.ts}

Local linked development (e.g. modo)

From the consumer project (apps/modo):

npm install file:../../uxm          # adjust path

Run npm run dev here to keep dist/ fresh; the consumer's dev server picks up changes after each recompile.

To iterate on the components themselves rather than on the integration, prefer npm run dev:modo — it needs no build step at all.

Releases

Releases are fully automated — never publish by hand. Merging to master runs semantic-release in CI, which derives the version bump from the Conventional Commit types, writes CHANGELOG.md, stamps the AI-skill version markers, publishes to npm, and creates the GitHub release.

This makes commit types carry semver meaning: fix → PATCH, feat → MINOR, a BREAKING CHANGE: footer → MAJOR. Running npm version or npm publish locally would desynchronise the tags from what CI has already released.

Contributing

  • Add a component: create src/ui/{name}/ with {name}.tsx, {name}.scss, index.ts. Re-export from both the folder index.ts and src/ui/index.ts. Add the compiled stylesheet to src/ui/styles.css as @import "./{name}/{name}.css"; in cascade order — the aggregator is hand-written, and a component whose @import is missing ships with no CSS. Add a README.md mirroring button/README.md (simple) or data-table/README.md (complex).
  • Update the AI skill in the same change: skills/viax-uxm/ needs a catalog row, a cheatsheet row, and a bullet under ### Unreleased in SKILL.md. Leave the version and component-count markers alone — CI stamps those at release via scripts/stamp-skill-version.mjs.
  • Add a preview: create {name}-preview.tsx next to the component — previews live beside their component, not in src/previews/. Use PreviewProps and project knob values as inline CSS vars so production CSS rules paint them. Never re-export a preview from a src/ui/ barrel (see the tree-shake guarantee above).
  • Add a token: add an entry to themeTokens in src/tokens/index.ts and declare the --color-* variable in src/tokens/index.cssnpm run check:drift fails if only one side changes. Reference it from component SCSS via var(--uxm-foo-bar, var(--color-new-token)) — never hardcode a colour, spacing, or radius.
  • Commits follow conventional-commit format (commitizen + commitlint enforced via husky); npm run commit walks you through it. Commit types drive the released version — see Releases.
  • Run npm run lint && npm run typecheck && npm run check:drift && npm test && npm run build before opening a pull request — the same gates CI runs.

License

MIT © viax.io