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>:
.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-schemeattribute — use it in CSS selectors:[data-color-scheme="dark"] .my-panel { background: #222; }color-schemeCSS property — enableslight-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:
<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
npm install datocms-react-ui@latest datocms-plugin-sdk@latest2. 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
styleprops 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) |
--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>:
.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:
.my-plugin__cta { background: light-dark(#4a90e2, #6aa9ec); color: var(--color--primary--ink);}