---
title: "Theming — React"
description: "Light, dark or the system's choice, your accent color, every color and corner of the viewer, from config or from CSS."
integration: "React"
source: "https://www.embedpdf.com/docs/viewer/react/customize/theming"
---

# Theming

Give the viewer your brand color, and every part follows it: pressed buttons, the active tab,
focus rings, the text selection, the handles of a selected annotation.

```js
{
  theme: { preference: 'system', accent: '#7c3aed' },
}
```

`preference` is `'light'`, `'dark'` or `'system'`, which follows the user's setting and switches
when they change it. `theme: 'dark'` is short for `theme: { preference: 'dark' }`. `theme` follows
its prop, so a theme switch in your app switches the viewer too.

## Every color

Change any of them with `tokens` for both light and dark, with `light` for light only, and with
`dark` for dark only:

```js
{
  theme: {
    accent: '#0f6e56',
    tokens: { radius: '2px' },
    light: { surface: '#fbfaf7' },
    dark: { surface: '#16201c', 'page-shadow': 'none' },
  },
}
```

When more than one sets a token, `light` or `dark` wins in its mode, then `tokens`, then `accent`.
A CSS variable wins over all of them: see [From CSS](#from-css).

This one gives each color its light value in `light` and its dark value in `dark`, and a switch in
the app sets `preference`:

**`colors.tsx`**

```tsx
import { useState } from 'react';
import { PDFViewer, type ThemeConfig } from '@embedpdf/viewer-react';

import './colors.css';

const welcome = '/pdfs/welcome.pdf';

// What both modes share, then the colors of each.
const brand: ThemeConfig = {
  accent: '#0f6e56',
  tokens: { radius: '2px' },
  light: { surface: '#fbfaf7', canvas: '#ece8df' },
  dark: {
    accent: '#4fbf98',
    surface: '#16201c',
    'surface-raised': '#1d2a25',
    canvas: '#0c1210',
    hover: '#24332d',
  },
};

export default function App() {
  const [dark, setDark] = useState(false);
  return (
    <div className={dark ? 'app dark' : 'app'}>
      <header className="bar">
        <label>
          <input
            type="checkbox"
            checked={dark}
            onChange={(event) => setDark(event.target.checked)}
          />
          Dark
        </label>
      </header>
      <PDFViewer
        className="viewer"
        src={welcome}
        theme={{ ...brand, preference: dark ? 'dark' : 'light' }}
      />
    </div>
  );
}
```

**`colors.css`**

```css
/* Your app: a bar with the switch, and the viewer under it */
.app {
  display: flex;
  flex-direction: column;
  height: 100%;
  --bar: #fbfaf7;
  --bar-text: #111827;
  --bar-line: #d1d5db;
  --bar-accent: #0f6e56;
}

.app.dark {
  --bar: #16201c;
  --bar-text: #f9fafb;
  --bar-line: #33433c;
  --bar-accent: #4fbf98;
  color-scheme: dark;
}

.bar {
  display: flex;
  justify-content: flex-end;
  padding: 8px 12px;
  border-bottom: 1px solid var(--bar-line);
  background: var(--bar);
  color: var(--bar-text);
  font-family: system-ui, sans-serif;
  font-size: 14px;
}

.bar label {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}

.bar input {
  margin: 0;
  accent-color: var(--bar-accent);
}

.viewer {
  flex: 1;
  min-height: 0;
}
```

> **Try it:** tick **Dark** above the viewer, then open the menu at the top left: the bars, the
> menus and the space behind the pages all take your dark colors. Untick it to go back.

| Token             | What it colors                                                                                                                                            | Light                          | Dark                           |
| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------- | :----------------------------- |
| `accent`          | Pressed buttons, [primary](layout#a-primary-button) buttons, the active tab, links, focus rings, and every part on the pages that has no color of its own | `#3b82f6`                      | `#60a5fa`                      |
| `background`      | Behind the bars and panels                                                                                                                                | `#f3f4f6`                      | `#111827`                      |
| `surface`         | Bars and panels                                                                                                                                           | `#ffffff`                      | `#1f2937`                      |
| `surface-raised`  | Menus, sheets, dialogs and floating bars                                                                                                                  | `#ffffff`                      | `#1f2937`                      |
| `text`            | Labels and text                                                                                                                                           | `#111827`                      | `#f9fafb`                      |
| `text-muted`      | Secondary text, such as a page count                                                                                                                      | `#6b7280`                      | `#9ca3af`                      |
| `border`          | Lines between bars, panels and groups                                                                                                                     | `#d1d5db`                      | `#4b5563`                      |
| `hover`           | A button under the pointer                                                                                                                                | `#f3f4f6`                      | `#374151`                      |
| `canvas`          | Behind the pages                                                                                                                                          | `#f3f4f6`                      | `#0d1117`                      |
| `page-background` | A page before its picture arrives                                                                                                                         | `#ffffff`                      | `#ffffff`                      |
| `page-shadow`     | The shadow under each page; `none` for none                                                                                                               | `0 6px 18px rgb(0 0 0 / 0.18)` | `0 6px 18px rgb(0 0 0 / 0.18)` |
| `tooltip`         | Behind a tooltip's words                                                                                                                                  | `text`'s color                 | `text`'s color                 |
| `tooltip-text`    | A tooltip's words                                                                                                                                         | `surface`'s color              | `surface`'s color              |
| `radius`          | The corners of buttons, menus, panels, tooltips and the bars floating over the pages                                                                      | `6px`                          | `6px`                          |
| `font`            | Every word the viewer writes, its tooltips' too                                                                                                           | your system's font             | your system's font             |

What's drawn on the pages has its own colors: the text selection, search matches, the handles of
a selected annotation, form fields. Every one follows `accent` until you set it, and every one is
listed on [Colors & theming](https://www.embedpdf.com/docs/headless/react/ui/theming#every-color) in the headless docs, as a
plugin setting and a CSS variable. Both work in the viewer.

## From CSS

Every token is also a CSS variable: `--epdf-` and its name. Set it on the viewer or on any element
around it. A CSS variable wins over the config:

```css
embedpdf-viewer {
  --epdf-accent: #e91e63;
  --epdf-radius: 0;
}

[data-theme='dark'] embedpdf-viewer {
  --epdf-surface: #101418;
}
```

Use it to keep colors in your stylesheet, to follow a theme class your app already switches, or
to give two viewers on one page their own look.

## Restyle a part

The main parts have a name you can reach with `::part()`, for what tokens don't cover:

```css
embedpdf-viewer::part(toolbar-button) {
  border-radius: 999px;
}

embedpdf-viewer::part(mode-tab-active) {
  font-weight: 600;
}

embedpdf-viewer::part(toolbar-review) {
  background: #fef3c7;
}
```

| Part                                      | What it is                                                                                                            |
| :---------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
| `toolbar`, `toolbar-<id>`                 | A bar, and the bar with that id: `toolbar-main`, `toolbar-pager`, or `toolbar-review` for a bar you added as `review` |
| `toolbar-button`, `toolbar-button-active` | A button in a bar, and a pressed one                                                                                  |
| `toolbar-button-primary`                  | A [primary](layout#a-primary-button) button                                                                           |
| `tooltip`                                 | A tooltip                                                                                                             |
| `mode-tab`, `mode-tab-active`             | A mode tab, and the selected one                                                                                      |
| `tab`, `tab-active`                       | A document tab, and the open one                                                                                      |
| `menu`, `menu-item`, `menu-item-active`   | A menu, a row, and the row of the current choice                                                                      |
| `panel`, `panel-header`                   | A panel, and its header                                                                                               |
| `page-loading`, `page-error`              | The spinner on a page whose picture is on its way, and the message on one that couldn't load                          |

Part names are kept between minor versions, like every other id.

## In your own components

[Your own components](components) inherit the viewer's font and text color, and can use every
token as a CSS variable:

```css
.version-picker {
  background: var(--epdf-surface);
  color: var(--epdf-text);
  border: 1px solid var(--epdf-border);
  border-radius: var(--epdf-radius);
}
```

## Good to know

### Your page's CSS stays out

The viewer draws inside its own shadow DOM, so your page's styles don't change it and its styles
don't change your page. A global `button { … }` rule, a CSS reset, or `html { font-size: 62.5% }`
leaves the viewer as it is. What does reach it is what you aim at it: the CSS variables above and
`::part()`.

### Tailwind

Set a variable with an arbitrary property on an element around the viewer:
`class="[--epdf-accent:var(--color-violet-600)]"`.

### Colors saved in the PDF

An annotation's color, a redaction's fill and a form field's look belong to the document, not to
the theme: they look the same in every app that opens the file. Their defaults are the tools'
settings: see [Annotations](../features/annotations#the-tools).
