Skip to main content

Compile

Source rewriting

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 an optional build mode. It follows your selected strategy and rewrites supported JSX source ahead of your app build.

It is not a strategy.

Current scope:

  • JSX source rewriting only
  • inline-first and classname-first only
  • temp mode mirrors transformed source and generated CSS under compile.tempOutDir
  • prod mode mutates source in place and does not write CSS files

Quick start

npx boss-css compile
  • Default output goes to compile.tempOutDir.
  • Use --prod or NODE_ENV=production to mutate files in place.
  • If you use a custom config directory, the default compile.tempOutDir follows it (<configDir>/compiled).
  • Compile reads your existing strategy config. It does not replace the strategy selection in .bo$$/config.js.

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

Boundary CSS outputs are mirrored there too when CSS is generated.

Non-JS/TS files are copied in temp mode. In prod mode they are left untouched, and CSS files are not written.

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

  • Follows your configured inline-first or classname-first strategy.
  • 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 !important in className tokens via a trailing ! (for example color:red!).
  • Rewrites className token strings like color:$$.token.color.white to token shorthand (color:white).
  • Emits generated CSS (minified with LightningCSS) in temp mode when output is non-empty.
  • Mirrors boundary CSS outputs in temp mode when boundaries are configured.
  • Removes Boss runtime imports when runtime usage is no longer required.

What compile does not do (yet)

  • It is not a strategy and does not add a new output mode.
  • Only inline-first and classname-first strategies are supported.
  • The JSX rewrite path only supports JSX today.
  • Classname-first expects dynamic values to be written as functions (prop={() => value}).
  • It does not guarantee that every file becomes free of Boss runtime imports. Files that still need runtime behavior keep those imports.

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 to var(--...).
  • A $$ element cannot be compiled (spread when compile.spread is 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 with void 0.

Notes

  • content is 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.