Skip to main content

Theming & UI Styles

Matching @ydesign/react-editor to your own brand is one of the most common integration needs. This page covers the styling stack, how to tweak theme colors, how to switch on dark mode, and how to override individual component styles.

Styling stack at a glance

@ydesign/react-editor doesn't lock you into a single UI framework — it combines several proven tools and lets each do what it's best at:

LayerTechnologyPurpose
Component libraryAnt Design v6Buttons, dropdowns, modals, inputs, etc.
Design tokensAntd Design Token (theme.useToken())Runtime-readable design variables: color, radius, font, spacing
Atomic classesTailwind CSS v4Layout, spacing, flex utilities
Layout containersstyled-componentsDesignEditorContainer, SidePanelWrap, WorkspaceWrap, etc.
Canvas coreStyle-free@ydesign/core ships no CSS at all

In practice, you have three independent levers when theming: Antd ConfigProvider, Tailwind dark mode, and raw CSS overrides.

1. Configure theme colors via ConfigProvider

This is the recommended starting point. Wrap the editor in Antd's <ConfigProvider /> — every component inside will follow the new tokens automatically:

import { ConfigProvider, theme } from 'antd';
import { DesignEditorApp } from '@ydesign/react-editor';

export default function App() {
return (
<ConfigProvider
theme={{
token: {
colorPrimary: '#ff6a00', // brand primary
colorInfo: '#ff6a00',
borderRadius: 6, // global corner radius
fontFamily: 'Inter, -apple-system, sans-serif',
},
}}
>
<DesignEditorApp store={store} />
</ConfigProvider>
);
}

Internally, most components read theme tokens like this:

import { theme } from 'antd';

const { token } = theme.useToken();
// token.colorPrimary, token.colorBgContainer, token.borderRadius, ...

So whenever you adjust tokens via ConfigProvider, the side panels, toolbar, and top bar pick up the change — no extra CSS required.

2. Enabling dark mode

Dark mode in @ydesign/react-editor is driven by two coordinated switches: Antd's dark algorithm and Tailwind's dark variant.

2.1 Antd dark algorithm

import { ConfigProvider, theme } from 'antd';

<ConfigProvider
theme={{
algorithm: theme.darkAlgorithm, // 👈 the key bit
token: {
colorPrimary: '#ff6a00',
},
}}
>
<DesignEditorApp store={store} />
</ConfigProvider>;

2.2 Tailwind dark mode

Tailwind is configured with darkMode: ['class', '[data-theme="dark"]'], meaning any ancestor with data-theme="dark" (or the .dark class) will activate Tailwind's dark: variants.

The simplest approach is to set an attribute on <html> or <body>:

<html data-theme="dark">
<!-- ... -->
</html>

Or toggle at runtime:

document.documentElement.setAttribute('data-theme', 'dark');
// Back to light
document.documentElement.removeAttribute('data-theme');

2.3 Dark / light toggle example

A full theme toggle you can copy:

import { useState } from 'react';
import { ConfigProvider, theme, Button } from 'antd';
import { DesignEditorApp } from '@ydesign/react-editor';

export default function App({ store }) {
const [isDark, setIsDark] = useState(false);

const toggle = () => {
const next = !isDark;
setIsDark(next);
document.documentElement.setAttribute('data-theme', next ? 'dark' : 'light');
};

return (
<ConfigProvider
theme={{
algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm,
token: { colorPrimary: '#ff6a00' },
}}
>
<Button onClick={toggle}>Toggle theme</Button>
<DesignEditorApp store={store} />
</ConfigProvider>
);
}

3. Override local component styles

For details that Antd tokens can't reach (the side panel tab's active background, a specific button's hover color, etc.), plain CSS overrides work well. Every internal component carries a stable class name you can target safely.

3.1 Stable class names

ClassComponent
.polotno-app-containerOutermost editor container (DesignEditorApp)
.polotno-side-tabs-containerSide tab container (vertical icon column)
.polotno-side-panel-tabEvery side tab button
.polotno-side-panel-tab.activeActive tab
.polotno-font-itemFont item in the Text panel
.polotno-close-panelClose button at the top-right of a side panel

ℹ️ The polotno- prefix above is a historical naming from an earlier iteration. It will be migrated to ydesign- over time, with aliases to preserve backwards compatibility.

3.2 Examples

/* Custom hover / active styles for side tabs */
.polotno-side-tabs-container .polotno-side-panel-tab:hover,
.polotno-side-tabs-container .polotno-side-panel-tab.active {
background-color: #ff6a00;
color: #fff;
}

/* Active tab highlight under dark mode */
[data-theme='dark'] .polotno-side-panel-tab.active {
background-color: #ff8a3d;
}

/* Editor background */
.polotno-app-container {
background: #1e1e1e;
}

/* Workspace with a radial-gradient backdrop */
.polotno-app-container .polotno-workspace-container {
background: radial-gradient(circle, #2a2a2a, #181818);
}

3.3 Finding class names

When you need to tweak something specific but can't locate the class, just open DevTools → inspect the element. @ydesign/react-editor uses styled-components heavily, so generated class names include hashes — but the stable parent classes listed above are enough to build a precise selector.

4. Font theming

The UI font follows Antd's fontFamily token. The fonts of elements on the canvas are independent and managed via store.fonts and global fonts (see Editor Configuration · Font Management).

<ConfigProvider
theme={{
token: {
fontFamily: '"PingFang SC", "Helvetica Neue", sans-serif',
},
}}
>
<DesignEditorApp store={store} />
</ConfigProvider>

5. Follow the system color scheme

To mirror the user's OS setting:

import { useEffect, useState } from 'react';

function useSystemTheme() {
const [isDark, setIsDark] = useState(() => window.matchMedia('(prefers-color-scheme: dark)').matches);

useEffect(() => {
const mq = window.matchMedia('(prefers-color-scheme: dark)');
const handler = (e: MediaQueryListEvent) => setIsDark(e.matches);
mq.addEventListener('change', handler);
return () => mq.removeEventListener('change', handler);
}, []);

useEffect(() => {
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
}, [isDark]);

return isDark;
}

Use it from your top-level component:

const isDark = useSystemTheme();

<ConfigProvider
theme={{
algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm,
token: { colorPrimary: '#ff6a00' },
}}
>
<DesignEditorApp store={store} />
</ConfigProvider>;

6. Things to keep in mind

  1. Don't override .ant-* internals directly. Those class names are implementation details and can change between Antd versions. Stick to ConfigProvider for theming.
  2. Tailwind utilities require Tailwind in the host project. If your project doesn't use Tailwind, @ydesign/react-editor still works — the editor's own styles are pre-bundled.
  3. You must import the style sheet once at your app entry:
    import '@ydesign/react-editor/style.css';
    Otherwise components will render unstyled.
  4. SSR: set data-theme on the server before hydration to avoid a light→dark flash on first paint.

Further reading