

# CSS Output

Choose whether Zyzz writes one class per declaration or one class per style.

## Overview

The compiler turns each reachable style into CSS ahead of time, and `cssOutput` decides how declarations become classes. Applying a style returns class names and never inserts CSS rules.

| Mode | Classes |
| --- | --- |
| atomic (default) | One per declaration or fallback array, shared when reuse keeps precedence. |
| grouped | One per style, containing its declarations and conditions. |

Set the mode in the config. It applies to config-bound helpers. Helpers imported directly from `zyzz` use the integration's mode instead, which is atomic unless an integration such as Babel sets `cssOutput`.

```ts title="zyzz.config.ts"
import { defineConfig } from 'zyzz'

export const { style, variants } = defineConfig({
  // Atomic output is the default, so this option is optional
  cssOutput: 'atomic',
})
```

## Atomic Output

Each declaration becomes its own class, and a fallback array, such as `display: ['block', 'grid']`, stays together in one class. Styles share a class when reuse keeps precedence, so the card and label below share `color: red`.

```tsx title="Card.tsx"
import { style } from './zyzz.config.js'

export function Card() {
  return (
    <article {...styles.card()}>
      <span {...styles.label()}>Account</span>
    </article>
  )
}

namespace styles {
  // Atomic classes: z-text-red z-p-8px
  export const card = style({ color: 'red', padding: '8px' })

  // Atomic classes: z-text-red
  export const label = style({ color: 'red' })
}
```

```css title="atomic.css"
/* Shared by the card and label */
.z-text-red {
  color: red;
}

/* Used by the card only */
.z-p-8px {
  padding: 8px;
}
```

## Grouped Output

Each style becomes one class. Switch the config to grouped output, and the same card compiles to one class per style.

```ts title="zyzz.config.ts"
import { defineConfig } from 'zyzz'

export const { style, variants } = defineConfig({
  // Emit one class per style instead of one per declaration
  cssOutput: 'grouped',
})
```

Names combine a hash of the module path with the member path, so modules can reuse member names without collisions.

```css title="grouped.css"
/* Generated for styles.card */
.z-X8T0-w-styles-card {
  color: red;
  padding: 8px;
}

/* Generated for styles.label */
.z-X8T0-w-styles-label {
  color: red;
}
```

Neither mode guarantees smaller or faster output, so compare CSS, JavaScript, and markup sizes for the application. The [benchmarks](/docs/introduction/benchmarks) use grouped output.

## Overrides

Both modes preserve cascade behavior, and atomic classes are shared only when reuse keeps precedence. Combine styles with `cx`, since multiple JSX spreads replace each other's styling props.

```tsx title="CompactCard.tsx"
import { cx } from 'zyzz'
import { style } from './zyzz.config.js'

export function CompactCard() {
  // Later arguments win, so the compact padding replaces the card's
  return <article {...cx(styles.card(), styles.compact())}>Account</article>
}

namespace styles {
  export const card = style({ color: 'red', padding: '8px' })

  export const compact = style({ padding: '4px' })
}
```

Class-string order does not determine precedence. `cx` also preserves variable bindings and variant attributes, and dynamic values bind to precompiled custom properties.

## Generated Names

Common atomic declarations use readable names, and variables follow authored paths, such as `--z-color-brand`. Apply returned props rather than copying class names. When combining independent configs, give each a distinct `id`.

```ts title="admin.config.ts"
import { defineConfig } from 'zyzz'

export const { style } = defineConfig({
  // Prefix this config's classes, variables, and theme scopes
  id: 'admin',
})
```

The compiler reports incompatible generated names when it can detect them. Consumers own collision avoidance across separately built stylesheets.

## Delivery

Rebuild CSS and generated code together after edits and when changing modes. Precompiled libraries keep their output mode, so a consumer's config does not change published classes. See [Getting Started](/docs/introduction/getting-started) for setup.

A custom pipeline selects the mode when compiling style data. `Css.compile` has no filesystem or browser side effects.

```ts
import { Style } from 'zyzz'
import { Css } from 'zyzz/web'

const styles = Style.define({
  card: { color: 'red', padding: '8px' },
})

// Load output.css and apply output.classes.card
const output = Css.compile({ cssOutput: 'grouped', styles })
```

## More

[Getting Started](/docs/introduction/getting-started)

Connect the integration that transforms styles and delivers CSS.

[Styling](/docs/guides/styling)

Combine styles with `cx` and override declarations in a predictable order.

[Benchmarks](/docs/introduction/benchmarks)

Review measured CSS, JavaScript, and compilation costs.

[Config Reference](/docs/api/core/defineConfig#optionscssoutput)

Set `cssOutput`, `output`, and `id` on a project config.
