Skip to content

Layout

These examples document the layout behaviors from the canonical TopoViewer test-case catalog. Each section is generated from one test case and keeps the live viewport, topology YAML, and stylesheet YAML together.

Manual layout

What This Demonstrates

Manual layout means the author supplies coordinates. This is the right mode for diagrams where placement carries meaning.

Expected Result

The live viewport should render "Manual layout" without blocking diagnostics. It should show: Manual layout preserves authored positions. The test metadata expects graphNodes: 3, minVisibleEdges: 2.

What To Inspect

  • Inspect layout options and node positions.
  • Check whether positions are authored manually, inferred, or preserved by layout settings.

Use When

Use this pattern when positions should be repeatable, inferred, or constrained by topology structure.

Manual layout

graph:
  id: manual-layout
  layers:
    - id: physical
      name: Physical
  nodes:
    - id: A
      name: A
      labels:
        node: router
      layers:
        - physical
      position:
        - 120
        - 140
    - id: B
      name: B
      labels:
        node: router
      layers:
        - physical
      position:
        - 330
        - 230
    - id: C
      name: C
      labels:
        node: router
      layers:
        - physical
      position:
        - 540
        - 140
  links:
    - id: a-b
      name: A to B
      source: A
      target: B
      labels:
        link: physical
      layers:
        - physical
    - id: b-c
      name: B to C
      source: B
      target: C
      labels:
        link: physical
      layers:
        - physical
layout:
  mode: manual
  width: 680
  height: 340
icons:
  router:
    glyph: R
    fill: "#2563eb"
    stroke: "#bfdbfe"
  service:
    glyph: VPN
    fill: "#7c3aed"
    stroke: "#ddd6fe"
  controller:
    glyph: NSP
    fill: "#f59e0b"
    stroke: "#fde68a"
labelFields:
  - name
stylesheet:
  - selector: node
    style:
      icon: router
      width: 84
      height: 60
      borderWidth: 3
      labelColor: var(--topoviewer-fg-strong)
      labelFontWeight: 800
  - selector: link
    style:
      label: Link
      lineColor: "#60a5fa"
      lineWidth: 1
      targetArrowShape: none
      labelColor: var(--topoviewer-fg-strong)
      textBackgroundColor: var(--topoviewer-edge-label-bg)

Force layout

What This Demonstrates

Force layout is auto-layout assistance. It is useful when topology data exists but the author does not want to maintain coordinates by hand.

Expected Result

The live viewport should render "Force layout" without blocking diagnostics. It should show: Force layout computes positions when the author omits coordinates. The test metadata expects graphNodes: 5, minVisibleEdges: 5.

What To Inspect

  • Inspect layout options and node positions.
  • Check whether positions are authored manually, inferred, or preserved by layout settings.

Use When

Use this pattern when positions should be repeatable, inferred, or constrained by topology structure.

Force layout

graph:
  id: force-layout
  layers:
    - id: physical
      name: Physical
  nodes:
    - id: A
      name: A
      labels:
        node: router
      layers:
        - physical
    - id: B
      name: B
      labels:
        node: router
      layers:
        - physical
    - id: C
      name: C
      labels:
        node: router
      layers:
        - physical
    - id: D
      name: D
      labels:
        node: router
      layers:
        - physical
    - id: E
      name: E
      labels:
        node: router
      layers:
        - physical
  links:
    - id: A-B
      name: A to B
      source: A
      target: B
      labels:
        link: physical
      layers:
        - physical
    - id: A-C
      name: A to C
      source: A
      target: C
      labels:
        link: physical
      layers:
        - physical
    - id: B-D
      name: B to D
      source: B
      target: D
      labels:
        link: physical
      layers:
        - physical
    - id: C-D
      name: C to D
      source: C
      target: D
      labels:
        link: physical
      layers:
        - physical
    - id: D-E
      name: D to E
      source: D
      target: E
      labels:
        link: physical
      layers:
        - physical
layout:
  mode: force
  width: 700
  height: 380
  iterations: 180
  linkDistance: 130
  chargeStrength: -420
  collideRadius: 55
icons:
  router:
    glyph: R
    fill: "#2563eb"
    stroke: "#bfdbfe"
  service:
    glyph: VPN
    fill: "#7c3aed"
    stroke: "#ddd6fe"
  controller:
    glyph: NSP
    fill: "#f59e0b"
    stroke: "#fde68a"
labelFields:
  - name
stylesheet:
  - selector: node
    style:
      icon: router
      width: 84
      height: 60
      borderWidth: 3
      labelColor: var(--topoviewer-fg-strong)
      labelFontWeight: 800
  - selector: link
    style:
      label: Link
      lineColor: "#60a5fa"
      lineWidth: 1
      targetArrowShape: none
      labelColor: var(--topoviewer-fg-strong)
      textBackgroundColor: var(--topoviewer-edge-label-bg)

CLOS layout

What This Demonstrates

CLOS layout infers staged placement from graph structure.

This example intentionally uses generic node names and directed links:

  • no manual node positions are authored
  • source -> target link direction defines the preferred root-to-leaf order
  • no network-specific labels such as spine or leaf are required

If your topology already has explicit stages, use layout.clos.stageKey and stageOrder. If the graph is not staged, use force; if placement must be operator-approved, use manual.

Expected Result

The live viewport should render "CLOS layout" without blocking diagnostics. It should show: CLOS layout infers staged placement from graph structure. The test metadata expects graphNodes: 6, minVisibleEdges: 8.

What To Inspect

  • Inspect layout options and node positions.
  • Check whether positions are authored manually, inferred, or preserved by layout settings.

Use When

Use this pattern when positions should be repeatable, inferred, or constrained by topology structure.

CLOS layout

graph:
  id: clos-layout
  layers:
    - id: fabric
      name: Fabric
  nodes:
    - id: A1
      name: Stage A1
      layers:
        - fabric
    - id: A2
      name: Stage A2
      layers:
        - fabric
    - id: B1
      name: Stage B1
      layers:
        - fabric
    - id: B2
      name: Stage B2
      layers:
        - fabric
    - id: C1
      name: Stage C1
      layers:
        - fabric
    - id: C2
      name: Stage C2
      layers:
        - fabric
  links:
    - id: A1-B1
      name: Stage A1 to Stage B1
      source: A1
      target: B1
      layers:
        - fabric
    - id: A1-B2
      name: Stage A1 to Stage B2
      source: A1
      target: B2
      layers:
        - fabric
    - id: A2-B1
      name: Stage A2 to Stage B1
      source: A2
      target: B1
      layers:
        - fabric
    - id: A2-B2
      name: Stage A2 to Stage B2
      source: A2
      target: B2
      layers:
        - fabric
    - id: B1-C1
      name: Stage B1 to Stage C1
      source: B1
      target: C1
      layers:
        - fabric
    - id: B1-C2
      name: Stage B1 to Stage C2
      source: B1
      target: C2
      layers:
        - fabric
    - id: B2-C1
      name: Stage B2 to Stage C1
      source: B2
      target: C1
      layers:
        - fabric
    - id: B2-C2
      name: Stage B2 to Stage C2
      source: B2
      target: C2
      layers:
        - fabric
layout:
  mode: clos
  width: 760
  height: 420
  clos:
    direction: topToBottom
    maxStages: 10
    nodeGap: 180
    groupGap: 220
icons:
  stage:
    glyph: "•"
    fill: "#1976d2"
    stroke: "#90caf9"
labelFields:
  - name
stylesheet:
  - selector: node
    style:
      icon: stage
      shape: rectangle
      width: 72
      height: 56
      borderWidth: 3
      labelColor: var(--topoviewer-fg-strong)
      labelFontWeight: 800
      labelBackgroundColor: var(--topoviewer-panel-bg)
      labelBackgroundOpacity: 0.92
      labelPadding: 3
  - selector: link
    style:
      curveStyle: straight
      lineColor: "#42a5f5"
      lineWidth: 3
      targetArrowShape: none