Skip to content

Distribution

Cindy Zhang edited this page Jun 23, 2026 · 1 revision

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.


Packages

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.)

Package Scopes

packages/
├── core/           → @xds/core
├── cli/            → @xds/cli
└── themes/
    ├── default/    → @xds/theme-default
    └── neutral/    → @xds/theme-neutral

What Ships in @xds/core

@xds/core ships both source and dist. The files field includes dist/, src/, and xds.md:

dist/ — Pre-compiled output

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';

src/ — Source code

Shipped for transparency and advanced use cases:

  • Swizzle: npx xds swizzle Button copies from src/ 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

CSS files

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

xds.md

A machine-readable summary of all components, props, and examples. Used by the CLI (xds component --brief-all) and AI assistants as context.


Consumer Setup

Minimal (no StyleX required)

// 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.

With a theme

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.

With StyleX (optional, for advanced consumers)

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.


Stable Class Names

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

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.


CLI Package

@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

Versioning

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.

Pre-1.0

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.

Registry

Packages are published to the registry. CI publishes automatically when a version bump merges to main — no manual publish step needed.


Build Pipeline

pnpm build runs tsup across all packages:

  1. TypeScript compilation — tsup compiles .tsx.js + .mjs + .d.ts + .d.mts
  2. StyleX extraction — StyleX plugin extracts all stylex.create() calls → atomic CSS
  3. CSS bundling — Extracted CSS is concatenated into xds.css, wrapped in @layer xds
  4. 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).


What Changed

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.


Related

Clone this wiki locally