Styling
Styling in Nodus is data-driven and CSS-like. Instead of looping over elements and setting colours by hand, you declare rules and classes that map element state to visual attributes. Nodus re-evaluates them as the graph changes, so styling scales with the graph instead of with your code.
This page explains the model and surveys the attributes you can set. For step-by-step recipes see Styling Elements; for the exact method signatures see the Styles API.
The styling model
There are three ways a visual attribute gets its value, and they layer on top of one another:
- Rules — declarative, apply to many elements, can be functions of the element. The scalable default.
- Classes — named bundles of attributes you opt elements into.
- Direct attributes — set on a single element with
setAttribute(s).
// 1. A rule: every "author" node is indigo.nodus.styles.addNodeRule( (node) => node.getData('role') === 'author', { color: '#4f46e5' },);
// 2. A class: anything tagged "hub" gets a big radius.nodus.styles.createClass({ name: 'hub', nodeAttributes: { radius: 20 } });nodus.getNode('ada').addClass('hub');
// 3. A direct attribute on one element.nodus.getNode('ada').setAttribute('color', '#ef4444');Rules
Rules are the workhorse. Add them per element type:
nodus.styles.addNodeRule(selectorOrAttrs, attrs?);nodus.styles.addEdgeRule(selectorOrAttrs, attrs?);nodus.styles.addRule({ /* options */ });You can pass a selector predicate plus attributes, or just attributes to
apply to all elements of that type. Inspect what’s registered with
getRuleList(), getNodeRules(), getEdgeRules().
A rule returns a StyleRule you can manage later — update(options),
refresh(), setIndex(i), getDefinition(), destroy().
Function-valued attributes
Any attribute in a rule may be a function of the element, computed per node
or edge. This is how you turn data into visuals:
nodus.styles.addNodeRule({ radius: (node) => 6 + Math.sqrt(node.getData('followers')), color: (node) => (node.getData('role') === 'author' ? '#4f46e5' : '#64748b'), text: { content: (node) => node.getData('name') },});When the underlying data changes, the function re-runs — your styling stays in sync with no manual bookkeeping.
Classes
A class is a reusable, named bundle of attributes:
const flagged = nodus.styles.createClass({ name: 'flagged', nodeAttributes: { color: '#ef4444', halo: { color: '#fecaca', width: 6 } },});
nodus.getNodes().filter((n) => n.getData('score') < 0).addClass('flagged');createClass returns a StyleClass. It can report and manage its membership —
getNodes(), getEdges(), add(elements), remove(elements),
clearNodes() — and be reindexed or updated with setIndex(i) / update(options).
Fetch one later with nodus.styles.getClass(name) or list all with
getClassList(). Apply and remove classes at the element or list level:
node.addClass('flagged');nodeList.addClass('flagged'); // broadcastnode.removeClass('flagged');Precedence
When more than one source sets the same attribute, the more specific source wins. From lowest to highest priority:
rules < classes < direct attributes- A rule provides the baseline for matching elements.
- A class an element opts into overrides the rules for that attribute.
- A direct
setAttributeon the element overrides both.
Among rules of the same kind, order matters — that’s what getIndex() /
setIndex(i) on a StyleRule control. To drop back to the rule/class value,
clear the direct attribute with resetAttributes(['color']) or
element.resetStyle().
State styling
Interactive states — hover, selected, disabled — have their own attribute overrides that apply automatically while the element is in that state:
nodus.styles.setHoveredNodeAttributes({ outerStroke: { color: '#111827', width: 3 } });nodus.styles.setHoveredEdgeAttributes({ width: 3 });
nodus.styles.setSelectedNodeAttributes({ color: '#f59e0b', radius: 18 });nodus.styles.setSelectedEdgeAttributes({ color: '#f59e0b' });
nodus.styles.setDisabledNodeAttributes({ color: '#cbd5e1' });nodus.styles.setDisabledEdgeAttributes({ color: '#e2e8f0' });These states are toggled per element with setSelected, setDisabled and the
hover interactions (see Handling Events). The state
attributes are layered on top of whatever the element would otherwise look like,
so you only declare the difference.
Visibility
Toggle whole categories of elements or just their labels:
nodus.styles.setNodesVisibility(true);nodus.styles.setEdgesVisibility(false);nodus.styles.setNodeTextsVisibility(true);nodus.styles.setEdgeTextsVisibility(false);For a single element, use element.setVisible(bool) / isVisible(), which also
broadcasts over a NodeList/EdgeList.
Themes
Apply a coordinated set of defaults in one call:
nodus.styles.setTheme(theme);A theme sets the base look — background, default node/edge colours and so on — that your rules and classes then build on.
The visual attribute catalog
These are the attributes Nodus understands and draws. Set them in a node/edge’s
attributes, in a rule, or directly with setAttribute(s).
Node attributes
x,y— position in graph coordinates.color— a CSS colour string. An array of colours turns the node into an equal-angle pie chart:color: ['#4f46e5', '#ef4444', '#10b981'].radius— the node’s size.shape— one ofcircle,square,triangle,triangleLeft,triangleRight,diamond,pentagon,hexagon,star.innerStroke/outerStroke—{ color, width, minVisibleSize }.outline— a boolean (outline: true) that draws a default ring; for a custom-coloured ring, useouterStrokeinstead.halo—{ color, width }, a soft glow behind the node.icon—{ content, font, color, scale }, e.g. an icon-font glyph.badge— a small text or icon overlay on the node.image— an image drawn on the node.text(the label) — see below.pulse—{ startRatio, endRatio, width, startColor, endColor, interval, duration }, animated expanding rings used to draw attention.layer— z-order; higher numbers draw on top. Used for hover, selection and drag.
Try it live — every built-in shape on one canvas:
And when color is an array, the node becomes an equal-angle pie chart:
Labels (text)
{ text: { content: 'Ada Lovelace', // string, or (node) => string color: '#111827', font: 'Inter', style: 'bold', position: 'bottom', // 'top' | 'bottom' | 'left' | 'right' | 'center' backgroundColor: 'inherit', // a pill behind the text; 'inherit' uses node color maxLineLength: 18, // wrap long labels minVisibleSize: 8, // hide when smaller than this on screen secondary: { // a second line, styled independently content: 'author', color: '#6b7280', font: 'Inter', style: 'italic', backgroundColor: 'inherit', }, },}Labels wrap automatically and honour explicit newlines (\n).
Edge attributes
shape—{ head, tail, style }:head/tail(the extremities):'arrow','open-arrow'(a chevron),'square','circle','none'.style:'dashed','dotted', or plain.
width— line thickness.color— a CSS colour or a gradient along the edge.halo— a glow around the edge.outline— an outline around the edge.text— a label rotated to follow the edge; the primary line sits above the edge andtext.secondarybelow. Honours\n.
Curvature is handled automatically: when several edges connect the same pair
of nodes they fan out into separate arcs, and an edge whose source === target
is drawn as a self-loop — you don’t set a curvature value yourself.
A directed, dashed edge:
nodus.styles.addEdgeRule({ shape: { head: 'arrow', style: 'dashed' }, width: 2, color: '#94a3b8',});Try it live — every head / tail extremity side by side:
And the line style options — dashed, dotted and solid:
When an edge’s source === target, it renders as a self-loop arc beside the
node — see The Graph Model.
Where to go next
- Styling Elements — practical, task-focused recipes.
- API: Styles — full method signatures and
StyleRule/StyleClassreference. - The Graph Model — the
attributesvsdatadistinction these visuals build on. - The Rendering Pipeline — how these attributes become pixels.