# How Node Types Are Styled by Function in Diagram Design

> Discover how Diagram Design styles nodes by function using a shape-family mapping system that links functional identifiers to CSS classes for visual appearance. Learn more about customizing your diagrams.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: deep-dive
- Published: 2026-08-16

---

**Diagram Design determines node styling through a shape-family mapping system where parsers assign functional shape identifiers (rect, diamond, container, etc.) that resolve to CSS classes defining visual appearance.**

The `cathrynlavery/diagram-design` repository implements a unified styling architecture that separates semantic node types from their visual representation. This approach enables consistent styling across Mermaid and Draw.io diagram formats while maintaining flexibility for different diagram functions.

## The Shape-Family Mapping Architecture

Node styling in Diagram Design operates through three coordinated layers:

1. **Parser layer** — extracts raw shape data from diagram sources
2. **Mapping layer** — normalizes shapes into functional families via `shape_family()`
3. **Rendering layer** — applies CSS classes based on the resolved family

This architecture ensures that a "decision" node looks identical whether it originates from Mermaid's diamond syntax or Draw.io's diamond geometry.

## How Parsers Assign Initial Shapes

### Mermaid Parser ([`mermaid_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/mermaid_extract.py))

The Mermaid extractor in [`skills/diagram-design/scripts/mermaid_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/mermaid_extract.py) parses node syntax and assigns shape identifiers based on suffixes:

| Syntax Pattern | Assigned Shape | Functional Purpose |
|--------------|---------------|-------------------|
| `Node` (no suffix) | `rect` | Process/operation |
| `Node?` or `Node⧈` | `diamond` | Decision/condition |
| `Node()` or `NodeO` | `ellipse` | Start/end terminator |
| Subgraph declaration | `container` | Logical grouping |

The `shape_family()` function at line 999 normalizes these assignments.

### Draw.io Parser ([`drawio_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/drawio_extract.py))

The Draw.io extractor in [`skills/diagram-design/scripts/drawio_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/drawio_extract.py) extracts geometry types from XML and maps them to equivalent shape families. Its `shape_family()` function at line 278 handles:

- Rectangle primitives → `rect`
- Diamond geometries → `diamond`
- Rounded rectangles → `ellipse` or `state`
- Table structures → `table`
- Swimlane/participant elements → `lifeline`

## Functional Node Families and Their Styling

The shape-family mapper collapses diagramspecific shapes into semantic categories used for styling:

| Function | Shape Family | CSS Class | Visual Characteristics |
|---------|-------------|-----------|----------------------|
| **Process/Operation** | `rect` | `.node-rect` | Solid rectangle, primary color fill |
| **Decision/Condition** | `diamond` | `.node-diamond` | Diamond geometry, accent color |
| **Start/End** | `ellipse` | `.node-ellipse` | Rounded ellipse, muted tone |
| **Container/Subgraph** | `container` | `.node-container` | Light background, bordered enclosure |
| **State** | `state` | `.node-state` | Rounded rectangle, distinct fill |
| **Data/Table** | `table` | `.node-table` | Grid borders, structural appearance |
| **Sequence Participant** | `lifeline` | `.node-lifeline` | Dashed vertical line representation |

## CSS Implementation in HTML Assets

The visual styling executes through scoped CSS in [`skills/diagram-design/assets/example-architecture.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/example-architecture.html). The stylesheet maps family classes to concrete appearance properties:

```css
/* Primary process nodes */
.node-rect {
  fill: #1e90ff;
  stroke: #000;
}

/* Decision points requiring user attention */
.node-diamond {
  fill: #ffcc00;
  stroke: #000;
}

/* Terminal states */
.node-ellipse {
  fill: #d3d3d3;
  stroke: #000;
}

/* Logical groupings */
.node-container {
  fill: #f5f5f5;
  stroke: #666;
}

/* Structured data representations */
.node-table {
  fill: #fafafa;
  stroke: #999;
}

/* Sequence diagram timing */
.node-lifeline {
  stroke: #777;
  stroke-dasharray: 4 2;
}

```

## Working with Node Styling Programmatically

### Creating a Decision Node

```python
from diagram_design import Diagram

diagram = Diagram()

# Parse Mermaid-style decision syntax

diagram.add_node(
    node_id="approve_gate",
    label="Budget Approved?",
    shape="diamond",  # Resolved by shape_family() 

    parent=None
)

# Output: shape_family("diamond") returns "diamond"

# Renders as: <g class="node-diamond">...</g>

```

### Creating a Container Structure

```python

# Extracted from Draw.io subgraph or Mermaid subflow

diagram.add_node(
    node_id="auth_boundary",
    label="Authentication Service",
    shape="container",
    parent=None,
    container=True  # Explicit container flag

)

# Output: shape_family("container") returns "container"  

# Renders as: <g class="node-container">...</g>

```

### Custom Shape Handling

```python

# Unrecognized shapes fallback to rect styling

diagram.add_node(
    node_id="legacy_node",
    label="Unknown Type",
    shape="custom_vendor_shape"  # Not in standard mapping

)

# Output: shape_family("custom_vendor_shape") defaults to "rect"

# Renders with .node-rect styling for consistency

```

## Key Implementation Files

| File Path | Responsibility | Critical Functions |
|-----------|--------------|------------------|
| [`skills/diagram-design/scripts/mermaid_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/mermaid_extract.py) | Mermaid syntax parsing, shape suffix detection | `shape_family()` (L999), node creation |
| [`skills/diagram-design/scripts/drawio_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/drawio_extract.py) | Draw.io XML parsing, geometry extraction | `shape_family()` (L278), table handling |
| [`skills/diagram-design/assets/example-architecture.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/example-architecture.html) | CSS styling definitions, render templates | Family class selectors |
| [`skills/diagram-design/SKILL.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/SKILL.md) | Architecture documentation, supported types | Node type reference |

## Summary

- **Shape assignment occurs at parse time**: Mermaid suffixes and Draw.io geometry both resolve to canonical shape identifiers
- **Family mapping normalizes semantics**: The `shape_family()` function in both extractors collapses variant shapes into functional categories
- **CSS classes drive rendering**: Family names translate directly to `.node-{family}` classes with predefined visual properties
- **Container and special types receive distinct styling**: Structural elements (containers, tables, lifelines) have dedicated families beyond basic flowchart shapes
- **Fallback behavior ensures consistency**: Unrecognized shapes default to `rect` styling rather than failing

## Frequently Asked Questions

### How does Diagram Design handle custom shapes not in the standard mapping?

Unrecognized shape identifiers pass through `shape_family()` and receive the default `rect` family assignment. This guarantees consistent rendering rather than broken styling, though custom CSS can target specific non-standard shapes through additional class attributes if needed.

### Where can I modify the color scheme for node types?

Edit the CSS rules in [`skills/diagram-design/assets/example-architecture.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/example-architecture.html). Each `.node-{family}` selector controls fill colors, stroke properties, and other SVG presentation attributes. The repository uses standard CSS, so modifications apply immediately to rendered output.

### Why are there two separate `shape_family()` functions?

Each parser operates independently because Mermaid and Draw.io represent shapes through different mechanisms—text suffixes versus XML geometry. Maintaining separate mapping functions allows format-specific logic while converging on identical family outputs for unified styling.