How Node Types Are Styled by Function in Diagram Design
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:
- Parser layer — extracts raw shape data from diagram sources
- Mapping layer — normalizes shapes into functional families via
shape_family() - 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)
The Mermaid extractor in 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)
The Draw.io extractor in 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 →
ellipseorstate - 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. The stylesheet maps family classes to concrete appearance properties:
/* 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
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
# 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
# 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 |
Mermaid syntax parsing, shape suffix detection | shape_family() (L999), node creation |
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 |
CSS styling definitions, render templates | Family class selectors |
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
rectstyling 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. 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →