React-only structural graph editor for typed, positioned flow documents.
Install @wyrd-company/react or the Flutter package and use it directly —
your DESIGN.md does the styling.
catalog/contracts/flow-editor.ymlsource of truth · verbatim
$schema: https://thewyrdingway.dev/schemas/component-contract.schema.ymlcontractVersion: 0.1.0identity:name: FlowEditortag: wyrd-flow-editorpackage: wyrd.component.flow-editortitle: Flow Editorsummary: React-only structural graph editor for typed, positioned flow documents.relationships:related-to: - graph-components - wyrd-flow-editorclassification:category: canvascomposition: collectioninteractive: true# GOVERNANCE: The graph-components-strategy target-strategy ruling says# WyrdFlowEditor is react required, flutter none. Unlike# WyrdGraphViewer, this contract can state that truthfully because the React# editor exists and no other target is planned.target-strategy:react: requiredsurface:properties: []slots: - name: rendererkeyed: truekey-domain: node-typerequired: truecardinality: exactly-oneaccepts: - componentsummary: One renderer per node-type key; unknown keys fall back to a built-in generic node.events: - name: graph-changepayload:graph:kind: objectfields:nodes:kind: arrayitem:kind: objectfields:id:kind: stringtype:kind: stringedges:kind: arrayitem:kind: objectfields:id:kind: stringtype:kind: stringchanges:kind: arrayitem:kind: objectfields:kind:kind: stringbubbles: falsecomposed: falsecancelable: falsesummary: Emitted after structural node or edge changes. - name: selection-changepayload:selected-node-ids:kind: arrayitem:kind: stringselected-edge-ids:kind: arrayitem:kind: stringbubbles: falsecomposed: falsecancelable: falsesummary: Emitted when selected graph items change.states: - name: defaultsource: platformsummary: Graph is visible and editable. - name: selectedsource: propertysummary: One or more graph items are selected. - name: connectingsource: interactionsummary: A pointer drag is creating a candidate edge.parts: - name: fieldsummary: Pannable and zoomable graph editing field. - name: panelsummary: Editor chrome surfaces such as controls and minimap. - name: nodesummary: Repeated rendered node shell. - name: handlesummary: Named port connection handle. - name: edgesummary: Drawn graph edge. - name: edge-labelsummary: Optional edge label pill.tokens:field:background: color.backgroundforeground: color.on-surfacemuted-foreground: color.on-surface-variantmin-block-size: 520pxpreferred-block-size: 720pxradius: rounded.mdborder-width: 1pxpanel:background: color.surfaceforeground: color.on-surfaceborder-color: color.outlineborder-width: 1pxradius: rounded.mdpill-radius: rounded.fullshadow: shadow.smgrid:dot-color: color.outlinegap: 24pxdot-size: 1.2pxselection:color: color.primarywidth: 2pxnode:background: color.surfaceforeground: color.on-surfacemuted-foreground: color.on-surface-variantborder-color: color.outlineselected-color: color.primaryborder-width: 1pxradius: rounded.mdshadow: shadow.smpadding: spacing.mdcompound-affordance-size: 0.65remcompound-affordance-border-width: 2pxhandle:background: color.primarybackground-hover: color.primary-hoverborder-color: color.surfaceborder-width: 2pxring-width: 1pxsize: 0.75remedge:color: color.outlineselected-color: color.primarystroke-width: 2pxselected-stroke-width: 2.5pxconnection-stroke-width: 2pxconnection-radius: 12pxinteraction-width: 18pxminimap-stroke-width: 1.2pxedge-label:background: color.surfaceforeground: color.on-surfaceborder-color: color.outlineborder-width: 1pxradius: rounded.fullfont-family: inheritfont-size: 0.75remfont-weight: "600"line-height: "1.25"controls:radius: rounded.smitem-min-inline-size: 2.25remitem-block-size: 2remminimap:inline-size: 9remradius: rounded.mdpadding: 0.375remgraph-node:gap: 0.375remkind-font-size: 0.625remkind-font-weight: "700"title-font-weight: "700"title-line-height: "1.2"compound-state-min-block-size: 84pxcompound-title-font-size: 0.875remcompound-title-font-weight: "800"state-min-block-size: 72pxstate-title-font-size: 0.8125remevent-min-block-size: 60pxevent-title-font-size: 0.75remtitle-meta-font-size: 0.75remtitle-meta-font-weight: "600"description-font-size: 0.6875remdescription-line-height: "1.25"event-description-font-size: 0.625remevent-description-line-height: "1.2"badge-font-size: 0.625rembadge-font-weight: "650"messages: []obligations:accessibility:role: applicationname: Host-provided accessible editor label.keyboard: - Delete or Backspace removes selected graph items. - Shift enables selection drag. - Meta or Control enables multi-selection.semantics: - Exposes rendered nodes as grouped graph items. - Preserves named-port labels on connection handles. - The ReactFlow attribution remains visible by the third-party-exception ruling.behavior:model: collectioninteractions: - Pan and zoom move the editor viewport without mutating the graph document. - Dragging a node emits a node-move graph change. - Dragging from one named port to another valid named port emits an edge-create graph change. - Deleting selected nodes or edges emits graph-change events for the removed items.hostEffects: - Dispatch graph-change. - Dispatch selection-change.conditions: - rtl - reduced-motion - density-compact - text-expansionsdui:status: blockedserializableProperties: []serializableSlots: []eventBindings: []notes: Graph documents and node type definitions are serializable through the graph contract; renderer registries are modeled as keyed component slots and are not serializable as SDUI data.examples: - name: menu-statechartstate: defaultproperties:graph:nodes: - id: menu.closedtype: stateposition:x: 40y: 60data:label: closeddescription: Initial leaf. OPEN enters open. - id: event.opentype: eventposition:x: 300y: 20interactive: falsedata:label: OPENdescription: "guard: disabled == false" - id: event.closetype: eventposition:x: 300y: 160interactive: falsedata:label: CLOSE / ESCAPEdescription: Dismissal returns closed. - id: menu.opentype: stateposition:x: 520y: 60data:label: opendescription: CLOSE and ESCAPE return closed.edges: - id: menu.closed:outgoing->event.open:incomingtype: transitionfrom:node: menu.closedport: outgoingto:node: event.openport: incomingdata:label: OPEN - id: event.open:outgoing->menu.open:incomingtype: transitionfrom:node: event.openport: outgoingto:node: menu.openport: incoming - id: menu.open:outgoing->event.close:incomingtype: transitionfrom:node: menu.openport: outgoingto:node: event.closeport: incomingdata:label: CLOSE - id: event.close:outgoing->menu.closed:incomingtype: transitionfrom:node: event.closeport: outgoingto:node: menu.closedport: incomingnode-types: - type: statelabel: Behavior statesize:width: 184minHeight: 72ports: - name: incominglabel: Incoming transitionsdirection: inputside: left - name: outgoinglabel: Outgoing transitionsdirection: outputside: right - type: eventlabel: Transition eventsize:width: 150minHeight: 60ports: - name: incominglabel: Incoming transitionsdirection: inputside: left - name: outgoinglabel: Outgoing transitionsdirection: outputside: rightslots:renderer: state | event
01
Examples
Generated from the contract's examples block.
Previews are the published @wyrd-company/react component, themed by the
brand's role tokens — flip the site's light/dark toggle and they follow.
One renderer per node-type key; unknown keys fall back to a built-in generic node.
Event
Payload
Summary
graph-change
graph, changes
Emitted after structural node or edge changes.
selection-change
selected-node-ids, selected-edge-ids
Emitted when selected graph items change.
03
Tokens
The contract's token bindings. Open the shared contextual sidebar from any example;
primary swatches reflect the color you pick there. Error swatches show the active
preset's values; editing any knob switches to custom and resets error to TWW · Ink.
Binding
Token
field.background
color.background
field.foreground
color.on-surface
field.muted-foreground
color.on-surface-variant
field.min-block-size
520px
field.preferred-block-size
720px
field.radius
rounded.md
field.border-width
1px
panel.background
color.surface
panel.foreground
color.on-surface
panel.border-color
color.outline
panel.border-width
1px
panel.radius
rounded.md
panel.pill-radius
rounded.full
panel.shadow
shadow.sm
grid.dot-color
color.outline
grid.gap
24px
grid.dot-size
1.2px
selection.color
color.primary
selection.width
2px
node.background
color.surface
node.foreground
color.on-surface
node.muted-foreground
color.on-surface-variant
node.border-color
color.outline
node.selected-color
color.primary
node.border-width
1px
node.radius
rounded.md
node.shadow
shadow.sm
node.padding
spacing.md
node.compound-affordance-size
0.65rem
node.compound-affordance-border-width
2px
handle.background
color.primary
handle.background-hover
color.primary-hover
handle.border-color
color.surface
handle.border-width
2px
handle.ring-width
1px
handle.size
0.75rem
edge.color
color.outline
edge.selected-color
color.primary
edge.stroke-width
2px
edge.selected-stroke-width
2.5px
edge.connection-stroke-width
2px
edge.connection-radius
12px
edge.interaction-width
18px
edge.minimap-stroke-width
1.2px
edge-label.background
color.surface
edge-label.foreground
color.on-surface
edge-label.border-color
color.outline
edge-label.border-width
1px
edge-label.radius
rounded.full
edge-label.font-family
inherit
edge-label.font-size
0.75rem
edge-label.font-weight
600
edge-label.line-height
1.25
controls.radius
rounded.sm
controls.item-min-inline-size
2.25rem
controls.item-block-size
2rem
minimap.inline-size
9rem
minimap.radius
rounded.md
minimap.padding
0.375rem
graph-node.gap
0.375rem
graph-node.kind-font-size
0.625rem
graph-node.kind-font-weight
700
graph-node.title-font-weight
700
graph-node.title-line-height
1.2
graph-node.compound-state-min-block-size
84px
graph-node.compound-title-font-size
0.875rem
graph-node.compound-title-font-weight
800
graph-node.state-min-block-size
72px
graph-node.state-title-font-size
0.8125rem
graph-node.event-min-block-size
60px
graph-node.event-title-font-size
0.75rem
graph-node.title-meta-font-size
0.75rem
graph-node.title-meta-font-weight
600
graph-node.description-font-size
0.6875rem
graph-node.description-line-height
1.25
graph-node.event-description-font-size
0.625rem
graph-node.event-description-line-height
1.2
graph-node.badge-font-size
0.625rem
graph-node.badge-font-weight
650
04
States
State
Source
Summary
default
platform
Graph is visible and editable.
selected
property
One or more graph items are selected.
connecting
interaction
A pointer drag is creating a candidate edge.
05
Accessibility
From the contract's accessibility obligations. Role application; Host-provided accessible editor label.
Delete or Backspace removes selected graph items.
Shift enables selection drag.
Meta or Control enables multi-selection.
Exposes rendered nodes as grouped graph items.
Preserves named-port labels on connection handles.
The ReactFlow attribution remains visible by the third-party-exception ruling.
Status: blocked. Graph documents and node type definitions are serializable through the graph contract; renderer registries are modeled as keyed component slots and are not serializable as SDUI data.