Repository navigation
Distribution
How Astryx packages are structured, built, versioned, and shipped. Covers the monorepo layout, what consumers install, what's in each bundle, and how theming works across the dist boundary.
Astryx is a pnpm workspaces monorepo. The published packages (all versioned together as a Changesets fixed group) are @astryxdesign/core, @astryxdesign/cli, @astryxdesign/build, and the public themes (@astryxdesign/theme-neutral, @astryxdesign/theme-butter, @astryxdesign/theme-chocolate, @astryxdesign/theme-gothic, @astryxdesign/theme-matcha, @astryxdesign/theme-stone, @astryxdesign/theme-y2k):
| Package | Purpose |
|---|---|
@astryxdesign/core |
Components, hooks, utilities, tokens, CSS |
@astryxdesign/cli |
CLI tooling (npx astryx component, npx astryx theme build, etc.) |
@astryxdesign/build |
Shared StyleX build plugins (Babel, PostCSS, Vite, Next) |
@astryxdesign/theme-neutral |
Default theme (ships with core) |
@astryxdesign/theme-* |
Additional published themes (butter, chocolate, gothic, matcha, stone, y2k) |
All published packages are versioned together — a single version bump applies to all. See the Release Process for the fixed-group mechanics. (@astryxdesign/lab and @astryxdesign/vega are private and not published.)
packages/
├── core/ → @astryxdesign/core
├── cli/ → @astryxdesign/cli
└── themes/
├── neutral/ → @astryxdesign/theme-neutral
└── butter/ → @astryxdesign/theme-butter
@astryxdesign/core ships both source and dist. The files field includes dist/, src/, and astryx.md:
The primary consumption path. Built by pnpm build (Babel + tsc + StyleX compilation). The package is "type": "module" — the output is ESM only, so there is no .mjs/.d.mts pair:
dist/
├── index.js # ESM entry
├── index.d.ts # Type declarations
├── astryx.css # Pre-compiled StyleX → atomic CSS (@layer astryx-base)
├── astryx.umd.js # Single-file UMD bundle for the CDN path (window.Astryx)
├── Button/
│ ├── index.js # Per-component ESM entry
│ └── index.d.ts
├── ... # One directory per component
└── theme/
└── ... # Token definitions, theme utilities
Each component is a separate entry point for tree-shaking:
// Barrel import (entire library)
import { Button, Card } from '@astryxdesign/core';
// Direct import (tree-shakeable)
import { Button } from '@astryxdesign/core/Button';Shipped for transparency and advanced use cases:
-
Swizzle:
npx astryx swizzle Buttoncopies fromsrc/so consumers get the real implementation - Source maps: IDEs can jump to source for debugging
- StyleX consumers: Apps that run StyleX in their own build can compile from source for optimal atomic dedup
| File | What it contains | When to import |
|---|---|---|
@astryxdesign/core/astryx.css |
All component styles, pre-compiled from StyleX, wrapped in @layer astryx-base
|
Always — this is the main CSS entry point |
@astryxdesign/core/reset.css |
Minimal CSS reset (box-sizing, margin reset) | Optional — if you don't have your own reset |
@astryxdesign/core/tailwind-theme.css |
Astryx tokens mapped to Tailwind theme variables | Optional — if you consume Astryx tokens from Tailwind |
A machine-readable summary of all components, props, and examples. Used by the CLI (astryx component --brief-all) and AI assistants as context.
// 1. Import CSS (once, in your app root)
import '@astryxdesign/core/astryx.css';
// 2. Use components
import { Button } from '@astryxdesign/core';
<Button variant="primary" className="mt-4">Save</Button>No Babel plugin. No PostCSS config. The CSS is pre-compiled — consumers just import the file.
The @layer astryx-base wrapper means consumer styles (Tailwind, CSS modules, plain CSS) naturally win over Astryx defaults in specificity, without needing !important.
import '@astryxdesign/core/astryx.css';
import { Theme } from '@astryxdesign/core';
import { neutral } from '@astryxdesign/theme-neutral';
<Theme theme={neutral}>
<App />
</Theme>Themes override CSS custom properties. Theme is a pure CSS variable setter — no runtime style computation. See Why StyleX for the full theming story.
Consumers who use StyleX in their own apps can compile Astryx from source for optimal atomic dedup:
// postcss.config.mjs or vite/webpack config
stylexPlugin({
include: [
'src/**/*.{ts,tsx}',
'node_modules/@astryxdesign/core/src/**/*', // Compile from source
],
});This is the power-user path. Most consumers should use the pre-compiled CSS.
Every component renders stable, predictable class names for CSS-based theming and overrides:
<!-- Button variant="primary" size="md" -->
<button class="astryx-button primary md xfa3kd x1jk2e ...">Save</button>The pattern is astryx-{component} plus variant values as additional classes. The xfa3kd-style classes are internal StyleX atomics — don't target those.
Stable class names enable:
- CSS-based theme overrides via
defineTheme()component styles - Consumer CSS targeting (
.astryx-button.primary { ... }) - Test selectors (
[class~="astryx-button"])
Theme packages (@astryxdesign/theme-neutral, @astryxdesign/theme-butter) ship the same dual structure:
dist/
├── index.js # Theme definition (JS)
├── index.mjs
├── index.d.ts
└── index.d.mts
src/
└── index.ts # Theme source
Themes are JavaScript objects created with defineTheme(). They can optionally be pre-compiled to static CSS via npx astryx theme build for zero-runtime theming.
@astryxdesign/cli ships executable scripts and templates:
bin/
├── astryx.mjs # CLI entry point
src/
├── commands/ # Command implementations
templates/
├── ... # Scaffolding templates
docs/
├── ... # Generated doc data
Key commands:
-
npx astryx component <Name>— View component docs -
npx astryx component --brief-all— All components in brief format (for AI context) -
npx astryx theme build <file>— Pre-compile a theme to static CSS -
npx astryx init— Scaffold a new project
All packages are versioned together on the same number, with exact internal package pins. This keeps dependency alignment simple. Read npm latest or the newest GitHub Release for the current published version rather than copying a number into this page.
While packages are 0.x, the stable breaking tier is the next minor and compatible work ships in a patch. Main's fixed-group version is the owner-declared next plan: a [breaking] Changeset is admitted only while that declaration is the next minor, and a Changeset never chooses or bumps the version. Releases cut from an exact green main commit, consolidate on a marked release branch, publish and tag that branch, squash its exact outputs back to main, then declare the next plan separately—patch successor by default. The Release Process is authoritative.
Stable packages publish from the immutable release tag through .github/workflows/release.yml with npm trusted publishing (OIDC) and provenance. Merging to main never publishes a stable version. Eligible main pushes publish canaries only while main's declaration is strictly above the newest stable tag; trusted merge-back equality publishes nothing.
pnpm build builds each package in dependency order. For core, lab, charts and richtext that is:
-
Transpilation — Babel compiles
.ts/.tsx→ ESM.js, mirroring the source tree intodist/ -
Declarations —
tsc --project tsconfig.build.jsonemits.d.ts -
StyleX extraction — the StyleX Babel plugin extracts every
stylex.create()call → atomic CSS -
CSS bundling —
scripts/build-css.mjsconcatenates the extracted CSS intoastryx.css, wrapped in@layer astryx-base -
UMD bundle (core only) —
scripts/build-umd.mjsrollsdist/index.jsintoastryx.umd.jsfor the CDN path -
Per-component entries — every component directory is its own subpath entry point for tree-shaking, via the generated
exportsmap
@astryxdesign/vega is the one package still built with tsup.
The sync-exports.js script auto-generates package.json exports from the source directory structure. Manual export additions go in STATIC_EXPORTS in that script (otherwise they get clobbered on the next sync).
Astryx originally shipped source-only — consumers had to run StyleX's Babel + PostCSS plugins to compile component styles. This meant every consuming app needed StyleX build configuration, which was a significant adoption barrier.
The shift to pre-compiled CSS (issue #506) decoupled the internal authoring tool from the consumer experience. Astryx still uses StyleX internally for all the reasons documented in Why StyleX — but consumers never need to know or care. They get a CSS file and typed React components.
- Why StyleX — Why StyleX is the right internal authoring tool
- Release Process — Declared versions, release branches, publishing, merge-back, and next-plan workflow
- System Architecture — Overall system design
-
API Conventions — The three styling escape hatches (
className,style,xstyle)
Start here Astryx Philosophy Contributing with AI Assistants Contributing
Architecture System Architecture Architecture Cheat Sheet Theming Infrastructure Distribution
Building a component Component Lifecycle Component Authoring Guide API Conventions Design Conventions
Quality Component Audit Rubric Accessibility Checklist AST-009 assistive-technology verification
Operations Release Process Night Watch Overview