Redesign chart visualizations to match Civilytics white-paper style
Deploy to git-pages / deploy (push) Successful in 18s

Rebuilds all 6 charts around a validated categorical race palette, row-based
sex encoding, and a consistent observed-vs-modeled mark convention (diamond
vs. filled bar/density) instead of ad hoc per-chart color schemes. Adds a
shared student-group filter (defaults to all 8 groups) that scopes every
chart's data from one place in ChartPanel.

Drops the D3 dependency entirely in favor of plain SVG, removing the
imperative-DOM bug class behind this app's repeated "fix the fix" commits.
Replaces the fake symmetric-normal posterior approximation with a skewed,
median-preserving fit to the API's interval bounds, clearly labeled as an
approximation. Fixes two broken SVG fill attributes, a decorative model
dropdown that never affected its chart, dead code, an orphaned component,
and a broken CSS token reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 18:45:37 -04:00
co-authored by Claude Sonnet 5
parent 6c8e1e8409
commit 67caa02fe2
19 changed files with 910 additions and 1580 deletions
+40
View File
@@ -0,0 +1,40 @@
/**
* Plain-HTML chart legend — replaces every chart's hand-rolled SVG legend.
* Both of the app's stray-`}` CSS bugs lived inside hand-written SVG legend
* `fill="..."` strings; keeping legends out of SVG removes that bug class.
*
* items: [{ color, label, shape: 'swatch' | 'diamond' | 'line' }]
*/
export default function ChartLegend({ items }) {
return (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1.25rem', marginTop: 'var(--space-2)' }}>
{items.map((item) => (
<div key={item.label} style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}>
<LegendMark shape={item.shape} color={item.color} />
<span style={{ fontSize: '0.8rem', color: 'var(--cv-ink-2)' }}>{item.label}</span>
</div>
))}
</div>
)
}
function LegendMark({ shape, color }) {
if (shape === 'diamond') {
return (
<span
style={{
display: 'inline-block',
width: '9px',
height: '9px',
background: color,
transform: 'rotate(45deg)',
flexShrink: 0,
}}
/>
)
}
if (shape === 'line') {
return <span style={{ display: 'inline-block', width: '16px', height: '3px', background: color, borderRadius: '2px', flexShrink: 0 }} />
}
return <span style={{ display: 'inline-block', width: '11px', height: '11px', background: color, borderRadius: '3px', flexShrink: 0 }} />
}