Skip to content
 
 

Repository files navigation

Website for the Transparency Ranking in Germany

Repository of lobbyranking.de – pull requests are welcome.


This site is generously supported by BrowserStack, www.browserstack.com for cross-browser compatibility.

Built with Next.js and Data Love.

Tech stack

  • Next.js 16 with the App Router, exported as a fully static site (output: "export" in next.config.tsnpm run build writes out/). There is no server runtime in production.
  • React 19 server components for everything that renders at build time; a handful of "use client" components handle the interactive bits (year toggle, category switcher, the embeddable explorer).
  • TypeScript, strict.
  • CSS Modules (*.module.css next to each component) plus a small global stylesheet (app/globals.css). No CSS framework.
  • d3-scale for the bar-chart scales, gray-matter + marked to turn the per-state Markdown into HTML. That's the whole dependency list.
  • Node 22 (.node-version), >=20.9.0 enforced in package.json.
  • Deployed on Render as a static site (build → publish out/).

Note on the Next.js version: this is a newer Next.js than most references describe. AGENTS.md is auto-generated by next dev; check node_modules/next/dist/docs/ before relying on older App Router conventions.

Project structure

app/
  (main)/                 route group for the public site (shares Header/Footer/About)
    page.tsx              home page: Germany map + ranking explorer + accordion
    laender/[slug]/       one page per Bundesland, statically generated
    methodik/             methodology page
    layout.tsx            <html>, metadata, Header/Footer/AboutSection shell
    not-found.tsx
  (embed)/                route group for the bare iframe embed (no chrome)
    embed/                /embed/ – just the ranking explorer, used by external sites
  globals.css
  robots.ts, sitemap.ts   generated at build time

components/               presentational + interactive React components
  *.tsx + *.module.css    one CSS Module per component
  icons/

lib/                      all non-visual logic, imported via the "@/lib/..." alias
  data.ts                 loads + normalizes data/data_<year>.json
  scoring.ts              turns raw indicators into per-category and overall scores
  rankingViews.ts         builds the ranked bar-chart datasets the UI consumes
  states.ts               reads content/states/*.md (frontmatter + body)
  statePaths.ts           SVG path data for the clickable Germany map
  constants.ts            YEARS, DEFAULT_YEAR, category order, colors
  types.ts                shared TypeScript types
  fonts.ts                next/font setup

content/states/*.md       editable descriptive text per Bundesland (YAML frontmatter + Markdown)
data/data_<year>.json     the ranking data, one file per survey year
scripts/fetch-data.ts     refreshes data from the Google Sheet (run manually)
public/                   static assets (images, PDFs, fonts) served from /

build.js                  legacy – leftover from the previous Jekyll/Angular site

How it works

Rendering. Every page is generated at build time. app/(main)/laender/[slug]/page.tsx uses generateStaticParams() over the state slugs, so each Bundesland becomes a static HTML file. trailingSlash: true keeps the old /laender/thueringen/ URL shape from the previous site (bookmarks and SEO depend on it).

Data flow. data/data_<year>.jsonlib/data.ts (decodes UTF-8 / UTF-16 exports, normalizes messy fields into typed Indicators) → lib/scoring.tslib/rankingViews.ts → components. Scoring is deliberately not a plain points ratio: each category's percentage is rounded first, then the four rounded percentages are averaged and rounded again (getTotalFor in lib/scoring.ts, ported 1:1 from the old site).

Years. The ranking has been run for multiple years (YEARS in lib/constants.ts); DEFAULT_YEAR is what the home page and map show first. Add a year by dropping in data/data_<year>.json and extending YEARS / the Year type.

State content. Each Bundesland's prose lives in content/states/<slug>.md with YAML frontmatter (name, status, optional title, ifg_* fields). lib/states.ts parses these; status: draft marks a state whose write-up isn't finished.

Installation and running it

First install Node.js, then run

npm install
npm run dev

Other scripts: npm run build (static export to out/), npm run start (serve a production build), npm run lint.

Building for production

npm run build

This produces a fully static site in out/, ready to deploy to any static host.

Updating the ranking data

Currently Data is updated by adding a data_YEAR.json file to /data. There is another way to update Data via Google sheets but in order to do so Data would have to be correctly stored there first.

The ranking data lives in a Google Sheet and is pulled into data/data.json on demand — it is not fetched automatically as part of npm run build, so builds never depend on Google Sheets being reachable. To refresh it after the sheet has been updated:

npm run fetch-data

Then commit the resulting data/data.json.

Editing state content

Each Bundesland's descriptive text lives in content/states/*.md as plain Markdown with YAML frontmatter — edit these files directly and rebuild.

About

Visualisation of the IFG Index

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages