Skip to content

Embed contract: data-* attributes and driver API

This is the reference for the stable surface a host depends on. If you are starting out, read the task-oriented Embedding guide first - it explains which output to produce and how to size, theme, and drive a map - and come here for the exact attribute vocabulary and driver method signatures.

An nf-metro SVG is a self-describing, driveable artifact. A host page can:

  1. Inline the SVG (or load it via <img> / <object>).
  2. Load one driver script.
  3. Call a documented API to highlight lines, select nodes by process pattern, or read the embedded manifest without touching internals.

This page documents the two halves of the contract: the data-* attributes carried by the SVG and the driver API that a host uses to manipulate it. The Data manifest page covers the manifest format (nodes, groups, regions, overlays) in more depth.


Every rendered SVG carries two complementary sets of attributes.

These attributes are consumed by the driver and are the stable addresses for CSS-level interaction:

AttributeElementValue
data-station-idStation marker <rect>/<circle> and associated label/icon <g>The station’s stable id (matches node.id in the manifest).
data-station-linesStation marker element onlyComma-separated list of line ids passing through the station.
data-station-labelStation marker element onlyHuman-readable label (HTML-escaped).
data-section-idSection box and associated label <g>The section’s stable id (matches region.id in the manifest).
data-section-nameStation marker elements within a sectionHuman-readable section name (HTML-escaped).
data-section-linesSection box element onlyComma-separated list of line ids present in the section.
data-line-idEdge path elementsThe id of the line this edge belongs to.

Querying examples:

// All station markers for a specific station id:
svg.querySelectorAll('[data-station-id="align"]');
// All edges belonging to a line:
svg.querySelectorAll('[data-line-id="star_salmon"]');
// All section boxes that include a given line:
svg.querySelectorAll("[data-section-lines]").forEach((el) => {
const lines = el.getAttribute("data-section-lines").split(",");
if (lines.includes("star_salmon")) {
/* ... */
}
});

A second set - data-node-id and data-node-cx/-cy/-r (plus data-node-groups/-region) - carries the coordinate and pattern data overlays need. It is written by the manifest system and specified in full under Per-node attributes on the Data manifest page.

Both sets join on the station id (data-station-id = data-node-id = node.id in the manifest JSON).


Option A - embed the HTML output (simplest). nf-metro render --format html produces a fully self-contained interactive page with the driver already inlined. Copy the inline snippet from the Embed modal to paste it into any host page.

Option B - load the driver separately. Export the driver script and load it alongside the SVG:

Terminal window
nf-metro embed-script -o nf-metro-embed.js

Then on the host page:

<!-- 1. Inline the SVG (must contain data-* attributes and manifest) -->
<div id="my-map">
<div class="nf-metro-canvas">
<!-- paste SVG here -->
</div>
<div class="nf-metro-legend"></div>
<div class="nf-metro-tip"></div>
</div>
<!-- 2. Load the driver -->
<script src="nf-metro-embed.js"></script>
<!-- 3. Attach and capture the API -->
<script>
const api = attachMetroMap({
root: document.getElementById("my-map"),
lines: [
{
id: "star_salmon",
label: "STAR + Salmon",
color: "#e05c5c",
style: "solid",
},
/* ... */
],
embed: null,
});
</script>

The lines array must match the lines embedded in the SVG. The easiest way to obtain it is from the groups array in the manifest (see getManifest below).

attachMetroMap(opts) returns an API object with the following methods. All methods are no-ops when the SVG has no manifest or no matching elements.

Activate a line by its id string. All stations and edges not belonging to that line are hidden; the map zooms to the visible subset. Calling with the currently active id clears the filter (same as clearHighlight()).

api.highlightLine("star_salmon");

Remove any active line filter and station selection, returning the map to its initial unfiltered state.

api.clearHighlight();

Return the embedded manifest JSON object (parsed from the <metadata id="diagram-manifest"> element), or null if the SVG has no manifest. Use this to build lines arrays, read node coordinates for overlays, or look up process patterns.

const manifest = api.getManifest();
if (manifest) {
console.log(manifest.nodes.map((n) => n.id));
}

Match processName against each node’s patterns array (case-insensitive regex) and visually highlight the matching stations. Non-matching stations are dimmed. Calling with a string that matches no node is a no-op.

// Highlight the station(s) whose patterns match this Nextflow process name:
api.selectNode("NFCORE_RNASEQ:RNASEQ:ALIGN_STAR_SALMON:STAR_ALIGN");

CSS classes written by selectNode:

ClassApplied to
nf-metro-station-selectedMatching station marker elements ([data-station-lines]).
nf-metro-station-dimAll [data-station-id] elements that are not a match.
nf-metro-selectingThe root element while a selection is active.

The default templates ship CSS for these classes. When loading the driver separately, add your own styles:

.nf-metro-station-selected rect,
.nf-metro-station-selected circle {
stroke: #fff;
stroke-width: 2;
}
.nf-metro-station-dim {
opacity: 0.2;
transition: opacity 0.2s;
}

Alias for clearHighlight().


For a coordinate-accurate progress overlay (e.g. lighting up stations as a pipeline runs), draw a transparent layer that shares the base SVG’s viewBox and place markers at each node’s manifest coordinates. The overlay_svg() helper builds that layer, and the manifest tutorial, Light up a diagram as a job runs, walks the full read-match-draw recipe end to end.

The highlightLine / selectNode API and the overlay approach are complementary:

  • Driver API - manipulates the base SVG’s existing DOM elements by adding CSS classes. Zero extra elements; works without the manifest.
  • Overlay - adds new elements in a separate layer at exact coordinates from the manifest. Suitable for progress indicators, status badges, and annotation.

The following snippet builds a self-contained host page that loads a separately-generated SVG and driver, then wires keyboard shortcuts to the public API.

<!doctype html>
<html>
<head>
<style>
#map-root {
position: relative;
}
.nf-metro-canvas svg {
width: 100%;
height: auto;
}
.nf-metro-legend {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 8px;
}
.nf-metro-tip {
position: fixed;
pointer-events: none;
}
/* Station selection styles */
.nf-metro-station-selected rect,
.nf-metro-station-selected circle {
stroke: #4cf;
stroke-width: 2;
}
.nf-metro-station-dim {
opacity: 0.15;
transition: opacity 0.2s;
}
</style>
</head>
<body>
<div id="map-root">
<div class="nf-metro-canvas">
<!-- Inline the SVG exported by: nf-metro render map.mmd -o map.svg -->
</div>
<div class="nf-metro-legend"></div>
<div class="nf-metro-tip"></div>
</div>
<script src="nf-metro-embed.js"></script>
<script>
const manifest = (() => {
const el = document.querySelector("#diagram-manifest");
return el ? JSON.parse(el.textContent) : null;
})();
const lines = (manifest?.groups || []).map((g) => ({
id: g.id,
label: g.label,
color: g.color,
style: "solid",
}));
const api = attachMetroMap({
root: document.getElementById("map-root"),
lines,
embed: null,
});
// Example: drive from your application state
function onProcessStarted(fqProcessName) {
api.selectNode(fqProcessName);
}
function onPipelineDone() {
api.clearHighlight();
}
</script>
</body>
</html>

Both the manifest schema and the driver contract are versioned. The Python constants are:

from nf_metro.manifest import MANIFEST_SCHEMA_VERSION # e.g. "1.0"
from nf_metro.render.driver import DRIVER_CONTRACT_VERSION # e.g. "1.0"

The schema version follows major.minor semantics: the minor part increments for additive (backward-compatible) changes; the major part increments for breaking changes. Consumers must ignore unknown fields (additive tolerance).

This surface is stable as of nf-metro 1.0: within a major version the contract only grows in backward-compatible ways. Pin to a specific nf-metro release only if you depend on the exact bytes of the output.