Use the Proper UI tokens and a prebuilt stylesheet in any project, with or without Tailwind and with or without React.
@properui/tokens is the Proper UI token layer as plain CSS files, for projects that are not using the React components: a Vue, Angular, Svelte or
Astro app, a server-rendered site, or a single HTML page. The same colours, type scale, radii, shadows and dark mode as the React library, from the
same source. The files are generated at build time from packages/ui/src/styles (theme.css, typography.css, globals.css and the presets), so
they never drift from what the components use.
React remains the primary, fullest layer. This package is only the tokens and a base stylesheet; for components outside React see Frameworks.
What each file is for
| File | Use it when |
|---|---|
properui.min.css / properui.css | You have no build step. A prebuilt stylesheet: Tailwind's preflight, every token, the dark-mode block, and the @properui/html component classes. |
tokens.css | You have a build step but no Tailwind. Every token as a plain CSS custom property: light values on :root, dark values on .dark-mode. No Tailwind syntax. |
theme.css | You use Tailwind CSS v4 in any framework. The @theme token layer and prose styles, so bg-primary, text-tertiary and text-display-lg work. |
presets/<name>.css | You use theme.css and want one of the shipped presets. A @theme override block. |
presets/plain/<name>.css | You use tokens.css or properui.css and want a preset. The same override as a plain :root block. |
Presets: brand (the default purple), blue, indigo, teal, green, orange, rose and slate-mono.
No build step: link the prebuilt stylesheet
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@properui/tokens/dist/properui.min.css" />
The same file is served at https://properui.dev/css/properui.min.css and on unpkg at https://unpkg.com/@properui/tokens. Pin a version in
production (@properui/tokens@0.1.0) so a release never changes a live page underneath you.
The stylesheet only contains the utilities the @properui/html snippets use, not all of Tailwind. For your own styles, read the tokens with var():
.panel {
background: var(--color-bg-secondary);
color: var(--color-text-primary);
border: 1px solid var(--color-border-secondary);
border-radius: var(--radius-xl);
padding: calc(var(--spacing) * 4);
font-size: var(--text-md);
line-height: var(--text-md--line-height);
}
It does not load a web font. --font-body asks for Inter first and falls back to the system stack, so load Inter yourself if you want it.
Without Tailwind: tokens.css
Install the package and import the file once, wherever your framework takes global CSS:
npm install @properui/tokens
// Vue (main.ts), Svelte (+layout.svelte), Astro (a layout's frontmatter), plain Vite (main.ts)
import "@properui/tokens/tokens.css";
// Angular: angular.json, projects.<app>.architect.build.options
"styles": ["node_modules/@properui/tokens/dist/tokens.css", "src/styles.css"]
Every token is then a CSS variable on :root: --color-bg-primary, --color-text-secondary, --color-fg-brand-primary,
--color-border-tertiary, --text-sm and --text-sm--line-height, --radius-lg, --shadow-md, and the rest. The Tailwind defaults a token reads
(--spacing and the stock colour ramps some utility colours point at) are included, so the file stands on its own. Utility classes like
bg-primary are Tailwind's and are not part of this file; write your CSS against the variables.
With Tailwind v4: theme.css
@import "tailwindcss";
@import "@properui/tokens/theme.css";
@custom-variant dark (&:where(.dark-mode, .dark-mode *));
The @custom-variant line is the one globals.css declares for the React library: it points Tailwind's dark variant at the .dark-mode class
instead of the media query, so anything built on it (including @properui/html) follows the same toggle. This is the same @theme layer the React
components are written against, so every semantic utility (bg-primary, text-tertiary, border-secondary, bg-brand-solid) and every typography
token (text-display-lg, text-md) works in your markup, in any framework. The same rules apply as in React: semantic tokens only, no dark:
utilities, logical properties for anything directional.
Adding a preset
Load the preset after the token file it overrides. With Tailwind:
@import "tailwindcss";
@import "@properui/tokens/theme.css";
@import "@properui/tokens/presets/teal.css";
Without Tailwind, or with the prebuilt stylesheet, use the plain version:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@properui/tokens/dist/properui.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@properui/tokens/dist/presets/plain/teal.css" />
import "@properui/tokens/presets/plain/teal.css";
import "@properui/tokens/tokens.css";
A preset only redeclares primitives: the brand ramp (--color-brand-*), the neutral ramp (--color-neutral-*, which every gray token reads), the
radius scale and, when set, the fonts. Both modes read those ramps, so a preset applies to dark mode as well. For a custom brand colour, build a
preset in the theme generator and copy its CSS; for the plain-CSS form, change @theme to :root in what it gives you.
Dark mode
Dark mode is class-based, exactly as in React: add dark-mode to an ancestor, usually <html>, and every token switches.
<html class="dark-mode"></html>
document.documentElement.classList.toggle("dark-mode", window.matchMedia("(prefers-color-scheme: dark)").matches);
Any subtree works too (<section class="dark-mode">), which is how a dark hero sits on a light page. See Dark mode for the
full strategy.