# Upgrading plugins for dark mode

This guide covers upgrading a plugin from `datocms-react-ui` / `datocms-plugin-sdk` **v2.1.5** (the last release without dark-mode support) to the new version that introduces semantic color tokens and full theme-aware rendering.

### New: semantic color tokens

`Canvas` has always injected CSS variables onto its wrapper div — previously a small set derived from `ctx.theme` (`--accent-color`, `--primary-color`, `--light-color`, `--dark-color`), with all other colors hardcoded inside the library's own CSS.

This release expands that to a full semantic palette. The host computes every token for the active theme and sends them via the new `ctx.cssDesignTokens` field; `Canvas` applies them verbatim. The token vocabulary is intentionally host-side so it can grow without an SDK or library release.

Every `--color--*` variable listed in the migration table below is now available inside any `<Canvas>`:

```css
.my-component {
  color: var(--color--ink);
  background: var(--color--surface);
  border: 1px solid var(--color--border);
}
```

All built-in components (`Button`, `TextInput`, `Section`, `Dropdown`, `Toolbar`, …) have been updated to use these tokens and now adapt to the active theme automatically.

### New: `ctx.colorScheme`

`ctx.colorScheme` is `'light'` or `'dark'` — `'system'` is already resolved by the host. The SDK also reflects this onto `document.documentElement` in two ways:

-   **`data-color-scheme`** **attribute** — use it in CSS selectors:`[data-color-scheme="dark"] .my-panel { background: #222; }   `
-   **`color-scheme`** **CSS property** — enables `light-dark()` and makes native form controls/scrollbars match:`.my-panel { background: light-dark(#fff, #222); }   `
    

For non-CSS decisions (image sources, syntax-highlighting presets, third-party widget themes) branch on `ctx.colorScheme` directly:

```jsx
<img src={ctx.colorScheme === 'dark' ? logoDark : logoLight} />
```

### Deprecated: `ctx.theme` and the legacy CSS variables

`ctx.theme` is still present and its shape is unchanged, but the host now **pins it to light-mode values only**, regardless of what theme the user has selected. The CSS variables derived from it (`--accent-color`, `--primary-color`, `--light-color`, `--dark-color`, `--semi-transparent-accent-color`) likewise always emit light values.

The older structural CSS variables emitted by `Canvas` (`--base-body-color`, `--border-color`, `--alert-color`, etc.) are also deprecated. They still resolve to the nearest semantic token so they follow the active theme, but they will be removed in a future major version.

---

## Upgrade steps

###### 1\. Update your dependencies

Terminal window

```bash
npm install datocms-react-ui@latest datocms-plugin-sdk@latest
```

###### 2\. Test in dark mode

This is the most important step. Upgrade immediately opts your plugin into the host's active theme. Open the DatoCMS app in dark mode and open your plugin — it will now render with dark colors.

Things to audit:

-   **Hardcoded colors in your CSS** — `color: #333`, `background: white`, etc. They won't follow the theme and contrast will break.
-   **Hardcoded SVG fills** in custom icons — switch to `fill="currentColor"` so they inherit the surrounding text color.
    
-   **Custom CSS mixed with library components** — verify the combinations look right in both light and dark.
-   **Inlined** **`style`** **props** with color values — treat the same as hardcoded CSS.
    

###### 3\. Replace deprecated CSS variables (optional now, required later)

If your plugin uses any of the legacy CSS variables in custom CSS, replace them with the semantic equivalents from the table below. The legacy vars still work for now but don't follow the active theme and will be removed in a future major.

###### 4\. Replace `ctx.theme` reads (optional now, required later)

If your plugin reads `ctx.theme` directly to build colors or styles, migrate to `ctx.cssDesignTokens`. If you use `datocms-react-ui`, `Canvas` applies the tokens for you and you just use `var(--color--*)` in your CSS.

---

## CSS variable migration table

| Legacy CSS variable | Replace with |
| --- | --- |
| \--base-body-color | \--color--ink |
| \--light-body-color | \--color--ink-subtle |
| \--placeholder-body-color | \--color--ink-placeholder |
| \--light-bg-color | \--color--surface-muted |
| \--lighter-bg-color | \--color--surface-muted |
| \--disabled-bg-color | \--color--disabled--surface |
| \--border-color | \--color--border |
| \--darker-border-color | \--color--border-hover |
| \--alert-color (as text) | \--color--danger-soft--ink |
| \--alert-color (as background) | \--color--danger-soft--surface |
| \--warning-color | \--color--warning-soft--ink |
| \--notice-color | \--color--success-soft--ink |
| \--warning-bg-color | \--color--warning-soft--surface |
| \--add-color (as background) | \--color--diff-added--surface |
| \--remove-color (as background) | \--color--diff-removed--surface |
| \--accent-color (as background) | \--color--primary--surface-secondary |
| \--accent-color (as text) | \--color--ink-link |
| \--primary-color | \--color--primary--surface |
| \--light-color | \--color--primary-soft--surface |
| \--dark-color | \--color--primary--surface-secondary (or --color--primary--ink if used as text) |
| \--semi-transparent-accent-color | \--color--focus--outline (focus rings) |

> [!WARNING] A few edge cases
> `--alert-color`, `--add-color`, and `--remove-color` were used as both foreground and background in the wild. Pick the semantic token that matches your actual usage.
> 
> **Migrate the accent group even if you only support light mode today.** `--accent-color`, `--primary-color`, `--light-color`, `--dark-color`, and `--semi-transparent-accent-color` are derived from `ctx.theme`, which is now pinned to light values only. They will be removed in a future major.

---

## Adding custom colors that respect the theme

Reserve custom colors for things genuinely outside the design system — brand illustrations, data-viz palettes, vendor-specific UI. Most needs are already covered by the `--color--*` tokens.

When a custom color is justified, define it once per theme using the `[data-color-scheme="dark"]` attribute the SDK already sets on `<html>`:

```css
.my-plugin {
  --my-brand: #4a90e2;
}

[data-color-scheme="dark"] .my-plugin {
  --my-brand: #6aa9ec;
}

.my-plugin__cta {
  background: var(--my-brand);
  color: var(--color--primary--ink);
}
```

On modern browsers, the CSS `light-dark()` function is a more concise alternative:

```css
.my-plugin__cta {
  background: light-dark(#4a90e2, #6aa9ec);
  color: var(--color--primary--ink);
}
```

## Related content in "Plugin SDK"

- [Introduction to the DatoCMS Plugin SDK](https://www.datocms.com/docs/plugin-sdk/introduction.md)
- [Build your first DatoCMS plugin](https://www.datocms.com/docs/plugin-sdk/build-your-first-plugin.md)
- [Real-world examples](https://www.datocms.com/docs/plugin-sdk/real-world-examples.md)
- [What hooks are](https://www.datocms.com/docs/plugin-sdk/what-hooks-are.md)
- [Config screen](https://www.datocms.com/docs/plugin-sdk/config-screen.md)
- [Custom pages](https://www.datocms.com/docs/plugin-sdk/custom-pages.md)
- [Sidebars and sidebar panels](https://www.datocms.com/docs/plugin-sdk/sidebar-panels.md)
- [Outlets](https://www.datocms.com/docs/plugin-sdk/form-outlets.md)
- [Field extensions](https://www.datocms.com/docs/plugin-sdk/field-extensions.md)
- [Manual field extensions](https://www.datocms.com/docs/plugin-sdk/manual-field-extensions.md)
- [Dropdown actions](https://www.datocms.com/docs/plugin-sdk/dropdown-actions.md)
- [Structured Text customizations](https://www.datocms.com/docs/plugin-sdk/structured-text-customizations.md)
- [Asset sources](https://www.datocms.com/docs/plugin-sdk/asset-sources.md)
- [Opening modals](https://www.datocms.com/docs/plugin-sdk/modals.md)
- [Event hooks](https://www.datocms.com/docs/plugin-sdk/event-hooks.md)
- [Customize record presentation](https://www.datocms.com/docs/plugin-sdk/customize-presentation.md)
- [React UI Components](https://www.datocms.com/docs/plugin-sdk/react-datocms-ui.md)
- [Button](https://www.datocms.com/docs/plugin-sdk/button.md)
- [Button group](https://www.datocms.com/docs/plugin-sdk/button-group.md)
- [Dropdown](https://www.datocms.com/docs/plugin-sdk/dropdown.md)
- [Form](https://www.datocms.com/docs/plugin-sdk/form.md)
- [Section](https://www.datocms.com/docs/plugin-sdk/section.md)
- [Sidebar panel](https://www.datocms.com/docs/plugin-sdk/sidebar-panel.md)
- [Spinner](https://www.datocms.com/docs/plugin-sdk/spinner.md)
- [Toolbar](https://www.datocms.com/docs/plugin-sdk/toolbar.md)
- [Sidebars and split views](https://www.datocms.com/docs/plugin-sdk/sidebars-and-split-views.md)
- [Additional permissions](https://www.datocms.com/docs/plugin-sdk/additional-permissions.md)
- [Working with form values](https://www.datocms.com/docs/plugin-sdk/working-with-form-values.md)
- [Publishing to Marketplace](https://www.datocms.com/docs/plugin-sdk/publishing-to-marketplace.md)
- [Releasing new plugin versions](https://www.datocms.com/docs/plugin-sdk/releasing-new-plugin-versions.md)
- [Migrating from legacy plugins](https://www.datocms.com/docs/plugin-sdk/migrating-from-legacy-plugins.md)
- [Upgrading plugins for dark mode](https://www.datocms.com/docs/plugin-sdk/upgrading-plugins-for-dark-mode.md)