world-2026-hub/DEVELOPMENT.md

6.6 KiB
Raw Blame History

Developer guide — World Cup 2026 Hub

Everything you need to run, understand, deploy and maintain the site. For a non-technical overview of what the project is, see the README.

The app is a static single-page app: one index.html, vanilla ES-module JavaScript, and JSON files as the only "database". There is no backend, no build step, no bundler, and no framework. You maintain the site by editing JSON — the code never needs to change to update scores or teams.


Run locally

python -m http.server
# then open http://localhost:8000

Any static file server works. A server is required — opening index.html directly from disk fails, because browsers block fetch() of local JSON files (file://).


Project structure

worldcup2026/
├── index.html              SPA shell (header, tabs, hero, panels, modal root)
├── manifest.json           PWA manifest · favicon.ico
├── assets/
│   ├── css/                style.css · bracket.css · stats.css · animations.css
│   ├── js/                 app.js (entry) · schedule.js · groups.js · bracket.js
│   │                       stats.js · modal.js · stadiums.js · storage.js
│   │                       i18n.js · calendar.js
│   ├── images/             flags/*.svg · stadiums/*.svg
│   └── icons/              PWA app icons + favicons
└── data/                   ← the only thing you edit to maintain the site
    ├── teams.json          48 teams: { id, name, flag }
    ├── groups.json         { "A": [4 team ids], … } × 12
    ├── matches.json        104 matches (UTC times; knockout uses bracketRef)
    ├── results.json        one entry per match: scores + status (+ penalties, + optional stats)
    ├── stadiums.json       name, city, capacity, image, IANA timezone
    └── bracket-config.json Round-of-32 slots + best-third assignment

Maintaining the data

The data is real World Cup 2026 data, refreshed as the tournament progresses. Everything the site shows is derived from the six data/*.json files — no code changes required.

The day-to-day refresh routine is documented in how-refresh-data.md; the original mock → real migration is kept as a schema reference in how-update.md.

Updating a result

Edit the match's entry in data/results.json:

{ "matchId": 74, "homeScore": 1, "awayScore": 1,
  "penalties": { "home": 4, "away": 3 }, "status": "finished" }
  • status: scheduledlivefinished. Standings and the bracket only count finished matches.
  • penalties is optional — only for knockout matches decided on penalties.

Adding / changing matches

data/matches.json. All times are UTC (the UI converts to local or stadium time). Group matches carry homeTeam/awayTeam; knockout matches carry a bracketRef (R32-1R32-16, R16-1…, QF-…, SF-…, THIRD-PLACE, FINAL) and their teams are resolved automatically from the standings.

After the group stage: fill the third-place slots

data/bracket-config.json is the only file to edit once the 8 best third-placed teams are known. Map each slot to a group letter (per FIFA's official combination table):

"thirdPlaceAssignment": { "1": "D", "2": "F", "3": "B", "…": "…" }

A slot's team becomes standings[group][3rd]. Slots left null show a "Best 3rd #N" placeholder. The 16 round32 entries define the bracket order (array position = bracket position) — they normally never change.

Teams, stadiums, images

  • teams.jsonflag is a path relative to assets/images/ (e.g. flags/bra.svg).
  • stadiums.jsontimezone must be a valid IANA name (e.g. America/Mexico_City); it drives the "stadium time" display and stays correct across DST.
  • Replace the SVGs in assets/images/ with new artwork keeping the same file names (or update the JSON paths).

UI labels (EN / PT)

Every user-facing string goes through t(key) — add new labels to both dictionaries in assets/js/i18n.js. Data values (team/stadium names, cities) come from JSON and are not translated.


Local storage

The app never modifies the JSON data. User state lives in the browser under wc2026_* keys:

Key Content
wc2026_simulation { "R32-6": { "winner": "FRA", "score": "2-1" }, … }
wc2026_favorites ["BRA", "MEX"]
wc2026_prefs { "lang": "en"|"pt", "timeMode": "local"|"stadium", "lastTab": "bracket", … }

Clearing site data resets picks, favourites and preferences.


Deployment

The live site is deployed automatically to Hostinger over FTP by GitHub Actions (.github/workflows/deploy.yml) on every push to master, and is served at lucaskalil.com/worldcup2026.

  • The workflow needs three repository secrets: FTP_SERVER, FTP_USERNAME, FTP_PASSWORD.
  • Development/documentation files (README.md, DEVELOPMENT.md, docs/, .agents/, the specs and how-*.md) are excluded from the upload — only index.html, assets/ and data/ reach the site.

Because every asset and data path in the code is relative (never starting with /), the same folder also works unchanged on GitHub Pages or any other static host — just publish the directory. When editing paths, always keep them relative.


Acceptance criteria (spec §18)

  • All matches are loaded via JSON
  • All results are loaded via JSON
  • The bracket is generated dynamically (config + standings + winner pairing)
  • Works on a static host (all paths relative, no server-side code)
  • Works on desktop and mobile (≤767 / 7681100 / 1100+ breakpoints)
  • Allows knockout-stage simulation (persisted, never mutates JSON)
  • Smooth animations (entry, hover, bracket line-draw; reduced-motion safe)
  • No backend dependency — fully static

Performance: total JS ≈ 74 KB across the ES modules (budget: < 300 KB), no external dependencies, no blocking third-party requests.


Roadmap (spec §19)

Dark/light theme, real-time statistics via a results API, FIFA ranking integration, World Cup history, expanded player-level stats, and push notifications. A service-worker offline mode (PWA "Tier 2") is designed but deliberately deferred — see .agents/issues.md.


Internal documentation

Deeper architecture notes, decisions and gotchas live in the .agents/ folder (project-map.md, project-memory.md, stats-screen-plan.md, issues.md, TODO.md). Read those before making significant changes.