A configurable, GitHub-Pages-hosted catalog and resource site, managed entirely through GitHub. There is no server, no database and no CMS login — Jekyll builds the site on GitHub Actions and deploys it to Pages, and every content change flows through a GitHub issue and a pull request.
This repository is shipped configured as an AI Use Case Catalog, where public-sector teams share AI use cases — source repos, cloud deployments, vendor solutions, write-ups. The identity it ships with is a generic one; nothing here is tied to a particular organization. The same template can be re-pointed at other uses without touching layout code: a project/asset portal, a cohort or training-program portal where teams publish outputs, an event calendar, or a curated resource library. See docs/configuration.md for how to retarget it.
Live demo: crypticpy.github.io/phct — a landing page introducing the template, with four complete sites built from this same repository behind it. Each is a real build with its own fields, filters, colours and sample content, search and submission form included:
- AI use case catalog — the configuration this repository ships with.
- Cohort portal — a training program's teams, cohort by cohort, with the events and cohorts modules on.
- Resource library — shorter entries and more of them: guides, toolkits and datasets.
- Blank catalog — the smallest useful starting point, ready to be renamed.
Where the coalition catalog lives: the health-coalition catalog this template grew out of is now its own repository, crypticpy/bchc-ai-use-case-catalog. This repository stays the generic template — see docs/ecosystem.md for the full map of the repository family.
| Catalog | Entry |
|---|---|
![]() |
![]() |
- GitHub-as-CMS. Anyone can propose an entry through a web form or a GitHub issue. Automation turns the issue into a pull request with the entry already drafted — screenshots downloaded into the entry folder and all — and a maintainer reviews and merges it.
- Schema-driven content model. One file,
_data/schema.yml, defines every field an entry has, and how it is presented: which fields reach a catalog card and in which slot, which become filters, which appear in the sidebar, what each option's short label, icon and tone are. The submission form, the issue template, the cards, the filter rail, the search index, the wizard defaults and the validator all derive from it. Seedocs/content-model.md. - Search that reads the whole write-up. Every entry is indexed per section, so a suggestion names the section it matched in, shows the sentence around the term and links straight to that heading; related entries are chosen by how distinctive a shared value is. An Atom feed of the newest entries lives at
/catalog/feed.xml. - Built for evaluation, not browsing. Cards are laid out to answer "could my team reuse this?" in about two seconds: a result line, a taxonomy chip family, and a signal strip for skills needed, data sensitivity, audience and readiness. Filters sit beside the results, restore from the URL, and announce their counts.
- Compare and print a decision. Shortlist up to three entries from the catalog and read them field by field at
/compare/, with the rows they agree on folded away and the shortlist in the URL so it pastes into an email. One button prints the comparison — or any entry page — as a clean brief with the links spelled out and a source stamp. Seedocs/compare.md. - Screenshots and links as first-class fields. An
imagesfield gives an entry a gallery with a keyboard-navigable lightbox and honest alt text; alinksfield carries labelled resources — a shared drive folder, a recorded demo, a vendor page — without needing a field per link. - Two configurators. A no-terminal setup wizard at
/setup/on the deployed site, and an equivalent CLI wizard (npm run setup). Both offer starting presets (AI use case catalog, cohort/program portal, resource library, blank) and write the same configuration files from the same shared logic. - Governance on the site, not in a PDF. A
governancemodule publishes how review works (tiers, targets, criteria), who does what, and the standing policies — privacy, licensing, data governance, accessibility, maintenance, appeals, conduct — from one data file,_data/governance.yml. Deprecated entries are kept for the record rather than deleted, a contributor guide walks submitters through review from their side, and aCODE_OF_CONDUCT.mdcovers everyone. A monthly workflow counts submissions, publications, contributing organizations and review turnaround from the repository's own issues and pull requests into_data/metrics.json, and the page shows them as "How the catalog is doing" — no analytics vendor involved. - Modules. Turn catalog, submit, carousel, stats, events, cohorts, resources and governance on or off independently; navigation and the home page adapt automatically, and pages under a disabled module are dropped from the build.
- Theming. Colors, fonts and corner rounding live in
_data/theme.ymland become CSS variables consumed by Tailwind — no CSS editing required for a rebrand. Every colour has one semantic job, so a re-skin cannot quietly break contrast. - Accessibility as a build rule, not a pass. Nothing is signalled by colour or icon alone, every control has a visible focus ring and a ≥3:1 border, filter changes are announced once, and the whole catalog still works with JavaScript disabled.
- CI content pipeline. Front-matter and file-size validation on every pull request, automatic thumbnail generation from uploaded PDFs, and workflows that scaffold cohort years, events and schedule updates from issues. Scaffolded content pull requests are labelled and title-prefixed at creation so code-review bots can be told to skip them — entries are data, not code; the admin guide documents the exact Sourcery dashboard setting.
Supported scale: releases enforce the complete build, payload, DOM, and interaction budgets
through 100 published entries. The deterministic release matrix also characterizes 500 entries as
the next target and 1,000 as a stress case; see docs/performance.md before
planning a larger catalog.
Allow roughly 15 minutes of hands-on setup. GitHub's initial builds can bring elapsed time to about 40 minutes; the detailed no-terminal tutorial includes every wait and recovery path.
Setting this up with an AI coding agent? Point it at
AGENTS.md— the setup runbook written for agents: what to ask you first, which files are yours to edit versus template-owned, and the settings only a human can click.
- Use this template on GitHub (or clone it) to create your own repository.
- Turn on the GitHub settings the automation needs. They are a handful of clicks in Settings and the Actions tab, and nothing below works until they are on.
docs/launch.mdlists each one with its click path and what breaks without it;docs/admin-guide.mdhas the same settings as a reference table, alongside the optional variables and secrets. - Configure the site — open
/setup/on the deployed site for the browser wizard (no terminal), or runnpm ci && npm run setuplocally. Both write_data/site.yml,_data/theme.yml,_data/schema.yml,_data/navigation.yml,_config.yml,.github/ISSUE_TEMPLATE/new-entry.ymland.github/ISSUE_TEMPLATE/config.yml, from the same four presets. With no terminal, paste the wizard's three_data/*.ymlfiles into the Apply setup issue form and the automation opens the pull request for you. - Clear the demo content — ten fictional organizations, a sample events calendar, a sample cohort. Until they are gone every page carries a Demo content banner saying so.
npm run eject:samplesremoves it all, turns the banner off and switches thegovernancemodule off until you have rewritten_data/governance.ymlin your own words; the Apply setup issue has a checkbox that does the same thing. - Commit and push.
Build & Deploypublishes tohttps://<owner>.github.io/<repo>/, working outurl/baseurlon its own (root domain for a<owner>.github.iorepo or aCNAMEfile,/<repo>otherwise); an expliciturlin_config.ymlalways wins.
Each of those steps has a detail you will want on the day: docs/launch.md is the full tutorial — the same path with what breaks if you skip a step, a first test submission end to end, and the pre-launch checklist.
- A contributor fills out the Submit form (
/submit/) — a stepped form that walks the schema's field groups one section at a time, with a short form that hides every optional question and a live preview of the card their entry will produce — or opens the Submit a use case GitHub issue form directly. - The form data becomes a GitHub issue labelled
content:new-entry. Screenshots are dragged onto the issue at this point. - The
New entry from issueworkflow runsscripts/new_entry_from_issue.mjs, which reads_data/schema.yml, downloads any attached images intocatalog/<slug>/screenshots/(up to 8 files, 15 MB total, PNG/JPEG/GIF/WebP), and opens a pull request containingcatalog/<slug>/index.md. The pull request body carries the maintainer checklist — the review criteria from_data/governance.yml, the mechanics, the review-status flip — and, when an answer matches a field'sescalate_onlist in the schema (an unticked PII/PHI attestation, PHI under Data it touches, a public-facing audience), a Closer review block and thereview:data-governancelabel. - Larger attachments — a
deck.pdf— are added to the entry folder directly in that pull request. Anyfilefield flaggedthumbnail: truegets athumb.jpgrendered from its first page automatically by theGenerate entry thumbnailsworkflow. - A maintainer reviews the entry against the checklist in
docs/admin-guide.md— plain language, no protected data on screen, alt text present, links that open for outsiders — and merges. The site rebuilds and the entry is live within a couple of minutes. - Existing entries can also be edited directly on GitHub — every entry page has a Suggest an edit on GitHub link. When an edit lands on
main, the deploy stamps the entry'supdated:date itself.
The shipped AI use case schema has 40 fields in eight groups. In outline:
| Group | Fields |
|---|---|
| About | title, one-sentence summary, result in one line, organization, what is being shared, use case category, area of work, stage, review status (maintainer-only) |
| How it's built | how AI is involved, types of AI, AI tools & models, where it runs, vendor or partner |
| Reuse | skills needed to set it up, readiness, source code, live demo, documentation, other resources, screenshots, slide deck |
| Sharing & licensing | license, access terms, portability, portability notes |
| What it took | cost band, running cost, procurement, approvals, equity note |
| Data & access | no-PII/PHI attestation, data it touches, data sources, who sees the output, data governance notes |
| Contact | contact name, contact title, contact email |
| The story | the full write-up, which becomes the page body |
Every one of those is a line in _data/schema.yml and none of them is named anywhere else — rename, remove or replace the lot for a different subject and the forms, filters, cards and validator follow. docs/content-model.md documents each property, the images and links shapes, and how to design a taxonomy that people actually filter by.
Cohorts and events follow the same issue → automation → pull request pattern (content:new-year, content:schedule, content:event-attachments, and the plain Add event details issue). See docs/admin-guide.md for the maintainer side of all of this.
- Branding, contact info, module toggles, home page copy —
_data/site.yml - Colors, fonts, corner rounding —
_data/theme.yml - Header navigation —
_data/navigation.yml(regenerated by the wizards from your modules, but hand-editable) - The entry content model —
_data/schema.yml(seedocs/content-model.md) - Events, cohort years, resource library —
_data/events.yml,_data/cohorts/<year>.yml,_data/resources.yml - Search synonyms, tag aliases, facet landing pages —
_data/search.yml(seedocs/search.md)
Full reference for every setting: docs/configuration.md. Every other document, and who each one is for: docs/index.md.
A fork is a copy, not a subscription: template releases do not reach you on their own. .gitattributes marks everything a deployment owns — _config.yml, _data/*.yml, content, images, and local operations records. The protected updater verifies two immutable PHCT releases, reconciles the complete template-owned tree to the target, and leaves those deployment paths untouched even though GitHub template repositories do not share commit history with PHCT.
git remote add template https://github.com/crypticpy/phct.git
git fetch template --tags
npm run upgrade:check -- --to v1.9.0 # read-only: what this exact release changes, in two listsThe whole protected-update and manual-recovery recipe: docs/upgrading.md.
The exact supported versions live in .ruby-version, .node-version, Gemfile.lock, and
packageManager in package.json. mise install can install both runtimes from mise.toml; other
version managers can read .ruby-version, .node-version, or .nvmrc.
mise trust mise.toml # after inspecting the pinned runtime definitions
mise install # optional; use any manager that installs the pinned versions
gem install bundler -v "$(cat .bundler-version)"
bundle install
npm ci
npm run doctor # plain-language runtime, dependency, ownership, and generated-file checks
npm run dev # http://127.0.0.1:4000/ with live reload — Tailwind watcher + jekyll serve in one terminal,
# regenerating the schema-derived files whenever _data/schema.yml or _data/site.yml changes
npm run build # generate schema-derived files, build CSS, build the Jekyll site into _site/Mise requires the per-clone trust step before reading mise.toml. If project-configuration trust
is not permitted in your environment, use another manager with the checked-in version files.
npm run dev -- --port 4001 --host 0.0.0.0 changes where it listens; output is prefixed [css], [jekyll] and [gen], and Ctrl-C stops everything. npm run serve and npm run watch:css still exist if you want the pieces separately.
Other useful scripts:
npm run setup # configuration wizard (see Quick start)
npm run generate # regenerate issue forms/routing + configurator defaults, and sync _config.yml from _data/site.yml
npm run validate # parse all _data/*.yml and run the front-matter / file-size checks CI runs on pull requests
npm test # Node unit tests; `npm run test:ruby` for the Ruby validators
npm run test:build # build every preset and module combination and check the rendered copy (needs Ruby; minutes, not seconds)
npm run verify # every non-browser release gate, including all preset/module builds
npm run a11y # pa11y-ci (WCAG 2 AA) against _site served on :4173 — see CONTRIBUTING.md
npm run lighthouse # Lighthouse CI against the same local serverContributing to the template itself? Start with CONTRIBUTING.md and ARCHITECTURE.md.
MAINTAINERS.mdrecords decision rights, release ownership, and the required backup-maintainer role.SUPPORT.mdexplains public support routes and response expectations.SECURITY.mdis the private vulnerability-reporting route; never publish a credential, personal information, PHI, or exploit detail in an issue.THIRD_PARTY_NOTICES.mdrecords licenses, provenance, and required attribution for the JavaScript, icons, and fonts shipped with PHCT.docs/maintaining.mdis the release, downstream-update, rollback, backup/restore, incident, and succession runbook.
_config.yml Jekyll build mechanics (title/description fall back to _data/site.yml)
_data/ site.yml, theme.yml, schema.yml, navigation.yml, governance.yml, events.yml, resources.yml, cohorts/<year>.yml,
metrics.json (written monthly by the Catalog metrics workflow)
_layouts/, _includes/ schema-driven templates (entry cards, filters, field rendering, etc.)
_plugins/ schema_filters.rb (card/weight/group/option_meta rules), theme_filters.rb, search_index.rb (/search.json), events.rb, modules.rb,
showcase.rb (only active in a showcase build)
assets/js/configurator/ shared logic behind both configurators (core.js, presets/, setup-page.js + steps/ + wizard/)
assets/js/submit.js turns the /submit/ form into a pre-filled GitHub issue URL
scripts/ setup.mjs, generate.mjs, validate.mjs, build_showcase.mjs, and the issue-to-PR automation scripts
.github/workflows/ pages, validate, quality (a11y + Lighthouse), smoke, new-entry, thumbnails, new-year, new-event, update-schedule, update-event-attachments,
verification-sweep, metrics
.github/ISSUE_TEMPLATE/ new-entry.yml is generated — do not hand-edit it, run `npm run generate`
_showcase/<id>/ sample content for the live examples on the template's own deployment; _data/showcase.yml is the
landing page's copy. Both are removed by `npm run eject:samples` (docs/showcase-plan.md)
catalog/<slug>/index.md published entries; screenshots live in catalog/<slug>/screenshots/
(ten sample entries ship with the template, marked `sample: true`)
cohorts/<year>/ cohort landing page + event pages (module: cohorts)
governance/ /governance/ — review process, roles and policies from _data/governance.yml (module: governance)
styleguide/ /styleguide/ — live rendering of the design system against your theme (noindex)
docs/ index.md (start here), launch.md, admin-guide.md, contributor-guide.md, incidents.md,
configuration.md, content-model.md, search.md, images.md, upgrading.md, decisions.md,
glossary.md, design-brief.md, design-system.md, roadmap.md
quality/ pa11y/Lighthouse configuration, dependency-license policy, and the digest-checked vendored-asset inventory
test/ configurator/, plugins/, scripts/ and fixtures/ — Node's test runner + Ruby minitest
ARCHITECTURE.md how the pieces fit; CONTRIBUTING.md — working on the template itself; SECURITY.md;
CODE_OF_CONDUCT.md how contributors and reviewers engage (linked from the governance page)
PHCT is MIT-licensed — see LICENSE. Bundled third-party files retain the licenses and
attribution recorded in THIRD_PARTY_NOTICES.md.


