political-data-visualization
SKILL.md
Political Data Visualization
Purpose
Create compelling, accessible data visualizations using pure HTML/CSS (no JavaScript) to display political metrics, voting patterns, parliamentary activity, and democratic accountability indicators for riksdagsmonitor.
Core Principles
- CSS-Only: No JavaScript frameworks - pure HTML/CSS for reliability and security
- Accessibility First: WCAG 2.1 AA compliant, screen reader friendly
- Semantic Markup: Use
<table>with ARIA for complex data - Progressive Disclosure: Show summary, reveal details on interaction
- Color-Blind Safe: Multiple visual cues beyond color (patterns, labels, icons)
- Responsive Design: Adapt visualizations to mobile/tablet/desktop
- Performance: Minimal CSS, no external libraries, fast rendering
Visualization Types
Progress Bars (Voting Discipline, Party Cohesion)
/* Semantic HTML */
<div class="progress" role="progressbar" aria-valuenow="87" aria-valuemin="0" aria-valuemax="100">
<span class="progress-label">Party Cohesion: 87%</span>
<div class="progress-bar" style="--value: 87%"></div>
</div>
/* CSS implementation */
.progress {
position: relative;
width: 100%;
height: 2rem;
background: var(--dark-bg);
border-radius: 0.5rem;
overflow: hidden;
border: 2px solid var(--primary-cyan);
}
.progress-bar {
height: 100%;
width: var(--value);
background: linear-gradient(90deg, var(--primary-magenta), var(--primary-cyan));
transition: width 0.5s ease-in-out;
}
.progress-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: var(--light-text);
font-weight: bold;
z-index: 1;
}
Bar Charts (MPs by Party, Committee Activity)
/* Horizontal bar chart */
<div class="bar-chart">
<div class="bar-row">
<span class="bar-label">S (Social Democrats)</span>
<div class="bar" style="--value: 30%; --color: var(--party-s)">
<span class="bar-value">107 MPs</span>
</div>
</div>
<div class="bar-row">
<span class="bar-label">M (Moderates)</span>
<div class="bar" style="--value: 19%; --color: var(--party-m)">
<span class="bar-value">68 MPs</span>
</div>
</div>
</div>
/* CSS */
.bar-chart {
display: grid;
gap: 1rem;
}
.bar-row {
display: grid;
grid-template-columns: 200px 1fr;
align-items: center;
gap: 1rem;
}
.bar {
position: relative;
height: 2.5rem;
width: var(--value);
max-width: 100%;
background: var(--color);
border-radius: 0.25rem;
display: flex;
align-items: center;
padding: 0 0.5rem;
}
.bar-value {
color: white;
font-weight: bold;
font-size: 0.875rem;
}
/* Responsive */
@media (max-width: 767px) {
.bar-row {
grid-template-columns: 1fr;
}
.bar-label {
font-size: 0.875rem;
}
}
Heat Maps (Voting Patterns, Committee Attendance)
/* Grid-based heat map */
<div class="heat-map">
<div class="heat-cell" style="--intensity: 1.0" title="100% attendance">
<span class="sr-only">100% attendance</span>
</div>
<div class="heat-cell" style="--intensity: 0.75" title="75% attendance">
<span class="sr-only">75% attendance</span>
</div>
<div class="heat-cell" style="--intensity: 0.5" title="50% attendance">
<span class="sr-only">50% attendance</span>
</div>
</div>
/* CSS */
.heat-map {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(2rem, 1fr));
gap: 0.25rem;
}
.heat-cell {
aspect-ratio: 1;
background: color-mix(
in srgb,
var(--primary-magenta) calc(var(--intensity) * 100%),
var(--dark-bg)
);
border: 1px solid var(--mid-bg);
border-radius: 0.25rem;
cursor: help;
transition: transform 0.2s;
}
.heat-cell:hover,
.heat-cell:focus {
transform: scale(1.2);
z-index: 1;
outline: 2px solid var(--primary-cyan);
}
Donut Charts (Coalition Breakdown, Vote Distribution)
/* CSS-only donut chart using conic-gradient */
<div class="donut-chart" style="
--s: 107;
--m: 68;
--sd: 73;
--total: 349;
--s-pct: calc(var(--s) / var(--total) * 100);
--m-pct: calc(var(--m) / var(--total) * 100);
--sd-pct: calc(var(--sd) / var(--total) * 100);
" role="img" aria-label="Coalition distribution: S 107, M 68, SD 73 of 349 MPs">
<div class="donut-hole">
<span class="donut-total">349</span>
<span class="donut-label">MPs</span>
</div>
</div>
/* CSS */
.donut-chart {
width: 200px;
height: 200px;
border-radius: 50%;
background: conic-gradient(
from 0deg,
var(--party-s) 0% var(--s-pct),
var(--party-m) var(--s-pct) calc(var(--s-pct) + var(--m-pct)),
var(--party-sd) calc(var(--s-pct) + var(--m-pct)) 100%
);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.donut-hole {
width: 60%;
height: 60%;
border-radius: 50%;
background: var(--dark-bg);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.donut-total {
font-size: 2rem;
font-weight: bold;
color: var(--primary-cyan);
}
.donut-label {
font-size: 0.875rem;
color: var(--light-text);
}
Timeline Visualizations (Legislative Process, MP Career)
/* Vertical timeline */
<ol class="timeline">
<li class="timeline-item">
<time datetime="2024-09-15">2024-09-15</time>
<div class="timeline-content">
<h3>Motion Submitted</h3>
<p>MP submitted motion to committee</p>
</div>
</li>
<li class="timeline-item">
<time datetime="2024-10-20">2024-10-20</time>
<div class="timeline-content">
<h3>Committee Review</h3>
<p>Committee issued report</p>
</div>
</li>
</ol>
/* CSS */
.timeline {
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
.timeline::before {
content: '';
position: absolute;
left: 2rem;
top: 0;
bottom: 0;
width: 2px;
background: var(--primary-cyan);
}
.timeline-item {
position: relative;
padding-left: 5rem;
padding-bottom: 2rem;
}
.timeline-item::before {
content: '';
position: absolute;
left: 1.5rem;
top: 0.25rem;
width: 1rem;
height: 1rem;
border-radius: 50%;
background: var(--primary-magenta);
border: 3px solid var(--dark-bg);
box-shadow: 0 0 0 3px var(--primary-cyan);
}
.timeline-item time {
position: absolute;
left: 0;
top: 0;
font-size: 0.875rem;
color: var(--primary-cyan);
}
Party Color Palette
:root {
/* Swedish political parties (official colors) */
--party-s: #E8112d; /* Socialdemokraterna (Red) */
--party-m: #52BDEC; /* Moderaterna (Blue) */
--party-sd: #DDDD00; /* Sverigedemokraterna (Yellow) */
--party-c: #009933; /* Centerpartiet (Green) */
--party-v: #DA291C; /* Vänsterpartiet (Red) */
--party-kd: #000077; /* Kristdemokraterna (Dark Blue) */
--party-l: #006AB3; /* Liberalerna (Blue) */
--party-mp: #83CF39; /* Miljöpartiet (Green) */
}
Accessibility Requirements
Screen Reader Support
<!-- Always include sr-only labels for data -->
<div class="progress-bar" style="--value: 87%">
<span class="sr-only">87% party cohesion</span>
</div>
<!-- Data tables for complex visualizations -->
<table class="data-table" role="table">
<caption>MPs by Party Distribution</caption>
<thead>
<tr>
<th scope="col">Party</th>
<th scope="col">MPs</th>
<th scope="col">Percentage</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Socialdemokraterna (S)</th>
<td>107</td>
<td>30.7%</td>
</tr>
</tbody>
</table>
<!-- Hide visual chart from screen readers, show data table -->
<div aria-hidden="true" class="visual-chart">...</div>
Color Contrast
/* Ensure 4.5:1 contrast ratio minimum */
.bar-label {
color: var(--light-text); /* #e0e0e0 on #0a0e27 = 12.6:1 ✅ */
}
/* Use patterns for color-blind accessibility */
.bar-striped {
background-image: repeating-linear-gradient(
45deg,
transparent,
transparent 10px,
rgba(255,255,255,0.1) 10px,
rgba(255,255,255,0.1) 20px
);
}
Keyboard Navigation
/* Make interactive elements focusable */
.heat-cell {
cursor: pointer;
position: relative;
}
.heat-cell:focus {
outline: 2px solid var(--primary-cyan);
outline-offset: 2px;
z-index: 1;
}
/* Tooltip on focus/hover */
.heat-cell::after {
content: attr(title);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--dark-bg);
color: var(--light-text);
padding: 0.5rem;
border-radius: 0.25rem;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s;
}
.heat-cell:hover::after,
.heat-cell:focus::after {
opacity: 1;
}
When to Use
- Dashboard Design: Political metrics overview
- Party Analysis: MP distribution, voting patterns, coalition strength
- Voting Records: Vote outcomes, party discipline, cross-party votes
- Committee Activity: Productivity metrics, attendance rates
- MP Profiles: Career timeline, voting record, committee assignments
- Electoral Analysis: Historical results, trends, forecasts
- Risk Assessment: 45 risk rules visualization, severity indicators
- Transparency Metrics: Democratic accountability scores
Examples
Good Pattern: Responsive Voting Discipline Dashboard
<section class="voting-dashboard">
<h2>Voting Discipline by Party (2024/25)</h2>
<div class="party-grid">
<div class="party-card">
<h3>Socialdemokraterna (S)</h3>
<div class="progress" role="progressbar" aria-valuenow="89" aria-valuemin="0" aria-valuemax="100">
<span class="progress-label">89% Cohesion</span>
<div class="progress-bar" style="--value: 89%; --color: var(--party-s)"></div>
</div>
<p class="stat">92 unanimous votes, 8 rebels</p>
</div>
<!-- Repeat for other parties -->
</div>
</section>
<style>
.party-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.party-card {
background: var(--mid-bg);
padding: 1.5rem;
border-radius: 0.5rem;
border-left: 4px solid var(--primary-cyan);
}
</style>
Anti-Pattern: JavaScript-Dependent Charts
<!-- ❌ BAD: Requires JavaScript library -->
<script src="https://cdn.example.com/charts.js"></script>
<canvas id="chart"></canvas>
<script>
new Chart(document.getElementById('chart'), {...});
</script>
<!-- ✅ GOOD: Pure CSS visualization -->
<div class="bar-chart" role="img" aria-label="MPs by party">
<!-- CSS-only bars -->
</div>
Anti-Pattern: Inaccessible Visualizations
<!-- ❌ BAD: No screen reader support -->
<div class="donut" style="background: conic-gradient(...)"></div>
<!-- ✅ GOOD: Full accessibility -->
<div class="donut" role="img" aria-label="Coalition: S 107, M 68, SD 73 MPs">
<!-- Visual chart -->
</div>
<table class="sr-only">
<caption>Coalition Distribution</caption>
<!-- Data table for screen readers -->
</table>
Remember
- CSS-only - no JavaScript libraries
- Accessibility mandatory - WCAG 2.1 AA, screen reader support
- Semantic HTML - use
<table>for data, proper ARIA roles - Responsive - adapt to mobile/tablet/desktop
- Color-blind safe - use patterns, labels, icons beyond color
- Performance - minimal CSS, fast rendering
- Party colors - use official colors for Swedish parties
- Progressive disclosure - summary view, details on interaction
- Data tables - always provide for screen readers
- Tooltip support - title attribute + CSS hover/focus
References
- CSS-Tricks: Chart.css
- A11Y: Accessible Data Visualizations
- MDN: conic-gradient()
- Web.dev: Building an accessible progress bar
- WCAG 2.1: Level AA
Version: 1.0
Last Updated: 2026-02-06
Maintained by: Hack23 AB
Weekly Installs
7
Repository
hack23/riksdagsmonitorGitHub Stars
2
First Seen
12 days ago
Security Audits
Installed on
opencode7
claude-code7
github-copilot7
codex7
amp7
cline7