

# Testing & Troubleshooting

Test compiled styles in a browser, and diagnose missing transforms, styles, and overrides.

## Overview

Test styles through the same compiler integration as the application. Tests then run transformed modules and load the generated stylesheet, so assertions can check computed styles instead of generated class names.

This Playwright test renders `<SaveButton disabled />` at `/testing` while the development server runs, then checks the button's computed padding and background.

```tsx
import { style } from 'zyzz'

export function SaveButton(props: SaveButton.Props) {
  return (
    <button {...styles.button()} disabled={props.disabled}>
      Save
    </button>
  )
}

export namespace SaveButton {
  export type Props = {
    disabled?: boolean | undefined
  }
}

namespace styles {
  export const button = style({
    backgroundColor: 'blue',
    color: 'white',
    padding: '8px 16px',
    // The test checks this disabled state
    selectors: {
      '&:disabled': { backgroundColor: 'gray' },
    },
  })
}
```

```ts
import { expect, test } from '@playwright/test'

test('renders the disabled button styles', async (context) => {
  await context.page.goto('http://localhost:3000/testing')
  const button = context.page.getByRole('button', { name: 'Save' })

  await expect(button).toBeDisabled()
  // Assert computed values, which stay stable across builds and output modes
  await expect(button).toHaveCSS('padding-top', '8px')
  await expect(button).toHaveCSS('padding-inline-start', '16px')
  await expect(button).toHaveCSS('background-color', 'rgb(128, 128, 128)')
})
```

## Vitest

Vitest runs test modules through the Vite config, so the `zyzz()` plugin transforms style definitions as it does in the application build. Node tests can then render components and call styles without a missing-transform error.

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

export default defineConfig({
  // Transform style definitions in test modules
  plugins: [zyzz()],
})
```

A Node environment applies no CSS, so it cannot check layout, cascade, or computed values. Run those assertions in a real browser.

## Compiler Output

A compiler pipeline or library build can test compiled CSS directly. `Css.compile` returns CSS and class names without browser or filesystem side effects, so load both into a browser page and check the result.

```ts title="card.test.ts"
import { chromium } from 'playwright'
import { expect, test } from 'vitest'
import { Style } from 'zyzz'
import { Css } from 'zyzz/web'

test('renders compiled card padding', async () => {
  // Compile style data to CSS and class names
  const output = Css.compile({
    styles: Style.define({ card: { padding: '16px' } }),
  })
  const browser = await chromium.launch()

  try {
    const page = await browser.newPage()
    // Apply the class and load the matching CSS
    await page.setContent(
      `<article class="${output.classes.card}">Card</article>`,
    )
    await page.addStyleTag({ content: output.css })

    const padding = await page
      .locator('article')
      .evaluate((element) => getComputedStyle(element).paddingTop)

    expect(padding).toBe('16px')
  } finally {
    await browser.close()
  }
})
```

A CSS snapshot can check emitted declarations, but it cannot establish cascade, layout, or stylesheet delivery. `Style.define` tests style data, not the source rewriting of callable `style()` definitions.

## Coverage

Tie assertions to observable behavior, and use the same configuration and build mode as the application:

* **States:** Exercise relevant hover, focus, disabled, and data or ARIA states.
* **Breakpoints:** Resize across the component's breakpoints and check layout.
* **Themes:** Switch themes and color schemes, then check computed colors and token-dependent sizes.
* **Updates:** Change variants and dynamic values, then check that replaced values stop applying.
* **Server rendering:** Check the initial HTML, hydration, and later navigation.
* **Production builds:** Run a production build to catch stylesheet delivery failures in split routes.

When an application supports both CSS output modes, run the same assertions in each. Avoid mocking the compiler or renderer when testing their integration.

## Troubleshooting

Find where behavior diverges: the diagnostic and transformed module first, then the stylesheet, then the element's computed styles. Browser developer tools show which declaration wins and which stylesheet supplies it.

### Missing Transform

`style.MissingTransformError` means untransformed authoring source reached execution. `variants.MissingTransformError` and `variable.MissingTransformError` report the same problem for those calls.

```ts
import { style } from 'zyzz'

// Throws when no integration transformed this module
export const card = style({ padding: '16px' })
```

Confirm that the integration transforms the reported module, including test fixtures and imported library source. Importing a config or extracting CSS does not rewrite callable authoring code.

### Missing Styles

Inspect the element's applied props and the loaded stylesheets. Confirm that the generated class appears in CSS from the same compilation, and check stylesheet requests in the production build.

```html
<!-- Standalone builds load the combined stylesheet beside the compiled modules -->
<link rel="stylesheet" href="/dist/zyzz.css" />
```

With split stylesheets, load `zyzz.shared.css` before the module stylesheets. Deliver the initialization script when the application saves theme selections.

### Rejected Tokens

Root `style` from `zyzz` accepts literal CSS values and has no design tokens. Use the helper from the project config, or `zyzz/default` for its bundled tokens.

```ts
// Token-aware helper with the bundled tokens
import { style } from 'zyzz/default'

namespace styles {
  export const card = style({
    backgroundColor: 'background.surface',
    borderRadius: 'md',
    padding: 4,
  })
}
```

Check that each token belongs to the property's category. See [Themes & Tokens](/docs/guides/themes) for configured tokens.

### Ignored Overrides

Inspect cascade layers, importance, conditions, specificity, and rule order. Moving a class within a class string does not change precedence, and separate JSX `className` or `style` props can replace spread props.

```tsx
import { cx, style } from 'zyzz'

export function Card() {
  return (
    // Later arguments win, so the selected color replaces the card's
    <article {...cx(styles.card(), styles.selected())}>Selected card</article>
  )
}

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

  export const selected = style({ color: 'blue' })
}
```

To add an external class, pass it to the style call, such as `styles.card({ className: 'external' })`. See the [`cx` contract](/docs/api/core/cx) for ordering and dynamic bindings.

### Watch Mode Failures

When a rebuild fails, the watching host keeps the last successful output, so the browser can show stale styles. Read the located diagnostic, fix the source, and confirm a successful rebuild before checking again.

```sh
# Reproduce a persistent failure with a one-shot build
npx zyzz build
```

Avoid deleting unrelated output files, since the host tracks the artifacts it owns.

## Catch Mistakes Earlier

Type checks verify inferred tokens, variants, and dynamic inputs. The [Oxlint plugin](/docs/api/oxlint) reports invalid styles, conflicting JSX props, and unused definitions before a build.

```sh
# Run the type checker and linter with the tests
npx tsc --noEmit && npx oxlint .
```

Linting and type checks complement browser tests. They cannot show whether the application loads the stylesheet or renders correctly on every supported platform.

## More

[Linting](/docs/api/oxlint)

Report invalid styles, conflicting JSX props, and unused definitions.

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

Connect the integration that transforms styles and delivers CSS.

[Themes & Tokens](/docs/guides/themes)

Bind token sets to typed style helpers through a project config.

[Compatibility](/docs/introduction/compatibility)

Review framework, runtime, browser, and native support limits.
