Compile
Boss compile rewrites your source code. Use it in CI/CD or production mode for real builds. In dev mode it writes to a temporary copy so you can inspect the output without mutating your working files. This approach keeps Boss tooling-agnostic (no Babel/Webpack/Vite plugin required).
Boss compile is a build-time, source-to-source optimization pass. It rewrites $$ JSX into plain DOM elements, emits CSS, and removes runtime imports when possible.
Quick start
npx boss-css compile
- Default output goes to
compile.tempOutDir. - Use
--prodorNODE_ENV=productionto mutate files in place. - If you use a custom config directory, the default
compile.tempOutDirfollows it (<configDir>/compiled).
Configuration
Boss config is loaded from .bo$$/config.js plus any bo$$ key in package.json.
// .bo$$/config.js
export default {
content: ["src/**/*.{ts,tsx,js,jsx,mjs,cjs}"],
selectorPrefix: "boss-",
selectorScope: ".scope ",
stylesheetPath: ".bo$$/styles.css",
compile: {
tempOutDir: ".bo$$/compiled",
spread: true,
stats: "quick",
},
}
selectorPrefix prefixes generated class names and CSS variables.
selectorScope scopes selectors and token variables (include a trailing space for descendant selectors).
CLI flags override compile keys:
npx boss-css compile --tempOutDir .bo$$/compiled --spread true
compile.stats supports false | "quick" | "detailed" (default: "quick").
npx boss-css compile --stats detailed
Output layout
When not in prod mode, output files mirror the source tree under compile.tempOutDir.
<root>/src/app.tsx -> <root>/.bo$$/compiled/src/app.tsx
<root>/.bo$$/styles.css -> <root>/.bo$$/compiled/.bo$$/styles.css
Non-JS/TS files are copied in temp mode. In prod mode they are left untouched.
Stats output
Quick:
npx boss-css compile stats (quick)
Mode: temp
Runtime free: yes
Runtime files: 0
Files processed: 42
Elements replaced: 128
Time: 1.24s
Detailed:
npx boss-css compile stats (detailed)
Mode: temp
Runtime free: no
Runtime files (2):
- src/components/ClientOnly.tsx
- src/app/layout.tsx
Files processed: 42
Files copied: 5
Files skipped: 0
Files total: 47
Elements replaced: 128
Value helper files (1):
- src/components/Padded.tsx
CSS output: /path/to/project/.bo$$/compiled/.bo$$/styles.css (2048 bytes)
Output dir: /path/to/project/.bo$$/compiled
Time: 1.24s
What compile does
- Parses JS/TS/JSX/TSX with SWC.
- Parses classname strings for boss syntax (including normal elements).
- Converts
$$JSX elements into DOM tags (inline-first or classname-first). - Rewrites
$$.$({ ... })into{ style: { ... } }to support spreads. - Rewrites
$$.$("...")into a string literal for className spreads. $$.$is a no-op marker (returns input at runtime) that tells the parser/compile to treat inputs as Boss props or className markers.- Supports
!importantin className tokens via a trailing!(for examplecolor:red!). - Rewrites className token strings like
color:$$.token.color.whiteto token shorthand (color:white). - Emits CSS (minified with LightningCSS) to
stylesheetPath. - Removes boss runtime imports when runtime usage is not required.
What compile does not do (yet)
- Only inline-first and classname-first strategies are supported.
npx boss-css compileonly supports JSX today.- Classname-first expects dynamic values to be written as functions (
prop={() => value}).
Runtime pruning rules
Boss imports are removed when runtime is not needed. Runtime is kept when any of these remain:
$$is used as a value that cannot be inlined (for example non-token$$references outside JSX).$$.token.*outside JSX is inlined tovar(--...).- A
$$element cannot be compiled (spread whencompile.spreadis false, or unresolved prepared components).
Input to output examples
1) Simple inline props
Input:
export const Example = () => <$$ color="red" padding={8} />
Output:
export const Example = () => <div style={{ color: "red", padding: "8px" }} />
CSS output: none (no CSS file is written when output is empty).
2) Pseudo props become class + CSS variable
Input:
export const Example = () => <$$ hover={{ color: "red" }} />
Output:
export const Example = () => (
<div className="hover:color" style={{ "--hover-color": "red" }} />
)
CSS:
.hover\:color:hover { color: var(--hover-color) }
3) Media queries with at
Input:
export const Example = () => <$$ at={{ dark: { fontStyle: "italic" } }} />
Output:
export const Example = () => (
<div className="at:dark:font-style" style={{ "--at-dark-font-style": "italic" }} />
)
CSS:
@media screen and (prefers-color-scheme: dark) {
.at\:dark\:font-style { font-style: var(--at-dark-font-style) }
}
4) Tokens become CSS variables
Input:
export const Example = () => <$$ color="white" />
Output:
export const Example = () => <div style={{ color: "var(--color-white)" }} />
CSS (default tokens):
:root { --color-white: #fff }
If you reference tokens via $$.token.*, compile inlines them to CSS variables:
export const Example = () => <$$ color={$$.token.color.white} />
5) $$.$({ ... }) marker unwrap
Input:
const props = $$.$({ color: 1 })
Output:
const props = { color: 1 }
$$.$ is a marker only; it does not generate className or style. Use it to mark Boss prop objects (for example when passing them into <$$ {...props} /> or style={$$.$({ ... })}). If you want a spreadable object with valid DOM props, use $$.style(...).
6) Spreads on $$ elements (compile.spread = true)
Input:
const domProps = $$.style({ color: 1 })
export const Example = () => <$$ {...domProps} />
Output:
const domProps = $$.style({ color: 1 })
export const Example = () => <div {...domProps} />
When compile.spread is false, the $$ element is left as-is and runtime imports are preserved.
6b) $$.$("...") className marker
Input:
const className = $$.$("display:flex hover:color:red")
export const Example = () => <div className={className} />
Output:
const className = "display:flex hover:color:red"
export const Example = () => <div className={className} />
6c) Token strings in className are normalized
Input:
export const Example = () => <div className="color:$$.token.color.white" />
Output:
export const Example = () => <div className="color:white" />
7) Existing className/style are preserved and merged
Input:
export const Example = () => (
<$$ className="card" style={{ display: "block" }} hover={{ color: "red" }} />
)
Output:
export const Example = () => (
<div className="card hover:color" style={{ display: "block", "--hover-color": "red" }} />
)
CSS:
.hover\:color:hover { color: var(--hover-color) }
8) Classname parser on normal elements
Input:
export const Example = () => <div className="display:flex hover:color:red" />
Output (JSX unchanged):
export const Example = () => <div className="display:flex hover:color:red" />
CSS:
.display\:flex { display: flex }
.hover\:color\:red:hover { color: red }
9) Grouped selector classnames
Input:
export const Example = () => <div className="hover:{color:red;text-decoration:underline}" />
Output (JSX unchanged):
export const Example = () => (
<div className="hover:color:red hover:text-decoration:underline" />
)
CSS:
.hover\:color\:red:hover { color: red }
.hover\:text-decoration\:underline:hover { text-decoration: underline }
10) Dynamic values use a helper for units
Input:
export const Example = ({ pad }: { pad: number }) => <$$ padding={pad} />
Output:
import { createBossValue } from "boss-css/compile/runtime"
const __bossValue = createBossValue("px")
export const Example = ({ pad }: { pad: number }) => (
<div style={{ padding: __bossValue(pad) }} />
)
11) Dynamic as compiles into a local component
Input:
export const Example = () => <$$ as={tag} />
Output:
export const Example = () => (function () {
const __BossCmp = tag
return <__BossCmp />
})()
12) Prepared components are inlined when available
Input:
$$.PreparedUppercaseA = $$.$({ color: "red", hover: { color: "blue" } })
export const Example = () => <$$.PreparedUppercaseA fontWeight="bold" />
Output:
export const Example = () => (
<div className="hover:color" style={{ color: "red", "--hover-color": "blue", fontWeight: "bold" }} />
)
Prepared definitions can live in any file matched by content. Compile collects them before transforming, so usages in other files can be inlined too. Cross-file inlining only happens when the prepared definition is fully static (no dynamic expressions and as is a static string). Non-static prepared definitions keep runtime behavior and the assignment remains in the output.
13) Custom CSS blocks
Input:
$$.css`
.banner { background: linear-gradient(#fff, #eee); }
`
$$.css({ ".card": { padding: 12, color: "red" } })
Output:
/* removed from JS output when used as a statement */
CSS:
.banner { background: linear-gradient(#fff, #eee); }
.card { padding: 12px; color: red }
Notes:
- Template literals must be static (no
${}expressions). - Object values must be static; nested objects are treated as selectors or
@rules. - Top-level declarations without an explicit selector are wrapped in
:root. - If
$$.css(...)is used in an expression position, it is replaced withvoid 0.
Notes
contentis required for compile to know which files to scan.- CSS output is written only when non-empty.
- Only inline-first and classname-first strategies are supported right now.
- Inline-first keeps first-level CSS props inline (deep contexts use CSS variables).
- Prepared components can be inlined across files when their definitions are static.
- Non-CSS attributes on
$$elements are preserved and not converted into CSS variables.