feat(baoyu-diagram): add architecture enrichment and structural layout patterns

This commit is contained in:
Jim Liu 宝玉 2026-04-14 11:32:55 -05:00
parent dcd0f81433
commit acbcf19ba2
7 changed files with 322 additions and 3 deletions

View File

@ -141,7 +141,7 @@ Assess whether the material should produce one diagram or multiple sub-diagrams.
**Single diagram** when: **Single diagram** when:
- Material is focused on one concept, one mechanism, one process - Material is focused on one concept, one mechanism, one process
- Named elements count is manageable (under ~6 for flowchart, under ~4 actors for sequence, under ~3 containers for structural) - Named elements count is manageable (under ~6 for flowchart, under ~4 actors for sequence, under ~3 containers for structural — but architecture diagrams may have 1020 elements in a single diagram; see Step 5a item 6, "Architecture enrichment")
- One "After seeing this diagram, the reader understands ___" sentence covers the whole material - One "After seeing this diagram, the reader understands ___" sentence covers the whole material
**Multiple sub-diagrams** when: **Multiple sub-diagrams** when:
@ -279,7 +279,7 @@ For each diagram in the confirmed plan (1 to N, overview diagram generated last)
Read the current diagram's plan entry. Extract or refine these five things from the source material: Read the current diagram's plan entry. Extract or refine these five things from the source material:
1. **Named elements** — list every distinct actor, component, service, state, or phase explicitly named. Count them. If the count is 6+, plan multiple diagrams rather than cramming everything into one (see `flowchart.md` → "Planning before you write SVG"). 1. **Named elements** — list every distinct actor, component, service, state, or phase explicitly named. Count them. If the count is 6+ for simple flowcharts, plan multiple diagrams rather than cramming everything into one (see `flowchart.md` → "Planning before you write SVG"). **Exception**: structural architecture diagrams (microservices, cloud topologies, system designs) routinely need 1020 named elements in a single diagram — services, databases, gateways, message buses, clients. 10 is the minimum where splitting is unnecessary; enriched diagrams (item 6 below) typically reach ≥12. Do not split an architecture diagram just because the element count is high; instead, see "Architecture enrichment" (item 6 below) and `structural.md` → "Full architecture layout".
2. **Relationship type** — for each interaction between elements, classify it: 2. **Relationship type** — for each interaction between elements, classify it:
- Sequential steps / order of operations → flowchart signal - Sequential steps / order of operations → flowchart signal
@ -294,6 +294,22 @@ Read the current diagram's plan entry. Extract or refine these five things from
5. **Language** — CJK vs. Latin. Affects text-width multipliers in Step 5d (15 px/char vs. 8 px/char for titles). Mixed content (CJK labels with some Latin terms) counts as CJK. 5. **Language** — CJK vs. Latin. Affects text-width multipliers in Step 5d (15 px/char vs. 8 px/char for titles). Mixed content (CJK labels with some Latin terms) counts as CJK.
6. **Architecture enrichment** — when the type is **structural** and the topic is an architecture or infrastructure diagram ("microservices architecture", "Kubernetes cluster", "cloud topology", "system design"), actively expand the named elements beyond what the user literally wrote. The user's prompt is a **seed**, not a complete spec. A bare "microservices architecture" should produce a diagram with ≥12 named elements, not 5 generic boxes. Apply these enrichment rules:
- **Multiple client types**: if the system serves end users, show ≥2 clients (Web app + Mobile app, or Browser + CLI). Each with a tech subtitle (React SPA, iOS/Android).
- **Gateway details**: technology name (Kong, Nginx, Envoy), responsibilities (rate limiting, auth/routing), port (:443). Not just "API Gateway".
- **Per-service specifics**: each microservice gets a technology and port subtitle (Go :8081, Java :8082, Python :8083, Node.js :8084). The reader should learn the tech stack from the diagram.
- **Database per service**: each service connects to its own data store. Show the databases as a separate column or tier (PostgreSQL, MongoDB, Elasticsearch, Redis) with role subtitles (Users DB, Orders DB, Cache/Queue).
- **Message bus / event bus**: if services communicate asynchronously, show messaging infrastructure (Kafka, RabbitMQ, Event Bus) as small labeled connector pills between the services that use them.
- **Auth service**: when JWT/OAuth is mentioned or implied, separate it from business services as a distinct component with protocol subtitle (OAuth 2.0 / JWT).
- **Color categories**: architecture diagrams with ≥3 component types trigger the structural architecture exception (see `design-system.md` rule 9). Assign one ramp per category: services=teal, databases=purple, gateways=coral, message buses=amber. Mandatory legend.
- **Summary panel** (optional): a bottom section with 23 columns summarizing key architecture principles (Client Applications, Microservices, Infrastructure). Add when the diagram has ≥10 named elements.
- **Title + subtitle**: architecture diagrams always get a `.title` at the top with the architecture name and a `.ts` subtitle describing the approach (e.g., "Domain-driven design", "Event-driven microservices").
The enrichment principle: **a reader should learn something specific from the diagram**. "User service / Go :8081" teaches more than "User service / Accounts & profiles". Technology choices, ports, and protocols are the details that make an architecture diagram useful rather than decorative.
Skip enrichment for non-architecture structural diagrams (biological containment, CPU caches, file systems) — those benefit from simplicity, not tech details.
#### 5b: Load type reference #### 5b: Load type reference
The type was determined in the plan. Load the matching reference file. The type was determined in the plan. Load the matching reference file.

View File

@ -87,6 +87,7 @@ The template's `@media (prefers-color-scheme: dark)` block handles this automati
6. **Exception — sequence diagrams may use one ramp per actor, up to 4 ramps total.** Each actor's header, lifeline context, and the arrows *originating* from that actor share a single ramp. Actor identity is a category (not a sequence), so this obeys the "encode meaning" rule — it just allows more categories than the normal ≤2-ramp cap. The legend is implicit: every ramp is anchored by its labeled actor header at the top of the diagram. See `sequence.md`. 6. **Exception — sequence diagrams may use one ramp per actor, up to 4 ramps total.** Each actor's header, lifeline context, and the arrows *originating* from that actor share a single ramp. Actor identity is a category (not a sequence), so this obeys the "encode meaning" rule — it just allows more categories than the normal ≤2-ramp cap. The legend is implicit: every ramp is anchored by its labeled actor header at the top of the diagram. See `sequence.md`.
7. **Exception — poster flowcharts may use up to 4 ramps, one per distinct agent/role.** Same principle as the sequence exception: the drafter, the critic, the synthesizer, and the judge are four different *kinds* of operation, not four sequential steps. Each ramp anchors identity. The stage's title box makes the legend implicit. See `flowchart-poster.md`. 7. **Exception — poster flowcharts may use up to 4 ramps, one per distinct agent/role.** Same principle as the sequence exception: the drafter, the critic, the synthesizer, and the judge are four different *kinds* of operation, not four sequential steps. Each ramp anchors identity. The stage's title box makes the legend implicit. See `flowchart-poster.md`.
8. **Exception — phase-band diagrams may use up to 3 ramp-colored arrow classes** when each ramp encodes a distinct *path type* (e.g., normal flow vs. exploit path vs. data exfiltration). The path type is the information the arrow carries — the ramp makes it visible at a glance rather than relying on labels on every crossing arrow. **A legend strip is mandatory.** See `flowchart-phase-bands.md`. 8. **Exception — phase-band diagrams may use up to 3 ramp-colored arrow classes** when each ramp encodes a distinct *path type* (e.g., normal flow vs. exploit path vs. data exfiltration). The path type is the information the arrow carries — the ramp makes it visible at a glance rather than relying on labels on every crossing arrow. **A legend strip is mandatory.** See `flowchart-phase-bands.md`.
9. **Exception — structural architecture diagrams may use up to 4 ramps, one per component category.** When a structural diagram depicts an infrastructure or software architecture with ≥3 semantically distinct categories of components (e.g., services, databases, gateways, message buses), assign one ramp per category — up to 4 ramps total. The categories are determined by the component's *role* in the system, not its position. A **legend strip is mandatory** (same rule as phase-band diagrams). Typical category→ramp mappings for infrastructure: services=teal, databases=purple, gateways/ingress=coral, message buses/queues=amber. But any 4 ramps work as long as each ramp encodes a distinct component type, not a sequential position. See `structural.md` → "Full architecture layout".
### If colors or arrow styles encode meaning, add a one-line legend ### If colors or arrow styles encode meaning, add a one-line legend

View File

@ -59,6 +59,33 @@ For CJK content, replace the 8 with 15 and the 7 with 13.
**Round up to the nearest 10px** for cleaner coordinates. A box that "needs" 167px becomes 170px. **Round up to the nearest 10px** for cleaner coordinates. A box that "needs" 167px becomes 170px.
### Common spacing failures — Wrong / Right
Three mistakes that produce valid SVG but visually broken diagrams. Use these as a quick sanity check.
**Vertical gap too small (nodes overlap):**
```
Wrong: Node A ends at y=176, Node B starts at y=180 → 4px gap, labels nearly touching
Right: Node A ends at y=176, Node B starts at y=236 → 60px gap (the minimum vertical gap)
```
**Legend inside container boundary:**
```
Wrong: Container bottom at y=380, legend at y=370 → legend reads as container content
Right: Container bottom at y=380, legend at y=400 → 20px clear air, legend is diagram metadata
(extend viewBox H to 436 to fit)
```
**Horizontal tier overflow:**
```
Wrong: 4 boxes × 180 + 3 gaps × 20 = 780 → overflows the 600px usable width by 180px
Right: 4 boxes × 130 + 3 gaps × 20 = 580 → fits within 600px, centered at x=50
(shorten labels or drop subtitles to fit the narrower boxes)
```
### Worked examples ### Worked examples
- `"JWT authentication"` (18 chars, title) → `18 × 8 + 24 = 168` → round to **170** - `"JWT authentication"` (18 chars, title) → `18 × 8 + 24 = 168` → round to **170**

View File

@ -268,6 +268,40 @@ Pick `ymid` so the horizontal channel runs through empty space between rows.
**Fix**: replace every hardcoded color on a glyph element with the corresponding class. If you need a color that isn't in the 9-ramp palette, you don't need a new color — you're asking the glyph to say something it shouldn't. See `glyphs.md` → "Hard rules" for the full policy. **Fix**: replace every hardcoded color on a glyph element with the corresponding class. If you need a color that isn't in the 9-ramp palette, you don't need a new color — you're asking the glyph to say something it shouldn't. See `glyphs.md` → "Hard rules" for the full policy.
## 29. Arrow bleeds through semi-transparent fill
**Symptom**: a connector line that passes behind a node is faintly visible *through* the node's fill — especially noticeable on illustrative diagrams with gradient overlays or on nodes where the fill color is very light.
**Check**: does any node use a translucent or very light fill (stop 50 from a ramp) through which a 1.5px stroke behind it would show? The default `c-{ramp}` fills are opaque enough that this rarely matters, but when a plan calls for a translucent overlay (gradient, or a manually lightened fill), the bleed becomes visible.
**Fix**: draw an opaque background rect at the same position *before* the styled rect. The background rect masks the arrow; the styled rect paints on top with the desired appearance:
```svg
<!-- Opaque mask to hide arrow behind this node -->
<rect x="200" y="100" width="180" height="56" rx="6" class="box"/>
<!-- Styled node on top -->
<g class="c-purple">
<rect x="200" y="100" width="180" height="56" rx="6"/>
<text class="th" x="290" y="124" text-anchor="middle" dominant-baseline="central">Title</text>
</g>
```
The `box` class works as the mask because it uses opaque fill in both light and dark mode. Only add the mask rect when bleed-through is visible — most diagrams don't need it because the z-order rule (connectors drawn before nodes) already handles opaque fills.
## 30. Legend clips into a container boundary
**Symptom**: a legend strip sits inside (or overlaps) a dashed container's bottom edge — the legend text reads as part of the container's content instead of as diagram-level metadata.
**Check**: find the lowest container boundary: `container_y + container_h`. The legend's top edge must be ≥20px below that boundary. If the legend sits inside or touching any container, it's a bug.
**Fix**: move the legend below all container boundaries. Calculate: `legend_y = max(all_container_bottoms) + 20`. Then extend viewBox H to accommodate: `H = legend_y + legend_h + 20`. Never squeeze a legend inside a container to save vertical space — expand the viewBox instead.
```
Container bottom at y=380
Legend at y=400 → 20px clear ✓
viewBox H = 400 + 16 + 20 = 436
```
--- ---
## Quick self-review template ## Quick self-review template
@ -283,5 +317,7 @@ Before writing the file, mentally run through:
> 7. Colors: ≤ 2 ramps → ___ and ___ → assigned by category ✓ > 7. Colors: ≤ 2 ramps → ___ and ___ → assigned by category ✓
> 8. No hardcoded text fills ✓ > 8. No hardcoded text fills ✓
> 9. No comments in final output ✓ > 9. No comments in final output ✓
> 10. No arrow bleed-through on translucent fills (mask rect if needed) ✓
> 11. Legend sits ≥20px below all container boundaries ✓
If any of these feel fuzzy, the diagram isn't ready. If any of these feel fuzzy, the diagram isn't ready.

View File

@ -4,6 +4,21 @@ Load this file when the prompt asks about **network infrastructure**: "where do
Sub-pattern for diagrams whose subject is **where the wires go** — which devices sit where, which security zones contain them, which links are wired vs wireless. The structural diagram type is the right home for this (devices are containers, zones are outer containers, cables are arrows), but topology drawings have some conventions of their own. Sub-pattern for diagrams whose subject is **where the wires go** — which devices sit where, which security zones contain them, which links are wired vs wireless. The structural diagram type is the right home for this (devices are containers, zones are outer containers, cables are arrows), but topology drawings have some conventions of their own.
## Domain color conventions
When a structural or network diagram has 3+ component categories, these conventional ramp assignments help the reader parse component types at a glance. These are **suggestions**, not hard rules — the ≤2 ramp constraint from `design-system.md` still applies for simple diagrams with only 12 categories.
| Component domain | Suggested ramp | Rationale |
|--------------------|---------------|----------------------------------------------|
| Frontend / Client | teal | cool, user-facing |
| Backend / API | green | processing, data transformation |
| Database / Storage | purple | depth, persistence |
| Cloud / Infra | amber | external boundary, warning-adjacent |
| Security / Auth | coral | attention, trust boundary |
| External / Generic | gray | neutral, not categorized |
When a diagram uses ≥3 of these, include a one-line legend strip at the bottom mapping colors to categories. When the diagram is simple enough to stay within 2 ramps, prefer the semantic pairing from `design-system.md` over these domain conventions.
## When to use it ## When to use it
- The reader's question is *"what is connected to what, and across which boundary"*, not *"what happens when a request arrives"* (that's a sequence diagram). - The reader's question is *"what is connected to what, and across which boundary"*, not *"what happens when a request arrives"* (that's a sequence diagram).
@ -63,6 +78,23 @@ Two distinct link styles, both already in the template:
Place the legend at the bottom of the canvas, 20px above the bottom edge, aligned with the subject matter above it. Place the legend at the bottom of the canvas, 20px above the bottom edge, aligned with the subject matter above it.
## Security zone containers
When a network diagram includes explicit trust boundaries (security groups, VPNs, firewalls-as-perimeters), use a **coral-tinted dashed container** to distinguish security zones from structural zones. This gives the reader an immediate visual signal: gray dashed = organizational grouping, coral dashed = trust boundary.
```svg
<rect x="60" y="100" width="560" height="160" rx="12" fill="none"
stroke-dasharray="4 4" class="arr-coral"/>
<rect class="c-coral" x="60" y="92" width="120" height="16" rx="8"/>
<text class="ts" x="120" y="104" text-anchor="middle">Trust boundary</text>
```
The coral container uses `class="arr-coral"` instead of `class="arr-alt"` — both are dashed, but `arr-coral` carries the semantic color. The pill label uses `class="c-coral"` for matching fill/stroke.
Typical security zone labels: *Security group*, *Trust boundary*, *VPN tunnel*, *Private subnet*, *Encrypted zone*, *DMZ* (when DMZ is drawn as a security boundary rather than an organizational tier).
When mixing structural zones (gray) and security zones (coral) in the same diagram, add a legend entry for the coral dash: `[- -] Trust boundary` alongside the structural zone entries.
## Tiered top-down layout ## Tiered top-down layout
Network topology almost always reads top-down because the conventional mental model is *"traffic flows from the public internet down into the protected core"*. Lay out the zones vertically, one tier per row: Network topology almost always reads top-down because the conventional mental model is *"traffic flows from the public internet down into the protected core"*. Lay out the zones vertically, one tier per row:

View File

@ -137,6 +137,174 @@ Layout:
viewBox: max_y = 40 + 280 = 320 → H = 340 viewBox: max_y = 40 + 280 = 320 → H = 340
## Full architecture layout
A richer variant for topics like "microservices architecture", "Kubernetes cluster", "cloud topology", or any system design where the reader expects to see **technology choices, data stores, messaging, and client types** — not just generic labeled boxes. This is the most information-dense structural layout in the skill, routinely containing 1220 named elements and using up to 4 color ramps (see `design-system.md` rule 9).
### When to use
- "Microservices architecture" / "system architecture" / "platform design"
- Any structural prompt where the user's seed is a broad system name and the reader expects specific technologies
- The reader needs to learn **what** is in the system (tech stack, ports, protocols), not just **how it's organized**
### When not to use
- Simple containment (VPC with two subnets) — use the basic structural pattern above
- Two subsystems cooperating — use the subsystem architecture pattern below
- The user provided detailed source material that already lists every component — follow the source instead of enriching
### Layout structure
The canonical full architecture layout has **5 horizontal tiers** inside an outer container, plus external clients and an optional summary panel below:
```
Tier 0 (external): Client boxes — outside the container, top-left
Tier 1: API Gateway / Ingress — wide box spanning the top
Tier 2: Microservices row — 34 service boxes
Tier 2.5: Message bus pills — small labeled connectors between services
Tier 3: Data stores row — database boxes aligned under their owning services
Tier 4 (optional): Shared infrastructure — service discovery, config, monitoring
```
An auth service sits outside or beside the gateway when it's a cross-cutting concern.
### Color budget (4 ramps)
| Category | Ramp | Used on |
|-----------------|---------|------------------------------------------------------|
| Services | teal | Microservice boxes, auth service |
| Databases | purple | PostgreSQL, MongoDB, Elasticsearch, Redis |
| Gateway/Ingress | coral | API Gateway box |
| Message bus | amber | Kafka, RabbitMQ, Event Bus connector pills |
| Clients | gray | Web app, Mobile app (neutral — not part of the system)|
| Infrastructure | gray | Service discovery, config & secrets (neutral) |
A **legend strip** is mandatory — place it inside the container at the bottom or just below it.
### Geometry
Starting-point coordinates for a typical 3-service architecture. **Recompute** per diagram using `layout-math.md` formulas — the values below assume 3 services, 3 databases, and standard 56px-tall boxes. Adding a 4th service column or extra tiers requires recalculating x/w/gap to fit within the 600px usable width.
Standard layout at viewBox `680 × H`:
```
Element x y w h rx
──────────────────────── ──── ──── ──── ─── ────
Outer container 40 80 600 var 20 c-blue (or c-gray)
Title 60 50 — — — .title
Subtitle 60 72 — — — .ts
Client boxes (external)
Web App 40 20 140 56 6 c-gray
Mobile App 40 var 140 56 6 c-gray
API Gateway 200 120 280 80 6 c-coral
(title + 23 subtitle lines: tech, responsibilities, port)
Auth Service 40 var 140 56 6 c-teal
(sits left of gateway or below clients)
Services row (inside container)
Service 1 100 250 140 56 6 c-teal
Service 2 270 250 140 56 6 c-teal
Service 3 440 250 140 56 6 c-teal
(or 4 services: w=120, gap=16)
Message bus pills
(small 90×24 rx=12 amber pills between services, y≈320)
Data stores row
DB 1 100 370 140 56 6 c-purple
DB 2 270 370 140 56 6 c-purple
DB 3 440 370 140 56 6 c-purple
Shared infrastructure (optional)
Service discovery 140 460 160 44 6 c-gray
Config & secrets 380 460 160 44 6 c-gray
Legend strip 100 var — — — .ts + swatches
```
Adjust y coordinates to maintain ≥40px vertical gaps between tiers. viewBox H is typically 560700 depending on tier count.
### Label enrichment
Every box in a full architecture diagram carries **technology-specific subtitles**:
| Component | Title (th) | Subtitle (ts) |
|-----------------|--------------------|-------------------------|
| Client | Web App | React SPA |
| Client | Mobile App | iOS/Android |
| Gateway | API Gateway | Kong / Nginx |
| | | Rate limiting |
| | | :443 |
| Auth | Auth Service | OAuth 2.0 / JWT |
| Service | User Service | Go :8081 |
| Service | Order Service | Java :8082 |
| Message bus | Kafka / RabbitMQ | (pill label only) |
| Database | PostgreSQL | Users DB |
| Database | MongoDB | Orders DB |
| Database | Redis | Cache / Queue |
| Infrastructure | Service discovery | (single-line) |
The gateway box is taller (h=80) to hold 3 subtitle lines. Service and database boxes use standard 2-line layout (h=56). Message bus elements are small pills (h=24).
### Message bus connector pills
Small rounded-rect pills sitting on the arrows between services, styled with `c-amber`:
```svg
<g class="c-amber">
<rect x="225" y="316" width="90" height="24" rx="12"/>
<text class="ts" x="270" y="328" text-anchor="middle" dominant-baseline="central">Event Bus</text>
</g>
```
Place pills at the midpoint of the arrow between two services. The arrow is split into two segments with the pill in between.
### Summary panel (optional)
For diagrams with ≥10 elements, add a summary panel below the outer container. Three columns of bullet points summarizing key architecture principles:
```
┌─────────────────┬─────────────────┬──────────────────┐
│ • Client Apps │ • Microservices │ • Infrastructure │
│ - React SPA │ - Polyglot │ - Kubernetes │
│ - iOS/Android │ - Independent │ - Kong Gateway │
│ - Unified API │ - Event-driven│ - Kafka │
│ - JWT auth │ - DB per svc │ - Prometheus │
└─────────────────┴─────────────────┴──────────────────┘
```
Each column is a `c-gray` box with a colored bullet (matching its category ramp) as the header indicator. Place at y = container_bottom + 40, spanning the full 600px usable width as three equal boxes (w=186, gap=21, total=186×3+21×2=600).
### Footer caption
Add a `.caption` footer below the summary panel: the architecture name + design philosophy in one line (e.g., "Microservices Architecture · Domain-driven design"). Centered at x=340, y = panel_bottom + 30.
### Worked example — Microservices + K8s + API Gateway
Plan (expanded from a seed prompt "Microservices architecture"):
```
Clients: Web App (React SPA), Mobile App (iOS/Android)
Gateway: API Gateway (Kong/Nginx, Rate Limiting, Auth/Routing, :443)
Auth: Auth Service (OAuth 2.0 / JWT)
Services: User Service (Go :8081), Order Service (Java :8082),
Product Service (Python :8083), Notification Svc (Node.js :8084)
Message bus: Kafka/RabbitMQ, Event Bus (×2)
Data stores: PostgreSQL (Users DB), MongoDB (Orders DB),
Elasticsearch (Products), Redis (Cache/Queue)
Container: Kubernetes Cluster (dashed border)
Legend: Service, Database, Gateway, Message Bus
Summary: 3 columns (Client Applications, Microservices, Infrastructure)
Footer: "Microservices Architecture · Domain-driven design"
Color budget: teal (services), purple (databases), coral (gateway), amber (bus)
Named elements: 18
```
viewBox: `680 × 780`. The diagram is tall — that's expected for a full architecture layout.
## Subsystem architecture pattern ## Subsystem architecture pattern
A variant for topics that have **two or three parallel subsystems cooperating** — the canonical shape is "two sibling dashed-border containers, each holding a short internal flow, with a labeled cross-system arrow linking them". Unlike the default structural diagram (outer container + inner regions), this pattern places the subsystems *side by side at the top level* and lets each one carry its own mini-flowchart. A variant for topics that have **two or three parallel subsystems cooperating** — the canonical shape is "two sibling dashed-border containers, each holding a short internal flow, with a labeled cross-system arrow linking them". Unlike the default structural diagram (outer container + inner regions), this pattern places the subsystems *side by side at the top level* and lets each one carry its own mini-flowchart.

View File

@ -206,11 +206,50 @@ Or directly to the shape itself if there's no wrapping `<g>`:
**Do not nest `c-*` groups.** The CSS uses direct-child selectors — `<g class="c-blue"><g>...</g></g>` won't apply the fill to the inner shapes. If you need a click handler (future), put it on the same group that carries the color class, not a wrapper. **Do not nest `c-*` groups.** The CSS uses direct-child selectors — `<g class="c-blue"><g>...</g></g>` won't apply the fill to the inner shapes. If you need a click handler (future), put it on the same group that carries the color class, not a wrapper.
## Optional background grid
For structural diagrams with a technical/blueprint feel, an optional background grid can reinforce the engineering aesthetic. **This is rare** — most diagrams should keep the transparent background for seamless embedding. Only consider the grid when the diagram shows infrastructure, network topology, or hardware architecture where the grid reads as "engineering paper" rather than decoration.
Add the pattern to `<defs>` and a full-viewport rect as the first visual element:
```svg
<defs>
<!-- arrow marker (always present) -->
<marker id="arrow" .../>
<!-- grid pattern (optional, structural diagrams only) -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--grid-stroke, rgba(0,0,0,0.06))" stroke-width="0.5"/>
</pattern>
</defs>
<!-- Grid background (first visual element, behind everything) -->
<rect width="680" height="H" fill="url(#grid)"/>
```
Define the CSS variable in both modes so the value is explicit and discoverable (not buried in an inline fallback). Add these inside the existing `<style>` block:
```css
svg { --grid-stroke: rgba(0,0,0,0.06); }
@media (prefers-color-scheme: dark) {
svg { --grid-stroke: rgba(255,255,255,0.05); }
/* ... existing dark-mode overrides ... */
}
```
With the explicit light-mode declaration, the inline fallback in the `<pattern>` stroke is redundant but harmless — keep it as a safety net for renderers that strip `<style>` blocks.
**Rules:**
- Grid stroke opacity must stay ≤0.08 in both modes — the grid is a texture, not a visual element
- The grid rect is always the first child after `</defs>`, so every other element paints on top
- Never use the grid on illustrative, sequence, or class diagrams — it fights with their visual language
- If the grid competes with the diagram's lines for visual attention, remove it
## What to emit after the template ## What to emit after the template
Visual elements in this order: Visual elements in this order:
1. Background decorations (dashed frame for a schematic container, for example) 1. Background decorations (optional grid rect, dashed frame for a schematic container)
2. Containers (outer group rectangles for structural diagrams) 2. Containers (outer group rectangles for structural diagrams)
3. Connectors and arrows (so they sit behind the boxes they connect, preventing visible overlap) — both solid `.arr`/`.arr-{ramp}` primary flows and `.arr-alt` alternative/optional/weak flows belong in this layer 3. Connectors and arrows (so they sit behind the boxes they connect, preventing visible overlap) — both solid `.arr`/`.arr-{ramp}` primary flows and `.arr-alt` alternative/optional/weak flows belong in this layer
4. Nodes (rects with text) 4. Nodes (rects with text)