-
Notifications
You must be signed in to change notification settings - Fork 27
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 @xds/core, @xds/cli, @xds/build, and the public themes (@xds/theme-default, @xds/theme-neutral, @xds/theme-butter, @xds/theme-chocolate, @xds/theme-daily, @xds/theme-gothic, @xds/theme-matcha, @xds/theme-stone, @xds/theme-y2k):
| Package | Purpose | Current Version |
|---|---|---|
@xds/core |
Components, hooks, utilities, tokens, CSS | 0.0.15 |
@xds/cli |
CLI tooling (npx xds component, npx xds theme build, etc.) |
0.0.15 |
@xds/build |
Shared build tooling (tsup/StyleX config) | 0.0.15 |
@xds/theme-default |
Default theme (ships with core) | 0.0.15 |
@xds/theme-neutral |
Neutral theme variant | 0.0.15 |
@xds/theme-* |
Additional published themes (butter, chocolate, daily, gothic, matcha, stone, y2k) | 0.0.15 |
All published packages are versioned together — a single version bump applies to all. See the Release Process for the fixed-group mechanics. (@xds/lab, @xds/vega, and @xds/theme-brutalist are private and not published.)
packages/
├── core/ → @xds/core
├── cli/ → @xds/cli
└── themes/
├── default/ → @xds/theme-default
└── neutral/ → @xds/theme-neutral
@xds/core ships both source and dist. The files field includes dist/, src/, and xds.md:
The primary consumption path. Built by pnpm build (tsup + StyleX compilation):
dist/
├── index.js # CJS bundle
├── index.mjs # ESM bundle
├── index.d.ts # CJS type declarations
├── index.d.mts # ESM type declarations
├── xds.css # Pre-compiled StyleX → atomic CSS (@layer xds)
├── index.css # Component CSS (imported by JS)
├── Button/
│ ├── index.js # Per-component CJS entry
│ ├── index.mjs # Per-component ESM entry
│ ├── index.d.ts
│ └── index.d.mts
├── ... # 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 '@xds/core';
// Direct import (tree-shakeable)
import { Button } from '@xds/core/Button';Shipped for transparency and advanced use cases:
-
Swizzle:
npx xds 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 |
|---|---|---|
@xds/core/xds.css |
All component styles, pre-compiled from StyleX, wrapped in @layer xds
|
Always — this is the main CSS entry point |
@xds/core/reset.css |
Minimal CSS reset (box-sizing, margin reset) | Optional — if you don't have your own reset |
@xds/core/typography.css |
Typography defaults (font stacks, base sizes) | Optional — if you want Astryx typography defaults |
A machine-readable summary of all components, props, and examples. Used by the CLI (xds component --brief-all) and AI assistants as context.
// 1. Import CSS (once, in your app root)
import '@xds/core/xds.css';
// 2. Use components
import { Button } from '@xds/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 xds wrapper means consumer styles (Tailwind, CSS modules, plain CSS) naturally win over Astryx defaults in specificity, without needing !important.
import '@xds/core/xds.css';
import { Theme } from '@xds/core';
import { neutral } from '@xds/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/@xds/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 (@xds/theme-default, @xds/theme-neutral) 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 xds theme build for zero-runtime theming.
@xds/cli ships executable scripts and templates:
bin/
├── xds.mjs # CLI entry point
src/
├── commands/ # Command implementations
templates/
├── ... # Scaffolding templates
docs/
├── ... # Generated doc data
Key commands:
-
npx xds component <Name>— View component docs -
npx xds component --brief-all— All components in brief format (for AI context) -
npx xds theme build— Pre-compile a theme to static CSS -
npx xds create-component <Name>— Scaffold a new component
All packages are versioned together on the same number. This keeps dependency alignment simple — if you're on @xds/core@0.0.15, you use @xds/theme-default@0.0.15.
Current versions are 0.0.x. Breaking changes can happen between patches during pre-1.0. The Release Process documents the full workflow: changesets, version bumps, build, publish, tag.
Packages are published to the registry. CI publishes automatically when a version bump merges to main — no manual publish step needed.
pnpm build runs tsup across all packages:
-
TypeScript compilation — tsup compiles
.tsx→.js+.mjs+.d.ts+.d.mts -
StyleX extraction — StyleX plugin extracts all
stylex.create()calls → atomic CSS -
CSS bundling — Extracted CSS is concatenated into
xds.css, wrapped in@layer xds - Per-component entries — Each component directory gets its own entry point for tree-shaking
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 — Version bumps, changesets, publishing workflow
- System Architecture — Overall system design
-
API Conventions — The three styling escape hatches (
className,style,xstyle)