---
name: css-html-diagrams
description: Create pure CSS/HTML pipeline and architecture diagrams with zero JavaScript dependencies. Animated SVG arrows, dark-themed nodes, responsive layouts, hover effects. Use instead of Mermaid, ASCII art, or external diagram tools when you need flowcharts, pipelines, architecture diagrams, or data flow visualizations in web pages.
license: MIT
metadata:
  author: Paulo Silveira
  version: "1.0"
---

# CSS/HTML Diagrams — Zero-Dependency Pipeline & Architecture Diagrams

Create professional, animated diagrams using only HTML and CSS. No JavaScript, no Mermaid, no external tools. Works in any framework (Astro, Next.js, plain HTML). Responsive, dark-themed, accessible.

## Why Not Mermaid?

| Concern | Mermaid | Pure CSS/HTML |
|---------|---------|---------------|
| Dependencies | mermaid.js (~500KB) or rehype-mermaid + Playwright | Zero |
| Styling control | Limited — fights your design system | Full — your colors, fonts, spacing |
| Responsive | Hard to control on mobile | Trivial — flexbox wraps naturally |
| Animation | None (static SVG output) | Animated arrows, hover effects, transitions |
| Dark mode | Requires theme config | Native — you write the colors |
| Accessibility | Generated SVG, opaque structure | Semantic HTML, screen-reader friendly |
| Bundle size | +500KB (client) or +Playwright (build) | 0 bytes additional |

## Core Approach

1. **Container**: a flex column (vertical) or row (horizontal)
2. **Nodes**: styled divs with background, border, border-radius
3. **Arrows**: inline SVGs with animated stroke-dasharray
4. **Groups**: wrapper divs with label + node-row inside
5. **Responsive**: flexbox wrap + media queries flip horizontal to vertical

## The CSS Foundation

Drop this CSS once per page or component. All diagrams on the page reuse it.

```css
/* === Pipeline container === */
.pipeline {
  display: flex; flex-direction: column; align-items: center;
  gap: 0; margin: 1.5rem auto; max-width: 720px;
}

/* === Nodes === */
.pipeline .node {
  background: #1e293b; border: 1.5px solid #475569; border-radius: 12px;
  padding: 0.55rem 0.9rem; font-size: 0.82rem; color: #e2e8f0;
  text-align: center;
  transition: border-color 0.2s, transform 0.2s;
  font-family: system-ui, -apple-system, sans-serif;
}
.pipeline .node:hover { border-color: #7c3aed; transform: translateY(-2px); }
.pipeline .node-sub { font-size: 0.68rem; color: #94a3b8; margin-top: 2px; }

/* Accent — key decision points, AI/LLM nodes, sources */
.pipeline .node--accent {
  background: #7c3aed; border: 2.5px solid #a78bfa; color: #fff;
  padding: 0.8rem 1.2rem; border-radius: 16px; font-weight: 600;
}
.pipeline .node--accent:hover { border-color: #c4b5fd; }
.pipeline .node--accent .node-sub { color: #ddd6fe; }

/* Warn — human steps, manual review, leadership input */
.pipeline .node--warn {
  background: #f59e0b; border-color: #d97706; color: #1e293b; font-weight: 500;
}
.pipeline .node--warn .node-sub { color: #78350f; }

/* === Node layout === */
.pipeline .node-row {
  display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: center;
}
.pipeline .group-box {
  background: #0f172a; border: 1px solid #334155; border-radius: 14px;
  padding: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem;
  align-items: center;
}
.pipeline .group-label {
  font-size: 0.7rem; color: #64748b; text-transform: uppercase;
  letter-spacing: 0.05em; text-align: center;
}

/* === Vertical arrow (animated) === */
.arrow-down { display: flex; justify-content: center; padding: 0.15rem 0; }
.arrow-down svg { width: 24px; height: 28px; }
.arrow-down path {
  stroke: #7c3aed; stroke-width: 2.5; fill: none;
  stroke-dasharray: 10 10; animation: flow-down 0.8s linear infinite;
}
.arrow-down polygon { fill: #7c3aed; }
@keyframes flow-down {
  from { stroke-dashoffset: 20; } to { stroke-dashoffset: 0; }
}

/* === Horizontal arrow (animated) === */
.arrow-right { display: flex; align-items: center; padding: 0 0.15rem; }
.arrow-right svg { width: 36px; height: 20px; flex-shrink: 0; }
.arrow-right path {
  stroke: #7c3aed; stroke-width: 2.5; fill: none;
  stroke-dasharray: 10 10; animation: flow-right 0.8s linear infinite;
}
.arrow-right polygon { fill: #7c3aed; }
@keyframes flow-right {
  from { stroke-dashoffset: 16; } to { stroke-dashoffset: 0; }
}

/* === Horizontal pipeline mode === */
.pipeline--row {
  flex-direction: row; flex-wrap: wrap; max-width: 100%;
  justify-content: center; align-items: center;
}
@media (max-width: 640px) {
  .pipeline--row { flex-direction: column; }
  .pipeline--row .arrow-right svg { width: 20px; height: 28px; }
  .pipeline--row .arrow-right { transform: rotate(90deg); }
}

/* === Side-input pattern (dashed arrow) === */
.pipeline .orq-row {
  display: flex; align-items: center; gap: 0.75rem;
  flex-wrap: wrap; justify-content: center;
}
.orq-row .side-arrow { display: flex; align-items: center; }
.orq-row .side-arrow svg { width: 32px; height: 20px; }
.orq-row .side-arrow path {
  stroke: #d97706; stroke-width: 2; fill: none;
  stroke-dasharray: 5 3; animation: flow-right 1s linear infinite;
}
.orq-row .side-arrow polygon { fill: #d97706; }

/* === Accessibility === */
@media (prefers-reduced-motion: reduce) {
  .arrow-down path, .arrow-right path, .orq-row .side-arrow path {
    animation: none;
  }
}
```

## HTML Building Blocks

### Vertical arrow

```html
<div class="arrow-down">
  <svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg>
</div>
```

### Horizontal arrow

```html
<div class="arrow-right">
  <svg viewBox="0 0 36 20"><path d="M0 10 H26" /><polygon points="24,5 36,10 24,15" /></svg>
</div>
```

### Dashed side arrow (secondary inputs)

```html
<div class="side-arrow">
  <svg viewBox="0 0 32 20"><path d="M0 10 H22" /><polygon points="20,5 32,10 20,15" /></svg>
</div>
```

## Pattern 1: Simple Vertical Pipeline

The most common pattern — top-to-bottom flow.

```html
<div class="not-prose pipeline">
  <div class="node node--accent">Source<div class="node-sub">API / webhook / RSS</div></div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="node">Process<div class="node-sub">transform data</div></div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="node">Store<div class="node-sub">database / file</div></div>
</div>
```

## Pattern 2: Fan-Out (One Input, Multiple Outputs)

```html
<div class="not-prose pipeline">
  <div class="node node--accent">Podcast Episode</div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="group-box">
    <div class="group-label">Distribution</div>
    <div class="node-row">
      <div class="node">X Thread</div>
      <div class="node">LinkedIn</div>
      <div class="node">Instagram</div>
      <div class="node">Newsletter</div>
    </div>
  </div>
</div>
```

## Pattern 3: Side Input (Human-in-the-Loop)

A secondary input feeds into the main flow (e.g., human review, leadership decisions).

```html
<div class="not-prose pipeline">
  <div class="node">Data</div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="orq-row">
    <div class="node node--warn">Human Review<div class="node-sub">approval / edits</div></div>
    <div class="side-arrow"><svg viewBox="0 0 32 20"><path d="M0 10 H22" /><polygon points="20,5 32,10 20,15" /></svg></div>
    <div class="node node--accent">AI Processor<div class="node-sub">LLM classification</div></div>
  </div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="node">Output</div>
</div>
```

## Pattern 4: Horizontal Pipeline

Linear left-to-right flows (CI/CD, video production). Stacks vertically on mobile.

```html
<div class="not-prose pipeline pipeline--row">
  <div class="node">Write</div>
  <div class="arrow-right"><svg viewBox="0 0 36 20"><path d="M0 10 H26" /><polygon points="24,5 36,10 24,15" /></svg></div>
  <div class="node node--accent">Review</div>
  <div class="arrow-right"><svg viewBox="0 0 36 20"><path d="M0 10 H26" /><polygon points="24,5 36,10 24,15" /></svg></div>
  <div class="node">Ship</div>
</div>
```

## Pattern 5: Grouped Sections

```html
<div class="not-prose pipeline">
  <div class="group-box">
    <div class="group-label">Sources</div>
    <div class="node-row">
      <div class="node">Podcast RSS</div>
      <div class="node">Blog</div>
      <div class="node">Tweet</div>
    </div>
  </div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="node node--accent">Orchestrator<div class="node-sub">classifies and routes</div></div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="group-box">
    <div class="group-label">Outputs</div>
    <div class="node-row">
      <div class="node">Article</div>
      <div class="node">Social Post</div>
      <div class="node">Email</div>
    </div>
  </div>
</div>
```

## Pattern 6: Branching (Decision Tree)

Use node-row after a decision node to show branches with labels.

```html
<div class="not-prose pipeline">
  <div class="node node--accent">Classifier<div class="node-sub">LLM</div></div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="node-row">
    <div class="node">Path A<div class="node-sub">condition 1</div></div>
    <div class="node">Path B<div class="node-sub">condition 2</div></div>
  </div>
</div>
```

For labeled arrows (e.g., "yes/no"), add a small text span:

```html
<div style="text-align:center;font-size:0.65rem;color:#94a3b8;margin-top:0.1rem">
  classifies as "note"
</div>
<div class="arrow-down">
  <svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg>
</div>
```

## Design Decisions

### Color Semantics

| Color | Class | Use For |
|-------|-------|---------|
| Slate (#1e293b) | `.node` (default) | Data, storage, standard steps |
| Purple (#7c3aed) | `.node--accent` | AI/LLM, decision points, key systems |
| Amber (#f59e0b) | `.node--warn` | Human steps, manual review, leadership |
| Dark slate (#0f172a) | `.group-box` bg | Grouping container |

### Adding New Node Types

Follow the naming convention `node--{semantic-name}`:

```css
.pipeline .node--success {
  background: #059669; border-color: #34d399; color: #fff; font-weight: 500;
}
.pipeline .node--danger {
  background: #dc2626; border-color: #f87171; color: #fff; font-weight: 500;
}
```

### When to Use Which Pattern

| Scenario | Pattern |
|----------|---------|
| ETL / data pipeline | Vertical |
| Content distribution | Fan-out |
| AI + human workflow | Side input |
| CI/CD, video production | Horizontal |
| System architecture | Grouped sections |
| Complex topology | Combine patterns |

## Advanced: Concentric Circles

For non-linear diagrams (ecosystem maps, layered architectures), use absolute positioning with `border-radius: 50%`:

```css
.circles-container {
  position: relative; width: 100%; aspect-ratio: 1; max-width: 500px; margin: 2rem auto;
}
.circle {
  position: absolute; border-radius: 50%; aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 1.5px solid rgba(56, 189, 248, 0.35);
}
.circle-label {
  position: absolute; font-size: clamp(0.6rem, 1.5vw, 0.8rem);
  color: #94a3b8; white-space: nowrap;
}
```

Pair with `IntersectionObserver` for scroll-reveal (add `data-visible` to trigger CSS transitions). This is the only pattern that requires minimal JavaScript.

## Migrating from Mermaid

1. **Identify node types**: Map Mermaid styles to node classes (accent, warn, default)
2. **Identify flow direction**: TD/TB = vertical pipeline, LR = horizontal pipeline
3. **Convert nodes**: `A["Label"]` becomes `<div class="node">Label</div>`
4. **Convert arrows**: `A --> B` becomes an arrow-down div between nodes
5. **Handle branches**: `A --> B` + `A --> C` becomes node-row with B and C
6. **Handle labels**: `A -->|"label"| B` becomes node-sub text or arrow label
7. **Handle styles**: `style A fill:#7c3aed` becomes `.node--accent`

### Before (Mermaid)

```
flowchart TD
    A["Source"] --> B{"Classifier"}
    B -->|"yes"| C["Accept"]
    B -->|"no"| D["Reject"]
    style B fill:#7c3aed,color:#fff
```

### After (CSS/HTML)

```html
<div class="not-prose pipeline">
  <div class="node">Source</div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="node node--accent">Classifier</div>
  <div class="arrow-down"><svg viewBox="0 0 24 28"><path d="M12 0 V18" /><polygon points="7,16 12,26 17,16" /></svg></div>
  <div class="node-row">
    <div class="node">Accept<div class="node-sub">yes</div></div>
    <div class="node">Reject<div class="node-sub">no</div></div>
  </div>
</div>
```

## Common Pitfalls

1. **Forgetting `not-prose`** — inside a prose/markdown container, typography styles override the pipeline. Always use `<div class="not-prose pipeline">`.
2. **Nesting pipelines** — don't put a pipeline inside a pipeline. Use groups and node-rows instead.
3. **Dynamic Tailwind classes** — in frameworks like Astro, dynamic Tailwind classes in JS template strings won't be generated. Use hex colors inline or define classes in a `<style>` block.
4. **Overloading nodes** — keep node text to 1-3 words. Use prose around the diagram for details.
5. **External SVG files** — arrow SVGs must be inline in the DOM for CSS animation to work.
6. **Testing mobile** — node-rows with 4+ items need to wrap. Check with a narrow viewport.
