> ## Documentation Index
> Fetch the complete documentation index at: https://docs.9am.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# CEF constraints

> FiveM runs Chromium 103. These are the things that break in-game while working perfectly in your browser.

## Why this page exists

FiveM's embedded browser is **Chromium 103**. Your development browser is several years newer.

That gap is expensive because the failures are silent and one-sided: `bun run start` looks perfect, you ship, and the UI is broken in-game. Every item below was paid for once already. The kit carries the fixes so you do not pay again.

<Warning>
  Before using any modern CSS feature in a NUI, check it against Chromium 103. The browser preview will happily lie to you.
</Warning>

## oklch() is not parsed

The 9AM tokens are authored in OKLCH. Chromium 103 cannot read them, so every token resolves to nothing and the interface renders **completely unstyled** in-game.

`postcss.config.js` converts them at build time:

```js theme={null}
export default {
  plugins: {
    'postcss-color-converter': {
      outputColorFormat: 'rgb',
      ignore: ['hex']
    },
    '@tailwindcss/postcss': {},
    autoprefixer: {}
  }
}
```

<Warning>
  Do not remove `postcss-color-converter`. This is the single most destructive thing you can do to a 9AM NUI, and it is invisible until you load the game.
</Warning>

Verify after any build change:

```bash theme={null}
bun run build && grep -c oklch dist/assets/*.css   # must be 0
```

`bun src/scripts/9am-ui.mjs doctor` runs this check for you, including scanning your built CSS.

Tailwind v4 separately emits a hex fallback before each `color-mix()` behind an `@supports` guard, which is why `color-mix` is safe to use.

## Wheel scroll does nothing on clipped containers

CEF's compositor will not scroll a `clip-path`'d container with the mouse wheel. The event reaches the page; the scroll never happens. Since `Viewport` uses `clip-path`, that means nothing scrolls.

`installSmoothScroll()` catches the wheel event and drives the scroll from JavaScript. Call it once in `main.tsx`.

## mask does not apply to backdrop-filter output

A gradient mask over a `backdrop-filter` is ignored ([crbug.com/41465359](https://bugs.chromium.org/p/chromium/issues/detail?id=41465359), fixed in later Chrome).

`Viewport` fakes the gradient by stacking bands of fixed blur with progressively shorter spans. The blur values are chosen so the combined result increases linearly toward the edge — each step lands under half a pixel, so the eye cannot pick out the bands.

## No :has(), no @container

Both need Chromium 105+.

Where the kit needs them it computes the result in JavaScript instead. `ViewportHeader` detects a `ViewportAction` child at the React level and applies its grid columns directly, rather than using `:has()`.

If you find yourself reaching for either, restructure the component or resolve it in JS.

## overflow + border-radius does not clip composited layers

Rounded corners with `overflow: hidden` do not clip composited layers — scrolled content and `backdrop-filter` output escape the radius.

Use `clip-path` instead, which the compositor applies to every layer:

```jsx theme={null}
<div style={{ clipPath: "inset(0 round var(--radius-2xl))" }}>
```

This is why `Viewport` uses `clip-path`, which is in turn why the wheel-scroll fix is required. The two are linked.

## Tailwind v4: @theme inline emits no custom properties

Not a CEF issue, but it bites in the same way — silently, and only in some contexts.

Variables declared in `@theme inline` are inlined into the generated utilities. They do **not** exist as CSS custom properties at runtime:

```jsx theme={null}
{/* Renders nothing — the variable does not exist */}
<div style={{ background: "var(--color-primary-500)" }} />

{/* Correct */}
<div className="bg-primary-500" />
```

And because Tailwind scans source files as text, the class name must appear in full. `` `bg-primary-${n}` `` generates no CSS — spell out each class you intend to use.

## Checklist before shipping a NUI

<Steps>
  <Step title="Confirm the CSS is CEF-safe">
    ```bash theme={null}
    bun run build
    grep -c oklch dist/assets/*.css   # 0
    ```
  </Step>

  <Step title="Confirm smooth scroll is installed">
    `installSmoothScroll()` runs in `main.tsx`.
  </Step>

  <Step title="Test in-game, not just in the browser">
    Scrolling, blurred edges, and anything using a modern selector need a real CEF check. `ensure <resource>` and look at it.
  </Step>

  <Step title="Run doctor">
    ```bash theme={null}
    bun src/scripts/9am-ui.mjs doctor
    ```
  </Step>
</Steps>
