Skip to content

Resist Now

Resist Now homepage showing daily rotating briefs, issue navigation, and latest analysis cards across 16 policy areas
Civic Engagement & Advocacy

Resist Now

432 pages. One person. A civic engagement platform built in Astro that scales like a newsroom without hiring one.

Location Austin, TX
Platform Astro + Cloudflare Pages
Visit site

The Problem

The site started on WordPress. Publishing one brief took 45 minutes of formatting, linking, and cross-referencing. Stats went stale in pages no one remembered to update. Internal links broke silently. There was no way to match a brief to a relevant letter or call script.

16 policy areas. 50 states. News breaking daily. A traditional CMS would need a staff of 3-5. The budget was zero.

Why Astro. 432+ pages rendered statically for speed and SEO. MDX for editorial pages with interactive components. Zero framework runtime in the browser. Astro Content Collections with Zod schema validation enforce 24 fields per brief at build time. No React, no Vue, no hydration cost. Vanilla inline scripts handle filters, copy buttons, and map interactions. Full build: 10 seconds.

Why Cloudflare Pages. Free tier handles the traffic. Auto-deploy from GitHub push. Edge caching. No server to manage. GitHub Actions trigger scheduled builds for future-dated content, quarterly audits, and quality gates.

Admin dashboard showing 256 published briefs, 79% letter coverage, and content freshness heatmap

The taxonomy problem. 237+ briefs, 50 states, 12 learn pages, 7 series, and 135 letters all need to find each other. A single tag registry governs the vocabulary. Tags drive letter matching, brief-to-hub routing, state page filtering, and auto-linking. If a brief references a category that does not exist, the build fails before it ships.

Component architecture. 10+ reusable Astro components with zero framework dependencies. StateMap renders SVG choropleth maps from d3-geo Albers USA projections at build time. MoneyTrail visualizes funding flows with named actors and dollar amounts. ThenNow shows temporal change between sourced dates. Every component has sr-only fallback tables for screen readers. A research-backed content ordering framework governs how components are sequenced on every page type.

MoneyTrail component tracing $485 million from four organizations through state legislatures to $215 million in TV ad spending

What runs without staff. A rehype plugin generates 285 cross-links at build time. A prebuild script flags stats past their freshness interval. 36,075 internal links verified in under 2 seconds on every build. 25+ content quality rules enforced automatically. A scoring algorithm matches letters to briefs by category and tag overlap. Scheduled deploys publish future-dated content daily. The operator writes content. The system handles everything else.

Learn page with FunnelChart showing how bills narrow from 11,000 introduced to 274 signed into law

What We Built

Publishing Workflow

Problem: publishing one brief on WordPress took 45 minutes. Solution: briefs are Markdown files with structured frontmatter. Astro Content Collections validate 24 fields at build time. Future-dated briefs publish automatically via scheduled GitHub Actions deploys at 6 AM Central. Sitemaps regenerate on every deploy. Publishing time dropped from 45 minutes to under 15. The owner spends time writing, not formatting.

Governed Data System

Problem: stats go stale and nobody notices until a reader emails. Solution: 1,188 statistics stored in TypeScript with source URL, freshness interval, source tier (1-3), and last-verified date. Each stat has a slug. Pages reference the slug, not the number. Update one stat and every page using it rebuilds with the new value. The prebuild script flags stats past their freshness threshold. Before this system, the same number appeared hardcoded in 5 different pages with 3 different values.

Letter Matching System

Problem: action pages were a dead end. Readers landed, read a vague ask, and left. Solution: 135 letters stored as TypeScript data with call scripts, key facts, and Resistbot/Democracy.io send links. A scoring algorithm matches letters to briefs by category and tag overlap. The reader finishes a brief about Medicaid cuts and immediately sees a letter about Medicaid cuts with a call script and a one-tap send button. Coverage: 82% of briefs have a matched letter. Before: 0% had a relevant, copy-paste-ready letter.

Content Architecture

Five page types serve different reader intents. 16 issue hubs (2,000+ words, MDX with interactive components) earn search traffic on broad topics. 237+ briefs cover specific news with evidence and action. 50 state pages provide local context. 12 learn pages explain foundational concepts (gerrymandering, filibuster, judicial review). 7 editorial series package related briefs into narrative arcs with data visualization. Astro Content Collections with Zod schemas validate 24 frontmatter fields per brief at build time.

Data Visualization Components

10 reusable Astro components with zero framework dependencies. No React. No runtime JS beyond vanilla inline scripts for hover/click interaction. StateMap renders SVG choropleth maps from d3-geo projections at build time. MoneyTrail visualizes funding flows with named actors and dollar amounts. ThenNow shows temporal change between specific dates. BarChart uses Chart.js. ComparisonTable, ProofPoint, WhyItMatters, and FunnelChart are pure HTML/CSS. Each component has sr-only fallback tables for screen readers.

Quality Gates

36,075 internal links checked in under 2 seconds on every build. A content quality script validates 25+ rules: heading length, paragraph density, link text, letter word count, source format. A ratchet system tracks warning count. New content cannot increase warnings above baseline. Old content is grandfathered. 7 GitHub Actions workflows run scheduled deploys, quality gates, event feeds, and quarterly audits. Full build: 432 pages in 10 seconds.

Auto-Linking

Problem: 237 briefs and 12 learn pages needed cross-links. Manual linking does not scale. Solution: a rehype plugin runs at build time, scans all prose, and auto-links first mentions of learn page topics. 285 links generated on every build. Adding a new learn page creates links across the entire site on the next deploy. The plugin skips headings, existing links, code blocks, and component props. Matching phrases come from the learn page registry, not a hardcoded list.

Content Ordering Framework

Every page follows a 6-phase arc backed by behavioral science: Hook (identifiable victim effect), Scale (cognitive load theory), System (accordion journalism structure), Impact (threat paired with efficacy per Witte's EPPM), Direction (solutions journalism research), Close (recency effect for CTA placement). The framework adapts by page type. Learn pages lead with definitions because the reader searched 'what is X.' Series pages lead with a human story because the reader is exploring. One set of rules governs all page types from a single source of truth.

The Numbers

237+
Evidence-backed policy briefs
1,188
Governed statistics with source tracking
135
Letters with call scripts and send tools
432+
Total pages across all content types
36,075
Internal links verified every build
285
Cross-links generated automatically at build time
7
GitHub Actions workflows
~10s
Full site build time (432 pages)

The Details

Services

Content Strategy & ArchitectureInformation Architecture & TaxonomyData Governance & Stat ManagementAI Pipeline Design & AutomationComponent Design & Data VisualizationWeb Development & PerformanceSEO & Content Quality SystemsEditorial Workflow Design

Built with

Astro 6Cloudflare PagesGitHub Actions (7 workflows)Claude API (Sonnet + Haiku)Tailwind CSS v4Chart.js + Pure SVGJSON-LD (Organization + Article)Pagefind (site search)Resistbot + Democracy.io