Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions frontend/src/components/DataProducts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.data-products {
display: grid;
gap: var(--space);
}

@media screen and (min-width: 720px) {
.data-products {
grid-template-columns: 1fr 1fr;
}
}

.data-products__product {
display: flex;
flex-direction: column;
gap: var(--space-xxs);
padding: var(--space-xs);

border-radius: var(--border-radius-sm);
border: 1px solid var(--gray-light);
box-shadow: var(--shadow);
}

.data-products__action {
margin-block-start: auto;
font-weight: 500;
}
56 changes: 56 additions & 0 deletions frontend/src/components/DataProducts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import "./DataProducts.css";

function DataProducts() {
return (
<div class="data-products">
<article class="data-products__product">
<h3 class="gamma">Dataset</h3>
<p>
Download a full copy of our data as a set of CSV files, updated on a
weekly basis. Perfect for large-scale data analysis.
</p>
<p class="data-products__action">
<a
href="https://github.com/howtheyvote/data"
rel="noopener noreferrer"
target="_blank"
>
Download & Documentation
</a>
</p>
</article>
<article class="data-products__product">
<h3 class="gamma">API</h3>
<p>
Get real-time access to the latest votes or search for votes by
keyword.
</p>
<p class="data-products__action">
<a href="/developers">Documentation</a>
</p>
</article>
<article class="data-products__product">
<h3 class="gamma">Download vote results</h3>
<p>
Download the results of a single vote in JSON or CSV formats, ready to
be opened in Excel and other tools.
</p>
<p class="data-products__action">
<a href="#">See how</a>
</p>
</article>
<article class="data-products__product">
<h3 class="gamma">Embed vote results</h3>
<p>
Embed the results of a single vote on your website using our
visualization templates.
</p>
<p class="data-products__action">
<a href="#">See how</a>
</p>
</article>
</div>
);
}

export default DataProducts;
18 changes: 18 additions & 0 deletions frontend/src/components/Showcase.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.showcase {
display: grid;
gap: var(--space);
}

.showcase__item {
display: grid;
gap: var(--space-xxs);
padding: var(--space-xs);

border: 1px solid var(--gray-light);
border-radius: var(--border-radius-sm);
box-shadow: var(--shadow);
}

.showcase__meta {
color: var(--color-text-light);
}
25 changes: 25 additions & 0 deletions frontend/src/components/Showcase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import showcase from "../data/showcase.json";

import "./Showcase.css";

function Showcase() {
return (
<div class="showcase">
{showcase.map(({ url, title, author, description }) => (
<article class="showcase__item">
<header>
<h3 class="gamma">
<a href={url} rel="noopener noreferrer" target="_blank">
{title}
</a>
</h3>
<p class="showcase__meta">{author}</p>
</header>
<p>{description}</p>
</article>
))}
</div>
);
}

export default Showcase;
7 changes: 7 additions & 0 deletions frontend/src/css/typography.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ strong {
margin-top: var(--space-xxs);
}

.prose blockquote {
padding: var(--space-xs);
background-color: var(--gray-lightest);
border: 1px solid var(--gray-light);
border-radius: var(--border-radius-sm);
}

.prose > * + * {
margin-top: var(--space-xs);
}
Expand Down
62 changes: 62 additions & 0 deletions frontend/src/data/showcase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
[
{
"url": "https://institutdelors.eu/content/uploads/2026/04/R130-EP-Political-Dynamics_FR.pdf",
"title": "Analysing political dynamics in the new European Parliament: Political Cleavages and Division Lines",
"author": "Jacques Delors Institute",
"description": "In this study, the authors analyze how consistently different political groups and national parties vote together on key issues like the budget, the environment, and foreign relations."
},
{
"url": "https://www.swp-berlin.org/publications/products/studien/2024S11_Stand_EU_Integration.pdf#page=45",
"title": "The European Green Deal: Raising Ambitions and Further Developing European Climate Policy (German)",
"author": "German Institute for International and Security Affairs",
"description": "In this research article, the authors analyze voting patterns across key legislative initiatives of the European Green Deal."
},
{
"url": "https://novayagazeta.eu/articles/2025/11/26/moscows-minions-en",
"title": "Moscow’s minions: A new pro-Kremlin bloc is taking shape in the European Parliament",
"author": "Novaya Gazeta Europe, EUobserver",
"description": "In this article, the authors analyze voting records to identify MEPs that frequently vote in favor of Russian interests."
},
{
"url": "https://digitalomat24.de/",
"title": "Digital-O-Mat (German)",
"author": "Digitalcourage Braunschweig",
"description": "This tool by German NGO Digitalcourage allows users to compare their own views with the actual voting behavior of party representatives on ten key digitalization issues voted on between 2019 and 2024."
},
{
"url": "https://www.eesc.europa.eu/en/our-work/publications-other-work/publications/unmasking-far-rights-anti-worker-agenda",
"title": "Unmasking the far right’s anti-worker agenda",
"author": "European Economic and Social Committee Worker’s Group",
"description": "This report analyzes the voting behavior of far-right MEPs in eight key votes such as minimum wages, corporate accountability, gender pay gap, or working conditions of platform workers."
},
{
"url": "https://arxiv.org/abs/2507.03214",
"title": "Multi-polarization during the 9th European Parliament",
"author": "NOVA University Lisbon",
"description": "In this research article, the authors analyze voting data to map how different political groups form shifting alliances across different policy areas, revealing voting patterns that go beyond a simple left-right divide."
},
Comment on lines +36 to +37

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
"url": "https://nicolaivonondarza.substack.com/",
"title": "EU Analytics",
"author": "Nicolai von Ondarza",
"description": "Once a month, this blog by political scientist Nicolai von Ondarza reviews key votes and political dynamics in the European Parliament and the Council."
},
{
"url": "https://www.svt.se/datajournalistik/det-har-ar-de-folkvalda-i-europaparlamentet/",
"title": "These are the elected members of the European Parliament (Swedish)",
"author": "SVT Nyheter",
"description": "This interactive article by Swedish public broadcaster SVT explores who the Members of the European Parliament are and how similarly they vote, using voting records to visualize relationships and agreement between politicians."
},
{
"url": "https://www.dw.com/en/how-hard-right-eu-lawmakers-vote-on-climate-migration/a-69218119",
"title": "How hard-right EU lawmakers vote on big issues",
"author": "Deutsche Welle",
"description": "Journalists from Deutsche Welle analyze the voting behavior of the far-right political groups ECR and ID on issues such as climate change, migration, and support for Ukraine."
},
{
"url": "https://web.archive.org/web/20260227114928/https://www.br.de/nachrichten/deutschland-welt/afd-im-europaparlament-radikaler-als-die-radikalen-eine-datenanalyse,U818Keo",
"title": "The AfD in the European Parliament: More radical than the radicals (German)",
"author": "Bayerischer Rundfunk",
"description": " Journalists from German public broadcaster BR conducted a data analysis of voting behavior by MEPs of German far-right party AfD on key issues such as climate change and rule of law."
}
]
124 changes: 124 additions & 0 deletions frontend/src/pages/OpenDataPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import App from "../components/App";
import BaseLayout from "../components/BaseLayout";
import DataProducts from "../components/DataProducts";
import Hero from "../components/Hero";
import PageNav from "../components/PageNav";
import PageNavItem from "../components/PageNavItem";
import Showcase from "../components/Showcase";
import Stack from "../components/Stack";
import Wrapper from "../components/Wrapper";
import type { Page } from "../lib/server";

export const OpenDataPage: Page = () => {
return (
<App title={"Open Data"}>
<BaseLayout>
<Stack space="lg">
<div>
<Hero
title="Open Data"
text="We believe it should be easy to work with European Parliament voting data. That’s why we publish our data under an open license."
/>
<PageNav>
<PageNavItem href="#products">Data</PageNavItem>
<PageNavItem href="#license">License</PageNavItem>
<PageNavItem href="#tutorials">Tutorials</PageNavItem>
<PageNavItem href="#showcase">Showcase</PageNavItem>
<PageNavItem href="#faq">FAQ</PageNavItem>
</PageNav>
</div>
<div class="px">
<Wrapper>
<Stack space="xl">
<Stack>
<Stack space="xs">
<h2 id="data" class="delta">
Data
</h2>
<p>
We make our data avaialble in multiple ways to suit
different use cases – whether you want to conduct a
large-scale data analysis or are interested in the results
of individual votes.
</p>
</Stack>
<DataProducts />
</Stack>

<Stack space="xs">
<h2 id="license" class="delta">
License
</h2>

<div class="prose">
<p>
We make our data available under the{" "}
<a href="http://opendatacommons.org/licenses/odbl/1.0/">
Open Database License (ODbL)
</a>
. Any rights in individual contents of the database are
licensed under the{" "}
<a href="http://opendatacommons.org/licenses/dbcl/1.0/">
Database Contents License
</a>
, unless otherwise noted.
</p>
<p>
Photos of MEPs and vote summaries are <strong>not</strong>{" "}
covered by the Database Contents License. These contents
are sourced from the official website of the European
Parliament. Please refer to the respective{" "}
<a href="https://www.europarl.europa.eu/legal-notice/en/">
copyright notice
</a>
.
</p>

<p>
Under the Open Database License, you are free to use,
modify, and build upon the data from HowTheyVote.eu as
long as you comply with the license conditions. In
particular, you must:
</p>
<p>
<strong>Provide attribution:</strong> You must include a
link to HowTheyVote.eu as the source and make clear the
license, so that others are aware that they can also
freely use the data. For example:
</p>
<blockquote>
Data source:{" "}
<a href="https://howtheyvote.eu">
HowTheyVote.eu (Open Database License)
</a>
</blockquote>
<p>
<strong>Share under the same license:</strong> When you
make available data from HowTheyVote.eu or a database
based on it, it must continue to be licensed under the
Open Database License.
</p>
</div>
</Stack>

<Stack>
<Stack space="xs">
<h2 id="showcase" class="delta">
Showcase
</h2>
<p>
See how researchers, journalists, NGOs, and public
institutions use data from HowTheyVote.eu. Are you using
it as well? We’d love to hear from you!
</p>
</Stack>
<Showcase />
</Stack>
</Stack>
</Wrapper>
</div>
</Stack>
</BaseLayout>
</App>
);
};
2 changes: 2 additions & 0 deletions frontend/src/server.entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
MemberSharepicPage,
loader as memberSharepicLoader,
} from "./pages/MemberSharepicPage";
import { OpenDataPage } from "./pages/OpenDataPage";
import { SearchPage, loader as searchLoader } from "./pages/SearchPage";
import { VotePage, loader as voteLoader } from "./pages/VotePage";
import {
Expand Down Expand Up @@ -47,6 +48,7 @@ app.registerPage(
);
app.registerPage(["/developers", "/developers/*"], DevelopersPage);
app.registerPage("/imprint", ImprintPage);
app.registerPage("/data", OpenDataPage);
app.registerPage("/about", AboutPage, aboutLoader);

// Redirect vote shortlinks (e.g. howtheyvote.eu/162190)
Expand Down
1 change: 1 addition & 0 deletions frontend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"jsxImportSource": "preact",
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"resolveJsonModule": true,
}
}
Loading