Attention
Explore attention behavior with a live diagram and its two source files. Each example includes an edit to try in your own copy.
Object focus
Object focus is the default interactive attention pattern. Click the Checkout flow path, a node, or a link in the live viewport; the selected object is highlighted while unrelated context stays visible but muted. Click empty viewport space to clear the focus and return to the normal topology view.
Expected Result
Click one topology object to highlight it while dimming the surrounding context.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, change
attention.clickModefromdim-contexttohide-context. - Reload, then click node
client. Unrelated objects disappear instead of dimming. Click empty viewport space to restore the starting topology. - Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-object-focus
layers:
- id: service
labels:
name: Service
nodes:
- id: client
labels:
role: endpoint
name: Client
data:
severity: normal
layers:
- service
position:
- 80
- 180
- id: edge
labels:
role: gateway
name: Edge
data:
severity: normal
layers:
- service
position:
- 270
- 180
- id: api
labels:
role: service
name: API
data:
severity: critical
layers:
- service
position:
- 460
- 180
- id: cache
labels:
role: support
name: Cache
data:
severity: normal
layers:
- service
position:
- 460
- 320
links:
- id: link-client-edge
source: client
target: edge
labels:
link: service
name: Client to edge
layers:
- service
- id: link-edge-api
source: edge
target: api
labels:
link: service
name: Edge to API
layers:
- service
- id: link-api-cache
source: api
target: cache
labels:
link: support
name: API to cache
layers:
- service
paths:
- id: checkout-flow
sequence:
- client
- edge
- api
labels:
path: service
name: Checkout flow
data:
severity: critical
layers:
- service
attention:
interactive: true
clickMode: dim-context
version: "0.2"
layout:
mode: manual
width: 620
height: 420
icons:
endpoint:
glyph: C
fill: "#26313c"
stroke: "#a3b0bd"
gateway:
glyph: G
fill: "#26313c"
stroke: "#a3b0bd"
service:
glyph: API
fill: "#26313c"
stroke: "#a3b0bd"
support:
glyph: S
fill: "#26313c"
stroke: "#a3b0bd"
labelFields:
- labels.name
stylesheet:
- selector: node
style:
width: 86
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
iconColor: var(--topoviewer-fg-strong)
- selector: node[labels.role = "endpoint"]
style:
icon: endpoint
- selector: node[labels.role = "gateway"]
style:
icon: gateway
- selector: node[labels.role = "service"]
style:
icon: service
- selector: node[labels.role = "support"]
style:
icon: support
- selector: link
style:
curveStyle: smooth-step
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 1.5
targetArrowShape: none
opacity: 0.72
labelFontSize: 12
labelFontWeight: 600
- selector: link[labels.link = "support"]
style:
lineDashPattern: 5 5
- selector: path
style:
label: Checkout flow
curveStyle: smooth-taxi
lineColor: "var(--topoviewer-info)"
lineWidth: 4
animated: true
targetArrowShape: triangle
labelFontSize: 12
labelFontWeight: 600
Change focus
Change focus is a declarative attention query for operational change. This small topology starts with attention already applied: CORE-1 and the degraded core-1-core-2 link changed after the selected timestamp, so they are highlighted while the unchanged objects remain visible but muted.
Expected Result
Focus objects with recent change metadata while preserving topology context.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, change
attention.query.changes.sinceto"2026-06-16T00:00:00Z", after this example's recorded changes. - Reload. CORE-1 and the degraded core link are no longer matched by the change query; their stored timestamps and status stay unchanged.
- Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-change-focus
layers:
- id: physical
labels:
name: Physical
- id: control
labels:
name: Control
nodes:
- id: EDGE-1
labels:
node: router
role: edge
name: EDGE-1
data:
severity: normal
layers:
- physical
position:
- 80
- 180
- id: CORE-1
labels:
node: router
role: core
name: CORE-1
data:
severity: major
changedAt: "2026-06-15T10:30:00Z"
revision: 8
layers:
- physical
position:
- 250
- 120
- id: CORE-2
labels:
node: router
role: core
name: CORE-2
data:
severity: normal
layers:
- physical
position:
- 420
- 180
- id: PE-1
labels:
node: router
role: pe
name: PE-1
data:
severity: normal
layers:
- physical
position:
- 250
- 300
- id: NOC
labels:
node: controller
role: control
name: NOC
data:
severity: normal
changedAt: "2026-06-01T08:00:00Z"
revision: 3
layers:
- control
position:
- 590
- 120
links:
- id: edge-core-1
source: EDGE-1
target: CORE-1
labels:
link: physical
name: EDGE-1 to CORE-1
layers:
- physical
- id: core-1-core-2
source: CORE-1
target: CORE-2
labels:
link: physical
name: Changed core link
data:
status: degraded
changedAt: "2026-06-15T10:30:00Z"
revision: 8
layers:
- physical
- id: core-1-pe-1
source: CORE-1
target: PE-1
labels:
link: physical
name: CORE-1 to PE-1
layers:
- physical
- id: edge-pe-1
source: EDGE-1
target: PE-1
labels:
link: physical
name: EDGE-1 to PE-1
layers:
- physical
- id: core-2-noc
source: CORE-2
target: NOC
labels:
link: control
protocol: telemetry
name: CORE-2 to NOC telemetry
layers:
- control
attention:
query:
changes:
since: "2026-06-10T00:00:00Z"
mode: dim-context
version: "0.2"
layout:
mode: manual
width: 700
height: 420
icons:
router:
glyph: R
fill: "#26313c"
stroke: "#a3b0bd"
svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48" rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24 8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
controller:
glyph: NOC
fill: "#26313c"
stroke: "#a3b0bd"
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 82
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
iconColor: var(--topoviewer-fg-strong)
- selector: node[labels.role = "control"]
style:
icon: controller
width: 92
borderColor: "var(--topoviewer-warning)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-warning) 12%,
var(--topoviewer-panel-bg))"
- selector: node[data.severity = "major"]
style:
borderColor: "var(--topoviewer-warning)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-warning) 12%,
var(--topoviewer-panel-bg))"
- selector: link
style:
curveStyle: smooth-step
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 1.6
targetArrowShape: none
opacity: 0.78
labelFontSize: 12
labelFontWeight: 600
- selector: link[labels.protocol = "telemetry"]
style:
label: Telemetry
curveStyle: bezier
lineColor: "var(--topoviewer-info)"
lineDashPattern: 3 4
targetArrowShape: triangle
- selector: link[data.status = "degraded"]
style:
lineColor: "var(--topoviewer-warning)"
lineWidth: 3
Region collapse
Region collapse demonstrates progressive disclosure. The access metro region starts declaratively collapsed into one aggregate summary node; click the Access metro summary in the live viewport to expand the region and reveal its member nodes and internal links. Click the expanded region hull to collapse it back into the summary node.
Expected Result
Collapse a region into an aggregate summary, then click it to expand member nodes.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, add
expandedGroupIds: [access-metro]underattention.aggregate, alongsidegroups. - Reload.
Access metrostarts expanded into its member nodes; other configured groups remain collapsed. This sets the initial view without a click. - Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-region-collapse
layers:
- id: physical
labels:
name: Physical
nodes:
- id: AGG-1
labels:
node: router
role: aggregation
name: AGG-1
data:
severity: major
layers:
- physical
position:
- 220
- 130
- id: ACC-1
labels:
node: router
role: access
name: ACC-1
data:
severity: normal
layers:
- physical
position:
- 90
- 270
- id: ACC-2
labels:
node: router
role: access
name: ACC-2
data:
severity: normal
layers:
- physical
position:
- 350
- 270
- id: PE-1
labels:
node: router
role: pe
name: PE-1
data:
severity: normal
layers:
- physical
position:
- 590
- 190
links:
- id: agg-acc-1
source: AGG-1
target: ACC-1
labels:
link: access
name: AGG-1 to ACC-1
layers:
- physical
- id: agg-acc-2
source: AGG-1
target: ACC-2
labels:
link: access
name: AGG-1 to ACC-2
layers:
- physical
- id: agg-pe-1
source: AGG-1
target: PE-1
labels:
link: uplink
name: AGG-1 to PE-1
layers:
- physical
regions:
- id: access-metro
members:
- AGG-1
- ACC-1
- ACC-2
labels:
region: metro
name: Access metro
layers:
- physical
attention:
aggregate:
groups:
- id: access-metro
by: region
regionId: access-metro
label: Access metro
expandOnClick: true
layout:
mode: manual
width: 720
height: 430
icons:
router:
glyph: R
fill: "#26313c"
stroke: "#a3b0bd"
svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48" rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24 8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 82
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
- selector: node[labels.role = "pe"]
style:
borderColor: "var(--topoviewer-info)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-info) 12%, var(--topoviewer-panel-bg))"
- selector: link
style:
curveStyle: smooth-step
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 1.8
targetArrowShape: none
opacity: 0.8
labelFontSize: 12
labelFontWeight: 600
- selector: link[labels.link = "uplink"]
style:
lineColor: "var(--topoviewer-info)"
lineWidth: 2.6
- selector: region
style:
backgroundColor: "var(--topoviewer-region-fill)"
borderColor: "var(--topoviewer-region-stroke)"
borderRadius: 6
borderWidth: 1
padding: 70
minWidth: 420
minHeight: 260
zIndex: -20
labelFontSize: 14
labelFontWeight: 600
headerPadding: 48
Dense summary drill-down
Dense summary drill-down keeps a busy topology useful without making zoom decide what the operator meant. The overview shows one summary per metro, including hidden node count, link count, and worst severity. The PE full mesh between metros is represented as counted aggregate links instead of a pile of individual transport links. Click a metro summary to inspect that region while the rest of the topology stays compressed; drag the expanded region hull to reposition its members, or click the hull to collapse it again.
Summary symbols represent metro groups, not individual routers. The badge counts five hidden objects; each inter-metro connector counts four PE links. The curved North–West connector is direct and does not transit the East metro. This is an authored topology; status dots show the worst saved member state.
Expected Result
Keep dense metro topologies readable with summary nodes, counted full-mesh links, and explicit click-to-expand drill-down.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, add
expandedGroupIds: [north-metro]underattention.aggregate, alongsidegroups. - Reload.
NORTH metrostarts expanded into its member nodes; other configured groups remain collapsed. This sets the initial view without a click. - Restore the original settings and reload to compare with the starting view.
version: '0.2'
graph:
id: attention-dense-summary-drilldown
layers:
- id: physical
labels:
name: Physical
- id: transport
labels:
name: Transport
nodes:
- id: north-pe-a
labels: {role: pe, metro: north, tier: backbone, name: NORTH PE-A}
layers: [physical, transport]
position: [170, 135]
data: {severity: major, status: degraded}
- id: north-pe-b
labels: {role: pe, metro: north, tier: backbone, name: NORTH PE-B}
layers: [physical, transport]
position: [310, 135]
data: {severity: normal, status: up}
- id: north-agg-a
labels: {role: aggregation, metro: north, tier: aggregation, name: NORTH AGG}
layers: [physical]
position: [240, 230]
data: {severity: minor, status: constrained}
- id: north-edge-a
labels: {role: edge, metro: north, tier: access, name: NORTH EDGE-A}
layers: [physical]
position: [165, 325]
data: {severity: normal, status: up}
- id: north-edge-b
labels: {role: edge, metro: north, tier: access, name: NORTH EDGE-B}
layers: [physical]
position: [315, 325]
data: {severity: normal, status: up}
- id: east-pe-a
labels: {role: pe, metro: east, tier: backbone, name: EAST PE-A}
layers: [physical, transport]
position: [610, 135]
data: {severity: normal, status: up}
- id: east-pe-b
labels: {role: pe, metro: east, tier: backbone, name: EAST PE-B}
layers: [physical, transport]
position: [750, 135]
data: {severity: normal, status: up}
- id: east-agg-a
labels: {role: aggregation, metro: east, tier: aggregation, name: EAST AGG}
layers: [physical]
position: [680, 230]
data: {severity: normal, status: up}
- id: east-edge-a
labels: {role: edge, metro: east, tier: access, name: EAST EDGE-A}
layers: [physical]
position: [605, 325]
data: {severity: normal, status: up}
- id: east-edge-b
labels: {role: edge, metro: east, tier: access, name: EAST EDGE-B}
layers: [physical]
position: [755, 325]
data: {severity: normal, status: up}
- id: west-pe-a
labels: {role: pe, metro: west, tier: backbone, name: WEST PE-A}
layers: [physical, transport]
position: [1050, 135]
data: {severity: critical, status: down}
- id: west-pe-b
labels: {role: pe, metro: west, tier: backbone, name: WEST PE-B}
layers: [physical, transport]
position: [1190, 135]
data: {severity: normal, status: up}
- id: west-agg-a
labels: {role: aggregation, metro: west, tier: aggregation, name: WEST AGG}
layers: [physical]
position: [1120, 230]
data: {severity: major, status: degraded}
- id: west-edge-a
labels: {role: edge, metro: west, tier: access, name: WEST EDGE-A}
layers: [physical]
position: [1045, 325]
data: {severity: normal, status: up}
- id: west-edge-b
labels: {role: edge, metro: west, tier: access, name: WEST EDGE-B}
layers: [physical]
position: [1195, 325]
data: {severity: normal, status: up}
links:
- id: north-pe-a-north-pe-b
source: north-pe-a
target: north-pe-b
labels: {relation: regional-pe-mesh, name: North PE protection}
layers: [transport]
- id: north-pe-a-north-agg-a
source: north-pe-a
target: north-agg-a
labels: {relation: metro-backbone, name: North PE-A to AGG}
layers: [physical]
- id: north-pe-b-north-agg-a
source: north-pe-b
target: north-agg-a
labels: {relation: metro-backbone, name: North PE-B to AGG}
layers: [physical]
- id: north-agg-a-north-edge-a
source: north-agg-a
target: north-edge-a
labels: {relation: access-uplink, name: North edge A uplink}
layers: [physical]
- id: north-agg-a-north-edge-b
source: north-agg-a
target: north-edge-b
labels: {relation: access-uplink, name: North edge B uplink}
layers: [physical]
- id: east-pe-a-east-pe-b
source: east-pe-a
target: east-pe-b
labels: {relation: regional-pe-mesh, name: East PE protection}
layers: [transport]
- id: east-pe-a-east-agg-a
source: east-pe-a
target: east-agg-a
labels: {relation: metro-backbone, name: East PE-A to AGG}
layers: [physical]
- id: east-pe-b-east-agg-a
source: east-pe-b
target: east-agg-a
labels: {relation: metro-backbone, name: East PE-B to AGG}
layers: [physical]
- id: east-agg-a-east-edge-a
source: east-agg-a
target: east-edge-a
labels: {relation: access-uplink, name: East edge A uplink}
layers: [physical]
- id: east-agg-a-east-edge-b
source: east-agg-a
target: east-edge-b
labels: {relation: access-uplink, name: East edge B uplink}
layers: [physical]
- id: west-pe-a-west-pe-b
source: west-pe-a
target: west-pe-b
labels: {relation: regional-pe-mesh, name: West PE protection}
layers: [transport]
- id: west-pe-a-west-agg-a
source: west-pe-a
target: west-agg-a
labels: {relation: metro-backbone, name: West PE-A to AGG}
layers: [physical]
- id: west-pe-b-west-agg-a
source: west-pe-b
target: west-agg-a
labels: {relation: metro-backbone, name: West PE-B to AGG}
layers: [physical]
- id: west-agg-a-west-edge-a
source: west-agg-a
target: west-edge-a
labels: {relation: access-uplink, name: West edge A uplink}
layers: [physical]
- id: west-agg-a-west-edge-b
source: west-agg-a
target: west-edge-b
labels: {relation: access-uplink, name: West edge B uplink}
layers: [physical]
- id: north-pe-a-east-pe-a
source: north-pe-a
target: east-pe-a
labels: {relation: inter-metro-full-mesh, name: North-East PE mesh 1}
layers: [transport]
- id: north-pe-a-east-pe-b
source: north-pe-a
target: east-pe-b
labels: {relation: inter-metro-full-mesh, name: North-East PE mesh 2}
layers: [transport]
- id: north-pe-b-east-pe-a
source: north-pe-b
target: east-pe-a
labels: {relation: inter-metro-full-mesh, name: North-East PE mesh 3}
layers: [transport]
- id: north-pe-b-east-pe-b
source: north-pe-b
target: east-pe-b
labels: {relation: inter-metro-full-mesh, name: North-East PE mesh 4}
layers: [transport]
- id: north-pe-a-west-pe-a
source: north-pe-a
target: west-pe-a
labels: {relation: inter-metro-full-mesh, name: North-West PE mesh 1}
layers: [transport]
- id: north-pe-a-west-pe-b
source: north-pe-a
target: west-pe-b
labels: {relation: inter-metro-full-mesh, name: North-West PE mesh 2}
layers: [transport]
- id: north-pe-b-west-pe-a
source: north-pe-b
target: west-pe-a
labels: {relation: inter-metro-full-mesh, name: North-West PE mesh 3}
layers: [transport]
- id: north-pe-b-west-pe-b
source: north-pe-b
target: west-pe-b
labels: {relation: inter-metro-full-mesh, name: North-West PE mesh 4}
layers: [transport]
- id: east-pe-a-west-pe-a
source: east-pe-a
target: west-pe-a
labels: {relation: inter-metro-full-mesh, name: East-West PE mesh 1}
layers: [transport]
- id: east-pe-a-west-pe-b
source: east-pe-a
target: west-pe-b
labels: {relation: inter-metro-full-mesh, name: East-West PE mesh 2}
layers: [transport]
- id: east-pe-b-west-pe-a
source: east-pe-b
target: west-pe-a
labels: {relation: inter-metro-full-mesh, name: East-West PE mesh 3}
layers: [transport]
- id: east-pe-b-west-pe-b
source: east-pe-b
target: west-pe-b
labels: {relation: inter-metro-full-mesh, name: East-West PE mesh 4}
layers: [transport]
regions:
- id: north-metro
members: [north-pe-a, north-pe-b, north-agg-a, north-edge-a, north-edge-b]
labels: {metro: north, name: NORTH metro}
layers: [physical, transport]
- id: east-metro
members: [east-pe-a, east-pe-b, east-agg-a, east-edge-a, east-edge-b]
labels: {metro: east, name: EAST metro}
layers: [physical, transport]
- id: west-metro
members: [west-pe-a, west-pe-b, west-agg-a, west-edge-a, west-edge-b]
labels: {metro: west, name: WEST metro}
layers: [physical, transport]
attention:
aggregate:
groups:
- id: north-metro
by: region
regionId: north-metro
label: NORTH metro
- id: east-metro
by: region
regionId: east-metro
label: EAST metro
- id: west-metro
by: region
regionId: west-metro
label: WEST metro
expandOnClick: true
links:
grouping:
enabled: true
threshold: 2
by: [endpoints, layer]
expandOnClick: true
toggles:
- id: showEdgeLabels
default: true
labels:
name: Edge labels
version: '0.2'
icons:
pe:
glyph: PE
fill: '#26313c'
stroke: '#a3b0bd'
aggregation:
glyph: A
fill: '#26313c'
stroke: '#a3b0bd'
edge:
glyph: E
fill: '#26313c'
stroke: '#a3b0bd'
group:
alt: Expandable runtime group
svg: >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#87b6af"
stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="24"
height="24" rx="3" fill="#26313c" stroke="none"/><rect x="8" y="2" width="8" height="6" rx="1"/><rect
x="2" y="16" width="8" height="6" rx="1"/><rect x="14" y="16" width="8" height="6" rx="1"/><path d="M12
8v4M6 16v-4h12v4"/></svg>
glyph: MAP
fill: '#26313c'
stroke: '#a3b0bd'
labelFields:
- labels.name
- label
- count
stylesheet:
- selector: node
style:
width: 76
height: 58
borderWidth: 1.5
labelFontSize: 14
labelFontWeight: 600
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
iconColor: var(--topoviewer-fg-strong)
- selector: node[labels.role = "pe"]
style:
icon: pe
width: 92
height: 64
borderWidth: 2.5
labelFontWeight: 600
labelColor: var(--topoviewer-fg-strong)
- selector: node[labels.role = "aggregation"]
style:
icon: aggregation
width: 84
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
- selector: node[labels.role = "edge"]
style:
icon: edge
labelColor: var(--topoviewer-fg-strong)
- selector: node[isAggregate = "true"]
style:
width: 176
height: 104
backgroundColor: color-mix(in srgb, var(--topoviewer-accent) 12%, var(--topoviewer-panel-bg))
borderColor: var(--topoviewer-accent)
borderWidth: 3
labelColor: var(--topoviewer-fg-strong)
labelFontSize: 15
labelFontWeight: 600
icon: group
shape: roundRectangle
iconSize: 48
- selector: link
style:
curveStyle: bezier
controlPointStepSize: 40
lineColor: var(--topoviewer-edge-default)
lineWidth: 1.8
opacity: 0.72
targetArrowShape: none
labelFontSize: 12
labelFontWeight: 600
- selector: link[labels.relation = "inter-metro-full-mesh"]
style:
lineColor: '#8b78b8'
lineWidth: 2.2
opacity: 0.52
- selector: link[isAggregate = "true"]
style:
lineColor: '#8b78b8'
lineWidth: 4
lineDashPattern: 12 6
labelColor: var(--topoviewer-fg-strong)
textBackgroundColor: var(--topoviewer-edge-label-bg)
textBackgroundOpacity: 0.96
opacity: 0.95
- selector: link[isLinkAggregate = "true"]
style:
lineColor: '#8b78b8'
lineWidth: 4
lineDashPattern: 12 6
labelColor: var(--topoviewer-fg-strong)
textBackgroundColor: var(--topoviewer-edge-label-bg)
textBackgroundOpacity: 0.96
opacity: 0.95
- selector: region
style:
backgroundColor: var(--topoviewer-region-fill)
borderColor: var(--topoviewer-region-stroke)
borderWidth: 1.2
borderRadius: 8
selectable: true
draggable: true
zIndex: -30
labelFontSize: 14
labelFontWeight: 600
headerPadding: 48
- selector: region[id = "north-metro"]
style:
minHeight: 300
minWidth: 300
padding: 58
- selector: region[id = "east-metro"]
style:
minHeight: 300
minWidth: 300
padding: 58
- selector: region[id = "west-metro"]
style:
minHeight: 300
minWidth: 300
padding: 58
- selector: link[source = "aggregate:north-metro"][target = "aggregate:west-metro"][isAggregate = "true"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
- selector: link[source = "aggregate:north-metro"][target = "west-pe-a"][isAggregate = "true"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
- selector: link[source = "aggregate:north-metro"][target = "west-pe-b"][isAggregate = "true"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
- selector: link[source = "north-pe-a"][target = "aggregate:west-metro"][isAggregate = "true"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
- selector: link[source = "north-pe-a"][target = "west-pe-a"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
- selector: link[source = "north-pe-a"][target = "west-pe-b"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
- selector: link[source = "north-pe-b"][target = "aggregate:west-metro"][isAggregate = "true"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
- selector: link[source = "north-pe-b"][target = "west-pe-a"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
- selector: link[source = "north-pe-b"][target = "west-pe-b"]
style:
curveStyle: unbundled-bezier
controlPointDistance: -280
controlPointWeight: 0.5
layout:
mode: manual
width: 1360
height: 460
Advanced zoom policy
Advanced zoom policy is an optional host-controlled behavior for map-style overview/detail transitions. The recommended operator workflow is still explicit: click an aggregate summary to expand it, then click the expanded region hull or parent object to collapse it. Use zoom thresholds only when the embedding experience intentionally wants detail to follow viewport scale.
Expected Result
Optionally bind aggregate expansion to zoom thresholds when a host needs map-style overview/detail transitions.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, set
attention.aggregate.viewport.collapseBelowZoom: 0.7andexpandAboveZoom: 1.0. - Reload, then use the zoom controls to move below 0.7: both metros collapse. Zoom above 1.0: their member nodes return. Cross each threshold without clicking summaries so the zoom policy drives this comparison.
- Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-advanced-zoom-policy
layers:
- id: physical
labels:
name: Physical
nodes:
- id: WEST-1
labels:
role: aggregation
site: west
name: WEST-1
data:
severity: normal
layers:
- physical
position:
- 0
- 90
- id: WEST-2
labels:
role: access
site: west
name: WEST-2
data:
severity: major
layers:
- physical
position:
- 220
- 90
- id: EAST-1
labels:
role: aggregation
site: east
name: EAST-1
data:
severity: normal
layers:
- physical
position:
- 720
- 90
- id: EAST-2
labels:
role: access
site: east
name: EAST-2
data:
severity: critical
layers:
- physical
position:
- 940
- 90
links:
- id: west-access
source: WEST-1
target: WEST-2
layers:
- physical
labels:
name: West access
- id: metro-backbone
source: WEST-2
target: EAST-1
layers:
- physical
labels:
name: Metro backbone
- id: east-access
source: EAST-1
target: EAST-2
layers:
- physical
labels:
name: East access
regions:
- id: west-metro
members:
- WEST-1
- WEST-2
layers:
- physical
labels:
name: West metro
- id: east-metro
members:
- EAST-1
- EAST-2
layers:
- physical
labels:
name: East metro
attention:
aggregate:
groups:
- id: west-metro
by: region
regionId: west-metro
label: West metro
- id: east-metro
by: region
regionId: east-metro
label: East metro
expandOnClick: true
viewport:
collapseBelowZoom: 1
expandAboveZoom: 1.35
version: '0.2'
icons:
router:
glyph: R
fill: '#26313c'
stroke: '#a3b0bd'
svg: >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48"
rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24
8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 82
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
- selector: node[isAggregate = "true"]
style:
width: 132
height: 78
backgroundColor: color-mix(in srgb, var(--topoviewer-accent) 12%, var(--topoviewer-panel-bg))
borderColor: var(--topoviewer-accent)
labelColor: var(--topoviewer-fg-strong)
- selector: link
style:
curveStyle: smooth-step
lineColor: var(--topoviewer-edge-default)
lineWidth: 2.2
targetArrowShape: none
opacity: 0.82
labelFontSize: 12
labelFontWeight: 600
- selector: link[isAggregate = "true"]
style:
lineColor: '#8b78b8'
lineWidth: 4
lineDashPattern: 10 5
- selector: region
style:
backgroundColor: var(--topoviewer-region-fill)
borderColor: var(--topoviewer-region-stroke)
borderRadius: 6
borderWidth: 1
zIndex: -20
labelFontSize: 14
labelFontWeight: 600
headerPadding: 48
- selector: region[id = "west-metro"]
style:
minHeight: 210
minWidth: 330
padding: 70
- selector: region[id = "east-metro"]
style:
minHeight: 210
minWidth: 330
padding: 70
layout:
mode: manual
width: 1120
height: 300
Link grouping
Link grouping demonstrates threshold-based edge aggregation. Three transport links between the same two routers start as one summary link labeled 3 links; click the summary link to reveal each member as a Cytoscape-style bundled Bezier edge. Use the explicit Collapse 3 links control to return to the summary without turning member-edge selection into a hidden toggle.
Expected Result
Group parallel links by endpoint and layer when the count crosses a threshold.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, change
attention.links.grouping.thresholdfrom2to4. - Reload. These three parallel links no longer meet the threshold, so they render individually instead of as one counted summary.
- Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-link-grouping
layers:
- id: transport
labels:
name: Transport
nodes:
- id: PE-1
labels:
role: pe
name: PE-1
layers:
- transport
position:
- 120
- 150
- id: PE-2
labels:
role: pe
name: PE-2
layers:
- transport
position:
- 500
- 150
links:
- id: pe-1-pe-2-a
source: PE-1
target: PE-2
labels:
media: fiber
name: 100G A
layers:
- transport
- id: pe-1-pe-2-b
source: PE-1
target: PE-2
labels:
media: fiber
name: 100G B
layers:
- transport
- id: pe-1-pe-2-c
source: PE-1
target: PE-2
labels:
media: fiber
name: 100G C
layers:
- transport
attention:
links:
grouping:
enabled: true
threshold: 2
by:
- endpoints
- layer
expandOnClick: true
toggles:
- id: showEdgeLabels
default: true
labels:
name: Edge labels
version: "0.2"
icons:
router:
glyph: PE
fill: "#26313c"
stroke: "#a3b0bd"
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 86
height: 62
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
iconColor: var(--topoviewer-fg-strong)
- selector: link
style:
curveStyle: bezier
controlPointStepSize: 34
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 2.2
targetArrowShape: none
labelColor: "var(--topoviewer-fg-strong)"
textBackgroundColor: "var(--topoviewer-edge-label-bg)"
textBackgroundOpacity: 0.95
labelFontSize: 12
labelFontWeight: 600
- selector: link[id = "pe-1-pe-2-a"]
style:
lineColor: "var(--topoviewer-info)"
lineWidth: 3
- selector: link[id = "pe-1-pe-2-b"]
style:
lineColor: "var(--topoviewer-accent)"
lineWidth: 3
- selector: link[id = "pe-1-pe-2-c"]
style:
lineColor: "var(--topoviewer-danger)"
lineWidth: 3
- selector: link[isLinkAggregate = "true"]
style:
lineColor: "#8b78b8"
lineWidth: 4
lineDashPattern: "10 5"
labelColor: "var(--topoviewer-fg-strong)"
textBackgroundColor: "var(--topoviewer-edge-label-bg)"
textBackgroundOpacity: 0.96
layout:
mode: manual
width: 720
height: 360
Query primitives
Query primitives demonstrates the general focus query surface. The topology declares stable IDs, labels, nested data, and link media metadata; the MkDocs attention block focuses CORE-1, all access nodes, objects with critical severity or fanout 12, and the fiber link selected through a stylesheet-compatible selector.
Expected Result
Focus by explicit IDs, labels, data fields, and stylesheet-compatible selectors.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, replace the entire
attention.queryvalue with{ ids: [NOC], mode: dim-context }. Remove its other criteria; focus criteria are combined. - Reload. NOC is the only focused object, while the previous role, severity, and fiber-link matches return to context.
- Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-query-primitives
layers:
- id: physical
labels:
name: Physical
- id: control
labels:
name: Control
nodes:
- id: CORE-1
labels:
node: router
role: core
site: fra
name: CORE-1
data:
severity: critical
metrics:
fanout: 12
layers: [physical]
position: [120, 150]
- id: DIST-1
labels:
node: router
role: distribution
site: fra
name: DIST-1
data:
severity: major
layers: [physical]
position: [300, 150]
- id: ACCESS-1
labels:
node: router
role: access
site: fra
name: ACCESS-1
data:
severity: normal
layers: [physical]
position: [500, 70]
- id: ACCESS-2
labels:
node: router
role: access
site: fra
name: ACCESS-2
data:
severity: normal
layers: [physical]
position: [500, 230]
- id: NOC
labels:
node: controller
role: control
name: NOC
data:
severity: normal
layers: [control]
position: [300, 330]
links:
- id: core-dist
source: CORE-1
target: DIST-1
labels:
link: physical
media: fiber
name: CORE-1 to DIST-1 fiber
layers: [physical]
- id: dist-access-1
source: DIST-1
target: ACCESS-1
labels:
link: physical
media: copper
name: DIST-1 to ACCESS-1
layers: [physical]
- id: dist-access-2
source: DIST-1
target: ACCESS-2
labels:
link: physical
media: copper
name: DIST-1 to ACCESS-2
layers: [physical]
- id: noc-core
source: NOC
target: CORE-1
labels:
link: control
protocol: telemetry
media: management
name: NOC telemetry to CORE-1
layers: [control]
attention:
query:
ids: [CORE-1]
labels:
role: access
data:
severity: critical
metrics.fanout: 12
selectors:
- link[labels.media = "fiber"]
mode: dim-context
version: "0.2"
layout:
mode: manual
width: 720
height: 420
icons:
router:
glyph: R
fill: "#26313c"
stroke: "#a3b0bd"
svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48" rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24 8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
controller:
glyph: NOC
fill: "#26313c"
stroke: "#a3b0bd"
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 82
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
iconColor: var(--topoviewer-fg-strong)
- selector: node[labels.role = "control"]
style:
icon: controller
width: 92
borderColor: "var(--topoviewer-warning)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-warning) 12%,
var(--topoviewer-panel-bg))"
- selector: node[data.severity = "critical"]
style:
borderColor: "var(--topoviewer-danger)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-danger) 12%,
var(--topoviewer-panel-bg))"
- selector: node[labels.role = "access"]
style:
borderColor: "var(--topoviewer-info)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-info) 12%,
var(--topoviewer-panel-bg))"
- selector: link
style:
curveStyle: smooth-step
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 1.7
targetArrowShape: none
opacity: 0.8
labelFontSize: 12
labelFontWeight: 600
- selector: link[labels.media = "fiber"]
style:
label: Fiber
lineColor: "var(--topoviewer-info)"
lineWidth: 3
- selector: link[labels.protocol = "telemetry"]
style:
label: Telemetry
curveStyle: bezier
lineColor: "var(--topoviewer-info)"
lineDashPattern: 3 4
targetArrowShape: triangle
Region focus
Region focus uses graph.regions[].members as the declarative grouping source. The attention query focuses the access metro region, so its member nodes become prominent while the PE outside the region and the surrounding links stay as dimmed context.
Expected Result
Focus a region and its member nodes while preserving surrounding context.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, remove
ACC-2only from regionaccess-metro'smemberslist. Keep the node and its links. - Reload. That node becomes dimmed context instead of a focused region member, and the auto-fit hull adjusts to its remaining members.
- Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-region-focus
layers:
- id: physical
labels:
name: Physical
nodes:
- id: AGG-1
labels:
node: router
role: aggregation
name: AGG-1
data:
severity: major
layers: [physical]
position: [220, 130]
- id: ACC-1
labels:
node: router
role: access
name: ACC-1
data:
severity: normal
layers: [physical]
position: [90, 270]
- id: ACC-2
labels:
node: router
role: access
name: ACC-2
data:
severity: normal
layers: [physical]
position: [350, 270]
- id: PE-1
labels:
node: router
role: pe
name: PE-1
data:
severity: normal
layers: [physical]
position: [590, 190]
links:
- id: agg-acc-1
source: AGG-1
target: ACC-1
labels:
link: access
name: AGG-1 to ACC-1
layers: [physical]
- id: agg-acc-2
source: AGG-1
target: ACC-2
labels:
link: access
name: AGG-1 to ACC-2
layers: [physical]
- id: agg-pe-1
source: AGG-1
target: PE-1
labels:
link: uplink
name: AGG-1 to PE-1
layers: [physical]
regions:
- id: access-metro
members: [AGG-1, ACC-1, ACC-2]
labels:
region: metro
name: Access metro
layers: [physical]
attention:
query:
regionIds: [access-metro]
mode: dim-context
layout:
mode: manual
width: 720
height: 430
icons:
router:
glyph: R
fill: "#26313c"
stroke: "#a3b0bd"
svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48" rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24 8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 82
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
- selector: node[labels.role = "pe"]
style:
borderColor: "var(--topoviewer-info)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-info) 12%, var(--topoviewer-panel-bg))"
- selector: link
style:
curveStyle: smooth-step
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 1.8
targetArrowShape: none
opacity: 0.8
labelFontSize: 12
labelFontWeight: 600
- selector: link[labels.link = "uplink"]
style:
lineColor: "var(--topoviewer-info)"
lineWidth: 2.6
- selector: region
style:
backgroundColor: "var(--topoviewer-region-fill)"
borderColor: "var(--topoviewer-region-stroke)"
borderRadius: 6
borderWidth: 1
padding: 70
minWidth: 420
minHeight: 260
zIndex: -20
labelFontSize: 14
labelFontWeight: 600
headerPadding: 48
Dependency focus
Dependency focus uses directed links and path sequences as an adjacency graph. This example starts from CORE-1, walks two downstream hops, marks reached nodes as related, and leaves the links as dimmed context so the blast radius is visible without hiding the topology.
Expected Result
Traverse directed topology relationships to show downstream blast radius.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, change
attention.query.dependency.depthfrom2to1. - Reload. The seed and its immediate downstream neighbors remain emphasized; the access nodes two hops away return to dimmed context.
- Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-dependency-focus
layers:
- id: physical
labels:
name: Physical
- id: control
labels:
name: Control
nodes:
- id: CORE-1
labels:
node: router
role: core
name: CORE-1
data:
severity: critical
layers: [physical]
position: [90, 190]
- id: DIST-1
labels:
node: router
role: distribution
name: DIST-1
data:
severity: major
layers: [physical]
position: [300, 190]
- id: ACCESS-1
labels:
node: router
role: access
name: ACCESS-1
data:
severity: normal
layers: [physical]
position: [540, 90]
- id: ACCESS-2
labels:
node: router
role: access
name: ACCESS-2
data:
severity: normal
layers: [physical]
position: [540, 290]
- id: NOC
labels:
node: controller
role: control
name: NOC
data:
severity: warning
layers: [control]
position: [300, 340]
links:
- id: core-dist
source: CORE-1
target: DIST-1
labels:
link: physical
name: CORE-1 to DIST-1
layers: [physical]
- id: dist-access-1
source: DIST-1
target: ACCESS-1
labels:
link: physical
name: DIST-1 to ACCESS-1
layers: [physical]
- id: dist-access-2
source: DIST-1
target: ACCESS-2
labels:
link: physical
name: DIST-1 to ACCESS-2
layers: [physical]
- id: core-noc
source: CORE-1
target: NOC
labels:
link: control
protocol: telemetry
name: CORE-1 to NOC telemetry
layers: [control]
attention:
query:
dependency:
from: [CORE-1]
direction: downstream
depth: 2
mode: dim-context
version: "0.2"
layout:
mode: manual
width: 760
height: 430
icons:
router:
glyph: R
fill: "#26313c"
stroke: "#a3b0bd"
svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48" rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24 8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
controller:
glyph: NOC
fill: "#26313c"
stroke: "#a3b0bd"
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 82
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
iconColor: var(--topoviewer-fg-strong)
- selector: node[labels.role = "control"]
style:
icon: controller
width: 92
borderColor: "var(--topoviewer-warning)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-warning) 12%,
var(--topoviewer-panel-bg))"
- selector: link
style:
curveStyle: smooth-step
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 1.8
targetArrowShape: triangle
opacity: 0.82
labelFontSize: 12
labelFontWeight: 600
- selector: link[labels.protocol = "telemetry"]
style:
label: Telemetry
curveStyle: bezier
lineColor: "var(--topoviewer-info)"
lineDashPattern: 3 4
Hide context
Hide context mode is useful when context should be removed from the rendered view instead of muted. The topology marks two PE nodes with labels.role: pe; the attention query focuses that label and hides every non-matching node and link.
Expected Result
Use hide-context mode when the focused set should be isolated instead of dimmed.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, change
attention.query.modefromhide-contexttodim-context. - Reload. The same PE nodes remain focused, while the previously hidden core nodes and links return as muted context.
- Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-hide-context
layers:
- id: physical
labels:
name: Physical
nodes:
- id: PE-1
labels:
node: router
role: pe
name: PE-1
data:
severity: major
layers: [physical]
position: [90, 170]
- id: CORE-1
labels:
node: router
role: core
name: CORE-1
data:
severity: normal
layers: [physical]
position: [260, 100]
- id: CORE-2
labels:
node: router
role: core
name: CORE-2
data:
severity: normal
layers: [physical]
position: [440, 100]
- id: PE-2
labels:
node: router
role: pe
name: PE-2
data:
severity: minor
layers: [physical]
position: [610, 170]
- id: RR-1
labels:
node: router
role: route-reflector
name: RR-1
data:
severity: normal
layers: [physical]
position: [350, 290]
links:
- id: pe-1-core-1
source: PE-1
target: CORE-1
labels:
link: physical
name: PE-1 to CORE-1
layers: [physical]
- id: core-1-core-2
source: CORE-1
target: CORE-2
labels:
link: physical
name: CORE-1 to CORE-2
layers: [physical]
- id: core-2-pe-2
source: CORE-2
target: PE-2
labels:
link: physical
name: CORE-2 to PE-2
layers: [physical]
- id: rr-core
source: RR-1
target: CORE-1
labels:
link: control
name: RR-1 to CORE-1
layers: [physical]
attention:
query:
labels:
role: pe
mode: hide-context
version: "0.2"
layout:
mode: manual
width: 720
height: 400
icons:
router:
glyph: R
fill: "#26313c"
stroke: "#a3b0bd"
svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48" rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24 8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 82
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
- selector: node[labels.role = "pe"]
style:
borderColor: "var(--topoviewer-info)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-info) 12%, var(--topoviewer-panel-bg))"
- selector: link
style:
curveStyle: smooth-step
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 1.8
targetArrowShape: none
opacity: 0.82
labelFontSize: 12
labelFontWeight: 600
Parent and label collapse
Parent and label collapse shows the other aggregate group types. Service child nodes under PE-1 collapse by parent-child relationship, and access nodes collapse by labels.role: access; clicking the PE-1 services summary expands only that parent-derived group.
Expected Result
Collapse parent-child objects and label-defined groups into aggregate summaries.
Try It
- Copy the two YAML tabs into your own project.
- In topology.yaml, add
expandedGroupIds: [pe-services]underattention.aggregate, alongsidegroups. - Reload.
PE-1 servicesstarts expanded into its member nodes; other configured groups remain collapsed. This sets the initial view without a click. - Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-parent-label-collapse
layers:
- id: physical
labels:
name: Physical
- id: service
labels:
name: Service
nodes:
- id: CORE-1
labels:
node: router
role: core
name: CORE-1
data:
severity: normal
layers: [ physical, service ]
position: [ 650, 190 ]
- id: PE-1
labels:
node: router
role: pe
name: PE-1
data:
severity: major
layers: [ physical, service ]
position: [ 380, 190 ]
- id: SVC-A
parent: PE-1
labels:
node: service
role: service
service: l3vpn
name: VPN-A
data:
severity: critical
layers: [ service ]
position:
- 280
- 0
- id: SVC-B
parent: PE-1
labels:
node: service
role: service
service: l3vpn
name: VPN-B
data:
severity: minor
layers: [ service ]
position:
- 280
- 80
- id: ACC-1
labels:
node: router
role: access
name: ACC-1
data:
severity: normal
layers: [ physical ]
position: [ 90, 120 ]
- id: ACC-2
labels:
node: router
role: access
name: ACC-2
data:
severity: major
layers: [ physical ]
position: [ 90, 260 ]
links:
- id: svc-a-core
source: SVC-A
target: CORE-1
labels:
link: service
name: VPN-A to CORE-1
layers: [ service ]
- id: svc-b-core
source: SVC-B
target: CORE-1
labels:
link: service
name: VPN-B to CORE-1
layers: [ service ]
- id: acc-1-pe
source: ACC-1
target: PE-1
labels:
link: access
name: ACC-1 to PE-1
layers: [ physical ]
- id: acc-2-pe
source: ACC-2
target: PE-1
labels:
link: access
name: ACC-2 to PE-1
layers: [ physical ]
- id: pe-core
source: PE-1
target: CORE-1
labels:
link: uplink
name: PE-1 to CORE-1
layers: [ physical ]
attention:
aggregate:
groups:
- id: pe-services
by: parent
parentId: PE-1
label: PE-1 services
- id: access-role
by: label
key: role
value: access
label: Access nodes
expandOnClick: true
version: "0.2"
layout:
mode: manual
width: 780
height: 450
icons:
router:
glyph: R
fill: "#26313c"
stroke: "#a3b0bd"
svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48" rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24 8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
service:
glyph: SVC
fill: "#26313c"
stroke: "#a3b0bd"
labelFields:
- labels.name
stylesheet:
- selector: node
style:
icon: router
width: 82
height: 60
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
iconColor: var(--topoviewer-fg-strong)
- selector: node[labels.role = "service"]
style:
icon: service
width: 94
height: 54
backgroundColor: "color-mix(in srgb, #8b78b8 12%, var(--topoviewer-panel-bg))"
borderColor: "var(--topoviewer-accent)"
- selector: node[labels.role = "access"]
style:
borderColor: "var(--topoviewer-info)"
backgroundColor: "color-mix(in srgb, var(--topoviewer-info) 12%,
var(--topoviewer-panel-bg))"
- selector: link
style:
curveStyle: smooth-step
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 1.8
targetArrowShape: none
opacity: 0.82
labelFontSize: 12
labelFontWeight: 600
- selector: link[labels.link = "service"]
style:
label: Service
lineColor: "#8b78b8"
lineWidth: 2.8
targetArrowShape: triangle
- selector: link[labels.link = "uplink"]
style:
label: Uplink
lineColor: "var(--topoviewer-info)"
lineWidth: 2.6
Aggregate badge and status
Aggregate badge and status defaults make a collapsed group useful before drill-down. The summary node shows the hidden member count as a badge and the worst member severity as a status marker.
Expected Result
Collapsed aggregate summaries can expose hidden member count and worst severity as compact node cues.
Try It
- Copy the two YAML tabs into your own project.
- Keep the Access region summary collapsed. In topology.yaml, change ACC-2's data.severity from critical to normal.
- Reload. The summary's worst severity changes from critical to major because AGG-1 is still major. Its member-count badge remains 3.
- Restore the original settings and reload to compare with the starting view.
version: "0.2"
graph:
id: attention-aggregate-badge-status
layers:
- id: physical
labels:
name: Physical
nodes:
- id: ACC-1
labels:
role: access
name: ACC-1
data:
severity: normal
layers: [physical]
position: [120, 110]
- id: ACC-2
labels:
role: access
name: ACC-2
data:
severity: critical
layers: [physical]
position: [120, 230]
- id: AGG-1
labels:
role: aggregation
name: AGG-1
data:
severity: major
layers: [physical]
position: [300, 170]
- id: PE-1
labels:
role: pe
name: PE-1
data:
severity: normal
layers: [physical]
position: [520, 170]
links:
- id: acc1-agg
source: ACC-1
target: AGG-1
layers: [physical]
labels:
name: ACC-1 to AGG-1
- id: acc2-agg
source: ACC-2
target: AGG-1
layers: [physical]
labels:
name: ACC-2 to AGG-1
- id: agg-pe
source: AGG-1
target: PE-1
layers: [physical]
labels:
name: AGG-1 to PE-1
regions:
- id: access-region
labels:
region: access
name: Access region
members:
- ACC-1
- ACC-2
- AGG-1
layers: [physical]
attention:
aggregate:
groups:
- id: access-region
by: region
regionId: access-region
label: Access region
expandOnClick: true
version: "0.2"
layout:
mode: manual
width: 650
height: 360
icons:
router:
glyph: R
fill: "#26313c"
stroke: "#a3b0bd"
svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#f2f5f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="48" height="48" rx="6" fill="#26313c" stroke="none"/><path d="M8 24h32M24 8v32M8 24l6-6m-6 6 6 6m26-6-6-6m6 6-6 6M24 8l-6 6m6-6 6 6M24 40l-6-6m6 6 6-6"/></svg>
alt: Router
stylesheet:
- selector: node
style:
icon: router
width: 92
height: 68
iconSize: 46
borderWidth: 2
labelColor: var(--topoviewer-fg-strong)
labelFontWeight: 600
labelFontSize: 14
metaColor: transparent
backgroundColor: var(--topoviewer-panel-bg)
borderColor: var(--topoviewer-edge-default)
- selector: node[isAggregate = "true"]
style:
shape: roundRectangle
labelPosition: bottom
badgeBackgroundColor: "color-mix(in srgb, var(--topoviewer-danger) 12%, var(--topoviewer-panel-bg))"
badgeColor: "var(--topoviewer-fg-strong)"
badgeBorderColor: "var(--topoviewer-danger)"
statusPlacement: topRight
statusSize: 13
outlineColor: "var(--topoviewer-danger)"
outlineWidth: 4
outlineOpacity: 0.72
underlayColor: "color-mix(in srgb, var(--topoviewer-danger) 18%, var(--topoviewer-panel-bg))"
underlayPadding: 10
underlayOpacity: 0.58
- selector: node[labels.role = "pe"]
style:
backgroundColor: "color-mix(in srgb, var(--topoviewer-info) 12%, var(--topoviewer-panel-bg))"
borderColor: "var(--topoviewer-info)"
- selector: link
style:
lineColor: "var(--topoviewer-edge-default)"
lineWidth: 2
targetArrowShape: none
labelFontSize: 12
labelFontWeight: 600
- selector: region
style:
backgroundColor: "var(--topoviewer-region-fill)"
borderColor: "var(--topoviewer-region-stroke)"
borderWidth: 1
zIndex: -20
labelFontSize: 14
labelFontWeight: 600
headerPadding: 48
- selector: region[id = "access-region"]
style:
minHeight: 250
minWidth: 360
padding: 64