Plate I · An atlas of San Francisco
San Francisco, line by line.
The coast, the neighborhoods, the lines that connect them. Beautiful offline SVG maps, ready for your stories, guides and data.
renderMap(data, { animation: true }). Every line is SVG; nothing loads from a tile server.- 11
- supervisorial districts
- 3
- district map years
- 250
- neighborhood areas, three definitions
- 0
- runtime dependencies
Chapter II · How the map is made
Six layers, one controller.
Scroll through the same interactive map the library gives you. Each step is a single call on the controller from createMap; the code beside it is exactly what runs.
Loading the map…
-
I
Begin with the coast.
Every map starts as land and water: a precise coastline with the Bay, the Presidio and Treasure Island. Geography is passed in explicitly, so small pages stay small.
import { createMap } from '@kahwee/sf-map-svg'; import { fullMapData } from '@kahwee/sf-map-svg/data/full'; const map = createMap(fullMapData, { mode: 'basemap' }); document.querySelector('#map').append(map.element); -
II
Draw eleven districts.
Supervisorial districts carry the soft palette the library is known for. They are selectable, keyboard-reachable, and announce themselves to screen readers.
map.setMode('districts'); -
III
Watch the lines move.
San Francisco redrew its districts in 2002, 2012 and 2022. With
districtMorphthe outlines travel from one map to the next, then settle into the exact published geometry.createMap(fullMapData, { features: { districtMorph: true } }); map.setDistrictYear(2012); -
IV
Three ways to draw a neighborhood.
There is no single neighborhood map. SFAR realtor areas are the default; SF Find and the city’s analysis areas are kept as their own definitions.
layerTransitionscrossfades between them.map.setMode('neighborhoods'); map.setSource('sf-find'); // 'realtor' | 'sf-find' | 'analysis' -
V
Add parks, roads and rails.
Parks, highways, key roads and BART stations are independent, optional layers. Switch any of them at runtime without losing the camera or selection.
map.configure({ layers: { landmarks: true, highways: true, keyRoads: true, bartStations: true }, }); -
VI
Put your data on top.
Markers keep their identity across updates, enter with a gentle stagger, and the camera eases to a selection. Motion always yields to the reader’s reduced-motion setting.
map.setMarkers([ { id: 'ferry', label: 'Ferry Building', position: [-122.3937, 37.7955 ]}, { id: 'dolores', label: 'Dolores Park', position: [-122.4269, 37.7596 ]}, ]); map.selectMarker('ferry');
Chapter III · Examples
The city votes, district by district.
Three worked examples built on certified results. Each is a complete page you can read, share, and download from — and a pattern you can copy.
Example 1 · Local measures
San Francisco’s local measures
Forty-four ballot measures across four elections and three district maps, with comparisons, a district table, and downloads.
Open the measuresExample 2 · State ballot
California propositions in San Francisco
All ten 2024 propositions as small multiples, then one at a time: where the city said yes, and where it said no.
Open the propositionsExample 3 · Candidates
Supervisorial votes
Who each supervisorial district chose, from 2016 to 2026: winners, margins, and every candidate’s share.
Open the votesAlso in the atlasOne spot, three San FranciscosThe layers studioA schematic BART journeyEvery example
Chapter IV · Build with it
Static when you can, interactive when you need.
Render SVG on a server or at build time with no DOM at all, or mount a keyboard-accessible controller in the browser. Both take the same explicit geography.
import { renderMap } from '@kahwee/sf-map-svg/static';
import { staticMapData } from '@kahwee/sf-map-svg/data/static';
const { svg, project } = renderMap(staticMapData, {
year: 2022,
layers: { neighborhoodLines: true, bartStations: true },
animation: true, // draws itself, even inside <img>
});import { createMap } from '@kahwee/sf-map-svg';
import { fullMapData } from '@kahwee/sf-map-svg/data/full';
const map = createMap(fullMapData, {
mode: 'districts',
features: { layerTransitions: true, districtMorph: true },
});
document.querySelector('#map').append(map.element);
map.on('districtchange', ({ id }) => console.log(id));Offline
Self-contained SVG with no tiles, fonts or network requests.
Explicit data
Import only the geography a page needs, from a 1 KB station list to every layer.
Accessible
Titles, roles, keyboard selection and live status messages are built in.
Considerate motion
Every animation is opt-in and yields to reduced motion.