Skip to main content

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.
Before using any modern CSS feature in a NUI, check it against Chromium 103. The browser preview will happily lie to you.

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:
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.
Verify after any build change:
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, 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:
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:
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

1

Confirm the CSS is CEF-safe

2

Confirm smooth scroll is installed

installSmoothScroll() runs in main.tsx.
3

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.
4

Run doctor