Simple static page HTML, CSS, JS for world cup
Find a file
2026-06-25 22:04:38 -03:00
.agents docs: log daily refresh 24/06/2026 2026-06-24 23:03:49 -03:00
.github/workflows fix(deploy): correct FTP server-dir path for Hostinger 2026-06-13 23:30:33 -03:00
assets feat(stats): rotate tied teams in leader cards + new metric cards 2026-06-19 10:03:51 -03:00
data data: update 25/06/2026 08:47 TUNxNED 1x3 JPNxSWE 1x1 2026-06-25 22:04:38 -03:00
.gitignore ci: add GitHub Actions FTP deploy to Hostinger 2026-06-13 23:04:03 -03:00
complement-spec-worldcup2026-en.md docs: add project specification documents 2026-06-12 16:11:49 -03:00
favicon.ico feat(pwa): add Tier 1 support — installable app with manifest and icons 2026-06-16 15:09:34 -03:00
how-refresh-data.md docs: drop DATA_VERSION from workflow and project memory 2026-06-18 18:25:50 -03:00
how-update.md docs: log daily refresh 15/06/2026 2026-06-15 22:41:26 -03:00
index.html feat(pwa): add Tier 1 support — installable app with manifest and icons 2026-06-16 15:09:34 -03:00
manifest.json feat(pwa): add Tier 1 support — installable app with manifest and icons 2026-06-16 15:09:34 -03:00
README.md docs: stage J round 1 — release polish audit + README stats section 2026-06-17 11:10:18 -03:00
world-cup-2026-hub-spec-en.md docs: add project specification documents 2026-06-12 16:11:49 -03:00

World Cup 2026 Hub

A static, single-page hub for the FIFA World Cup 2026 (Mexico · USA · Canada, 48 teams): full match schedule, live group standings, an interactive knockout bracket with a prediction/simulation mode, and a stadium guide. Built with vanilla HTML/CSS/JS — no backend, no framework, no build step. All content lives in JSON files.

UI languages: English / Português (toggle in the header, auto-detected on first visit).


Features

  • Schedule — all 104 matches with filters (date, group, phase, team, stadium), free-text search, date sorting, and a "My matches" favorites filter.
  • Groups — standings computed live from results (3/1/0 points, goal difference, goals for), qualification highlights.
  • Knockout bracket — generated dynamically from standings + bracket-config.json; hover highlights a match's full path; mouse-wheel/pinch zoom; drag to pan.
  • Simulation mode — pick winners and scores for unplayed knockout matches; picks propagate through the rounds, persist locally, and never touch the JSON data.
  • Bracket challenge — once real knockout results land, your saved picks are scored ("X of Y picks correct", per phase).
  • Share prediction — copy a link that carries your bracket picks (base64 in the URL).
  • Favorites — star teams anywhere; their matches get highlighted across the app.
  • Time zones — show kickoff times in your local time or the stadium's time.
  • Add to calendar — download any match as an RFC 5545 .ics file.
  • Match modal — details for every match, with space reserved for future stats.
  • Stats — a sub-navigated screen (Overview · Teams · Records · Comparator): tournament-to-date aggregates and goals-by-stage/round charts; a verdict hero (champion + podium) that takes over once the final is played; a final ranking 148 by stage reached; team record cards (biggest win, win streak, champion's path); a "format debuts" band; and an A-vs-B team comparator. Sections appear only when they have data (graceful degradation).
  • Responsive (mobile / tablet / desktop), keyboard-accessible, honors prefers-reduced-motion.

Run locally

python -m http.server
# 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.

Deploy to GitHub Pages

  1. Push this repository to GitHub.
  2. Repository Settings → Pages → Source: deploy from branch, main / root.
  3. Done — the site works under https://<user>.github.io/<repo>/ because every asset and data path in the code is relative (never start a path with / when editing).

Project structure

worldcup2026/
├── index.html              SPA shell (header, tabs, hero, panels, modal root)
├── assets/
│   ├── css/                style.css · bracket.css · animations.css
│   ├── js/                 app.js (entry) · schedule.js · groups.js · bracket.js
│   │                       modal.js · stadiums.js · storage.js · i18n.js · calendar.js
│   └── images/             flags/*.svg · stadiums/*.svg (placeholders)
└── 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)
    ├── stadiums.json       name, city, capacity, image, IANA timezone
    └── bracket-config.json Round-of-32 slots + best-third assignment

Maintaining the data

The current files contain mock data (real country names, fictional results) so every feature can be exercised. Replace them with real data as the tournament unfolds — no code changes needed.

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 draws.

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.

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:

"thirdPlaceAssignment": { "1": "C", "2": "A", "3": null,  }

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 placeholder SVGs in assets/images/ with real artwork keeping the same file names (or update the JSON paths).

UI labels (EN/PT)

Every UI string goes through t(key) — add new labels to both dictionaries in assets/js/i18n.js. Data values (team/stadium names) are not translated.

Local storage

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, favorites, and preferences — the JSON content is never modified by the app.

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 GitHub Pages (all paths relative, no server-side code)
  • Works on desktop and mobile (≤767 / 7681439 / 1440+ breakpoints)
  • Allows knockout-stage simulation (persisted, never mutates JSON)
  • Smooth animations (entry, hover, bracket line-draw; reduced-motion safe)
  • No backend dependency — fully static, works offline after first load

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

Roadmap (spec §19)

PWA install, dark/light theme, real-time statistics, results API, FIFA ranking, World Cup history, team comparison, push notifications.