Diagram - Styling and theming
Build scoped visual systems with theme roles, custom node content and composable copied controls.
#Usage
import { Diagram } from '@primeui/vue-diagram';<Diagram.Root :store="store" page-id="page">
<Diagram.Viewport label="Workflow editor" style="height: 24rem" />
</Diagram.Root>This skeleton assumes an initialized store with page page. Use
First Diagram for model creation,
style imports and store disposal. This guide teaches practical customization;
Theming reference lists the complete token defaults
and contracts.
#Choose the styling layer
| Layer | Use it for |
|---|---|
| Diagram roles | Canvas, node/connector/text colors, selection, guides, previews, overlays and shared panel spacing. |
| Public state and identity hooks | One node, one connector, selected/unavailable content or an interaction surface. |
| Node definitions and copied UI parts | Product content, controls, panels, typography and their composition. |
| Saved geometry and artwork | Shape type, bounds, rotation, symbol paints, labels, dash patterns and decorators. |
| Export options and adapters | Explicit portable output, including custom HTML content. |
Keep runtime geometry and editing behavior in Diagram. CSS and copied parts own presentation. A local theme change creates no model edit or history entry.
#Default visual language
Both copied presets share the overview's neutral surfaces, subtle shape borders,
rounded corners and compact floating controls. Surface and primary theme changes
follow the surrounding application. PrimeOne controls remain PrimeVue components;
Tailwind controls remain HTML and headless parts. The shared stylesheet bundles
Geist and applies it to the canvas, copied editor and workspace overlays.
--p-diagram-font-family exposes the inherited family for application styling;
Geist is the default in both presets. Saved label font families remain authored
data, and export font embedding is configured separately.
The defaults are optional CSS. Override the roles on a common ancestor to tune the whole editor without changing the model:
.product-diagram {
--p-diagram-editor-radius: 0.875rem;
--p-diagram-editor-overlay-radius: 0.75rem;
--p-diagram-editor-control-radius: 0.4375rem;
--p-diagram-node-card-radius: 14px;
}Node-stroke and node-fill apply consistently to rectangles, ellipses, diamonds and theme-painted symbols. Override node-stroke once to change all of their outlines. Explicit saved symbol paints retain their authored values. The card-radius role affects ordinary rectangular nodes only; notation artwork, frames, lanes and compartments keep their geometry. CSS radius does not alter exported geometry; authored rounded shapes belong in a saved symbol.
#Start with a local override
Load shared styles once, then put application overrides after them. Both presets use Diagram roles and the shared structural stylesheet.
@import '@primeui/diagram-style/style.css';
@import '@primeui/diagram-style/themes/primeone.css';
.review-diagram {
--p-diagram-node-fill: light-dark(#eff6ff, #172554);
--p-diagram-node-stroke: light-dark(#1e40af, #93c5fd);
--p-diagram-connector: light-dark(#1e40af, #93c5fd);
--p-diagram-selection: light-dark(#1d4ed8, #bfdbfe);
--p-diagram-adornment-surface: var(--p-diagram-node-fill);
}Put the class on the root, or on a common wrapper around the root and copied
controls. A viewport override affects nodes, labels and graphics inside it;
sibling toolbars and panels need their own scope or a shared ancestor.
light-dark() follows the applicable color-scheme; use your application's
light/dark mode configuration rather than forcing every example into one mode.
#Coordinate a design system
A visual system combines surfaces, foregrounds, focus, node content and controls. The following designs use the same saved workflow, routes, ports and interaction commands. Switching designs preserves selection, moved geometry and history. Reset diagram restores the fixture while retaining the chosen design.
| Design | Token decisions | Markup decisions |
|---|---|---|
| Review | Warm neutral canvas, blue interaction color, amber follow-up path. | Start-aligned title, description and ownership footer. |
| Blueprint | Blue canvas and outlines, cyan focus, smaller panel/content insets and control radius. | Centered title and technical metadata; the description is hidden. |
| Dispatch | Green working surfaces, clear focus, coral follow-up path and tighter control groups. | Start-aligned content with separate status surfaces. |
Try Workflow steps, drag a node, use Undo, toggle Grid/Rulers, collapse the sidebar and switch the surrounding showcase color mode. Selection and connector handles retain the same behavior in every design. The dashed follow-up path is saved connector data; only its ordinary color is local CSS. The diamond is saved shape geometry; the custom task content is HTML.
Define application tokens and map coordinated roles on the same scope:
.product-diagram {
--product-surface: light-dark(#fffdf8, #201e1a);
--product-text: light-dark(#292a30, #f4f0e7);
--product-accent: light-dark(#3759a1, #afc5ff);
--p-diagram-editor-surface: var(--product-surface);
--p-diagram-editor-primary: var(--product-accent);
--p-diagram-editor-focus: var(--product-accent);
--p-diagram-editor-hover: color-mix(in srgb, var(--product-accent) 9%, var(--product-surface));
--p-diagram-editor-active: color-mix(in srgb, var(--product-accent) 16%, var(--product-surface));
--p-diagram-editor-text: var(--product-text);
--p-diagram-text: var(--product-text);
--p-diagram-selection: var(--product-accent);
--p-diagram-guide-color: var(--product-accent);
--p-diagram-preview-fill: color-mix(in srgb, var(--product-accent) 12%, transparent);
--p-diagram-overlay-background: var(--product-surface);
}CSS aliases resolve where they are declared. The global theme's selection role
references primary, but changing only a descendant's primary does not recompute
that inherited selection value. Likewise, changing node-fill alone does not
recompute inherited adornment-surface. Map dependent roles locally when changing
a complete design. This also keeps nested or separate instances predictable:
redeclare the roles that differ on the inner instance, and its siblings retain
the outer theme. Avoid unscoped button or .p-diagram-viewport rules in global
styles. In Vue scoped styles, use :deep(...) when targeting descendant
runtime markup, or scope an unscoped rule under your application class.
#Build a gradient studio
Signal studio gives the same headless canvas a more expressive visual language: gradient-edged HTML nodes, tinted surfaces, curved connections and a floating PrimeVue toolbar. The five-node response pipeline is sample content; it does not connect to a model, run a workflow or call external services.
Switch Spectrum to Theme accent, then change the showcase primary or surface palette. Both designs follow the surrounding light/dark mode. Toggle Gradients to compare the treatment with plain surfaces. Styling choices keep the same store, selection and edit history. Drag a node, switch the palette, then Undo to verify the separation. Reset graph restores the fixture and clears history while retaining the palette and gradient choice.
The demo maps two local accent roles into Diagram selection, guides and connector
colors. Gradient borders and card faces belong to its HTML node definition;
they do not alter the saved rectangular geometry. The floating controls use
PrimeVue buttons with the public Diagram.CommandButton part. All CSS is scoped to
.signal-studio, including the separately mapped PrimeVue button roles.
.signal-studio {
--signal-a: light-dark(#087e96, #67e8f9);
--signal-b: light-dark(#7c3aed, #c4b5fd);
--signal-surface: var(--p-content-background);
--signal-face: linear-gradient(135deg, color-mix(in srgb, var(--signal-a) 6%, var(--signal-surface)), var(--signal-surface) 65%);
--signal-rim: linear-gradient(130deg, var(--signal-a), var(--signal-b));
--p-diagram-node-card-radius: 16px;
}
.signal-studio .signal-card {
border: 1px solid transparent;
border-radius: var(--p-diagram-node-card-radius, 8px);
background:
var(--signal-face) padding-box,
var(--signal-rim) border-box;
}On narrow canvases, the initial view keeps the central node readable. Next node selects and reveals each node at 100%; Fit pipeline shows the whole graph. Pan and zoom remain available. These sample cards have fixed sizes and do not resize or rotate. Their HTML, gradients and CSS palettes need explicit content adapters and resolved paints for portable export; see Carry a design into export.
#Style content and a specific path
Node definitions replace content while the runtime retains shape rendering, geometry, selection and clipping. The copied content shell is optional:
import { Diagram } from '@primeui/vue-diagram';
import { DiagramNodeContentUI } from '@ui/diagram';<Diagram.Root class="product-diagram" :store="store" page-id="page">
<Diagram.NodeDef node-type="task" v-slot="{ node }">
<DiagramNodeContentUI align="start">
<template #header><strong>{{ node.data?.['title'] }}</strong></template>
<span>{{ node.data?.['detail'] }}</span>
<template #footer><small>{{ node.data?.['owner'] }}</small></template>
</DiagramNodeContentUI>
</Diagram.NodeDef>
<Diagram.Viewport label="Workflow" style="height: 24rem" />
</Diagram.Root>.product-diagram {
--p-diagram-node-content-padding: 0.875rem;
--p-diagram-node-content-gap: 0.5rem;
}
.product-diagram [data-node-id='revise'] {
--p-diagram-node-fill: light-dark(#fff0da, #3a291b);
--p-diagram-node-stroke: light-dark(#9a4d10, #ffd09f);
}
.product-diagram [data-connector-id='follow-up'] {
--p-diagram-connector: light-dark(#9a4d10, #ffd09f);
}Selected/focused connectors use the selection role instead of their ordinary
color. Their saved dash pattern and decorators remain intact. Use documented
data attributes and runtime state classes;
Diagram has no universal data-slot contract. Node selection is
data-selected='true', not merely the presence of that attribute.
The definition does not replace shape geometry or saved labels. Reserve room for labels when combining them with rich content, and avoid broad fallback content that hides a notation's own compartment or lane headers. For an Vue component, native editable fields or measured ports, use Node UI. For portable artwork and authored boundaries, use Shapes and Symbols.
#Compose and size copied controls
The showcase composes DiagramEditorUI, independent tools/history/zoom/aids,
a copied sidebar and application-owned step buttons. You can edit those copied
files or replace their markup while keeping public runtime commands and contexts.
A focused control composition can omit the full editor shell:
import { Diagram } from '@primeui/vue-diagram';
import { DiagramToolsUI, DiagramCommandGroupUI, DiagramZoomControlsUI } from '@ui/diagram';<Diagram.Root class="product-diagram" :store="store" page-id="page">
<div class="product-tools" role="group" aria-label="Workflow controls">
<DiagramToolsUI />
<DiagramCommandGroupUI :command-ids="['undo', 'redo', 'fit']" />
<DiagramZoomControlsUI />
</div>
<Diagram.Viewport label="Workflow" style="height: 28rem" />
</Diagram.Root>.product-diagram {
--p-diagram-ui-panel-padding: 0.875rem;
--p-diagram-ui-section-gap: 1rem;
--p-diagram-ui-group-gap: 0.5rem;
--p-diagram-ui-label-gap: 0.25rem;
--diagram-inspector-width: 18rem;
--diagram-control-height: 2.5rem;
}
.product-diagram .product-tools {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--p-diagram-ui-group-gap);
}Comfortable shell dimensions inherit these overrides. Compact density supplies
its own local panel/inspector/control sizes; target the copied editor to override
that preset, as shown in the reference.
Keep one inset owner for nested panels. The shell stacks non-collapsible panels at narrow widths,
but fixed node bounds remain world geometry; fit/zoom determines their screen size.
Changing density does not relayout the model.
After resizing, use Fit for an overview; zoom in to read node content on a narrow
screen. Fitting a wide saved workflow does not make its text a responsive HTML
layout.
PrimeOne copied controls also consume PrimeVue component themes. A Diagram accent does not automatically redefine every PrimeVue button, accordion or body-appended select overlay. Style those copied controls through their dependency's theme or local markup/classes. The Design Systems demo maps its PrimeVue Button tokens on the same wrapper as its Diagram roles. Primary filled/text controls follow the local accent, secondary text follows the local muted foreground, and focus/hover/pressed states use that design's interaction roles. Those dependency tokens are a separate mapping; they are not Diagram runtime tokens. Keep body-appended select overlays in their own theme scope. Tailwind copied parts use HTML/headless controls and shared Diagram roles. Exact projection regions, model scopes and optional imports are listed in UI parts. See Editor composition and Toolbar and actions for working replacements.
#Keep theme and authored paint separate
This focused example retains an explicit saved label color. Change the local roles, then select/focus the decision and switch the showcase light/dark mode.
An omitted label color inherits --p-diagram-text. A saved
font.color: '#92400e' remains that authored foreground, including in a dark
theme. This example supplies a scoped pale label surface to keep it readable;
that CSS background is not a saved label field. Saved font family/size/weight
also take their rendering precedence over inherited text styling.
Symbols resolve semantic nodeFill and currentColor against theme roles;
explicit supported hexadecimal paint stays authored. CSS changes never update
saved symbol paint, label color, node bounds or connection geometry. Avoid using
!important to erase intentional authored color when the model should retain
it. After font changes that alter metrics, invalidate
measurements.
#Carry a design into export
Rooted export and print capture the selected viewport's rendered templates and
CSS. Omit colorScheme to follow the source, or choose 'light' or 'dark'
through baseOptions on the output host. Independent controls use options
and an explicit source. The copied panel offers Follow app, Light, and Dark.
Leave background at Canvas default to preserve an explicit base background or
use the export canvas after mode selection.
CSS variables and light-dark() re-evaluate under the export mode. Application
bindings based on a Vue color-mode ref retain their captured values and branches. Use CSS
tokens for template colors that must change with the export color scheme.
The live app theme remains unchanged. See Export
for supported theme conditions, resources, and failure limits.
Detached portable exports retain explicit paint options. For those calls, supply resolved colors rather than CSS variables:
import { type DiagramExportOptions } from '@primeui/vue-diagram/export';
const reviewOutput: DiagramExportOptions = {
theme: {
nodeFill: '#fffdf8',
nodeStroke: '#79715f',
connector: '#79715f',
text: '#292a30',
surface: '#fffdf8'
},
styles: {
revise: { fill: '#fff0da', stroke: '#9a4d10' },
'follow-up': { stroke: '#9a4d10' }
}
};Use these options only for detached calls without a source. Declare custom
HTML types with customTypes and provide portable body/content adapters.
Authored label/symbol paint beats portable theme defaults. A styles entry
can override supported entity/label paint. Source-backed calls reject theme
paint objects and styles, including empty overrides. Keep actual templates
for rendered export instead of adding a second representation.
See Export for detached adapter examples and
Print for both preparation paths.
#Check interaction and media states
Review ordinary, selected, keyboard-focused, hovered, disabled, unavailable and pending states in both color modes. Include port/handle contrast, hollow marker surfaces, inline label editing, grid/ruler legibility and invalid-connection feedback. Keep selection distinct from the visible keyboard focus treatment. Avoid changing runtime positions or hit areas through decorative CSS; the scene still owns bounds, routes, clipping and handle geometry.
Shared styles reduce their owned transitions under
prefers-reduced-motion: reduce; custom animation needs its own handling.
There is no shared forced-colors profile that certifies every authored symbol,
application node or dependency control. See Accessibility
for the actual interaction contract and validation limits.