Styling
The designer renders inside Shadow DOM, so your page's CSS doesn't leak into it, and its CSS doesn't leak out. Three things are public, stable API for matching it to your application:
- the
themeattribute:light,darkorauto; - the
--rd-*custom properties (design tokens), set on thereport-designerelement; - the
::part()names of its main regions.
Anything else, such as internal class names, element structure and undocumented custom properties, is internal and can change in any release.
Themes#
<report-designer theme="auto" font-base-url="/rd-fonts/"></report-designer>
| Value | Behavior |
|---|---|
light (default) | The light palette |
dark | The dark palette |
auto | Follows the operating system's prefers-color-scheme, and switches live when the user changes it |
The page being designed always stays white (--rd-page), as it will print.
Design tokens#
Override tokens on the element from your page's stylesheet. Styles on the host element win over the designer's defaults in every theme:
report-designer {
--rd-accent: #0f4c81; /* buttons, selection, active tabs, focus ring */
--rd-font: 'Segoe UI', system-ui, sans-serif; /* UI font (not the document fonts) */
--rd-radius: 4px;
}
/* Only in the dark theme (theme="dark") */
report-designer[theme='dark'] {
--rd-panel: #101418;
--rd-bg: #0b0e12;
}
/* theme="auto" while the system is dark */
@media (prefers-color-scheme: dark) {
report-designer[theme='auto'] {
--rd-panel: #101418;
}
}
One brand color recolors everything. --rd-accent-hover, --rd-accent-soft and --rd-grid are derived from --rd-accent with color-mix() unless you override
them, so report-designer { --rd-accent: #c2410c; } alone gives a consistent orange UI: buttons, hover states, selected rows, focus halos and the canvas grid.
The complete list, with the defaults of each theme:
| Token | Purpose | Light | Dark |
|---|---|---|---|
--rd-font | UI font stack (toolbar, panels, dialogs; not document fonts) | 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif | 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif |
--rd-bg | Application background behind panels | #f4f5f7 | #16181d |
--rd-panel | Toolbar, side panels, dialogs and inputs | #ffffff | #1e2128 |
--rd-panel-2 | Secondary surfaces: panel headers, cards, hover fills | #f8f9fb | #252931 |
--rd-border | Hairline borders and separators | #e1e4ea | #2f343e |
--rd-border-strong | Hovered borders, scrollbar thumbs, dashed placeholders | #c9ced8 | #3c4250 |
--rd-text | Primary text | #1d2433 | #e6e8ee |
--rd-muted | Secondary text: labels, hints, section titles | #6b7385 | #9aa1b0 |
--rd-faint | Tertiary text: placeholders, rulers, counts | #9aa1b0 | #6b7385 |
--rd-accent | Brand/accent: primary button, selection, active tabs, focus ring | #2f6fed | #4c86ff |
--rd-accent-hover | Primary button hover (derived from --rd-accent by default) | color-mix(in srgb, var(--rd-accent) 86%, #000) | color-mix(in srgb, var(--rd-accent) 80%, #fff) |
--rd-accent-soft | Accent tint: active toggles, selected rows, focus halo (derived from --rd-accent by default) | color-mix(in srgb, var(--rd-accent) 12%, #fff) | color-mix(in srgb, var(--rd-accent) 24%, #1e2128) |
--rd-accent-text | Text on accent backgrounds | #ffffff | #ffffff |
--rd-danger | Errors and destructive actions | #d93a3a | #f06464 |
--rd-canvas | Canvas backdrop around the page | #e7e9ee | #111317 |
--rd-page | Paper color of the page on the canvas | #ffffff | #ffffff |
--rd-band-strip | Band label strips on the canvas | #eef0f4 | #262a33 |
--rd-margin | Page margin shading on the canvas | rgba(30, 40, 60, 0.035) | rgba(0, 0, 0, 0.05) |
--rd-grid | Canvas grid lines (derived from --rd-accent by default) | color-mix(in srgb, var(--rd-accent) 9%, transparent) | color-mix(in srgb, var(--rd-accent) 12%, transparent) |
--rd-guide | Smart-guide (snap) lines while dragging | #ff2d8a | #ff4f9f |
--rd-shadow-lg | Elevation of dialogs, drawers, popovers and toasts | 0 12px 32px rgba(16, 24, 40, 0.18) | 0 12px 32px rgba(0, 0, 0, 0.55) |
--rd-radius | Corner radius of buttons and controls | 6px | 6px |
Parts#
Style a whole region with ::part(). Parts inside nested panels are re-exported to the report-designer element, so one selector reaches them:
/* A dark, branded toolbar */
report-designer::part(toolbar) {
background: #0f172a;
border-bottom-color: #0f172a;
}
report-designer::part(toolbar-button) {
color: #cbd5e1;
}
report-designer::part(primary-button) {
background: #f59e0b;
color: #111827;
}
/* A softer canvas backdrop */
report-designer::part(canvas) {
background: #eef2f7;
}
| Part | Region |
|---|---|
::part(toolbar) | Top toolbar |
::part(toolbar-title) | The report icon, name and read-only badge at the left of the toolbar |
::part(toolbar-logo) | The report icon (replace it with your own through the `logo` slot) |
::part(toolbar-name) | The report name. Hide it when your app shows the name itself |
::part(toolbar-button) | Every icon button in the toolbar (also matches the Help button) |
::part(primary-button) | The "Preview PDF" button |
::part(sidebar) | Both side panels (left and right) |
::part(sidebar-left) | Left side panel (Insert / Fields / Layers) |
::part(sidebar-right) | Right side panel (Properties / Report) |
::part(tabs) | Tab strip at the top of each side panel |
::part(tab) | One tab in a tab strip |
::part(insert-panel) | Insert tab content (element palette) |
::part(palette-tile) | One element tile in the palette |
::part(fields-panel) | Fields tab content (data-contract tree) |
::part(layers-panel) | Layers tab content |
::part(properties-panel) | Properties tab content |
::part(report-panel) | Report tab content |
::part(canvas) | Canvas area (scrollable backdrop) |
::part(page) | The page sheet on the canvas |
::part(statusbar) | Bottom status bar |
::part(modal) | Modal dialogs (gallery, PDF import and preview) |
::part(gallery) | The "New from template" dialog |
::part(import) | The "Import PDF" dialog |
::part(preview) | The PDF preview dialog |
::part(help-drawer) | The help side drawer |
::part(toast) | Transient notifications |
::part() selectors can't reach elements inside a part (no ::part(toolbar) button). Use the dedicated parts (toolbar-button, tab, palette-tile)
or the tokens instead.
Recipes#
Hide the report name when your app shows it. If your page has its own header with the report's name, hide the designer's name and keep its icon:
report-designer::part(toolbar-name) {
display: none;
}
::part(toolbar-title) hides the icon, the name and the read-only badge together.
The template still has a name. Keep your field as the source and apply it when you save, for example
designer.addEventListener('save', (e) => store({ ...e.detail.template, name: nameField.value })). Don't assign designer.template on every
keystroke: that loads the template again and clears the undo history.
Use your own icon. Put an image or SVG inside the element with slot="logo". It replaces the default icon and is fitted into a
26 × 26 px box (keeping its aspect ratio):
<report-designer>
<img slot="logo" src="/assets/brand-mark.svg" alt="" />
</report-designer>
The icon is your own element in your page, so it follows your page's styles and Content Security Policy. Style the box around it with
::part(toolbar-logo), for example a background or rounded corners.
Match your app's brand and font.
report-designer {
--rd-accent: #7c3aed;
--rd-font: 'Inter', system-ui, sans-serif;
}
Follow your app's own dark mode switch (instead of the OS setting): set theme from your app's state, for example designer.setAttribute('theme', isDark ? 'dark' : 'light').
A compact, square look.
report-designer {
--rd-radius: 2px;
}
report-designer::part(palette-tile) {
border-radius: 2px;
}
Read-only template viewer with a quiet chrome.
report-designer[read-only]::part(toolbar) {
background: var(--rd-panel-2);
}
Try overrides live in the Playground: open your browser's developer tools and add CSS rules for report-designer.