# css.create

Source: https://plumeria.dev/docs/api-reference/javascript/create





```ts
export type create = <const T extends Record<string, CreateStyleValue>>(rule: T)=> CreateReturnType<T>;
export const create: create;
```

`create` defines css styles using plain JavaScript style objects.\
type-safe・autocomplete・lintable

Only the styles you use in `classStyle` will be ondemand compiled.

## Example [#example]

```tsx title="TypeScript"
import * as css from '@plumeria/core'

const styles = css.create({
  container: {
    display: 'flex',
    justifyContent: 'space-between',
  },
  text: {
    fontSize: '14px',
    textDecoration: 'none',
  },
})

export default function App() {
  return (
    <div
      classStyle={[
        styles.container,
        styles.text
      ]}
    />
  );
}
```

The call is cleaned up by the bundler.

**Compiled:**

```tsx
<div className="xxp6epoh xwbwwha1 xbief8v4 xzllkng3" />
```

**Generated CSS:**

```css
.xxp6epoh {
  display: flex;
}
.xwbwwha1 {
  justify-content: space-between;
}
.xbief8v4:not(#\#) {
  font-size: 14px;
}
.xzllkng3 {
  text-decoration: none;
}
```

Compiles each style CSS property into a unique, **atomic**, and **hashed** class name. This prevents style collisions and maximizes reusability.

## Dynamic values [#dynamic-values]

You can use functions to define dynamic styles. These are processed as dynamic values at build time.

```tsx
const styles = css.create({
  palette: (color: string) => ({
    backgroundColor: color,
  }),
});

export const Color = ({ color }) => {
  return (
    <span classStyle={styles.palette(color)}>Dynamic Color</span>
  )
}
```

Pass a plain value. The argument is written into the element, so an expression that is not the same on every read — a call with a side effect, a getter — is not read once.

## Variants bracket notation [#variants-bracket-notation]

You can resolve dynamic variant styles using standard bracket notation (e.g., `variantStyles[size]`). Plumeria's compiler automatically extracts these variants and creates static lookup tables at build time.

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

const styles = css.create({
  base: {
    display: 'inline-flex',
    borderRadius: '4px',
    backgroundColor: 'blue',
    color: 'white',
  }
});

const sizeStyles = css.create({
  small: {
    fontSize: '12px',
    padding: '4px 8px',
  },
  large: {
    fontSize: '18px',
    padding: '12px 24px',
  }
});

type Size = 'small' | 'large';
// can also use the type exhaustively via typeof sizeStyles

export const Button = ({ size }: { size: Size }) => {
  return (
    <button classStyle={[styles.base, sizeStyles[size]]}>Button</button>
  );
};
```

<Callout type="warn" title="Function keys are ignored">
  Function keys (dynamic styles) within the style object are ignored during bracket notation lookup table compilation.
</Callout>

### The last source wins [#the-last-source-wins]

An element takes a list, and a property set by more than one entry keeps the value from the one written last. That is what lets a variant override the base above.

The rule holds whatever the entries are — a plain style, a condition, a bracket group. Swapping two of them swaps which one applies:

```tsx
const styles = css.create({
  base: { color: 'white' },
  muted: { color: 'slategray' },
});

const toneStyles = css.create({
  brand: { color: 'royalblue' },
  danger: { color: 'crimson' },
});

// muted wins — it is written last
<button classStyle={[styles.base, toneStyles[tone], isMuted && styles.muted]} />

// toneStyles wins — now it is
<button classStyle={[styles.base, isMuted && styles.muted, toneStyles[tone]]} />
```

All three set `color` here. Entries that touch different properties never compete, so their order does not matter — `[styles.base, sizeStyles[size]]` above reads the same either way.

Merging goes property by property, and that includes nested blocks. Overriding a `:hover` colour leaves the rest of the `:hover` block in place.

This merging rule applies to the style list. Object spreads inside a style definition follow JavaScript semantics: a later property replaces the whole earlier value, including a nested object.

One pair the list does not settle is a shorthand and a longhand of the same property — `margin` against `marginTop`. The longhand wins wherever the two meet, whichever is written last. See [Specificity](/docs/reference/specificity).

<Callout title="After 18.1.1">
  The order holds across every kind of entry. Earlier releases resolved entries of different kinds by a fixed precedence, so an entry written later could be dropped.
</Callout>

### Conditional Expressions with Bracket Notation [#conditional-expressions-with-bracket-notation]

The condition can sit inside the brackets or around them. Both compile to a single lookup:

```tsx
<button classStyle={sizeStyles[isLarge ? keyA : keyB]} />
<button classStyle={isLarge ? sizeStyles[keyA] : sizeStyles[keyB]} />
<button classStyle={isActive && sizeStyles[key]} />
```

**Compiled:**

```js
({"small":"xhrr6ses x1v10r0u","large":"xpdfgu2b xj7b4jff"}[isLarge ? keyA : keyB] || "")
```

The branches of a condition are mutually exclusive — only one of them ever applies — so the compiler folds the condition into the key expression instead of expanding a combination per branch.

That leaves only two things worth knowing:

| Written as                                       | Lookup holds                 |
| ------------------------------------------------ | ---------------------------- |
| `sizeStyles[isLarge ? keyA : keyB]`              | the keys of `sizeStyles`     |
| `isLarge ? sizeStyles[keyA] : sizeStyles[keyB]`  | the same                     |
| `isLarge ? sizeStyles[keyA] : themeStyles[keyB]` | the keys of **both** objects |
| `isLarge ? sizeStyles.large : sizeStyles.small`  | **nothing** — see below      |

Reaching into two different objects is fine; the table simply carries both key sets.

When both keys are literals, read the property directly. A condition between two styles the compiler already knows needs no table at all:

```diff
- <button classStyle={sizeStyles[isLarge ? 'large' : 'small']} />
+ <button classStyle={isLarge ? sizeStyles.large : sizeStyles.small} />
```

**Compiled:**

```js
(isLarge ? "xpdfgu2b xj7b4jff" : "xhrr6ses x1v10r0u")
```

<Callout title="After 18.1">
  Both placements compile. Earlier releases rejected a condition placed around the brackets, so it had to be moved inside — that workaround is no longer needed.
</Callout>

### Optimization Tip: Keep Common Properties in Base Styles [#optimization-tip-keep-common-properties-in-base-styles]

Properties that do not vary belong in a base style — `styles.base` above — rather than repeated across every key of a variant object.

Anything the variants do not compete for stays outside the decision table, so it is emitted once instead of being multiplied into every combination.

## Zero-Runtime logic [#zero-runtime-logic]

Variants, dynamic props and conditional expressions are all resolved at compile time. Nothing merges objects or concatenates class strings in the browser, as many CSS-in-JS libraries do at every render.

### 1. Minimizing Combinations via Partial Cartesian Products [#1-minimizing-combinations-via-partial-cartesian-products]

The compiler builds a **property conflict graph** across the styles an element can receive, then expands only the axes that actually collide.

* **Independent axes** — a `size` and a `theme` object touching entirely different properties — stay separate lookups. Two axes of three options cost `3 + 3` entries.
* **Conflicting axes** — two objects that both set `color`, say — are grouped, and only that group becomes a combined decision table. The same two axes now cost `3 × 3`.
* **Mutually exclusive branches** — the arms of a `?:` or `&&` — are never a product at all. Only one arm applies, so they share one table keyed by which arm won.

Grouping is per conflict, not per element. Two objects competing for `color` and two more competing for `fontSize` are two groups on the same element, costing `3 × 3` plus `3 × 3` rather than one table of `3 × 3 × 3 × 3`.

It is also transitive, because overriding is: an object that sets both `color` and `fontSize` sits between those two groups and folds them into one, which is the answer that table has to give.

The output is therefore bound by the element's local axes of *conflict*, not by how many styles are in play. Stacking variants that never touch the same property stays additive however many you add.

### 2. Constant $O(1)$ Runtime Cost [#2-constant-o1-runtime-cost]

Whatever the table grows to is paid at build time. At runtime the browser reads a single entry from a flat, fully pre-computed object in $O(1)$ — no object allocation, no string building, and nothing for the garbage collector to reclaim.

<Callout title="Atomic static API">
  Unique atom classes are reused without duplication, keeping the total number of class names constant.\
  And all imports are stripped away by the compiler.
</Callout>
