# How the Interactive D3.js Visualization with Community Toggles Functions in code-review-graph

> Explore the interactive D3.js visualization in code-review-graph. Learn how community toggles color nodes, filter legends, and manage community visibility for a dynamic knowledge graph.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: how-to-guide
- Published: 2026-08-14

---

**The interactive D3.js visualization in code-review-graph renders the knowledge graph as a self-contained HTML page with a "Communities" toggle that colors nodes by their detected community, displays a filterable legend, and allows users to hide or show individual communities.**

The `code-review_graph.visualization` module generates this interactive experience using **D3.js v7** with zero external dependencies beyond the vendored library. The community toggle system combines data-driven aggregation, dynamic color scaling, and DOM manipulation to help developers explore large code review graphs through the lens of detected communities.

## Data Preparation and Community Aggregation

Before rendering, `generate_html()` calls `export_graph_data()` to obtain the full node and edge list. When the user selects **auto mode** and the graph exceeds the full-render budget, `_resolve_auto_mode()` automatically switches to **community aggregation mode**.

In this mode, `_aggregate_community()` (lines 37-45 of [`visualization.py`](https://github.com/tirth8205/code-review-graph/blob/main/visualization.py)) compresses the graph by creating **super-nodes** — one node per community with size proportional to member count:

```python
def _aggregate_community(data: dict) -> dict:
    ...
    # each community becomes a single node sized by member count

    super_nodes.append({
        "qualified_name": f"__community__{cid}",
        "name": info.get("name", f"Community {cid}"),
        "kind": "Community",
        ...
    })
    ...

```

This aggregation preserves a `community_details` map for drill-down rendering, allowing the visualization to scale to thousands of nodes while maintaining community structure.

## HTML Template Structure

The module embeds two template strings directly in [`visualization.py`](https://github.com/tirth8205/code-review-graph/blob/main/visualization.py):

- **`_HTML_TEMPLATE`** — full-graph layout with individual nodes
- **`_AGGREGATED_HTML_TEMPLATE`** — used when data are aggregated (community or file mode)

Both templates include the Communities toggle button at line 815:

```html
<button id="btn-community" title="Toggle community coloring"
        aria-label="Toggle community coloring" aria-pressed="false">
    Communities
</button>

```

The `__D3_SCRIPTS__` placeholder is replaced with the vendored D3 library during HTML generation.

## D3 Initialization and Color Scaling

When the generated page loads, the embedded JavaScript:

1. Parses the injected `__GRAPH_DATA__` JSON into `graphData`
2. Maps nodes and edges to internal `nodes` and `edges` arrays
3. Initializes `communityColorScale = d3.scaleOrdinal(d3.schemeTableau10)` at line 71

This **Tableau 10 color scale** provides visually distinct, accessible colors for up to 10 communities, with automatic recycling for larger community counts.

## The Community Toggle Mechanism

The core interaction lives in the event handler at lines 667-676:

```javascript
document.getElementById("btn-community").addEventListener("click", function() {
  communityColoringOn = !communityColoringOn;
  this.classList.toggle("active");
  this.setAttribute("aria-pressed", communityColoringOn);
  // recolour nodes and glow rings
  nodeGroup.selectAll("g.node-g").select(".node-shape")
    .transition().duration(300)
    .attr("fill", d => nodeColor(d));
  nodeGroup.selectAll("g.node-g").select(".glow-ring")
    .transition().duration(300)
    .attr("stroke", d => nodeColor(d));
  // show/hide the community legend
  var cl = document.getElementById("community-legend");
  if (communityColoringOn) cl.classList.add("visible");
  else cl.classList.remove("visible");
});

```

Both the node shape and its **glow ring** transition simultaneously over 300ms, creating a cohesive visual effect. The `aria-pressed` attribute ensures screen readers announce the toggle state correctly.

The `nodeColor(d)` function at lines 124-127 determines the actual color:

```javascript
function nodeColor(d) {
  if (communityColoringOn && d.community_id != null) {
    return communityColorScale(d.community_id);
  }
  return kindColorScale(d.kind || "Unknown");
}

```

When community coloring is active, nodes use their `community_id`; otherwise they fall back to colors based on node **kind** (file, function, class, etc.).

## Community Legend and Filtering

The `buildCommunityLegend()` function (lines 82-98) generates a dynamic list of checkboxes — one per detected community — using the same `communityColorScale` for consistent coloring:

```javascript
function buildCommunityLegend() {
  var legend = document.getElementById("community-legend");
  var communities = [...new Set(nodes.map(n => n.community_id).filter(id => id != null))];
  communities.sort((a, b) => a - b);
  
  communities.forEach(cid => {
    var item = document.createElement("div");
    item.className = "legend-item";
    var checkbox = document.createElement("input");
    checkbox.type = "checkbox";
    checkbox.checked = true;
    checkbox.dataset.cid = cid;
    checkbox.addEventListener("change", applyCommunityFilter);
    ...
  });
}

```

Each checkbox triggers `applyCommunityFilter()` (lines 109-141), which maintains a `hiddenCommunities` Set and updates node visibility:

```javascript
function applyCommunityFilter() {
  if (!communityColoringOn || !hiddenCommunities.size) { ... }
  nodeGroup.selectAll("g.node-g")
    .attr("display", d => {
      if (d._hidden) return "none";
      var cid = d.community_id ?? nodeToCommunity.get(d.qualified_name);
      return cid != null && hiddenCommunities.has(cid) ? "none" : null;
    });
  ...
}

```

This hides both the **node group** and its **label** when a community is unchecked, supporting focused exploration of specific subgraphs.

## Complete Interaction Flow

1. **Generation** — `generate_html()` writes the HTML, embeds JSON data, and copies [`d3.v7.min.js`](https://github.com/tirth8205/code-review-graph/blob/main/d3.v7.min.js) via `_write_d3_asset()`
2. **Initial render** — Nodes appear in default kind-colors; Communities button is inactive
3. **Toggle activation** — Clicking "Communities" flips `communityColoringOn`, transitions colors, and reveals the filterable legend
4. **Community filtering** — Check/uncheck legend items to update `hiddenCommunities` and hide/show corresponding nodes
5. **Persistent interactions** — Hover tooltips, detail panels, and flow highlighting work identically in both color modes

## Code Examples

### Generating an HTML file with automatic community aggregation

```python
from code_review_graph.visualization import generate_html
from code_review_graph.graph import GraphStore

store = GraphStore('/path/to/db')
output = generate_html(store, 'graph.html', mode='auto')
print(f'HTML written to {output}')  # switches to community mode if graph is large

```

### Programmatically controlling the community toggle

```javascript
// Activate community coloring
document.getElementById('btn-community').click();

// Hide community 3 programmatically
const checkbox = document.querySelector('input[data-cid="3"]');
checkbox.checked = false;
checkbox.dispatchEvent(new Event('change'));

```

### Selecting community-colored nodes with D3

```javascript
// Highlight all nodes in community 2
d3.selectAll('g.node-g')
  .filter(d => d.community_id === 2)
  .style('stroke-width', '3px');

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`code_review_graph/visualization.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/visualization.py) | HTML generation, community aggregation, D3 template embedding, and UI logic |
| [`code_review_graph/assets/d3.v7.min.js`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/assets/d3.v7.min.js) | Vendored D3 v7 library for standalone rendering |
| [`code_review_graph/graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph.py) | Core graph store supplying nodes, edges, and community IDs |
| [`code_review_graph/communities.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/communities.py) | Community detection and metadata for coloring/legend |

## Summary

- **Self-contained output** — The visualization module produces a single HTML file with embedded data and vendored D3, requiring no server or build step
- **Automatic aggregation** — Large graphs trigger community super-node creation via `_aggregate_community()` to maintain interactivity
- **Dual coloring modes** — Toggle between **kind-based** (file/function/class) and **community-based** coloring with smooth 300ms transitions
- **Interactive filtering** — Checkboxes in the community legend add/remove communities from view via `hiddenCommunities` and `applyCommunityFilter()`
- **Accessible design** — Proper `aria-pressed` states and keyboard-navigable controls support inclusive usage

## Frequently Asked Questions

### How does the community toggle handle large graphs with thousands of nodes?

When `mode='auto'` is selected and the node count exceeds the render budget, `_resolve_auto_mode()` automatically switches to community aggregation. The `_aggregate_community()` function at lines 37-45 compresses each community into a single super-node sized by member count, reducing thousands of nodes to tens of communities while preserving cross-community connection patterns.

### Can I customize the community colors used in the visualization?

The current implementation uses `d3.scaleOrdinal(d3.schemeTableau10)` hardcoded at line 71. To customize colors, you would modify the [`visualization.py`](https://github.com/tirth8205/code-review-graph/blob/main/visualization.py) template or override the `communityColorScale` definition in the generated HTML. The Tableau 10 scheme provides good perceptual differentiation for up to 10 communities before cycling.

### What happens to node interactions when community coloring is active?

All interactions remain fully functional. Hover tooltips, click-to-focus, detail panel updates, and flow highlighting work identically in both modes. The `nodeColor()` function simply changes its return value based on `communityColoringOn`, while event handlers and force simulation continue operating on the underlying data structures unchanged.

### How can I programmatically filter to show only specific communities?

After the page loads, access the legend checkboxes via their `data-cid` attributes. Unchecking a checkbox triggers the same `applyCommunityFilter()` function used by user interactions: `document.querySelector('input[data-cid="5"]').checked = false` followed by a `change` event dispatch will hide community 5. To show only specific communities, first uncheck all via `document.querySelectorAll('#community-legend input').forEach(cb => { cb.checked = false; cb.dispatchEvent(new Event('change')); })`, then check your target communities.