How Enma is packaged
A design system is a few layers that build on each other. Enma ships each layer as its own small package, plus one package that bundles them all. This page explains the layers, so you can pick what to install with confidence.
The layers
Tokens are the decisions, stored as data: every color for every theme and mode, the type scale, spacing, radius, shadows and motion. They live in tokens.json and know nothing about HTML or React. @enma/tokens ships that file plus versions of it each platform can read: CSS variables, typed TypeScript, plain objects for React Native, a Tailwind mapping and Figma variables.
CSS turns tokens into a styled system for the browser. @enma/css maps each theme onto the shared --e-* roles (so --e-accent means "this theme's accent") and styles every component with e- classes: e-btn, e-card, e-stat. Any web page can use it with no JavaScript at all.
Components add structure and behaviour. @enma/react renders the right markup and classes, counts numbers up on first view, moves focus with the arrow keys and announces values to screen readers. @enma/native does the same for React Native, where CSS doesn't exist, using the native theme objects from the tokens.
@enma/tokens ──▶ @enma/css ──▶ @enma/react
│
└────────────────────────▶ @enma/native
enma (re-exports all four through subpaths)
Why separate packages
- Each project pays only for what it uses. A Chrome extension written in plain TypeScript needs CSS, not React. A Tailwind setup needs only the values.
- One source of truth. Components never copy styles or values; they import them. Change a token and everything downstream follows.
- Platforms stay honest. React Native can't read CSS, so it depends on tokens directly instead of a web stylesheet.
This is the same shape mature systems use: GitHub's Primer has @primer/primitives, @primer/css and @primer/react; Adobe's Spectrum offers individual packages and an all-in-one package.
When to install the umbrella
Install enma for any new project. One install, everything available through subpaths:
| Import | Comes from |
|---|---|
enma/css | @enma/css |
enma/fonts/<theme> | @enma/css/fonts/<theme>.css |
enma/tailwind.css | @enma/tokens/tailwind.css |
enma/react | @enma/react |
enma/native | @enma/native |
enma/tokens | @enma/tokens |
The umbrella has no code of its own; it only re-exports. There is exactly one copy of every component and stylesheet, so nothing is bundled twice. React Native's dependencies are optional peers, so a web project never installs them.
Install a single package when a project is narrow on purpose: values only for a script or a Figma export, CSS only for plain HTML, an extension or a webview.
How versions move
All five packages share one version number. @enma/react@0.4.0 always pairs with @enma/tokens@0.4.0 and enma@0.4.0, so there is never a compatibility table to check. When one package changes, all of them get the new version.
- Patch (0.4.1): a bug fix with no visual or API change.
- Minor (0.5.0): a visual token change, a new component, prop or theme. Read the upgrade note in the changelog when anything changes visually.
- Major (1.0.0): an API break or a removed component, with a migration note.
Fonts
Fonts are not inside enma/css, because each app should load only its theme's faces. Import one line, for example enma/fonts/ember. The files are woff2, self-hosted from the package (no Google Fonts request), with font-display: swap. Your bundler copies them into the build.
Why enma@npm:enma-design in the install command
Enma is public on npm. The name enma was already taken there, so the umbrella is published as enma-design, and the install command aliases it back to enma. That keeps every import short (enma/react, enma/css) and the same in every project. The @enma scope was taken too, so the focused packages are published as @enma-design/tokens, @enma-design/css, @enma-design/react and @enma-design/native, and installed with the same kind of alias (@enma/css@npm:@enma-design/css), so imports read @enma/css.