Content Architecture
What started as a single-page site has grown into 190+ statically generated routes covering research articles, teaching materials, bibliography entries, results dashboards, persona quick-starts, organizational resources, and project documentation. This page explains how we organize that content, keep it maintainable, and ensure it performs well at scale.
Static Export Architecture
TABS uses Next.js with output: 'export' - every page is pre-rendered to static HTML at build time. There is no server. The entire site is a collection of HTML, CSS, and JavaScript files hosted on GitHub Pages.
Why Static Export?
- Performance - pages load instantly from a CDN with no server round-trip
- Security - no server to compromise; the attack surface is minimal
- Cost - GitHub Pages hosting is free for open source projects
- Reliability - no database, no server processes, no downtime
- Simplicity - deployment is copying files; rollback is redeploying a previous build
Trade-offs
Static export means we cannot use some Next.js features:
- No API routes - external APIs (Qualtrics, Prolific) are called from GitHub Actions workflows instead
- No server-side rendering - all data must be available at build time or fetched client-side
- No Next.js Image optimization - we use standard
<img>tags with theassetPath()helper
Site Organization
Content is organized into logical sections, each with a distinct purpose:
| Section | Pages | Purpose |
|---|---|---|
| Research Articles | 16 | Two branches of original research (8 articles each, including branch introductions) |
| Bibliography | 44 | Auto-generated citation pages: 22 individual-adoption + 21 organizational-adoption + 1 comprehensive index |
| Making of TABS | 51 | Architecture, integrations, AI-assisted development, mind maps, SEO, validity checks, and operational playbooks |
| Results & Data | 27 | Live and CRP-2026 dashboards: descriptive, reliability, factor analysis, sensitivity, findings, top barriers, glossary, and reproducibility |
| Technology Adoption Series | 13 | Teaching workshop pages: presentation viewer, handouts, slide tracks |
| Persona Quick-Starts | 12 | Index plus 11 role-specific landing pages dynamically generated under /start/[role] (CEO, CFO, CIO, CTO, CISO, COO, CMO, CSO, CHRO, CRO, Other) |
| Organizational Resources | 5 | Role-grouped guidance under /for-organizations |
| Concept Mapping & Lit Review | 5 | /concept-mapping (3) plus /lit-review-complex and /lit-review-mind-map |
| Policy Pages | 6 | Privacy, cookies, terms, security acknowledgements, vulnerability disclosure, contribution policy |
| Core Pages | 12+ | Homepage, barriers (3), about, FAQ, media, survey, survey-complete, get-involved, technology-adoption-models, tabs-presentation |
Data-Driven Content
Repetitive content is stored as structured data in src/data/ and rendered by reusable components. This prevents duplication and makes bulk updates straightforward:
- Team members - JSON files in
src/data/team/, aggregated byteam.ts - FAQs - JSON files in
src/data/faqs/, aggregated byfaqs.ts - Testimonials - JSON files in
src/data/testimonials/ - Impact metrics - live data in
src/data/impact.json, updated by the daily Google Analytics workflow - Survey response funnel - Qualtrics response counts in
src/data/qualtrics-metrics.jsonand the Prolific submission funnel insrc/data/disposition-summary.json, both surfaced on /results/survey-stats - Article series - navigation ordering and metadata in
technology-adoption-models-series.ts
SEO Strategy
Every page is optimized for search engines through several mechanisms:
- Kebab-case URLs - all route folders use hyphens (
/privacy-policy, not/PrivacyPolicy), following Google's recommendation - Metadata on every page - title, description, and canonical URL via Next.js Metadata API
- Dynamic sitemap - generated at build time with all 190+ routes (including parameterized routes such as the 11
/start/[role]persona pages and the teaching-series slide pages under/technology-adoption-series/[slide]), correct change frequencies, and priorities - Robots.txt - programmatically generated to allow search engine crawling
- Semantic HTML - proper heading hierarchy (h1 â h2 â h3), landmark regions, and structured content
- Canonical URLs - prevent duplicate content between custom domain and GitHub Pages deployment
Responsive Design
The site uses a mobile-first responsive design approach with Tailwind CSS:
- Mobile-first - base styles target mobile; breakpoints add desktop enhancements
- Breakpoints -
sm(640px),md(768px),lg(1024px),xl(1280px) - Mobile navigation - slide-out panel with overlay, replacing the desktop dropdown menu
- Flexible grids - content reflows from single column (mobile) to multi-column (desktop)
- Touch targets - buttons and links have minimum 44Ã44px touch areas on mobile
Shared Style System
With 16 research articles and 44 bibliography pages, visual consistency is essential. The src/lib/articleStyles.ts module exports shared Tailwind class constants used across all article-style pages:
import { ARTICLE_CLASSES, H1_CLASSES, H2_CLASSES }
from '@/lib/articleStyles'
// Every article page uses the same base classes:
<article className={ARTICLE_CLASSES}>
<h1 className={H1_CLASSES}>Page Title</h1>
<h2 className={H2_CLASSES}>Section Heading</h2>
</article>This means a single change to articleStyles.ts updates the typography, spacing, and layout of every article and bibliography page simultaneously.
Dual Deployment
The site is deployed to two locations simultaneously:
Custom Domain
technologyadoptionbarriers.org
No basePath - assets at root
GitHub Pages
<username>.github.io/<repo>/
Requires basePath via NEXT_PUBLIC_BASE_PATH
The assetPath() helper in src/lib/assetPath.ts automatically prepends the correct basePath to all asset URLs, making images and other static resources work in both environments.
Dynamic Routes (still static, but parameterized)
One source file can produce many statically-generated pages by pairing a Next.js dynamic segment with generateStaticParams(). We currently use this in two places:
- /start is a single hub page;
/start/[role]is one source file that emits 11 persona-specific landing pages at build time (CEO, CFO, CIO, CTO, CISO, COO, CMO, CSO, CHRO, CRO, plus a generic Otherfallback). Each persona's recommended next steps, framing, and example barriers come fromsrc/lib/personas.ts, not from individual hand-written pages. /technology-adoption-series/[slide]is one source file that emits a route for every slide in the teaching series (plus legacy URL aliases) at build time. The slide list is derived fromtechnologyAdoptionTeachingSeries.parts[].slidesinsrc/data/technology-adoption-teaching-series.ts.
Together these two parameterized routes are why the route count (190+) exceeds the source-file count (180+). Everything else on the site is a 1-to-1 source file to route mapping. We could templatize more (e.g., bibliography entries) but currently keep them as individual files so each entry can carry custom annotations alongside the auto-generated citation block.
190+ routes from 180+ source files, zero servers, one build step.