Loading CSS
View as MarkdownTimeless CSS ships as plain stylesheets in three tiers. There is no build step, no preprocessor, and no configuration — but two of the three tiers are required, and it is worth knowing which.
| Tier | Required | Holds |
|---|---|---|
tokens.css |
Yes | The cascade-layer order, and color-scheme |
core.css, or core/<component>.css |
Yes | Behavior: anchoring, box participation, scrolling, native control resets |
themes/atmosphere.css, or per component |
No | The look: colour, type, spacing, shadow, radius, motion |
Core is the tier people are surprised by. It is not styling — it is the implementation of “the surface opens beside its trigger”, “the closed menu is not on screen”, and “the filtered option is gone”. Without it a Select opens unanchored and a filtered option stays visible, which is a broken component rather than an unstyled one.
One stylesheet, or only what you use
Section titled “One stylesheet, or only what you use”For prototypes, documents, and anything where request granularity does not matter, import the theme — it pulls in the two required tiers itself:
import '@timelessui/components/css/themes/atmosphere.css'For applications, import the required tiers plus the exact component a route renders:
import '@timelessui/components/css/tokens.css'import '@timelessui/components/css/core/button.css'import '@timelessui/components/css/core/dialog.css'import '@timelessui/components/css/themes/atmosphere/tokens.css'import '@timelessui/components/css/themes/atmosphere/button.css'import '@timelessui/components/css/themes/atmosphere/dialog.css'Both paths produce the same public classes, custom properties, layers, and state selectors. The only difference is how much CSS reaches the browser. Each component page lists every stylesheet that component needs under Styling, core files included.
There is no default theme. themes/atmosphere.css names the theme it loads, and a second theme
would be a sibling of the same shape — themes/<name>.css beside themes/<name>/.
Always import tokens first
Section titled “Always import tokens first”tokens.css declares the order of the three Timeless layers:
@layer ui.tokens, ui.components, ui.utilities;Layers are ordered by first encounter, so whichever stylesheet is parsed first gets the
lower-priority layers. Load a component stylesheet before tokens.css and the order is established
by that file instead, which is how a rule you wrote in ui.utilities ends up losing to a component.
themes/atmosphere.css imports tokens first on your behalf. When importing granularly, put
tokens.css first yourself.
tokens.css also sets color-scheme: light dark, which is what makes light-dark() resolve to the
branch the reader asked for. Drop it and every scheme-dependent token silently returns its light
value.
See Theming for what those layers mean in practice and which tokens and component variables you can override.
Bringing your own theme
Section titled “Bringing your own theme”The theme tier is genuinely optional, and replacing it is the supported path — for a design system of your own, for Tailwind, or for any utility-class or CSS-in-JS setup. Import the two required tiers and nothing else:
import '@timelessui/components/css/tokens.css'import '@timelessui/components/css/core.css'Every component is then positioned, structurally intact, and operable, with no appearance of ours: UA colours, no radius, no shadow, the browser’s default face. Style the public anatomy with whatever you like — Theming covers the selectors that are yours to target.
A colour, radius, shadow, or type utility can never lose to Timeless on this path, and not by
convention: core is forbidden from declaring any of those properties, and a build check proves it.
The only properties core does declare are the behavioural ones — display, overflow, position,
the inset family, appearance and friends — so those are the only conflicts possible, and they
are conflicts you would be picking deliberately.
Two things to expect on this path. Sizing stays in the theme, so a few components sit at their
content size rather than a designed one — a Sheet panel is content-height instead of full-height
until you give it a size, and a Select surface is as wide as its widest option instead of as wide as
its trigger, because min-inline-size: anchor-size(width) is a design decision. And core carries no
affordances, so there is no hover highlight and no focus ring until you add them.
Target size comes with the sizing, which makes it yours. Without the theme, seven components fall
under the 24×24 CSS pixels
WCAG 2.2 SC 2.5.8 asks for —
the colour picker’s channel sliders, the number stepper’s step buttons, pagination’s page links, and
the recipes built from them. A control can still conform by being spaced rather than large, which is
why core does not force a floor on you: a dense tool that wants small targets is a legitimate
design, and picking between the two is exactly the kind of decision the theme tier holds. Whichever
you pick, decide it rather than inheriting it. apps/e2e records the seven and fails on an eighth,
so this list stays honest.