Plumeria logoPlumeria

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.

In notation:

Plumeria(Type)CompilationStatic Atomic CSS\mathtt{Plumeria(Type)} \xrightarrow{\text{Compilation}} \mathtt{Static\ Atomic\ CSS}

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.

styles.ts
import * as css from "@plumeria/core";

export const styles = css.create({
  common: {
    color: "gray",
  },
});
Component.tsx
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.

Component.tsx
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 \oplus combines styles left to right:

parse(ABC)"classA classB classC"parse(A \oplus B \oplus C) \to \text{"classA classB classC"}

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:

package.json
{
  "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

What "zero-runtime" really means

Strictly speaking, no styling solution is free. Even plain CSS adds a little build time as it moves through the bundler's pipeline, and zero-runtime CSS-in-JS adds parsing and transformation on top of that.

"Zero-runtime" doesn't claim the cost vanishes — it claims the cost moves. All of the work happens at build time, and nothing remains in the execution environment. Plumeria embraces this definition fully: the browser only ever receives class names and CSS.

Separation of layers

Plumeria is designed as two strictly separated layers. The abstraction layer is what you write: typed style definitions, colocated with your components. The output layer is what ships: plain atomic CSS. The build plugin removes the abstraction layer from the output completely, so style definitions never survive as JavaScript — only CSS, smaller than the definitions that produced it.

Because nothing ships at runtime, there were no runtime performance trade-offs to manage — so that effort went into the toolchain instead: the ESLint plugin and the bundler integrations. The result is that the cognitive overhead of style coding is eliminated structurally, at the abstraction layer, rather than managed by convention.


To learn what motivated this design, read Why Plumeria?. For integration with bundlers and the full API, see the API reference and Installation.

On this page