@krazdesign/build
v0.4.5
Published
Build plugins for XDS source builds — babel, PostCSS, and Vite integrations
Maintainers
Readme
@krazdesign/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 < kraz-base (library, kraz prefix) < kraz-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.
@krazdesign/build solves this by:
- Compiling XDS library code with
krazprefix (.kraz78zum5) - Compiling product code with default
xprefix (.x78zum5) - Placing each group in its own CSS
@layer
Packages
| Export | Purpose | Platform |
| --------------------------- | -------------------------------------------- | --------------------------- |
| @krazdesign/build/babel | Babel plugin: splits class prefixes per file | Next.js, any babel pipeline |
| @krazdesign/build/postcss | PostCSS plugin: compiles + splits CSS layers | Next.js |
| @krazdesign/build/vite | Vite plugin: wraps unplugin + splits layers | Vite, Storybook |
Install
npm install -D @krazdesign/build @stylexjs/babel-plugin @babel/coreFor Vite, also install:
npm install -D @stylexjs/unpluginNext.js Setup
1. babel.config.js
const path = require('path');
module.exports = {
presets: ['next/babel'],
plugins: [
[
'@krazdesign/build/babel',
{
dev: process.env.NODE_ENV !== 'production',
runtimeInjection: false,
treeshakeCompensation: true,
enableInlinedConditionalMerge: true,
aliases: {
'@krazdesign/core/*': [
path.join(__dirname, 'node_modules/@krazdesign/core/*'),
],
'@krazdesign/core': [
path.join(__dirname, 'node_modules/@krazdesign/core'),
],
},
unstable_moduleResolution: {type: 'commonJS'},
},
],
],
};2. postcss.config.js
const path = require('path');
module.exports = {
plugins: {
'@krazdesign/build/postcss': {
appDir: 'src',
babelPlugins: [
[
'@stylexjs/babel-plugin',
{
dev: process.env.NODE_ENV !== 'production',
runtimeInjection: false,
treeshakeCompensation: true,
enableInlinedConditionalMerge: true,
aliases: {
'@krazdesign/core/*': [
path.join(__dirname, 'node_modules/@krazdesign/core/*'),
],
'@krazdesign/core': [
path.join(__dirname, 'node_modules/@krazdesign/core'),
],
},
unstable_moduleResolution: {type: 'commonJS'},
},
],
],
},
},
};3. next.config.mjs
const nextConfig = {
transpilePackages: ['@krazdesign/core', '@krazdesign/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, kraz-base, kraz-theme, product;src/app/globals.css:
@import './layers.css';
@import '@krazdesign/core/reset.css';
@import '@krazdesign/theme-neutral/theme.css';
@stylex;
layers.cssmust be a separate file because webpack hoists@importcontent above inline CSS.
5. Browserslist
{
"browserslist": ["last 1 Chrome version"]
}Vite Setup
import {krazStylex} from '@krazdesign/build/vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
...krazStylex({
stylexOptions: {
dev: process.env.NODE_ENV === 'development',
runtimeInjection: false,
treeshakeCompensation: true,
unstable_moduleResolution: {
type: 'commonJS',
rootDir: __dirname,
},
},
}),
react(),
],
resolve: {
alias: {
'@krazdesign/core': path.resolve(
__dirname,
'node_modules/@krazdesign/core/src',
),
},
},
optimizeDeps: {
exclude: ['@krazdesign/core', '@krazdesign/theme-neutral'],
},
});How it works
Babel plugin (@krazdesign/build/babel)
Wraps @stylexjs/babel-plugin with two internal instances: one with classNamePrefix: 'kraz' 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/@krazdesign/
PostCSS plugin (@krazdesign/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 kraz-base - Product rules →
@layer product
Vite plugin (@krazdesign/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
[
'@krazdesign/build/babel',
{
// Patterns to identify library files (default shown)
libraryPatterns: [
'packages/core/',
'packages/themes/',
'node_modules/@krazdesign/',
],
// Class name prefix for library styles (default: 'kraz')
libraryPrefix: 'kraz',
// Class name prefix for product styles (default: 'x')
classNamePrefix: 'x',
// ... all @stylexjs/babel-plugin options
},
];PostCSS plugin
'@krazdesign/build/postcss': {
appDir: 'src', // Your app source directory
babelPlugins: [...], // StyleX babel plugin config
libraryPrefix: 'kraz', // Prefix for library CSS (default: 'kraz')
extraInclude: [...], // Additional glob patterns
layers: { // Layer names (defaults shown)
library: 'kraz-base',
product: 'product',
},
}Related
- example-nextjs-source: full Next.js source build example
@stylexjs/babel-plugin: the underlying StyleX compiler
