CLAUDE.md
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is the Jekyll-based website for GERDA (German Election Database), hosted at german-elections.com. The site provides documentation and download access for harmonized German election datasets (municipal, state, and federal elections).
Jekyll website repo — the research-root
../CLAUDE.mdR / stats conventions do not apply here.
Development Commands
# Install dependencies
bundle install
# Run local development server (auto-reloads on file changes)
bundle exec jekyll serve
# Build static site to _site/
bundle exec jekyll build
Note: Changes to _config.yml require restarting the server.
Architecture
Static Site Generator: Jekyll 4.3.3 with Minima theme
Content Pages (Markdown with YAML front matter):
index.md- Homepage with project overview, author info, citationelection-data.md- Data download links and dataset descriptionsr-package.md- R package documentationusage_notes.md- Detailed usage notes and data sources
Configuration:
_config.yml- Jekyll settings, site metadata, navigation order (header_pages)Gemfile- Ruby dependencies
Writing update-log entries
update-log.md is the changelog users read. Each entry is one file in
_updates/, named YYYY-MM-DD-slug.md, with front matter date (with a
time; entries sharing a day sort by it, newest first), title (the headline
in plain text, used only by the Atom feed at /updates.xml), and major: true
where it applies. The body is the entry as before: bold headline sentence,
then bullets. update-log.md loops over the collection; do not add entries
to it directly. When a data change lands in the processing repo, add an entry,
but write it the way the existing entries are written, not as a summary of
the work you just did.
- Bare minimum. One bold headline sentence, then at most 2–4 bullets of one or two sentences each. Writing too much is the most common failure by far — assume your first draft is twice as long as it should be, and cut.
- Include only what a user of the data needs. For each fact ask: does this
change how someone loads, filters or interprets the data? If not, drop it.
Drop in particular: why a bug happened, how the data was parsed or obtained,
which office supplied it, row counts, verification you performed, and any
claim that nothing else changed. That detail belongs in the processing repo’s
CLAUDE.mdor the commit message, not here. - Do keep the things that change how the data behaves: unusual
valid_votessemantics, whatNAmeans in a new column, renamed or removed variables, fields the source does not provide, and coverage that is still missing. - Coalesce. One entry per date, covering everything that shipped that day. Two entries with the same date is a mistake — merge them. Related changes across datasets belong in one entry with a bullet each (see 2026-06-27, 2026-07-27).
- Reserve
major: true. Major means a new dataset, a new election type, or a change that breaks existing code. A new state-year, a parser fix, or even a new state in an existing dataset is a normal entry. Most entries are not major. - Verify every number and superlative before publishing. Query the actual
.rdsoutputs — do not carry figures over from your own working notes or a commit message. Claims like “the longest series we carry”, counts of elections, municipalities or states, and coverage ranges are exactly the ones that turn out to be wrong.
Page weight rules
The site is static and served by GitHub Pages with gzip, so download size is the main speed lever. Keep these in place when regenerating assets:
- Boundary files (
assets/data/gerda_municipalities_2021.geojson,assets/data/meinungsbild/kreise.geojson) are simplified with mapshaper before committing; the raw exports are 38 MB and 21 MB, the committed versions about 7 MB and 2 MB. Regenerate withpython3 scripts/simplify_boundaries.py <raw> <out>, which runs mapshaper and then rewinds rings to the clockwise-exterior convention d3 needs (mapshaper alone writes the opposite winding, and the map renders as one solid block). Topology is preserved, and every feature keeps its properties. - Dashboard CSVs carry turnout and vote shares rounded to 4 decimals (
format_share()inscripts/generate_dashboard_data.py). Full-precision floats doubled the download. - Hero image is 1600×800:
map_elec_fed_combined.webp(about 160 KB) withmap_elec_fed_combined.jpgas fallback and social preview. It shows the 2025 federal election (turnout, CDU/CSU, SPD by municipality on 2021 boundaries) and is drawn byscripts/hero_map.R, which downloadsfederal_muni_harm_21through the gerda package and reads the 2021 VG250 shapes from the sibling data repo; convert its PNG withcwebp -q 82andsipsas the script header says. Do not commit the PNG. - Download table sizes and dates come from
_data/downloads.yml, written bypython3 scripts/update_download_data.py(Content-Length from the media mirror, last CSV commit date from the GitHub API). Rerun it after the data repo publishes; the table rows inelection-data.mdlook the values up by file stem, so a new dataset needs a row with the usual links and nothing else. - Scripts for the dashboard and Meinungsbild pages load with
defer; the preconnect hints for d3 and Plotly are emitted only on those two pages (_includes/head.html).
Related Repositories:
- Data processing: https://github.com/awiedem/german_election_data
- R package: https://github.com/hhilbig/gerda
- Local checkout:
/Users/hanno/Documents/GitHub/gerda— consult when verifying thatr-package.mdmatches the package’s actual functions, signatures, dataset catalog, and version. Canonical sources inside the checkout:DESCRIPTION(version),NAMESPACE(exported functions),NEWS.md(change history),README.md(reference prose), andR/*.R(function signatures and roxygen help).
- Local checkout: