@astryx-svelte/core

Svelte 5 components for Astryx, Meta's open source design system.
View Components

Styles: import a stylesheet, or compile it yourself

Components are styled with StyleX, and there are two ways to get their CSS. Pick one.

1. Import the pre-built stylesheet — no bundler configuration at all

The same thing Astryx's own package does, and what most projects should do:
ts
import '@astryx-svelte/core/base.css';
import '@astryx-svelte/core/astryx.css';
import '@astryx-svelte/theme-neutral/theme.css';
That is the entire setup. dist ships compiled, so nothing needs to run StyleX.
astryx.css carries every component's styles — 131 kB, one @layer astryx-base, checked against @astryx-svelte/core's published stylesheet on every test run: 1,463 shared atomic classes, zero differing rules. The cost is that you ship every component's CSS whether you use it or not.

2. Compile it yourself, from source

Every subpath also publishes a source condition pointing at the TypeScript. Ask your bundler for it and compile the package yourself, and you emit only the atomic classes your app actually reaches — smaller than the whole stylesheet, at the cost of configuration:
ts
// vite.config.ts — alongside the preset below
export default defineConfig({
plugins: [astryx(), sveltekit()],
resolve: { conditions: ['source'] }
});
This is also the property that makes the port verifiable: the compiler derives class names from the source, so authoring against Astryx's token references emits byte-identical atomic CSS.
Getting this route wrong fails without an error: the components render, and they render unstyled. If that happens, run pnpm exec astryx-svelte doctor — it accepts either route, and names whichever piece is missing.
For Vite (and therefore SvelteKit), use the preset:
ts
// vite.config.ts
import { astryx } from '@astryx-svelte/core/vite';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [astryx(), sveltekit()]
});
That is the whole setup. astryx() takes include (further packages that ship uncompiled .stylex.js), rootDir (the StyleX module-resolution root — a monorepo importing .stylex modules across packages wants the workspace root, not the default process.cwd()), and dev.
<details> <summary>What the preset does, and why it is three things rather than one</summary>
The plugin compiles the styles. The other two exist because Vite has two separate ways to route a dependency around its own plugin pipeline, and each defeats the compiler on its own:
ts
export default defineConfig({
plugins: [
stylex({
dev: process.env.NODE_ENV !== 'production',
runtimeInjection: false,
treeshakeCompensation: true,
useCSSLayers: true,
// Without explicit targets, lightningcss lowers `light-dark()` to a pair of
// `var(--lightningcss-*)` references that resolve to nothing, and every
// colour token silently goes empty.
lightningcssOptions: {
targets: { chrome: 123 << 16, firefox: 120 << 16, safari: (17 << 16) | (5 << 8) }
},
unstable_moduleResolution: { type: 'commonJS', rootDir: import.meta.dirname }
}),
sveltekit()
],
// Vite's dev-time pre-bundler runs esbuild outside the plugin pipeline, so anything
// it optimises never reaches the StyleX transform and `stylex.create` survives into
// the browser as a runtime no-op.
optimizeDeps: { exclude: ['@astryx-svelte/core'] },
// Same reasoning for the server build: an externalised dependency is imported from
// node_modules at runtime rather than transformed.
ssr: { noExternal: ['@astryx-svelte/core'] }
});
Written by hand, these options must match this package's own build exactly, or the atomic CSS you compile differs from the output verified against upstream. The preset is the only form in which "exactly" stays true without anyone maintaining it.
</details>
Both optimizeDeps.exclude and ssr.noExternal fail silently when missing. If a page renders with the right markup and none of the styling, check those two first.

Component Docs

Look up any component's full API — props, variants, examples, best practices, and theming — through the CLI:
bash
pnpm exec astryx-svelte component --list # every component, grouped
pnpm exec astryx-svelte component Button # full docs for one component
pnpm exec astryx-svelte util --list # the runes-based composables
pnpm exec astryx-svelte search button # components, utils, docs and templates at once
Everything is exported from the package root. There are no per-component subpath entrypoints — Astryx publishes one per component and this port publishes none, so @astryx-svelte/core is where Button lives and the barrel is tree-shaken by your bundler. The subpaths that do exist are for non-component surfaces:
SubpathWhat it is
@astryx-svelte/coreEvery component, every util, every props type
@astryx-svelte/core/themeTheme, useTheme, the token vars
@astryx-svelte/core/theme/definedefineTheme and its types, for authoring a theme
@astryx-svelte/core/theme/syntaxSyntax-highlighting themes for CodeBlock
@astryx-svelte/core/hooksThe composables, without the components
@astryx-svelte/core/utilsFramework-free helpers
@astryx-svelte/core/namingThe class-name helpers themes and integrations build against
@astryx-svelte/core/i18nThe message catalog runtime
@astryx-svelte/core/locales/*The shipped catalogs (en, fr-FR, pseudo)
@astryx-svelte/core/viteThe astryx() Vite preset, for compiling from source
@astryx-svelte/core/base.cssLayer order and color-scheme — always needed
@astryx-svelte/core/astryx.cssEvery component's styles, pre-built — see above

Page Layouts

Building a full page? Astryx's advice is to start from a template rather than composing from scratch, and the CLI has the command:
bash
pnpm exec astryx-svelte template --list # browse page and block templates
pnpm exec astryx-svelte template <id> --skeleton
43 page templates ship today — dashboards, chat, settings, auth, pricing and more — and every id matches upstream's. template <id> --skeleton prints the structure; without it you get the whole page.
Upstream also ships ~614 block templates, the smaller compositions you drop inside a page. Those are React source and are still being ported, so --list shows the page set only. Templates contributed by an integration or by any other installed package are discovered and injected the same way.
PackageDescription
@astryx-svelte/cliComponent docs, reference topics, themes, scaffolding, codemods
@astryx-svelte/theme-neutralThe default Astryx look — muted and minimal
@astryx-svelte/theme-butterWarm creamy yellows with a friendly blue accent
@astryx-svelte/theme-chocolateRich cozy browns with Fraunces headings
@astryx-svelte/theme-gothicDeep blue-grays and a display serif; dark-only
@astryx-svelte/theme-matchaEarthy greens, calm and organic
@astryx-svelte/theme-stoneWarm stone and slate, understated
@astryx-svelte/theme-y2kHot pinks, lime greens, and Poppins
@astryx-svelte/theme-liquid-glassmacOS translucent materials; no upstream counterpart