Public repository of the code for the 2025 Web3Privacy Now Congress held in Mumbai - built with simple web-standard HTML and CSS
- /fonts/ = folder that holds all the fonts used by the website
- /img/ = folder that holds all images used by the website
- /data/ = single source of truth for people, projects, and the schedule — see PEOPLE DATABASE and SCHEDULE
- /schedule/ = the congress agenda page — see SCHEDULE
- /backup/ = retired JSON files, kept only as a rollback fallback — see PEOPLE DATABASE
- /scripts/ = one-off Node scripts (e.g. the people-data migration script)
- index.html = what-you-code-is-what-you-get HTML
- input.css = Tailwind entry point — imports Tailwind and
w3pevent.css - output.css = compiled Tailwind output (auto-generated, do not edit)
- w3pevent.css = custom CSS classes — edit this file for styling changes
- package.json = Node.js config with
devandbuildscripts for Tailwind - .nvmrc = pinned Node.js version
- favion.png = website favicon (should be placed in main folder of hosting)
The site uses Tailwind CSS v4 with a build step that compiles input.css into output.css. The entry file (input.css) imports Tailwind and the custom stylesheet (w3pevent.css).
Prerequisites: Node.js (see .nvmrc for the recommended version)
npm installDevelopment (watches for changes and rebuilds automatically):
npm run devProduction build (minified output):
npm run buildAll speakers, community members, and project/partner members are now driven from a single source of truth: data/people.json. This replaced three previously separate, manually-maintained lists (the "2026 Speakers" section on the homepage, community/comminity.json, and projects/projects.json's inline member data) that used to drift out of sync with each other. Add a person once in data/people.json and they can appear correctly everywhere they're supposed to — no more copy-pasting the same person into multiple files.
data/people.json— flat array of every person. This is what you edit to add/update someone.data/projects.json— array of projects/partner orgs. Each project references its members bymemberIds(an array of personids frompeople.json) instead of embedding full member data.backup/projects.jsonandbackup/comminity.json— the old, retired data sources. Kept only as a rollback reference; the live site does not read them. Do not add new people here.scripts/migrate-people.mjs— the one-off script used to generatedata/people.jsonanddata/projects.jsonfrom the oldbackup/*.jsonfiles. You shouldn't need to run this again for day-to-day edits; it's kept for reference/rollback purposes.
{
"id": "kurt-opsahl",
"name": "KURT OPSAHL",
"photo": "img/kurtopsahl2.jpg",
"xUrl": "https://x.com/kurtopsahl",
"tagline": "Former Deputy Executive Director and General Counsel of EFF",
"role": "Associate General Counsel for Cybersecurity and Civil Liberties Policy",
"orgName": "Filecoin",
"orgUrl": "https://fil.org",
"industries": ["Privacy", "Digital Rights"],
"speaker2026": true,
"speakerOrder": 0
}| Field | Meaning |
|---|---|
id |
Stable, unique, kebab-case slug (e.g. kurt-opsahl). Referenced by data/projects.json's memberIds. If two different people share the same name, give the second one a suffixed id (e.g. wei-dai-2). |
name |
Display name, as shown on cards. |
photo |
Path to the photo, relative to the site root, with no leading ./ or ../ (e.g. img/kurtopsahl2.jpg, not ./img/... or ../img/...). Each page prefixes it correctly at render time. |
xUrl |
Link to the person's profile (X/Twitter, LinkedIn, etc.). Leave as "" if unknown — the card will render without a link instead of a broken one. |
tagline |
Short one-line description shown under the name. |
role |
Their role/title (e.g. "Founder", "Research Partner"). |
orgName / orgUrl |
Their organization's name and website. |
industries |
Array of category tags (e.g. ["Privacy", "Cryptography"]) — powers the filter pills on /community/. Defaults to ["Privacy"] if you leave it as []. |
speaker2026 |
Set to true to make this person appear in the homepage's "2026 Speakers" section. |
speakerOrder |
A number controlling their position in the "2026 Speakers" grid (lower = earlier). Only matters when speaker2026 is true. |
Add a new speaker to the homepage: add a new object to data/people.json with speaker2026: true and a speakerOrder (e.g. one higher than the current max). They'll automatically show up on the homepage, and also in /community/ and the homepage's community carousel — no HTML editing required.
Add someone to a project/partner card on /projects/: make sure they exist in data/people.json (add them if not), then add their id to that project's memberIds array in data/projects.json.
Update someone's photo, title, or link: edit their single entry in data/people.json — the change applies everywhere they're shown.
Remove someone from the "2026 Speakers" section without deleting them entirely: set speaker2026: false (or remove the field) instead of deleting their people.json entry, so they're still available for /community/ and any project they belong to.
- Homepage "2026 Speakers" section and the "Join the Community" carousel:
fetch('./data/people.json'), rendered byinitSpeakers2026()/initCommunityCarousel()inindex.html. /community/index.html:fetch('../data/people.json')./projects/index.html:fetch('../data/projects.json')+fetch('../data/people.json'), then resolves each project'smemberIdsagainst the loaded people list.
The /schedule/ page shows the congress agenda as a filterable list, built the same way as /projects/ and /community/ (fetch JSON, render cards, no build step).
data/schedule.json— array of sessions, one object per talk/panel/fireside chat.schedule/index.html— the page itself: fetches../data/schedule.jsonand../data/people.json, resolves each session's speakers, and renders the list with two independent filters (Stage, Format).
{
"id": "zk-identity-panel",
"title": "Zero-Knowledge Identity: Who Actually Ships This?",
"description": "Short 3-4 sentence summary, shown on the card.",
"descriptionFull": "Full description shown in the modal (can be longer).",
"stage": "Pearl",
"format": "Panel",
"startTime": "11:00",
"endTime": "11:45",
"date": "2026-11-02",
"speakers": [
{ "id": "alexander-urbelis", "sessionRole": "Moderator" },
{ "id": "anita-gurumurthy", "sessionRole": "Panelist" }
]
}| Field | Meaning |
|---|---|
stage |
Free text, not a fixed list — the Stage filter is generated from whatever values appear in the data, so new stages just work. |
format |
"Talk", "Panel", or "Fireside Chat" get their own color; any other value falls back to a neutral style. |
startTime / endTime |
"HH:MM" 24h strings. Sessions are sorted by startTime. |
speakers |
Array of { id, sessionRole? }. id must exist in data/people.json (same pattern as data/projects.json's memberIds). sessionRole (e.g. "Moderator", "Panelist") is this person's role in this session only — it's shown as a tag on their photo in the session's modal, separate from their general role/org in people.json. Omit sessionRole for formats like Talk where it doesn't apply. |
Add a session: add an object to data/schedule.json. Reference speakers already in data/people.json by id — add the person there first if they don't exist yet.
Add a new stage or format: just use the new value in a session's stage/format — no other file needs to change. New formats render with a neutral color until given their own entry in formatClassMap in schedule/index.html.
- This repository is automatically deployed on Github Pages
- The website has a unique subdomain: c25bue.web3privacy.info
- congress.web3privacy.info redirects to this deployment
All HTML is licensed Creative Commons Zero v1.0 Universal
The CSS used leverages TailwindCSS which is licensed under MIT
Please note that even though the code is open-sourced - the names, content, and images within may well have their own licenses from their respective owners.