How to Customize Theme Colors and Tokens in the Astryx Design System

You customize theme colors and tokens in Astryx by wrapping your application in a ThemeProvider that accepts a tokens object mapping CSS custom properties to values, or by generating a new theme via the xds theme add CLI command.

Astryx is Meta's open-source design system built on StyleX that uses a token-driven architecture for theming. Whether you need to override a few colors at runtime or create an entirely new brand theme, the system provides both programmatic APIs and CLI tooling to customize theme colors and tokens without modifying component source code.

Understanding the Token Architecture

Astryx themes are built on CSS custom properties (design tokens) defined in TypeScript declaration files. Each theme lives in packages/themes/<theme-name>/theme.css.d.ts and exports a complete map of available tokens.

For example, the y2k theme defines its contract in packages/themes/y2k/theme.css.d.ts. This file lists every available CSS variable, such as --color-primary, --radius-sm, and --spacing-lg, that components can consume. The actual values are injected at runtime by the ThemeProvider component exported from @astryxdesign/core.

The utility function themeProps() in packages/core/src/utils/themeProps.ts generates the stable astryx-<component> class names and mirrors visual props as data-* attributes, ensuring components maintain consistent styling hooks regardless of the active theme.

Overriding Tokens at Runtime with ThemeProvider

To customize theme colors without creating a new theme package, import the ThemeProvider from @astryxdesign/core and pass a tokens object that overrides specific CSS variables.

import { ThemeProvider } from '@astryxdesign/core';

const customTokens = {
  '--color-primary': '#0066ff',
  '--color-background': '#f4f7fb',
  '--radius-sm': '4px',
};

function App() {
  return (
    <ThemeProvider tokens={customTokens}>
      {/* Your Astryx components here */}
    </ThemeProvider>
  );
}

When the provider mounts, it injects a <style> block defining these custom properties on the root element. Every component referencing var(--color-primary) automatically picks up the new value. The provider merges your custom tokens with the base theme defaults, so you only need to specify values you want to change.

Creating a New Theme with the CLI

For complete theme customization, use the Astryx CLI to scaffold a new theme package. The CLI documentation in packages/cli/docs/theme.doc.mjs defines the xds theme commands.

Run the following to generate a fresh theme scaffold:

xds theme add my-brand

This creates:

Edit the generated .d.ts file to set your default token values:

/* packages/themes/my-brand/theme.css.d.ts */
export const vars = {
  '--color-primary': '#ff5733',
  '--color-secondary': '#33c1ff',
  '--spacing-lg': '24px',
};

Import these tokens into your application and pass them to ThemeProvider to activate your custom theme.

How Components Consume Tokens

Astryx components consume tokens through StyleX stylesheets that reference CSS variables. For example, the Button component in packages/core/src/Button/Button.stylex.ts defines its base styles as:

import { stylex } from '@stylex/css';

export const button = stylex.create({
  base: {
    backgroundColor: 'var(--color-primary)',
    borderRadius: 'var(--radius-sm)',
  },
});

Because components use var(--token-name) syntax, changing the theme at the provider level instantly updates the visual appearance without requiring code changes in individual components. The themeProps() utility ensures each component receives the correct astryx-* class name that scopes these variable references.

Dynamic Theme Updates with useTheme

For runtime theme customization, such as dark mode toggles or customer-specific branding, use the useTheme hook. This hook, demonstrated in apps/sandbox/src/components/themePreview/themeOverrides.ts, provides access to the nearest ThemeProvider's state.

import { useTheme } from '@astryxdesign/core';

function DarkModeToggle() {
  const { setTokens } = useTheme();

  const enableDark = () => {
    setTokens({
      '--color-background': '#1a1a1a',
      '--color-text': '#eeeeee',
    });
  };

  return <button onClick={enableDark}>Dark mode</button>;
}

The setTokens function performs a shallow merge with existing tokens, allowing you to update specific values while preserving the rest of the theme. This approach is ideal for A/B testing, user preference storage, or seasonal theme variations.

Summary

  • Theme tokens are CSS custom properties defined in packages/themes/<theme>/theme.css.d.ts files that provide type-safe contracts for available values.
  • Runtime customization uses the ThemeProvider component from @astryxdesign/core, which accepts a tokens object and injects CSS variables into the document root.
  • New theme creation is handled via the xds theme add <name> CLI command, which scaffolds the necessary files in packages/themes/.
  • Dynamic updates are possible through the useTheme hook, which exposes a setTokens method for runtime mutations.
  • Component integration happens automatically through StyleX stylesheets referencing var(--token-name), requiring no changes to component logic when themes change.

Frequently Asked Questions

How do I switch between light and dark modes in Astryx?

Use the useTheme hook to dynamically update tokens based on user preference. Call setTokens() with your dark mode values (such as '--color-background': '#1a1a1a') when the user toggles the mode. Store the preference in state or localStorage and apply it via ThemeProvider on initial load.

Can I override just one color without replacing the entire theme?

Yes. The ThemeProvider merges your provided tokens object with the base theme defaults. Pass only the specific tokens you want to override, such as { '--color-primary': '#custom-color' }, and all other tokens will retain their original values from the active theme.

Where are theme token definitions stored in the source code?

Token definitions are stored in packages/themes/<theme-name>/theme.css.d.ts files. For example, the y2k theme defines all its available CSS custom properties in packages/themes/y2k/theme.css.d.ts. These TypeScript files serve as the source of truth for what tokens a theme provides and their default values.

What is the difference between themeProps() and ThemeProvider?

themeProps() is a utility function in packages/core/src/utils/themeProps.ts that generates component class names and data attributes, while ThemeProvider is a React component that manages the actual CSS custom property values at runtime. The provider injects the values, and themeProps() ensures components have the correct styling hooks to consume them.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →