Skip to content

Flow: per-connector styling, wrapping layout, and standalone Flow.Node #613

Description

@awhobbs87

Summary

Flow is a great fit for linear/parallel workflow diagrams, but its node and connector rendering are tightly coupled, which blocks a class of "labelled ladder" diagrams: sequences where each connector carries semantic meaning (colour/direction) and where the diagram should wrap rather than pan. Today the only escape hatch is to abandon Flow entirely and hand-roll connectors.

This issue proposes three related enhancements so Flow can cover these cases natively.

Motivating use case

We have a "tier ladder" in an internal admin tool: the rate plans in a product family shown left-to-right as an upgrade/downgrade path. The connectors are the information, not just decoration:

  • Green connector: the path from the lowest tier up to the account's currently-active tier ("already traversed").
  • Red, reversed connector: a downgrade candidate.
  • Neutral connector: everything else.

The ladder also lives in a dense, narrow column, so it wraps into rows of three with a "continues below" down-connector, rather than panning horizontally.

We evaluated migrating this to Flow and could not, for the reasons below.

Current limitations (v2.6.0)

  1. Connectors are owned entirely by <Flow> and expose no per-connector styling. Connector geometry is computed in FlowDiagram from registered node rects; the only per-node knob that reaches a connector is disabled (greys it). There is no supported way to say "the connector arriving at / leaving this node is red" or "this connector points the other way". (See packages/kumo/src/components/flow/.)

  2. Flow.Node cannot be used outside <Flow>. Flow.Node calls useDescendantsContext, which throws useDescendantsContext must be used within DescendantsProvider when there is no <Flow> ancestor. So consumers cannot reuse just the node presentation while composing their own connectors.

  3. Layout is single-line + pan only; no wrapping mode. When a horizontal diagram overflows its container it pans. There is no row-wrapping layout for dense/narrow containers.

The net effect: Flow is all-or-nothing. If you need any per-connector semantics or a wrapping layout, you must drop Flow and reimplement connectors by hand, losing the component's measurement/anchor machinery.

Proposed enhancements

Any one of these would help; ideally 1 + 3.

  1. Per-connector styling API. For example, a connector prop on Flow.Node (styling the incoming edge) or a renderConnector/connectorVariant prop on Flow / Flow.Parallel:

    <Flow>
      <Flow.Node>Tier 1</Flow.Node>
      <Flow.Node connector={{ variant: 'success' }}>Tier 2 (active)</Flow.Node>
      <Flow.Node connector={{ variant: 'danger', direction: 'reverse' }}>Tier 3 (downgrade)</Flow.Node>
    </Flow>

    A small set of semantic variants (default | success | danger | muted) that map to Kumo tokens would cover most cases; a render prop would cover the rest.

  2. Wrapping layout mode. An opt-in layout="wrap" (or wrap / columns) on Flow that flows nodes into rows when they exceed the container width, with connectors drawn per-row and an inter-row indicator, as an alternative to canvas panning.

  3. Standalone Flow.Node presentation. Let Flow.Node render its styled body when there is no DescendantsProvider (no-op the connector registration) instead of throwing, so the node's look-and-feel can be reused while a consumer composes connectors themselves. This decouples node presentation from the connector engine.

Why native support is preferable

Hand-rolling connectors means reimplementing getBoundingClientRect tracking, ResizeObserver, scroll/resize remeasurement, and anchor bookkeeping that Flow already does well. Exposing a thin styling/layout seam would let these semantic-ladder diagrams stay on Flow and benefit from that machinery.

Happy to help prototype the connector-styling seam if the direction sounds reasonable.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions