Architecture: Frontend Guide¶
frontend/template.html is the single-file D3 explorer. build_site.py
inlines one subject's data into it to produce dist/<slug>.html. This
page documents its three views and the mechanics that tie them together.
Layout¶
The page is a header (title, entity-type legend, the view switcher, search, reset), a main area that swaps between the three views below, and an always-visible timeline strip along the bottom — the timeline is not one of the switchable views; it stays on screen no matter which of the three you're looking at, since it's the one element common to all of them.
Network view¶
The default view: a force-directed graph (d3.forceSimulation) of every
entity, with edges deduplicated per unordered pair (parallel relations
between the same two entities collapse into one visual link, listing all
of them on hover/click).
- Node size scales with graph degree, capped, with a larger fixed size for any person with a verified portrait.
- Node color follows entity type via the categorical palette: person (blue), place (aqua/green), organization (orange), artifact (violet).
- Label decluttering: only "hub" nodes (degree ≥ 2, or with a portrait) show their name by default; others reveal on hover or when selected — the same principle the Map view uses, adapted for its own zoom behavior.
- Legend (top right) toggles each entity type's nodes and their edges on/off.
Map view¶
Plots every place entity that carries attributes.lat/attributes.lng
(see Data model reference and
Data Accuracy & Provenance) on a
world map, using an embedded, pre-converted GeoJSON basemap — no tile
server, no runtime map-library dependency beyond D3 itself.
- Marker size scales with how many events happened at that place plus its graph degree — see the formula in Data Accuracy & Provenance, or just: busier places get bigger circles.
- The journey line — a faint dashed path connecting places in the order the subject's story visits them chronologically (by each place's earliest located event).
- Every label is permanently visible, not hover-only. A small layout pass tries each marker's label at 8 compass positions and up to 3 ring distances around its pin, taking the first spot that doesn't collide with an already-placed label or another marker (bigger/more- connected places claim their preferred spot first). A thin leader line is drawn whenever a label had to move off its default slot, so it's still clear which pin a displaced label belongs to.
- Markers stay a constant screen size while zooming — they live in a layer separate from the zoomed map geometry, with their screen position recomputed from the zoom transform on every tick. This is what lets a crowded cluster (e.g. several nearby cities) visibly spread apart, and its labels re-resolve, as you zoom in — rather than the cluster staying just as crowded relative to its own labels at every zoom level.
Milestones view¶
A scrollable, chronologically-sorted reel of "big moment" event types:
company_founded, invention, patent_filed, patent_granted,
product_launch, public_demonstration, publication, award. Each
card shows a small hand-drawn icon colored by its timeline lane (see
below), the date, location if known, and a short description.
The timeline¶
A zoomable/pannable horizontal strip (d3.zoom with a
translateExtent/extent locked to the timeline's own bounds) showing
every event as a mark, positioned by date.sort_start/sort_end and
grouped into five semantic swimlanes — every event_type belongs to
exactly one lane, and the lane's color is the color of every mark in
it, so row position and color reinforce the same grouping rather than
being two separate encodings to decode:
| Lane | Color | Event types |
|---|---|---|
| Life | blue | birth, death, education, retirement |
| Career & organizations | orange | employment_start/end, role_change, company_founded/sold/renamed |
| Inventions & process | violet | invention, patent_filed/granted, product_launch, other |
| Travel & gatherings | green | visit, meeting, relocation, conference, public_demonstration |
| Publications & recognition | gold | publication, award |
A range-precision event renders as a bar spanning its bounds rather
than a point.
Selection state, shared across every view¶
Clicking an entity, an event, a place marker, or a milestone card all
funnel into the same two functions, selectEntity(id) /
selectEvent(id), which:
- Populate the right-hand side panel with details, connections, and source citations.
- Dim everything not related to the selection — in the network graph, the map, the milestones list, and the timeline simultaneously, even in views you aren't currently looking at.
- Highlight what is related.
This is why switching from, say, the Milestones view (having selected an event there) back to the Network view shows that event's participants already highlighted — state isn't per-view, it's global.
Design tokens¶
Colors are CSS custom properties defined once in :root and referenced
by role throughout — see the reference palette in
Data Accuracy & Provenance for how
they were chosen and validated. Swapping the visual theme means editing
those variables in one place, not hunting through the rendering code.