Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

Guide

Getting started

OPUI ships first-class Astro components. Install the package, import what you need, and you're set.

Install via NPM

Install the package, open-props, and astro — the source CSS imports below resolve Open Props from your node_modules.

Terminal window
pnpm add opui-css open-props astro -S
Terminal window
npm install opui-css open-props astro -S

Use a component

Components can be imported from opui-css/astro:

---
import { Button } from "opui-css/astro"
---
<Button variant="filled">Click me</Button>

Load the CSS

Astro components ship markup only — the CSS still has to be loaded once per app. Import the pre-bundled theme from your global stylesheet (or a shared layout):

@import "opui-css/css/imports.css";

Or pick and choose pieces if you want a lighter footprint:

@import "opui-css/open-props.css";
@import "opui-css/core/palette.css";
@import "opui-css/css/theme.css";
@import "opui-css/core/normalize.css";
@import "opui-css/css/components.css";
@import "opui-css/core/utils.css";

Theming

Once installed, here's how the theming system fits together: a single palette source feeds intent tokens, which components read from. The focus ring, control sizes, and shadows are separate scales you can tune independently.

Palette source

The 16-step --color-N ramp is generated from a single OKLCH input: --palette-source. Setting it on :where(html) (or any ancestor) regenerates the entire palette in place.

:where(html) {
--palette-source: oklch(0.58 0.18 264);
--palette-hue-rotate-by: 0;
}
  • Must be oklch(). Hex, rgb(), and named colors won't work — the relative-color math reads the source's c and h channels directly.
  • Hue and chroma propagate; lightness is not used. The ramp keeps its own L curve, so the literal source color won't necessarily appear at any step.
  • --palette-hue-rotate-by is a separate knob for per-step warm/cool drift, in degrees.

To re-source the palette in a sub-tree (e.g. for status colors), override --palette-source on a scoped selector:

:where(.ui-warning) {
--palette-source: oklch(0.58 0.21 var(--hue-orange));
}

Motion

Use the --motion variable to turn motion on or off. The default value is 1.

Adjust the motion speed using util classes on the html element, or scope changes locally to an individual component instance.

Accessibility

By default, if a user has prefers-reduced-motion: reduce enabled --motion variable automatically shifts to 0, completely disabling transitions.

Global Classes

Adding these utility classes to the html element will override the OS preference.

  • .ui-motion-off: sets --motion: 0 (disables motion globally).
  • .ui-motion-on: sets --motion: 1 (enables default motion).
  • .ui-motion-debug: sets --motion: 10 (slows down transitions 10x to test and debug choreography).
<html lang="en" class="ui-motion-debug">

Local Overrides

Components use a local --_motion variable that allows you to disable motion for each component individually if you want.

<button class="ui-button" style="--_motion: 0">
Instant interaction
</button>