Skip to Content
πŸ“‹ ReferenceD3 Style Guide

Last Updated: 4/7/2026


D3 Style Guide & Conventions

This guide documents the D3.js coding conventions used in the β€œWhere Did They Go?” project. Following these standards ensures consistent, readable code across all map visualization components.

Indentation Rules for Selection Chains

The project uses a two-space vs. one-space indentation convention to distinguish between different types of D3 selection methods:

  • Two-space indent (. ): Methods that return the current selection (modify in place)
  • One-space indent (. ): Methods that return a new selection (filter or traverse)

This visual distinction helps developers quickly understand the data flow in complex D3 chains.

Example from the Codebase

selection = gTag .selectAll('.border') // new selection (1 space) .data(d.features, (d) => d.properties.id) .join( (enter) => { let s = enter .append('path') // new selection (1 space) .attr('d', (d) => { // current selection (2 spaces) d.geometry.coordinates[0].reverse(); return pathGen(d); }) .attr('stroke', 'black') // current selection (2 spaces) .attr('stroke-width', strokeWidth) .attr('opacity', '0%') .attr('fill-opacity', '0%') .classed('border', true) // current selection (2 spaces) .on('click', onBorderClick); fadeIn(s, { duration: fadeDuration }) return s; }, (update) => update, (exit) => fadeOut(exit, { duration: fadeDuration }).remove() );

Selection Method Categories

Methods Returning Current Selection (2 spaces)

These methods modify the current selection and return it for further chaining:

  • .attr(name, value) β€” Set attribute
  • .style(name, value) β€” Set CSS style
  • .property(name, value) β€” Set DOM property
  • .classed(name, boolean) β€” Add/remove CSS class
  • .text(value) β€” Set text content
  • .html(value) β€” Set HTML content
  • .on(event, handler) β€” Attach event listener
  • .each(function) β€” Invoke function for each element
  • .call(function) β€” Invoke function once with selection
  • .transition() β€” Create transition (returns transition object)

Methods Returning New Selection (1 space)

These methods filter or traverse the DOM, returning a new selection:

  • .select(selector) β€” Select first descendant
  • .selectAll(selector) β€” Select all descendants
  • .filter(predicate) β€” Filter selection
  • .data(data, key) β€” Join data (returns update selection)
  • .enter() β€” Enter selection
  • .exit() β€” Exit selection
  • .merge(selection) β€” Merge selections
  • .append(element) β€” Append new element
  • .insert(element, before) β€” Insert new element
  • .remove() β€” Remove elements

Why This Convention Matters

D3 selection chains can become long and complex. The indentation convention provides visual cues about the selection’s state:

  1. Readability: Quickly identify where the selection changes vs. where it’s being modified
  2. Debugging: Easier to trace which selection is being operated on
  3. Consistency: All developers follow the same pattern across components

Using the Interpolators Utility

The project provides color and value interpolation utilities in client/src/utility/Interpolators.js:

normalize(value, min, max)

Normalizes a numeric value to a 0–1 range:

import { normalize } from '@/utility/Interpolators'; const t = normalize(yearData[year], normData[year].min, normData[year].max); // t is now between 0 and 1

Returns 0 if min and max are equal (prevents division by zero).

interpolateColor(color1, color2, t)

Calculates an intermediate color between two RGB colors:

import { interpolateColor } from '@/utility/Interpolators'; const lightColor = { r: 198, g: 219, b: 239 }; // light blue const darkColor = { r: 8, g: 48, b: 108 }; // dark blue const color = interpolateColor(lightColor, darkColor, 0.5); // Returns: "rgb(103, 133, 173)"

The t parameter (typically 0–1) controls the interpolation:

  • t = 0 returns color1
  • t = 1 returns color2
  • t = 0.5 returns the midpoint color

Example: Heat Map Coloring

The BorderData.vue component uses both utilities to create a population heat map:

import { normalize, interpolateColor } from '@/utility/Interpolators'; const lightColor = { r: 198, g: 219, b: 239 }; const darkColor = { r: 8, g: 48, b: 108 }; function getCountyColor(d, year) { const yearData = d.properties["pop-by-year"]; if (Object.hasOwn(yearData, year)) { const t = normalize(yearData[year], normData[year].min, normData[year].max); return interpolateColor(lightColor, darkColor, t); } else { return 'rgb(240, 240, 240)'; // gray for missing data } } createTransition(selection) .attr('fill', (d) => getCountyColor(d, year)) .attr('fill-opacity', '100%');

Transition Helpers

The project provides transition utilities in client/src/d3/transitions/:

  • createTransition(selection) β€” Creates a standard transition with consistent duration
  • fadeIn(selection, options) β€” Fades elements to 100% opacity
  • fadeOut(selection, options) β€” Fades elements to 0% opacity

Use these instead of creating raw D3 transitions to maintain consistent animation timing across the application.

What’s Next