Cytoscape adapter
@ship-it-ui/cytoscape is a peer-dep adapter — three layered exports that
let a Cytoscape-driven graph share the design system's color tokens, dark /
light themes, and entity-type vocabulary without duplicating the
getComputedStyle + MutationObserver dance in every app.
Install#
pnpm add cytoscape @ship-it-ui/cytoscapeCytoscape itself is a peer dependency — the adapter does not bundle a version or pin to any specific extensions.
Stylesheet builder#
buildShipItStylesheet(options?) returns a Cytoscape StylesheetJson array
built from the live token palette. Pass a pre-resolved palette for SSR or
deterministic tests; append app-specific selectors via extra.
import cytoscape from 'cytoscape';
import { buildShipItStylesheet } from '@ship-it-ui/cytoscape';
const cy = cytoscape({
container: document.getElementById('graph'),
elements: [...],
style: buildShipItStylesheet(),
});The exported GRAPH_CANVAS_CLASS constants are the class names the
stylesheet recognizes (graph-canvas:path, graph-canvas:dim). Toggle them
on nodes / edges to drive the on-path and dimmed visuals.
Theme-aware re-resolver hook#
useShipItStylesheet(cyRef) re-applies the stylesheet whenever
<html data-theme> flips. It wires a MutationObserver against the document
root (skippable via observe: false) and returns a refresh() callback for
manual triggers — useful after a --color-accent hue-knob change.
const cyRef = useRef<cytoscape.Core | null>(null);
const { refresh } = useShipItStylesheet(cyRef);<GraphCanvas> wrapper#
<GraphCanvas engine={cytoscape} …/> owns the Cytoscape lifecycle, the
theme ↔ stylesheet sync, and a thin selection API. Pass the imported
cytoscape default export as engine — the wrapper never bundles
Cytoscape itself.
import cytoscape from 'cytoscape';
import { GraphCanvas } from '@ship-it-ui/cytoscape';
import { GraphInspector } from '@ship-it-ui/shipit';
<GraphCanvas
engine={cytoscape}
elements={elements}
layout={{ name: 'cose' }}
onSelect={(node) => setSelected(node.id())}
inspector={selected && <GraphInspector type={…} title={…} />}
/>;Custom entity types#
Node colors flow through the entity-type registry exported by
@ship-it-ui/shipit. Register your own types once and they appear in the
graph without forking the stylesheet:
import { registerEntityTypes } from '@ship-it-ui/shipit';
registerEntityTypes({
repository: {
glyph: '◆',
label: 'Repository',
toneClass: 'text-accent',
toneBg: 'bg-accent-dim',
colorVar: 'var(--color-accent)',
badgeVariant: 'accent',
},
});Once registered, nodes with data.entityType = 'repository' pick up the
accent ring automatically — buildShipItStylesheet walks the registry on
every call and emits one node[entityType = "…"] selector per registered
type, so the ring color is driven entirely by the type's colorVar. Register
before the first <GraphCanvas> mount (or call the imperative handle's
refreshStyles() afterward) so the stylesheet picks up the new entries.