Skip to content

Latest commit

 

History

History
247 lines (189 loc) · 6.25 KB

File metadata and controls

247 lines (189 loc) · 6.25 KB

@astryxdesign/build

Build plugins for XDS source builds. Provides babel, PostCSS, and Vite integrations that compile XDS library and product code with separate class name prefixes, which enables independent CSS layers:

reset < astryx-base (library, astryx prefix) < astryx-theme < product (app, x prefix)

Why?

StyleX generates atomic CSS: same declaration = same class name. Without separate prefixes, library and product classes collide and can't be placed in independent CSS layers, which breaks theme overrides.

@astryxdesign/build solves this by:

  1. Compiling XDS library code with astryx prefix (.astryx78zum5)
  2. Compiling product code with default x prefix (.x78zum5)
  3. Placing each group in its own CSS @layer

Packages

Export Purpose Platform
@astryxdesign/build/babel Babel plugin: splits class prefixes per file Next.js, any babel pipeline
@astryxdesign/build/postcss PostCSS plugin: compiles + splits CSS layers Next.js
@astryxdesign/build/vite Vite plugin: wraps unplugin + splits layers Vite, Storybook

Install

npm install -D @astryxdesign/build @stylexjs/babel-plugin @babel/core

For Vite, also install:

npm install -D @stylexjs/unplugin

Next.js Setup

1. babel.config.js

const path = require('path');

module.exports = {
  presets: ['next/babel'],
  plugins: [
    [
      '@astryxdesign/build/babel',
      {
        dev: process.env.NODE_ENV !== 'production',
        runtimeInjection: false,
        treeshakeCompensation: true,
        enableInlinedConditionalMerge: true,
        aliases: {
          '@astryxdesign/core/*': [path.join(__dirname, 'node_modules/@astryxdesign/core/*')],
          '@astryxdesign/core': [path.join(__dirname, 'node_modules/@astryxdesign/core')],
        },
        unstable_moduleResolution: {type: 'commonJS'},
      },
    ],
  ],
};

2. postcss.config.js

const path = require('path');

module.exports = {
  plugins: {
    '@astryxdesign/build/postcss': {
      appDir: 'src',
      babelPlugins: [
        [
          '@stylexjs/babel-plugin',
          {
            dev: process.env.NODE_ENV !== 'production',
            runtimeInjection: false,
            treeshakeCompensation: true,
            enableInlinedConditionalMerge: true,
            aliases: {
              '@astryxdesign/core/*': [path.join(__dirname, 'node_modules/@astryxdesign/core/*')],
              '@astryxdesign/core': [path.join(__dirname, 'node_modules/@astryxdesign/core')],
            },
            unstable_moduleResolution: {type: 'commonJS'},
          },
        ],
      ],
    },
  },
};

3. next.config.mjs

const nextConfig = {
  transpilePackages: ['@astryxdesign/core', '@astryxdesign/theme-neutral'],
  webpack: config => {
    // Resolve to source TypeScript instead of dist
    config.resolve.conditionNames = ['source', 'import', 'require', 'default'];
    return config;
  },
};

export default nextConfig;

4. CSS files

src/app/layers.css:

@layer reset, astryx-base, astryx-theme, product;

src/app/globals.css:

@import './layers.css';
@import '@astryxdesign/core/reset.css';
@import '@astryxdesign/theme-neutral/theme.css';

@stylex;

layers.css must be a separate file because webpack hoists @import content above inline CSS.

5. Browserslist

{
  "browserslist": ["last 1 Chrome version"]
}

Vite Setup

import {astryxStylex} from '@astryxdesign/build/vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    ...astryxStylex({
      stylexOptions: {
        dev: process.env.NODE_ENV === 'development',
        runtimeInjection: false,
        treeshakeCompensation: true,
        unstable_moduleResolution: {
          type: 'commonJS',
          rootDir: __dirname,
        },
      },
    }),
    react(),
  ],
  resolve: {
    alias: {
      '@astryxdesign/core': path.resolve(__dirname, 'node_modules/@astryxdesign/core/src'),
    },
  },
  optimizeDeps: {
    exclude: ['@astryxdesign/core', '@astryxdesign/theme-neutral'],
  },
});

How it works

Babel plugin (@astryxdesign/build/babel)

Wraps @stylexjs/babel-plugin with two internal instances: one with classNamePrefix: 'astryx' for library files, one with default 'x' for product files. Routes each file to the correct instance based on its path.

Library patterns (configurable):

  • packages/core/
  • packages/themes/
  • node_modules/@astryxdesign/

PostCSS plugin (@astryxdesign/build/postcss)

Compiles StyleX from both library and product source files in two separate passes with different prefixes. Wraps the results in named @layer blocks:

  • Library rules → @layer astryx-base
  • Product rules → @layer product

Vite plugin (@astryxdesign/build/vite)

Wraps @stylexjs/unplugin and intercepts the dev CSS endpoint (/virtual:stylex.css). Partitions the collected rules by file path and serves split-layer CSS.


Advanced Options

Babel plugin

[
  '@astryxdesign/build/babel',
  {
    // Patterns to identify library files (default shown)
    libraryPatterns: [
      'packages/core/',
      'packages/themes/',
      'node_modules/@astryxdesign/',
    ],

    // Class name prefix for library styles (default: 'astryx')
    libraryPrefix: 'astryx',

    // Class name prefix for product styles (default: 'x')
    classNamePrefix: 'x',

    // ... all @stylexjs/babel-plugin options
  },
];

PostCSS plugin

'@astryxdesign/build/postcss': {
  appDir: 'src',           // Your app source directory
  babelPlugins: [...],     // StyleX babel plugin config
  libraryPrefix: 'astryx',   // Prefix for library CSS (default: 'astryx')
  extraInclude: [...],     // Additional glob patterns
  layers: {                // Layer names (defaults shown)
    library: 'astryx-base',
    product: 'product',
  },
}

Related