---
title: "CSS Variables"
description: "Pohon UI uses CSS variables as design tokens for flexible, consistent theming with built-in light and dark mode support."
canonical_url: "https://vinicunca.dev/pohon/getting-started/theme/css-variables"
---
# CSS Variables

> Pohon UI uses CSS variables as design tokens for flexible, consistent theming with built-in light and dark mode support.

## Colors

Pohon UI provides UnoCSS utility classes for each [semantic color](https://vinicunca.dev/pohon/getting-started/theme/design-system#semantic-colors) you define, so you can use class names like `color-error` or `bg-success`:

```vue
<template>
  <span class="color-primary">Primary</span>
  <span class="color-secondary">Secondary</span>
  <span class="color-success">Success</span>
  <span class="color-info">Info</span>
  <span class="color-warning">Warning</span>
  <span class="color-error">Error</span>
</template>
```

Each utility class uses a CSS variable to set its color for light and dark modes:

```css [Light]
:root {
  --ui-color-primary: var(--ui-color-primary-500);
  --ui-color-secondary: var(--ui-color-secondary-500);
  --ui-color-success: var(--ui-color-success-500);
  --ui-color-info: var(--ui-color-info-500);
  --ui-color-warning: var(--ui-color-warning-500);
  --ui-color-error: var(--ui-color-error-500);
}
```

```css [Dark]
.dark {
  --ui-color-primary: var(--ui-color-primary-400);
  --ui-color-secondary: var(--ui-color-secondary-400);
  --ui-color-success: var(--ui-color-success-400);
  --ui-color-info: var(--ui-color-info-400);
  --ui-color-warning: var(--ui-color-warning-400);
  --ui-color-error: var(--ui-color-error-400);
}
```

> [!TIP]
> 
> You can adjust which shade each utility class uses for light and dark mode in your `main.css` file:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-color-primary: var(--ui-color-primary-700);
> }
> 
> .dark {
>   --ui-color-primary: var(--ui-color-primary-200);
> }
> ```

> [!WARNING]
> 
> `black` and `white` don't have multiple shades, so you can't [define a palette](https://vinicunca.dev/pohon/getting-started/theme/design-system#configure-colors) from them. To use solid black or white as your primary color, set the base variable directly in your `main.css` file:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-color-primary: black;
> }
> 
> .dark {
>   --ui-color-primary: white;
> }
> ```

### Foreground

Each semantic color also has a matching text color for content placed on top of it, so you can pair class names like `bg-primary` with `color-primary-foreground`:

```vue
<template>
  <span class="color-primary-foreground bg-primary">Primary</span>
  <span class="color-secondary-foreground bg-secondary">Secondary</span>
  <span class="color-success-foreground bg-success">Success</span>
  <span class="color-info-foreground bg-info">Info</span>
  <span class="color-warning-foreground bg-warning">Warning</span>
  <span class="color-error-foreground bg-error">Error</span>
</template>
```

Components use these classes on their solid backgrounds, e.g. the `solid` variant of [Button](https://vinicunca.dev/pohon/components/button), [Badge](https://vinicunca.dev/pohon/components/badge) or [Alert](https://vinicunca.dev/pohon/components/alert), the [Chip](https://vinicunca.dev/pohon/components/chip), the [Banner](https://vinicunca.dev/pohon/components/banner) or a checked [Checkbox](https://vinicunca.dev/pohon/components/checkbox).

Each utility class reads a `--ui-color-<name>-foreground` CSS variable. These variables are optional: when one is not defined, it falls back to `--ui-color-text-inverted`.

```css
.color-primary-foreground {
  color: var(--ui-color-primary-foreground, var(--ui-color-text-inverted));
}
```

> [!TIP]
> 
> `--ui-color-text-inverted` is white in light mode and dark in dark mode, which doesn't fit every palette. White text on a light shade like `green-500` or `amber-500` is hard to read, so define a foreground for each color in your `main.css` file:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-color-primary-foreground: var(--ui-color-primary-950);
>   --ui-color-warning-foreground: var(--ui-color-warning-950);
>   --ui-color-error-foreground: white;
> }
> ```
> 
> Define them inside `:root` and `.dark` separately when a color needs a different foreground in each mode.

> [!NOTE]
> 
> Check the contrast of each pair against the [WCAG](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html) minimum of 4.5:1. Some mid-tone shades like `blue-500` can't reach it with either white or dark text: in that case, use a darker shade for the color itself, e.g. `--ui-color-secondary: var(--ui-color-secondary-600)`.

## Text

Pohon UI provides UnoCSS utility classes for text colors, so you can use class names like `color-text-dimmed` or `color-text-muted`:

```vue
<template>
  <span class="color-text-dimmed">Dimmed</span>
  <span class="color-text-muted">Muted</span>
  <span class="color-text-toned">Toned</span>
  <span class="color-text">Text</span>
  <span class="color-text-highlighted">Highlighted</span>
  <span class="color-text-inverted bg-background-inverted">Inverted</span>
</template>
```

Each utility class uses a CSS variable to set its color for light and dark modes:

```css [Light]
:root {
  --ui-color-text-dimmed: var(--ui-color-neutral-400);
  --ui-color-text-muted: var(--ui-color-neutral-500);
  --ui-color-text-toned: var(--ui-color-neutral-600);
  --ui-color-text: var(--ui-color-neutral-700);
  --ui-color-text-highlighted: var(--ui-color-neutral-900);
  --ui-color-text-inverted: white;
}
```

```css [Dark]
.dark {
  --ui-color-text-dimmed: var(--ui-color-neutral-500);
  --ui-color-text-muted: var(--ui-color-neutral-400);
  --ui-color-text-toned: var(--ui-color-neutral-300);
  --ui-color-text: var(--ui-color-neutral-200);
  --ui-color-text-highlighted: white;
  --ui-color-text-inverted: var(--ui-color-neutral-900);
}
```

> [!TIP]
> 
> You can customize these CSS variables in your `main.css` file:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-color-text: var(--ui-color-neutral-900);
> }
> 
> .dark {
>   --ui-color-text: white;
> }
> ```
> 
> To change several of these variables at once, [change the `--ui-color-neutral-*` shades](https://vinicunca.dev/pohon/getting-started/theme/design-system#configure-colors) they read instead.

## Background

Pohon UI provides UnoCSS utility classes for background colors, so you can use class names like `bg-background` or `bg-background-muted`:

```vue
<template>
  <div class="bg-background">
    Default
  </div>
  <div class="bg-background-muted">
    Muted
  </div>
  <div class="bg-background-elevated">
    Elevated
  </div>
  <div class="bg-background-accented">
    Accented
  </div>
  <div class="color-text-inverted bg-background-inverted">
    Inverted
  </div>
</template>
```

Each utility class uses a CSS variable to set its color for light and dark modes:

```css [Light]
:root {
  --ui-color-bg: white;
  --ui-color-bg-muted: var(--ui-color-neutral-50);
  --ui-color-bg-elevated: var(--ui-color-neutral-100);
  --ui-color-bg-accented: var(--ui-color-neutral-200);
  --ui-color-bg-inverted: var(--ui-color-neutral-900);
}
```

```css [Dark]
.dark {
  --ui-color-bg: var(--ui-color-neutral-900);
  --ui-color-bg-muted: var(--ui-color-neutral-800);
  --ui-color-bg-elevated: var(--ui-color-neutral-800);
  --ui-color-bg-accented: var(--ui-color-neutral-700);
  --ui-color-bg-inverted: white;
}
```

> [!TIP]
> 
> You can customize these CSS variables in your `main.css` file:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-color-bg: var(--ui-color-neutral-50);
> }
> 
> .dark {
>   --ui-color-bg: var(--ui-color-neutral-950);
> }
> ```

## Border

Pohon UI provides UnoCSS utility classes for border colors, so you can use class names like `border-border` or `border-border-muted`:

```vue
<template>
  <div class="border border-border">
    Default
  </div>
  <div class="border border-border-muted">
    Muted
  </div>
  <div class="border border-border-accented">
    Accented
  </div>
  <div class="border border-border-inverted">
    Inverted
  </div>
</template>
```

Each utility class uses a CSS variable to set its color for light and dark modes:

```css [Light]
:root {
  --ui-color-border: var(--ui-color-neutral-200);
  --ui-color-border-muted: var(--ui-color-neutral-200);
  --ui-color-border-accented: var(--ui-color-neutral-300);
  --ui-color-border-inverted: var(--ui-color-neutral-900);
}
```

```css [Dark]
.dark {
  --ui-color-border: var(--ui-color-neutral-800);
  --ui-color-border-muted: var(--ui-color-neutral-700);
  --ui-color-border-accented: var(--ui-color-neutral-700);
  --ui-color-border-inverted: white;
}
```

> [!TIP]
> 
> You can customize these CSS variables in your `main.css` file:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-color-border: var(--ui-color-neutral-100);
> }
> 
> .dark {
>   --ui-color-border: var(--ui-color-neutral-900);
> }
> ```

## Chart

The [Chart](https://vinicunca.dev/pohon/components/chart) component paints its series with six CSS variables, in order. The first one follows your `primary` color:

```css [Light]
:root {
  --ui-chart-1: var(--ui-color-primary);
  --ui-chart-2: var(--color-blue-500);
  --ui-chart-3: var(--color-violet-500);
  --ui-chart-4: var(--color-amber-500);
  --ui-chart-5: var(--color-rose-500);
  --ui-chart-6: var(--color-cyan-500);
}
```

```css [Dark]
.dark {
  --ui-chart-1: var(--ui-color-primary);
  --ui-chart-2: var(--color-blue-400);
  --ui-chart-3: var(--color-violet-400);
  --ui-chart-4: var(--color-amber-400);
  --ui-chart-5: var(--color-rose-400);
  --ui-chart-6: var(--color-cyan-400);
}
```

> [!TIP]
> 
> You can customize these CSS variables in your `main.css` file:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-chart-2: var(--color-teal-500);
> }
> 
> .dark {
>   --ui-chart-2: var(--color-teal-400);
> }
> ```

## Focus

Pohon UI applies a `focus-visible` outline on every interactive element, tinted with the `color` prop of the component, e.g. `outline-primary/25` when `color="primary"` or `outline-border-inverted/25` when `color="neutral"`.

If you prefer a single outline color across your entire app regardless of the component color, you can add a global rule in your `main.css` file:

```css [Primary]
*,
::before,
::after {
  @apply outline-primary/25;
}

*:focus-visible,
*:has(> a:focus-visible) {
  --un-ring-color: var(--ui-color-primary);
}
```

```css [Neutral]
*,
::before,
::after {
  @apply outline-border-inverted/25;
}

*:focus-visible,
*:has(> a:focus-visible) {
  --un-ring-color: var(--ui-color-border-inverted);
}
```

The first rule tints the focus outline of every component, the second one aligns the ring recolor applied on focus by variants with a visible border like `outline` or `subtle`, including cards that highlight when their link is focused.

> [!NOTE]
> 
> Make sure to declare these rules outside of any `@layer` so they take precedence over the colors set by the component themes. Keep in mind the first rule also tints the native focus outline of your own elements, and variants recoloring a `border` on focus like [FileUpload](https://vinicunca.dev/pohon/components/file-upload) or [ContentSurround](https://vinicunca.dev/pohon/components/content-surround) keep following the component `color`.

## Radius

Pohon UI overrides UnoCSS's default `rounded-*` utilities with a unified border radius system, so you can use regular [border radius utilities](https://unocss.dev/docs/border-radius) like `rounded-xs` or `rounded-2xl`:

```vue
<template>
  <div class="rounded-xs">
    xs
  </div>
  <div class="rounded-sm">
    sm
  </div>
  <div class="rounded-md">
    md
  </div>
  <div class="rounded-lg">
    lg
  </div>
  <div class="rounded-xl">
    xl
  </div>
  <div class="rounded-2xl">
    2xl
  </div>
  <div class="rounded-3xl">
    3xl
  </div>
</template>
```

These utility classes are calculated based on a global `--ui-radius` CSS variable, which defines the base radius value applied across all components for a consistent look.

```css
:root {
  --ui-radius: 0.25rem;
}
```

Each utility multiplies this base value:

```css
:root {
  --radius-xs: calc(var(--ui-radius) * 0.5);
  --radius-sm: var(--ui-radius);
  --radius-md: calc(var(--ui-radius) * 1.5);
  --radius-lg: calc(var(--ui-radius) * 2);
  --radius-xl: calc(var(--ui-radius) * 3);
  --radius-2xl: calc(var(--ui-radius) * 4);
  --radius-3xl: calc(var(--ui-radius) * 6);
}
```

> [!TIP]
> 
> You can customize the base radius value in your `main.css` file:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-radius: 0.5rem;
> }
> ```

> [!WARNING]
> 
> Changing `--ui-radius` also affects the `rounded-xs` to `rounded-3xl` classes on your own markup and third-party components, not only Pohon UI components.

> [!NOTE]
> 
> Try the  theme menu in the header above to change the base radius value.

## Container

Pohon UI provides a `--ui-container` CSS variable that controls the maximum width of the [Container](https://vinicunca.dev/pohon/components/container) component.

```css
:root {
  --ui-container: 80rem; /* var(--container-7xl) */
}
```

> [!TIP]
> 
> You can customize this value in your `main.css` file to adjust container widths consistently throughout your application:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --container-8xl: 90rem;
> }
> 
> :root {
>   --ui-container: var(--container-8xl);
> }
> ```

## Header

Pohon UI provides a `--ui-header-height` CSS variable that controls the height of the [Header](https://vinicunca.dev/pohon/components/header) component.

```css
:root {
  --ui-header-height: 4rem;
}
```

> [!TIP]
> 
> You can customize this value in your `main.css` to adjust header height consistently throughout your application:
> 
> ```css [app/assets/css/main.css]
> :root {
>   --ui-header-height: 6rem;
> }
> ```

## Body

Pohon UI doesn't style the `<body>` element, so your app keeps full control over it. For consistent theming across light and dark modes, we recommend adding these defaults to your `main.css` file:

```css [app/assets/css/main.css]
@layer base {
  body {
    @apply antialiased color-text bg-background scheme-light dark:scheme-dark;
  }
}
```

> [!NOTE]
> 
> `@apply` needs UnoCSS's [`transformerDirectives`](https://unocss.dev/transformers/directives), which `presetVinicunca` enables by default through its [`directives`](https://vinicunca.dev/unocss-preset/getting-started/options#transformers) option. If you use a different preset, add the transformer to your `uno.config.ts` yourself.


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
