Use this page when the diagram's appearance belongs to the host application: start with a built-in theme, add per-node or per-edge styling, and connect the canvas to the application's design tokens. The examples render a small workflow, so you can see the fill, stroke, and theme changes on a mounted canvas.
Choose the styling layer
- Use
themefor the canvas-wide palette and defaults.LIGHT_THEMEandDARK_THEMEare shipped themes. - Use
style.strokeWidth,style.fill, andstyle.strokefor one node or edge. - Use
style.styleClasswithdefineStyle()for a reusable named style. Named styles follow the cascadetheme < type-default < named-class < element-inline < state. - Use a token bridge when the host already exposes CSS variables. A bridge makes the diagram read the host's shadcn, MUI, or Tailwind variables instead of maintaining a second palette.
The host element must have a resolved height. A canvas inside a container with no height appears blank; give it 100vh, a flex size, or a sized grid cell.
JavaScript
Install the element and engine packages:
bashnpm install @grafloria/element @grafloria/engine @grafloria/renderer
Mount a real spec with render, then keep the returned DiagramInstance for live changes. This sample starts dark, makes the middle node thicker, applies a named orange style to the first node, and connects the canvas to shadcn tokens.
html<button id="toggle">Toggle theme</button>
<div id="diagram" style="height:400px"></div>
jsimport { render, DARK_THEME, shadcnBridge, defineStyle } from '@grafloria/element';
const host = document.getElementById('diagram');
if (!host) throw new Error('Missing #diagram');
host.style.height = '400px';
defineStyle('warning-step', { fill: '#fed7aa', stroke: '#c2410c', strokeWidth: 2 });
const nodes = [
{ id: 'start', position: { x: 40, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Start' }, style: { styleClass: 'warning-step' } },
{ id: 'review', position: { x: 280, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Review' }, style: { strokeWidth: 5 } },
{ id: 'done', position: { x: 520, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Done' } },
];
const edges = [
{ id: 'start-review', source: 'start', target: 'review' },
{ id: 'review-done', source: 'review', target: 'done', style: { strokeWidth: 3 } },
];
const instance = render({ nodes, edges }, host, { theme: DARK_THEME });
instance.setTokenBridge(shadcnBridge());
instance.renderNow();
The host now contains three visible nodes and two links. The first node uses the named orange style, the second node has a 5px border, and the second link has a 3px stroke. setTokenBridge() changes the instance's palette without changing the node data; renderNow() repaints synchronously.
Use a built-in theme
Set the component's theme prop when the framework owns the diagram. The following snippets use the same nodes and edges in each framework; each host has a real height. The JavaScript example below shows a live theme swap through the instance.
The framework entry points are GrafloriaFlow for React, GrafloriaFlow for Vue, GrafloriaFlow for Qwik, and DiagramCanvasComponent for Angular.
tsximport { GrafloriaFlow, DARK_THEME } from '@grafloria/react';
import type { NodeSpec, EdgeSpec } from '@grafloria/react';
const nodes: NodeSpec[] = [
{ id: 'a', position: { x: 60, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Order' } },
{ id: 'b', position: { x: 300, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Ship' } },
];
const edges: EdgeSpec[] = [{ id: 'e1', source: 'a', target: 'b' }];
export default function StyledFlow() {
return (
<div style={{ height: '100vh' }}>
<GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} theme={DARK_THEME} />
</div>
);
}
vue<script setup lang="ts"> import { GrafloriaFlow, DARK_THEME } from '@grafloria/vue'; import type { NodeSpec, EdgeSpec } from '@grafloria/vue'; const nodes: NodeSpec[] = [ { id: 'a', position: { x: 60, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Order' } }, { id: 'b', position: { x: 300, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Ship' } }, ]; const edges: EdgeSpec[] = [{ id: 'e1', source: 'a', target: 'b' }]; </script> <template> <div style="height:100vh"> <GrafloriaFlow :default-nodes="nodes" :default-edges="edges" :theme="DARK_THEME" /> </div> </template>
tsimport { Component } from '@angular/core';
import { DiagramCanvasComponent } from '@grafloria/angular';
import { DARK_THEME, type NodeSpec, type EdgeSpec, type Theme } from '@grafloria/renderer';
@Component({
standalone: true,
imports: [DiagramCanvasComponent],
template: `<grafloria-diagram-canvas [nodes]="nodes" [edges]="edges" [theme]="theme" style="display:block;height:100vh" />`,
})
export class StyledFlowComponent {
theme: Theme = DARK_THEME;
nodes: NodeSpec[] = [
{ id: 'a', position: { x: 60, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Order' } },
{ id: 'b', position: { x: 300, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Ship' } },
];
edges: EdgeSpec[] = [{ id: 'e1', source: 'a', target: 'b' }];
}
tsximport { component$ } from '@builder.io/qwik';
import { GrafloriaFlow, DARK_THEME } from '@grafloria/qwik';
import type { NodeSpec, EdgeSpec } from '@grafloria/qwik';
const nodes: NodeSpec[] = [
{ id: 'a', position: { x: 60, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Order' } },
{ id: 'b', position: { x: 300, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Ship' } },
];
const edges: EdgeSpec[] = [{ id: 'e1', source: 'a', target: 'b' }];
export default component$(() => {
return <div style={{ height: '100vh' }}>
<GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} theme={DARK_THEME} />
</div>;
});
jsimport { render, LIGHT_THEME, DARK_THEME } from '@grafloria/element';
const host = document.getElementById('diagram') ?? document.body.appendChild(document.createElement('div'));
host.id = 'diagram';
host.style.height = '400px';
const instance = render({
nodes: [
{ id: 'a', position: { x: 60, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Order' } },
{ id: 'b', position: { x: 300, y: 80 }, size: { width: 170, height: 70 }, data: { label: 'Ship' } },
],
edges: [{ id: 'e1', source: 'a', target: 'b' }],
}, host, { theme: DARK_THEME });
let dark = true;
const toggle = document.getElementById('toggle');
if (!toggle) throw new Error('Missing #toggle');
toggle.addEventListener('click', () => {
dark = !dark;
instance.setTheme(dark ? DARK_THEME : LIGHT_THEME);
instance.renderNow();
});
In Angular, DiagramCanvasComponent is the canvas element. In the other framework bindings, GrafloriaFlow is rendered as a component. The JavaScript example uses the instance's setTheme() method; retain the instance rather than recreating the canvas.
Add named styles and a token bridge
Register a named style once, then name it from a node's style.styleClass. Namespace names in applications because the style registry is process-wide. An inline property outranks the named class, so this node is green even though its class declares orange:
jsimport { defineStyle } from '@grafloria/element';
defineStyle('review-step', { fill: '#fed7aa', stroke: '#c2410c', strokeWidth: 2 });
const node = {
id: 'review',
position: { x: 80, y: 80 },
size: { width: 180, height: 72 },
data: { label: 'Review' },
style: { styleClass: 'review-step', fill: '#22c55e' },
};
For a host that uses CSS variables, import one of the shipped bridges and pass it to the live instance:
jsimport { render, LIGHT_THEME, shadcnBridge, defineStyle } from '@grafloria/element';
const host = document.getElementById('diagram') ?? document.body.appendChild(document.createElement('div'));
host.id = 'diagram';
host.style.cssText = 'height:400px;width:800px;display:block';
defineStyle('review-step', { fill: '#fed7aa', stroke: '#c2410c', strokeWidth: 2 });
const node = {
id: 'review',
position: { x: 80, y: 80 },
size: { width: 180, height: 72 },
data: { label: 'Review' },
style: { styleClass: 'review-step', fill: '#22c55e' },
};
const instance = render({ nodes: [node], edges: [] }, host, { theme: LIGHT_THEME });
instance.setTokenBridge(shadcnBridge());
instance.renderNow();
shadcnBridge(), muiBridge(), and tailwindBridge() map the corresponding host vocabulary. The bridge is instance-scoped: two diagrams can use different themes on one page without one instance overwriting the other's CSS variables. To change the host palette, update the host's variables or class, then repaint the instance.
Pitfalls
- A host without a resolved height produces a blank canvas. Size the host or its flex/grid parent before mounting.
- A named style does not beat an element-inline property. Put the property on the node when that node must win.
- Do not use a Mermaid-style text string as the
render()spec.render()mounts a data object; text import is a separate API. - Use
./create-custom-nodes.mdwhen you need a custom node; omitcustom: trueand the renderer uses a stock rectangle. Register its renderer before mount. - Use
./build-er-and-uml-diagrams.mdfor custom-node update behavior; custom renderers run at mount, so update DOM you own or repaint the dashboard widget.
Was this page helpful?