DEV Community

refirst11
refirst11

Posted on • Originally published at plumeria.dev

Why Plumeria?

"CSS Modules are fine after all."

If you build web interfaces for a living, you have probably said this. After wrestling with runtime CSS-in-JS configuration, chasing specificity bugs across dynamic boundaries, or watching a utility-first framework bloat your markup, returning to the humble CSS Module feels like a relief.

That isn't a compromise made for lack of features. CSS Modules win because they are predictable: the CSS you write behaves exactly as written. There is no runtime parser guessing your intent, no injection-order races between chunks, and almost no runtime JavaScript — just a class mapping object.

But the safety has a price. You give up TypeScript-integrated styling, compile-time validation, dynamic theming, and seamless colocation.

Plumeria is designed to eliminate this compromise. It matches — and in several areas exceeds — the predictability of CSS Modules, while delivering the type-safe developer experience of a modern CSS-in-JS library.


The Zero-Trace Runtime

Try compiling this — note that the style is actually applied, not left unused:

import * as css from '@plumeria/core';

const styles = css.create({
  box: {
    padding: 16,
    color: 'red'
  }
});

export const Box = () => <div classStyle={styles.box}>Box</div>;
Enter fullscreen mode Exit fullscreen mode

Here is the entire JavaScript build output:

export const Box = () => <div className={'xqqbxt1d xq96bg3w'}>Box</div>;
Enter fullscreen mode Exit fullscreen mode

The declarations move to a generated stylesheet:

.xqqbxt1d { padding: 16px; }
.xq96bg3w { color: red; }
Enter fullscreen mode Exit fullscreen mode

The style still renders, yet import * as css from '@plumeria/core' and the entire css.create declaration have vanished. This is not dead-code elimination — nothing in this file is unused, and no bundler could remove a live call for you. The compiler resolves the class names statically and rewrites the call site, so the library never has a runtime form to eliminate in the first place. That disappearing import is the most concise illustration of a Zero-Trace Runtime — anything that shouldn't exist at runtime leaves no trace, not even an import statement. This isn't a best practice; it's a compiler contract.

Plumeria goes one step further than CSS Modules here. Where CSS Modules ship a class mapping object in your JS bundle, Plumeria inlines the final class name strings directly into the compiled output and deletes the css.create declarations. Not even a styling map remains in production JS. (See the Introduction for how the compilation works.)

Note the two class names in that output: one per property. Every property–value pair compiles to a global atomic class shared across your entire project, so your CSS stops growing in proportion to your component count — it plateaus.

Measured on identical Next.js apps, that is 1.83KB less client JavaScript than StyleX, which keeps its resolver — see the side-by-side benchmark.

Plumeria's strength doesn't come from flashy, single-purpose features. It comes from the cumulative effect of a few understated pillars that prove their value as your application grows.


Deterministic Cross-File Module Graph

Split, organize, and refactor styles across files without fear. Where traditional approaches let bundling order and import hierarchies alter CSS injection sequences — silently breaking layouts — Plumeria's output is determined strictly by the compiler's module graph. Your layouts look identical no matter how files are structured or in what order they are bundled.

Never Executes Your Application Code

Plumeria's compiler never executes your application code. It relies purely on fast static analysis (oxlint/ESLint) and direct AST rewrites, and the hand-written type definitions of @plumeria/core resolve without any evaluation. The build pipeline stays fast and lightweight — and it can never break on a runtime evaluation error, because there is no runtime evaluation.


Plumeria is for those who say, "I never compromise." Ready to try it? Start with Getting started.

Top comments (0)