@epilot/zenith-react
Accessible React components for Zenith, styled with Griffel and designed for independent micro-frontend delivery.
Install
npm install @epilot/zenith-react
@epilot/zenith-runtime installs with it; do not override it unless the host
serves the identical /v1 ABI. An app whose bundler config externalizes
@epilot/zenith-runtime/v1 gets the runtime from its host (root-config in the
portal): keep that external, and do not add, pin or bundle a runtime of its own.
Tokens are built in, so there is no third package. React and React DOM 18 or
newer are peers.
Zenith is an ordinary per-application dependency, not something loaded through a shared component-library import map.
Render
import { Button } from "@epilot/zenith-react"
export const Example = () => <Button variant="primary">Continue</Button>
There is no setup step: components install their token stylesheet as they render. To install it at startup instead:
import { prepareCoreTokens } from "@epilot/zenith-react/delivery"
await prepareCoreTokens()
Optional; no network.
Application styles that need a design token import a named binding from
@epilot/zenith-react/tokens. Do not add the token CSS as a second stylesheet,
and do not write generated --zenith-<hash>-* names by hand.
Drag-and-drop reordering for List is a separate entry, so apps that render a plain
List do not bundle dnd-kit:
import { ListItem } from "@epilot/zenith-react"
import { SortableList } from "@epilot/zenith-react/list/sortable"
Zenith ships no .css files, and its exports map has no stylesheet entry:
there is nothing to import. Porting from Volt, drop its style.css,
preflight.css and font.css imports and add no replacement — the token CSS
above is the only stylesheet, and the package installs it itself.
Use AppearanceBoundary for a scoped appearance that must also reach portalled
content — a theme or surface attribute on a subtree does not follow a Dialog,
Popover, Select, Tooltip or menu into document.body.
Lay your own screens out
Zenith ships components, not a layout system: there is no Grid, Stack,
Box or Spacer, and there never will be. The space between components is
yours.
makeZenithStyles is public API for that purpose, exported from the
package root alongside mergeClasses and shorthands. It is the same Griffel
API Zenith's own components use, so your layout ships its CSS with your
components and depends on no stylesheet loaded from somewhere else.
import { makeZenithStyles, shorthands } from "@epilot/zenith-react"
import { spacingLayout1, containerXl } from "@epilot/zenith-react/tokens"
const useStyles = makeZenithStyles({
page: {
display: "flex",
flexDirection: "column",
maxWidth: containerXl,
...shorthands.gap(spacingLayout1),
},
})
Take every value from @epilot/zenith-react/tokens. A literal hex, a utility
class from another stylesheet, or a raw pixel value is how a screen drifts away
from the system and stops following a token change.
A utility-class framework works too, but understand what you are depending on: those classes come from a stylesheet your component does not ship, so what your screen looks like is decided by whichever version of that stylesheet is deployed beside it.
Validate the host
- Exercise the application under its real content-security policy.
- Open and close every portalled component.
- Test keyboard order, focus restoration, screen-reader names and error states.
- Test two independently bundled Zenith consumers on one page, where that is a supported host scenario.
- Measure the actual entry and lazy chunks.
Migrate deliberately
Move one surface at a time. Do not reuse custom-property names, styling selectors or internal DOM structure from another component system. Where APIs differ, adapt at the application boundary and remove the adapter once the surface is fully on Zenith.
Connect an MCP client
A read-only Model Context Protocol (MCP) server for Zenith runs dynamically on
the documentation site at /api/mcp. It serves component APIs, design tokens,
and design guidelines directly to AI coding tools.
The catalogue is built from the commit the current latest release was
published from, so it describes exactly what npm install @epilot/zenith-react
installs — not unreleased work on main.
Point an MCP client (such as Cursor or Claude) at the Streamable HTTP endpoint:
{
"mcpServers": {
"zenith": {
"url": "https://zenith.epilot.cloud/api/mcp"
}
}
}
All queries and tools are strictly read-only and require no credentials.
Agents
A Zenith skill teaches a coding agent to use the MCP above; the "Coding agents" documentation page lists its modes. In Claude Code, one plugin install adds the skill and the MCP connection:
claude plugin marketplace add https://gitlab.com/e-pilot/product/frontend/zenith.git
claude plugin install zenith@zenith
Then run /zenith:build <screen> or /zenith:review. For Codex, Cursor,
Copilot and OpenCode, install the skill with
npx skills add https://gitlab.com/e-pilot/product/frontend/zenith --skill zenith
and add the MCP endpoint to the agent's configuration. The "Coding agents"
page in the documentation's setup section has the steps for each agent.
Which version you get
npm install @epilot/zenith-react resolves the latest dist-tag, which is
moved by hand; the automatic next builds never move latest.
Unproven builds stay on next with a prerelease version:
npm install @epilot/zenith-react@next
These are opt-in twice over — you name the tag, and a range like ^0.1.0 never
resolves a prerelease. Pin an exact version if you adopt one.
Upgrading to 0.2.0
0.2.0 moves every family from Radix to Base UI and serves the primitives from
the runtime. The "Upgrade to 0.2.0" page in the documentation's setup section
has the steps in order: the host serves the new runtime first (with react and
react-dom external), bump the packages, run the radix-to-base-ui codemod
(its output comes through your Prettier), run the migration sweep, add the test
setup, check the attribute renames, and keep the two rules for pages that still
mix Volt 1.x and Zenith.
Reference
The package is ESM-only and tree-shakeable. Resolved literal token values are in
@epilot/zenith-react/tokens.json. For tests, @epilot/zenith-react/testing/setup.cjs
(a PointerEvent for jsdom 26 and older, Vitest and Jest) and an optional Jest helper in
@epilot/zenith-react/testing/jest-preset.cjs.
Component reference, guidelines and examples are on the documentation site — the same catalogue the MCP endpoint above serves. Development and contribution rules live in the repository.