Skip to content

Card node layout

Card node layout is for nodes that need to read like compact operational records instead of plain device glyphs.

The node is still a roundRectangle. The nested nodeLayout object only changes the internal arrangement: icon box on the left, title and subtitle on the right. Badges and status markers stay node-level controls, so a card can keep a TurboFlow-style badge on the top-right shell while its icon cell renders either a glyph or an inline SVG. Use the badge size keys when the compact defaults are too small for the card body.

Use this pattern for service maps, application dependencies, Kubernetes objects, or operations views where every node needs a name plus one short piece of metadata.

Expected Result

Nested nodeLayout makes round-rectangle nodes read like compact service cards.

Try It

  1. Copy the two YAML tabs into your own project.
  2. In the node rule in stylesheet.yaml, set nodeLayout.content.align to "center".
  3. Reload. The card title and subtitle align centrally in their content cell; the icon remains separate.
  4. Restore the original settings and reload to compare with the starting view.

Card node layout

version: "0.2"
graph:
  id: node-card-layout
  layers:
    - id: services
      labels:
        name: Services
  nodes:
    - id: api
      labels:
        role: frontend
        state: healthy
        name: API gateway
      data:
        subtitle: ingress / active
        badge: "4"
      layers: [ services ]
      position:
        - 100
        - 150
    - id: orders
      labels:
        role: service
        state: busy
        name: Orders service
      data:
        subtitle: checkout path
        badge: "12"
      layers: [ services ]
      position:
        - 410
        - 150
    - id: database
      labels:
        role: database
        state: healthy
        name: Orders database
      data:
        subtitle: primary / eu-west
        badge: "1"
      layers: [ services ]
      position:
        - 720
        - 150
  links:
    - id: api-orders
      source: api
      target: orders
      layers: [ services ]
      labels:
        name: REST
    - id: orders-database
      source: orders
      target: database
      layers: [ services ]
      labels:
        name: SQL
version: "0.2"
layout:
  mode: manual
  width: 760
  height: 300
icons:
  router.nokia:
    glyph: "N"
    alt: Nokia router
    svg: |
      <svg
        xmlns:xlink="http://www.w3.org/1999/xlink"
        xmlns="http://www.w3.org/2000/svg"
        xml:space="preserve"
        style="enable-background:new 0 0 120 120;"
        viewBox="0 0 120 120"
        y="0px"
        x="0px"
        id="Layer_1"
        version="1.1"
        width="120px"
        height="120px"
        fill="none"
      >
        <style type="text/css">
          .st0 { fill: #26313c; }
          .st1 { fill: none; stroke: #FFFFFF; stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 10; }
        </style>
        <rect height="120" width="120" class="st0" />
        <g>
          <g>
            <path d="M71.7,19.7V48h28" class="st1" />
            <path d="M91.2,38.5l7.5,7.6c1.3,1.3,1.3,3.1,0,4.3L91.1,58" class="st1" />
          </g>
          <g>
            <path d="M20,47.8h28.4v-28" class="st1" />
            <path d="M38.8,28.3l7.6-7.5c1.3-1.3,3.1-1.3,4.3,0l7.7,7.6" class="st1" />
          </g>
          <g>
            <path d="M48,100.3V72H20" class="st1" />
            <path d="M28.5,81.5L21,73.9c-1.3-1.3-1.3-3.1,0-4.3l7.6-7.7" class="st1" />
          </g>
          <g>
            <path d="M100,71.9H71.6v28" class="st1" />
            <path d="M81.2,91.4l-7.6,7.5c-1.3,1.3-3.1,1.3-4.3,0l-7.7-7.6" class="st1" />
          </g>
        </g>
      </svg>
  service:
    glyph: SVC
    fill: "#26313c"
    stroke: "#a3b0bd"
  database:
    glyph: DB
    fill: "#26313c"
    stroke: "#a3b0bd"
labelFields:
  - labels.name
stylesheet:
  - selector: node
    style:
      shape: roundRectangle
      width: 240
      height: 64
      backgroundColor: "var(--topoviewer-panel-bg)"
      borderColor: "var(--topoviewer-edge-default)"
      borderWidth: 2
      color: "#e2e8f0"
      labelColor: "var(--topoviewer-fg-strong)"
      metaColor: "var(--topoviewer-fg-muted)"
      iconPadding: 6
      badgeColor: "var(--topoviewer-fg-strong)"
      badgeBackgroundColor: "color-mix(in srgb, var(--topoviewer-info) 12%,
        var(--topoviewer-panel-bg))"
      badgeBorderColor: "var(--topoviewer-info)"
      badgeBorderWidth: 2
      badgeFontSize: 11
      badgeFontWeight: 900
      badgeMinWidth: 22
      badgeMinHeight: 22
      badgePadding: 3
      badgeOffset: 11
      badgePosition: topRight
      statusPlacement: topRight
      statusSize: 16
      nodeLayout:
        type: card
        direction: horizontal
        icon:
          placement: left
          width: 44
          height: 44
        content:
          align: left
          titleField: name
          subtitleField: data.subtitle
      labelFontSize: 14
      labelFontWeight: 600
      metaFontSize: 12
      iconColor: var(--topoviewer-fg-strong)
  - selector: node[labels.role = "frontend"]
    style:
      icon: router.nokia
      borderColor: "var(--topoviewer-accent)"
      badgeLabel: "4"
  - selector: node[labels.role = "service"]
    style:
      icon: service
      borderColor: "var(--topoviewer-info)"
      badgeLabel: "12"
  - selector: node[labels.role = "database"]
    style:
      icon: database
      borderColor: "#8b78b8"
      badgeLabel: "1"
  - selector: node[labels.state = "healthy"]
    style:
      statusColor: "var(--topoviewer-success)"
  - selector: node[labels.state = "busy"]
    style:
      statusColor: "var(--topoviewer-warning)"
      outlineColor: "var(--topoviewer-warning)"
      outlineWidth: 2
      outlineOpacity: 0.45
  - selector: link
    style:
      curveStyle: bezier
      lineColor: "var(--topoviewer-edge-default)"
      lineWidth: 3
      targetArrowShape: triangle
      targetArrowColor: "var(--topoviewer-accent)"
      labelColor: "var(--topoviewer-fg-strong)"
      textBackgroundColor: "color-mix(in srgb, var(--topoviewer-accent) 12%,
        var(--topoviewer-panel-bg))"
      labelFontSize: 12
      labelFontWeight: 600