2026-08-10 12:54:55 -04:00

CRDC Arrests API Demo

A demonstration web application for the CRDC School Arrest Rate API, showing Bayesian model comparisons of school-based arrest rates across U.S. districts. Built with React + Vite, deployable as a static site or Docker container.

Quick Start (Development)

# Install dependencies
npm ci

# Start dev server on localhost:5173
npm run dev

# Build for production
npm run build      # outputs to dist/
npm run preview    # serve built files locally

What It Does

Visitors select a U.S. state, search for a school district (with suggestions of districts that have the most arrests), and see 6 charts comparing observed data against Bayesian model estimates:

  1. Arrests over time — raw counts by CRDC wave with per-1k rate labels
  2. Rate by student group — bar chart, most recent year (observed vs. modeled)
  3. District vs. national — highest-rate group compared to the U.S. average
  4. Model predictions vs. observed — four quadrants (one-year/three-year × baseline/covariate)
  5. Observed rate vs. model distribution — ridgeline-style comparison across models
  6. Exceedance probability — P(district > national) per student group

Architecture

Tech Stack

  • React 19 + Vite (static site generation, no backend required)
  • Plain CSS custom properties for styling (matches Civilytics design tokens exactly)
  • SVG-based charts rendered inline (no D3 or charting library dependencies)
  • Calls the public read-only API directly from the browser

File Structure

crdc-demo/
├── index.html                    # Entry point
├── vite.config.mjs               # Vite build config
├── src/
│   ├── main.jsx                  # React entry
│   ├── App.jsx                   # Main router (state → search → loading → charts)
│   ├── hooks/useApi.js           # API client with retry/backoff + endpoint wrappers
│   ├── components/
│   │   ├── StateSelector.jsx     # Landing screen — state dropdown/grid
│   │   ├── DistrictSearch.jsx    # Search + "interesting" district suggestions
│   │   ├── LoadingAnimation.jsx  # Animated histogram grid during data fetch
│   │   └── ChartPanel.jsx        # Orchestrates all 6 charts + data fetching
│   ├── charts/
│   │   ├── ArrestsOverTime.jsx       # Chart 1 — line chart by wave
│   │   ├── RateByGroupBar.jsx        # Chart 2 — bar chart by group
│   │   ├── DistrictVsNational.jsx    # Chart 3 — comparison vs. national avg
│   │   ├── ModelDrawsComparison.jsx  # Chart 4 — quadrant model comparison
│   │   ├── ObservedRateDensity.jsx   # Chart 5 — ridgeline proxy from intervals
│   │   └── ExceedanceProbability.jsx # Chart 6 — P(district > nat) per group
│   ├── styles/tokens.css          # Civilytics design tokens (colors, fonts, spacing)
│   └── data/national_rates.json    # Static national rates fixture for comparisons
├── Dockerfile                    # Multi-stage build → nginx static server
├── docker-compose.yml            # Local dev / self-hosted deployment
├── _config.yml                   # Git Pages (self-hosted equivalent) config
└── .github/workflows/deploy.yml  # CI/CD — builds and uploads artifacts

API Endpoints Used

Endpoint Purpose Frequency
/api/v1/models List available Bayesian model specs Once (cached)
/api/v1/districts?q=&state= District name/geo lookup → LEAID On keystroke
/api/v1/estimates/{leaid}?model=X&year=Y Estimates for one district/model/year/group ~40 calls per district
/data/national_rates.json Static national rates fixture (committed) Once per session

Deployment

The app is a static site deployed automatically via Gitea Actions. On every push to main, the workflow in .gitea/workflows/pages.yml builds the Vite app and copies dist/ to a pages branch served by the git-pages webhook receiver.

URL: https://pages.civilytics.org/crdc-demo/

CORS note: The CRDC API (crdc-api.civilytics.org) does not send CORS headers. When deployed as a static site, browser-based fetch requests will be blocked by the same-origin policy. To fix this, deploy proxy.php to your git-pages host and set the VITE_PROXY_URL environment variable (e.g., /crdc-demo/proxy.php). The app automatically detects proxy availability — when unset, it attempts direct API calls (works via Docker/nginx or same-origin setups).

The workflow follows the same pattern as Civilytics/sln-school-comparison:

  1. Triggers on push to main (only when source files change)
  2. Builds with Node 22 + Vite (npm ci && npm run build)
  3. Copies dist/ contents to an orphan pages branch
  4. Forces push — the git-pages webhook picks it up automatically

Note

: Ensure ${{ secrets.GITHUB_TOKEN }} is configured in repo settings on gitea.civilytics.org.

Option A.5: CORS Proxy for Git Pages

If you can't deploy PHP, create a simple reverse proxy in nginx or use an edge function that:

  1. Accepts ?target=/api/v1/... as a query parameter
  2. Forwards the request to `https://crdc-api.civilytics.org/api/v1/...
  3. Returns the response with Access-Control-Allow-Origin: *
  4. Set VITE_PROXY_URL at build time to point at this endpoint.

Option B: Docker (self-hosted fleet) — includes CORS proxy nginx config:

docker compose up -d    # builds + serves on localhost:8080

# Or build manually for your registry:
docker buildx build --platform linux/amd64 -t registry.civilytics.org/crdc-demo:latest .
docker push registry.civilytics.org/crdc-demo:latest

The container is ~5MB (nginx Alpine) and serves static files with immutable cache headers. A /healthz endpoint supports container orchestration health checks.

Environment Variables

Variable Default Description
VITE_API_BASE https://crdc-api.civilytics.org/api/v1 Override to point at a staging API (set in Dockerfile build or docker-compose)

Visual Design

This app follows the Civilytics visual identity as defined in:

  • theme/_tokens.scss — colors, typography, spacing tokens
  • social_media_posts.md — chart patterns from published social media figures
  • white_paper.qmd / R/paper_figures.R — white paper figure builders

Key design decisions:

  • Color palette: Warm paper background (#FAF7F2), civic navy text (#0E1A2B), ember accent (#C25311)
  • Data viz colors: Teal, plum, moss, brass from the supporting palette
  • Fonts: Libre Franklin/Inter for display and body, JetBrains Mono for code
  • Chart patterns: Pointrange comparisons (model vs. observed), ridgeline-style density bars, faceted small multiples

Citation & Attribution

The CRDC School Arrest Rate API: Knowles, J.E., & Miller, H. (2025). CRDC School Arrest Rate API v1. Civilytics Consulting. https://crdc-api.civilytics.org/api/v1/

Data source: US Department of Education, Office for Civil Rights. Civil Rights Data Collection (2021-22).

This research was supported by a grant from the American Educational Research Association which receives funds for its "AERA Grants Program" from the National Science Foundation under NSF award NSF-DRL #1749275. Opinions reflect those of the author and do not necessarily reflect those AERA or NSF.

S
Description
Demonstration web app for the CRDC School Arrest Rate API — explore school-based arrest rates by U.S. district with Bayesian model comparisons.
Readme
16 MiB
Languages
JavaScript 95.5%
CSS 3.2%
PHP 0.7%
Dockerfile 0.4%
HTML 0.2%