How the theme is structured
The theme installs as two stylesheets insrc/styles/:
Both are locked.
9am-ui check fails the build if either is edited locally — identical tokens across scripts is the whole point. Put your own styles in index.css, below the imports.
Dark mode is class-driven
dark: utility keys off the player’s operating system theme, which is neither what you want nor controllable from the UI. Instead the .dark class on <html> drives everything, and useTheme toggles it.
Dark is the shipped default. index.html sets the class before first paint so the wrong theme never flashes.
Palettes
Tokens are authored in OKLCH. Dark ships by default; light is a real second palette, not a tint.- Dark
- Light
Light mode’s
--primary is deliberately a deeper amber than dark mode’s bright gold. Bright gold is unreadable as text on white and washes out as a fill. Choosing one deeper gold that works both as bg-primary with dark text and as text-primary on white is why roughly forty text-primary usages needed no per-file changes when light mode was added.The primary ramp
Eleven fixed gold steps, independent of the active palette:bg-primary-500, text-primary-200, border-primary-700.
Radius
radius-sm is --radius - 4px, radius-md is - 2px, radius-lg equals it, radius-xl is + 4px.
In practice: rounded-2xl for containers and cards, rounded-lg for controls, rounded-xl for tiles and avatars.
Type
Apply Phudu with
!font-[phudu]:
Choosing a font stylesheet
9am-theme.css does not import fonts, because which stylesheet you want depends on your app.
Embedding is the better default: one command, nothing else to fetch, and in a NUI everything loads off local disk so there is no network round-trip to save.
It becomes the wrong choice the moment you have multiple entry points. Every DUI is its own CEF instance, so each one would decode the entire 322 KB payload again with nothing shared between them. The vehicle shop is the worked example — it has three entries and creates one DUI per showroom slot, so it uses the linked variant.
Styling conventions
These are what make a screen read as 9AM rather than generic shadcn.Translucent fills, not solid greys
border-black/10 dark:border-white/10.
Gold tint for accent states
#F6E3711a is gold at 10% opacity. It is the hover and active wash across links, tabs, and badges.
Press feedback
Motion
Transitions sit between 150 ms and 300 ms witheaseOut. Anything slower feels sluggish at the pace players interact with a NUI.
Base rules the theme applies
The theme sets a few NUI-specific defaults you should know about:html,body, and#rootare transparent andoverflow: hidden— the NUI is an overlay on the game, not a page.- Focus outlines are suppressed globally. A NUI is pointer-driven, and keyboard focus otherwise draws a ring around whatever was last clicked.
- Images are not draggable and number inputs have no spinner buttons.
.scrollbar-none,.scrollbar-mini, and.scrollbar-mini-absoluteutilities are available.