Proper UI

Design tokens (CSS)

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

FileUse it when
properui.min.css / properui.cssYou have no build step. A prebuilt stylesheet: Tailwind's preflight, every token, the dark-mode block, and the @properui/html component classes.
tokens.cssYou 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.cssYou 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>.cssYou use theme.css and want one of the shipped presets. A @theme override block.
presets/plain/<name>.cssYou 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.

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

FAQs