Skip to content

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:

  1. Rules — declarative, apply to many elements, can be functions of the element. The scalable default.
  2. Classes — named bundles of attributes you opt elements into.
  3. 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'); // broadcast
node.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 setAttribute on 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 of circle, 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, use outerStroke instead.
  • 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:

Node shapes Open in new tab ↗

And when color is an array, the node becomes an equal-angle pie chart:

Pie fills Open in new tab ↗

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 and text.secondary below. 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:

Edge extremities Open in new tab ↗

And the line style options — dashed, dotted and solid:

Dashed & dotted edges Open in new tab ↗

When an edge’s source === target, it renders as a self-loop arc beside the node — see The Graph Model.

Where to go next