Introduction
Plumeria is a compile-time CSS-in-JS library for React. You write style objects in TypeScript, and the compiler turns them into static atomic CSS — while removing every trace of the library from your JavaScript bundle. No runtime code ships, no styles are computed in the browser.
Usage
Styling is straightforward: define styles once with css.create(), and the compiler handles everything else. The atomic class names are determined at that point — all that remains is to attach them via the styleName prop.
import * as css from "@plumeria/core";
const styles = css.create({
text: {
fontSize: 16,
},
});
export const Footer = () => {
return <div styleName={styles.text}>MyText</div>;
};Styles compile to zero across file boundaries, too. You can colocate styles with components or share them across files — structure them to fit your project. The only requirement is that every file using Plumeria imports @plumeria/core, which is how the compiler finds it.
import * as css from "@plumeria/core";
export const styles = css.create({
common: {
color: "gray",
},
});import "@plumeria/core";
import { styles } from "./styles";
export const Component = () => {
return <p styleName={styles.common}>Shared style</p>;
};Specificity
The styleName prop accepts arrays, ternaries, and conditional expressions. Merging follows one rule: the right side wins. If isActive is true, the properties of active override the properties of base — always.
const styles = css.create({
base: {
color: "gray",
fontSize: 16,
},
active: {
color: "blue",
},
});
styleName={[styles.base, isActive && styles.active]}Because the compiler controls the merge, the cascade specificity problem disappears entirely. Writing the merge as an operation, where combines styles left to right, disjoint styles simply concatenate:
And when two styles set the same property, the losing atom on the left is filtered out at compile time — it never reaches the output:
Types all the way down
Styling shouldn't require a runtime. Plumeria treats styles as type schemas rather than runtime objects — in fact, the package's only entry point is a .d.ts file. There is no JavaScript to import:
{
"name": "@plumeria/core",
"exports": {
".": {
"types": "./lib/css.d.ts",
},
},
}Static styles resolve to class names entirely at compile time. The only dynamic pieces — function keys and variant props — compile down to CSS variables, so even they leave no JavaScript behind. Every API compiles to nothing but static class names and CSS:
- No runtime bundle
- No runtime evaluation
- No runtime parsing
- No runtime dependencies
Of course, "zero-runtime" doesn't claim the cost vanishes — it claims the cost moves. All of the work happens at build time, and the browser only ever receives class names and CSS.
To learn what motivated this design, read Why Plumeria?. For integration with bundlers and the full API, see the API reference and Installation.