The Global Anti-CCP Resistance Hub is a static React single-page application (SPA) built with Vite 7, React 19, and Tailwind CSS. All content is bundled at build time from JSON data files — no server is required to serve the site.
src/data/*.json ─────┐
src/pages/*.tsx ─────┤ npm run build dist/
src/components/*.tsx ─┼─ ────────────── → ├── index.html
src/locales/*.json ──┤ (Vite + Rollup) ├── assets/
src/index.css ───────┘ │ ├── index-*.js (310KB / 99KB gzip)
│ ├── index-*.css (86KB / 15KB gzip)
│ ├── vendor-react-* (11KB)
│ ├── vendor-router-* (35KB)
│ └── [81 lazy chunks]
└── (static assets)
- 100% TypeScript: Entire codebase is TypeScript — 0 JavaScript/JSX files remain (360+ .ts/.tsx files)
- Manual chunks: React and React Router split into cacheable vendor bundles
- Lazy loading: 81 sub-components loaded on demand (all page bundles under 50KB)
- Base path: Configurable via
VITE_BASE_PATHenv var — defaults to/for Cloudflare, set to/global-anti-ccp-resistance-hub/for GitHub Pages
React Router v7 handles all client-side navigation. Routes are defined in src/App.tsx.
/ → Dashboard
/intelligence → IntelligenceFeeds (3 tabs: Live Feeds, Regional, CCP Ops)
/prisoners → PoliticalPrisoners
/security → SecurityCenter (tabs: Overview, Tools, Guides, Quiz, Report)
/education → EducationCenter (tabs: Resources, Media, Guides, Tactics)
/take-action → TakeAction
/community → CommunitySupport
/resources → ResistanceResources
/directory → ResistanceDirectory
/data-sources → DataSources
/profiles → ProfilesIndex (links to all 15 profiles)
/profiles/:slug → Individual profile pages (15 total)
/campaigns → Redirect → /take-action
/communications → Redirect → /security
/tactics → Redirect → /education
/threats → Redirect → /intelligence
SPA routing on Cloudflare is handled by wrangler.jsonc (not_found_handling: "single-page-application"). On GitHub Pages, a 404.html redirect handles deep links.
All structured content lives in src/data/ as JSON files. Components import them directly — Vite inlines the data into the JavaScript bundle at build time.
src/data/
├── political_prisoners_research.json # 64 prisoners with status, dates, sources
├── sanctions_tracker.json # 47 sanctions across US/EU/UK/CA/AU
├── sanctioned_officials_research.json # 34 CCP officials under international sanctions
├── forced_labor_companies_research.json # 30 companies linked to forced labor
├── detention_facilities_research.json # 11 facilities with coordinates, capacity
├── timeline_events.json # 40 events from 1989–2026
├── security_center_data.json # Security tools and recommendations
├── data_sources.json # RSS sources and source categories
├── academic_experts_research.json # Expert researchers
├── human_rights_orgs_research.json # Organizations directory
├── international_responses_research.json # Government responses
├── police_stations_research.json # CCP overseas police stations
├── recent_news_research.json # Recent developments
├── recent_updates.json # Platform changelog entries
├── emergency_alerts.json # Active emergency alerts
├── live_data_feeds.json # Live data source configurations
├── live_statistics.json # Live statistics data
├── educational_modules.json # Educational content modules
├── legal_cases_research.json # Legal cases data
└── take_action_steps.json # Action step guides
- Verifiable: Every entry has source URLs that can be checked
- Testable: 3,602 automated tests validate data integrity (required fields, valid URLs, no CCP sources)
- No runtime dependency: Data is bundled into JavaScript — the site works offline after initial load
- Git-auditable: All changes go through PR review
App.tsx
├── Header (navigation, language selector, mobile menu)
├── Sidebar (desktop navigation — 7 items)
├── <Routes>
│ ├── Dashboard
│ ├── IntelligenceFeeds
│ │ ├── Tab: Live Feeds (RSS aggregation)
│ │ ├── Tab: Regional Status (HK, Xinjiang, Tibet, Taiwan)
│ │ └── Tab: CCP Operations (DetentionFacilities, PoliceStationsMap)
│ ├── SecurityCenter
│ │ ├── Tab: Overview
│ │ ├── Tab: Tools
│ │ ├── Tab: Guides (ChinaExitBan, WhistleblowerGuide)
│ │ ├── Tab: Quiz
│ │ └── Tab: Report (IncidentReportForm)
│ ├── profiles/:slug → Profile pages (5-tab layout each)
│ └── ... (other pages)
└── GlobalDisclaimer (footer)
All page-level components and heavy sub-components use React.lazy() + <Suspense>:
const Dashboard = lazy(() => import('./pages/Dashboard'));- LanguageContext — 8 locales, provides
t()translation function - ThemeContext — dark theme (terminal aesthetic is the only theme)
- SocketContext — lightweight no-op stub (Socket.IO was removed to save 107KB)
When VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY are configured:
src/services/supabaseClient.tscreates a Supabase clientsrc/services/supabaseService.tsprovides helpers for 4 form types:submitIncidentReport()→incident_reportstablesubmitVolunteerSignup()→volunteer_signupstablesubmitNewsletterSubscription()→newsletter_subscriberstablesubmitContactMessage()→contact_messagestable
When not configured, the client is null and forms display "Coming Soon" notices with links to real organizations.
Centralized system in src/utils/sourceLinks.ts for detecting CCP state media:
- 21 state media outlet names (e.g., Xinhua, CGTN, People's Daily, Global Times)
- 13 blocked domains (e.g., xinhuanet.com, cgtn.com, globaltimes.cn)
- 15 elevated-risk entries requiring extra scrutiny
- 4 utility functions:
isCCPSource(),isElevatedRisk(),getSourceRiskLevel(),validateSources() - 37 dedicated tests in
src/test/ccp-influence-detection.test.ts
3,602 tests across 192 files using Vitest + jsdom + React Testing Library.
| Category | Files | What it tests |
|---|---|---|
| Data integrity | 15+ files | JSON fields, valid URLs, no CCP sources, chronological order, cross-dataset consistency |
| Component tests | 60+ files | React components render, user interactions, form submissions |
| Accessibility | 3 files | ARIA labels, keyboard nav, contrast ratios, heading hierarchy |
| Design system | 1 file | Scans all TSX for prohibited CSS patterns (8 automated checks) |
| URL health | 2 files | No CCP domains, no insecure HTTP URLs in data, HTTPS validation |
| i18n | 1 file | All 8 locales have identical key sets |
| Security | 3 files | CSP headers, WebRTC leak detection, supabase key validation |
| Performance | 3 files | Bundle size budget, performance resilience, responsive layout |
| TypeScript purity | 1 file | Enforces 0 JS/JSX files, no @ts-nocheck, strict mode |
| Services | 4 files | Supabase service, live data service, data API, source links |
| Profile pages | 15 files | All 15 profile pages render with correct data |
| Other | 80+ files | Sitemap, manifest/PWA, routes, utilities, defensive coding, import hygiene |
See CLOUDFLARE_DEPLOY.md. Uses wrangler.jsonc for SPA routing. Global CDN, free tier.
Configured via .github/workflows/deploy.yml. Sets VITE_BASE_PATH=/global-anti-ccp-resistance-hub/ automatically. Triggered on push to master.
Run npm run build and serve the dist/ directory. Ensure your host serves index.html for all routes (SPA fallback).