# Why Plumeria?

Source: https://plumeria.dev/docs/why-plumeria



## "CSS Modules are fine after all." [#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 [#the-zero-trace-runtime]

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

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

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

export const Box = () => <div classStyle={styles.box}>Box</div>;
```

Here is the *entire* JavaScript build output:

```tsx
export const Box = () => <div className={'xqqbxt1d xq96bg3w'}>Box</div>;
```

The declarations move to a generated stylesheet:

```css
.xqqbxt1d { padding: 16px; }
.xq96bg3w { color: red; }
```

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.

Nothing of the library reaches the component, so a React Server Component keeps its styles with no `'use client'` boundary to add.

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](/docs) 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](https://github.com/refirst11/stylex-plumeria-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 [#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 [#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.

## Already on CSS Modules [#already-on-css-modules]

The migration is a command, not a rewrite:

```sh
npx @plumeria/codemod migrate --from css-modules
```

Each `*.module.css` becomes a `*.styles.ts` beside it and its consumers are pointed at it — including the descendant rules, which have no combinator to translate into and become a [marker and its extended styles](/docs/api-reference/javascript/marker) instead. What cannot be converted is listed by file and line rather than guessed at. See [`@plumeria/codemod`](/docs/api-reference/codemod).

The command runs the other way as well:

```sh
npx @plumeria/codemod migrate --from plumeria
```

Styles go back to `*.module.css`, theme tokens and animations to global CSS, and the call sites to `className`. It is an export rather than a round trip — a dynamic style leaves as a custom property, a marker as the `@container style()` rule it compiles to — so what returns is the equivalent stylesheet, not the files you started from. Leaving is a command too.

***

Plumeria is for those who say, "I never compromise." Ready to try it? Start with [Getting started](/docs/getting-started).
