Appearance
Customize PPTX editor appearance with CSS color tokens, dark mode, borders, spacing, and control states.
Put a theme class on a wrapper around PptxEditor.Root, and set --doc-* properties on .acme-theme .pptx-editor. Each .pptx-editor scope declares its own defaults, so a property set on the wrapper alone does not reach the chrome. Load your CSS after the editor stylesheet.
Set colorMode on PptxEditor.Root for dark controls. Only the chrome changes: the slide keeps the colors that the file defines, in both modes.
Apply an application color theme
PPTX Editor uses the same --doc-* token names as DOCX Editor, so one override themes both products. Put your theme class on an element around PptxEditor.Root, and scope the tokens to every .pptx-editor inside it. Dialogs render outside PptxEditor.Viewport with their own .pptx-editor scope, so a rule on the viewport alone does not reach them.
.acme-theme .pptx-editor,
.acme-theme .pptx-editor.dark {
--doc-primary: var(--app-brand);
--doc-primary-light: var(--app-brand-surface);
--doc-on-primary: var(--app-on-brand);
--doc-focus-ring: var(--app-focus-ring);
}Define light and dark application values for the mapped tokens. The shared rule keeps both editor themes connected to your application's active palette.
Style your controls
Keep CSS under your wrapper class. These tokens and selectors work in both frameworks.
.acme-theme .pptx-editor {
--doc-font-ui: 'Inter', system-ui, sans-serif;
--doc-radius-surface: 16px;
--doc-radius-control: 8px;
--doc-dialog-radius: 16px;
--doc-dialog-padding: 24px;
}
.acme-theme .pptx-toolbar__button[data-active] {
background: var(--doc-primary-light);
}
.acme-theme .pptx-toolbar__button[data-disabled] {
opacity: 0.5;
}A part that you build yourself gets the tokens from the .pptx-editor scope. Inside PptxEditor.Viewport, the viewport gives that scope. Outside the viewport, put the classes from useScopeClassName() on the root element of your part. The hook returns '' inside the viewport, and 'pptx-editor ' or 'pptx-editor dark ' outside it:
import { useScopeClassName } from '@pptx-editor.dev/react';
export function AcmePanel() {
return <aside className={`${useScopeClassName()}acme-panel`} />;
}In Vue, useScopeClassName() returns a computed ref. A part that you portal to document.body is outside the viewport in the page, so add pptx-editor to it yourself, with useEditorColorMode() for the theme.
To style one dialog, pass a class through its popups renderer. The class goes on the dialog element, beside .pptx-dialog:
import { PptxEditor, type PptxEditorPopups } from '@pptx-editor.dev/react';
const popups: PptxEditorPopups = {
pageSetup: (props) => <PptxEditor.PageSetupDialog {...props} className="acme-dialog" />,
};Appearance options
| Area | Configuration | Scope |
|---|---|---|
| Interface palette | Override --doc-* properties on .acme-theme .pptx-editor. | Toolbar, menus, panes, dialogs, and notices. |
| Utility palette | --foreground and --radius. | The light active toggle and the slide show controls. |
| Dark mode | Set colorMode on PptxEditor.Root: light (default), dark, or system. | The chrome, not the slide. |
| Typography | --doc-font-ui and the dialog font properties. | The whole chrome: toolbar, menus, panes, notes, and dialogs. Not slide fonts. |
| Corner radius | --doc-radius-surface, --doc-radius-control, --doc-radius-pill, and the dialog radius properties. | Cards, controls, pills, and dialogs. |
| Control shape | Add className to named parts. | The selected part and its interaction states. |
| Custom elements | Use a supported part's asChild. | Your element receives the part's behavior. |
| Slide appearance | Use the deck's theme, layouts, and formatting commands. | Slide content and saved formatting. |
Interface palette reference
Set CSS color values for these properties. Override related foreground and background properties together. The default stylesheet defines separate light and dark palettes. This table lists only the properties that a PPTX Editor rule reads.
| Purpose | Properties |
|---|---|
| Main surfaces | --doc-surface, --doc-popover-bg, --doc-bg (the workspace behind the slide) |
| Hover and input surfaces | --doc-bg-hover (also the hovered or open font family field), --doc-bg-input |
| Primary actions | --doc-primary, --doc-primary-hover (the hovered primary dialog button), --doc-primary-light, --doc-on-primary |
| Active toggles | --doc-toggle-active-bg, --doc-toggle-active-fg |
| Text and icons | --doc-text, --doc-popover-fg, --doc-text-muted, --doc-text-subtle (disabled icons) |
| Tooltips | --doc-tooltip-bg, --doc-tooltip-text |
| Borders | --doc-border, --doc-border-dark |
| Errors | --doc-error, --doc-error-bg |
| Warnings | --doc-warning, --doc-warning-bg, --doc-warning-text |
| Focus and selection | --doc-focus-ring, --doc-selection, --doc-selection-mark (the outlines, marquee, and handle edges of a selection on the slide) |
| Caret and slide guides | --doc-caret, --doc-smart-guide, --doc-cursor-format-paint |
| Collaboration accents | --doc-accent |
| Behind the slide | --doc-page-bg (rail thumbnails), --doc-page-bg-rendered (the canvas stage) |
| Overlay | --doc-overlay |
| Complete shadow values | --doc-card-shadow (the toolbar, rail, notes, and dialogs), --doc-card-shadow-strong (menus and popovers), --doc-shadow-lg (tooltips) |
| Slide show | --doc-slideshow-bg, --doc-slideshow-black, --doc-slideshow-white, --doc-slideshow-text, --doc-slideshow-bar-bg, --doc-slideshow-bar-hover, --doc-slideshow-focus |
The shadow properties accept CSS box-shadow values. Tokens such as --doc-bg-subtle and --doc-border-light are defined, but do not affect the editor's built-in styles.
Utility palette reference
The stylesheet defines shadcn HSL properties. Only these two properties affect the built-in controls:
| Property | Value type | Use |
|---|---|---|
--foreground | HSL components, such as 222.2 84% 4.9% | The active toggle background in light mode (through --doc-toggle-active-bg). |
--radius | CSS length | The corner radius of the slide show controls. |
Other names, such as --primary and --ring, do not affect the built-in controls.
Size, typography, and layer reference
| Property | Value type | Default or behavior |
|---|---|---|
--doc-font-ui | Font stack | The chrome font: the toolbar, menus, panes, notes, and dialogs. Falls back to the system font. Slide text keeps the deck's fonts. |
--doc-dialog-font-family | Font stack | Falls back to --doc-font-ui. |
--doc-dialog-font-size | CSS length | 13px. |
--doc-radius-surface | CSS length | The corners of the floating cards: the toolbar, menus, the slide rail, the notes pane, the side panel, the find bar, and the notices. Each keeps its own value when unset: 10px to 14px. |
--doc-radius-control | CSS length | The corners of the controls inside them: buttons, fields, menu rows, tabs, the notes field, and tooltips. Each keeps its own value when unset: 6px to 8px. |
--doc-radius-pill | CSS length | The corners of the rounded pills: the zoom pill, its buttons and zoom level, and the chips in the side panel and dialogs. Defaults to 999px, a full pill. |
--doc-dialog-radius | CSS length | Dialog panel. Falls back to --doc-radius-surface, then 12px. |
--doc-dialog-control-radius | CSS length | Dialog buttons and fields. Falls back to --doc-radius-control, then 8px. |
--doc-dialog-padding | One CSS length | Dialog inset: 24px. |
--doc-slide-padding | One CSS length | The room around the slide in the canvas, on each side: 24px. Zoom to fit keeps it. |
--doc-z-overlay | Integer | Menus and popovers: 50. |
--doc-z-context | Integer | Context menus and submenus: 55. |
--doc-z-tooltip | Integer | Chrome tooltips, above every other layer: 60. |
--doc-z-slideshow | Integer | The slide show, when full screen is refused: 2147483000. |
Keep the layer order when you change these values. Some floating chrome uses fixed values: the zoom pill and the font notice use 25, the edit notice 26, and the find bar 30. Keep --doc-z-overlay above 30, so menus open above them. An ancestor's stacking context can still limit an editor overlay.
Theme scope and interaction states
PptxEditor.Viewport carries .pptx-editor. Menus, popovers, and context menus render inside the .pptx-editor element of the control that opens them, so keep the toolbar and the menu bar inside the viewport. Dialogs render outside the viewport and carry .pptx-editor on their own element. Scope your theme on an element around PptxEditor.Root, as in the example above, so both receive it.
Add classes through component props instead of removing the library's classes. Toolbar buttons and menu rows carry data-active when a toggle is on, and data-disabled when the engine refuses the command. The slide rail and the notes pane carry data-open="true" or data-open="false".
A disabled control carries aria-disabled="true", not the native disabled attribute. It stays focusable, runs nothing, and gives its reason to assistive technology, so do not hide it with pointer-events: none or display: none. Keep visible focus indicators and readable text in each theme.
Dark mode behavior
colorMode="system" follows the operating system and updates when it changes. With a dark mode, PptxEditor.Viewport and every dialog carry dark beside .pptx-editor. Declare dark overrides with .acme-theme .pptx-editor.dark after the editor stylesheet.
Use useEditorColorMode() to style your own panels with the mode that PptxEditor.Root resolved. It returns colorMode, which is light or dark. Both adapters export the ColorMode type (light, dark, or system) and the ResolvedColorMode type (light or dark).
In dark mode, the rail's slide numbers, the side panel tabs, and the notes placeholder meet a 4.5:1 contrast ratio. Keep that ratio when you override the dark tokens.
Dark mode never changes the slide, and it never writes a color into the .pptx file.
High contrast themes
With a forced-colors theme (@media (forced-colors: active)), the browser replaces the chrome colors with system colors and removes shadows. The stylesheet then draws each state with system colors:
- A selected slide in the rail and in the slide sorter, and the current layout tile, get a
Highlightborder inside the thumbnail. - Pressed toolbar toggles, the checked view switch option, selected tabs, and the hovered or focused menu row use
HighlightwithHighlightText. - Focus rings use
Highlight. Disabled controls useGrayText, and a disabled pressed toggle gets aGrayTextedge. - Menus, the find bar, the zoom pill, the tooltip, and the notes card get a
CanvasTextedge. - Color swatches and gradient previews keep their own colors (
forced-color-adjust: none).
If you override these states, use system colors in a forced-colors media query of your own.
Each color swatch in the color menus and the Format Background dialog is 24 by 24 pixels, the minimum target size of WCAG 2.5.8. In a dialog that is too narrow for the ten columns, as on a phone, the swatches shrink to 20 by 20 pixels.
React and Vue reference
import { PptxEditor, useEditorColorMode } from '@pptx-editor.dev/react';
import '@pptx-editor.dev/core/styles/editor.css';
function ReviewPanel() {
const { colorMode } = useEditorColorMode();
return <aside className={colorMode === 'dark' ? 'panel panel--dark' : 'panel'} />;
}
export function Workspace({ bytes }: { bytes: Uint8Array }) {
return (
<div className="acme-theme">
<PptxEditor.Root document={bytes} colorMode="dark">
<PptxEditor.Viewport>
<PptxEditor.Toolbar />
<PptxEditor.Content />
</PptxEditor.Viewport>
<ReviewPanel />
</PptxEditor.Root>
</div>
);
}<script setup lang="ts">
import {
PptxEditorContent,
PptxEditorRoot,
PptxEditorToolbar,
PptxEditorViewport,
} from '@pptx-editor.dev/vue';
import '@pptx-editor.dev/core/styles/editor.css';
defineProps<{ bytes: Uint8Array }>();
</script>
<template>
<div class="acme-theme">
<PptxEditorRoot :document="bytes" color-mode="dark">
<PptxEditorViewport>
<PptxEditorToolbar />
<PptxEditorContent />
</PptxEditorViewport>
</PptxEditorRoot>
</div>
</template>In a component inside PptxEditorRoot, useEditorColorMode() returns colorMode as a computed ref.
More control
See the --doc-* token definitions for every token and its light and dark values.