7.6 KiB
World Cup 2026 Hub — Spec Complement (Claude Code Prompt)
This document complements
World-Cup-2026-Hub-Spec.mdand takes precedence over it in case of conflict. Read both before starting implementation. Stack: HTML5/CSS3/JS ES2022+ (ES Modules), no backend, no frameworks, no bundler, no linting/automated tests.
1. Data Schemas (data/)
teams.json
[
{ "id": "MEX", "name": "Mexico", "flag": "mexico.svg" }
]
groups.json
{
"A": ["MEX", "CAN", "USA", "PAN"]
}
12 groups (A–L), 4 teams each — 48 teams total.
matches.json
Keeps the original spec's format. For knockout matches, use bracketRef instead of fixed homeTeam/awayTeam:
{
"id": 73,
"phase": "Round of 32",
"date": "2026-06-29",
"time": "16:00",
"stadium": "Azteca",
"city": "Mexico City",
"bracketRef": "R32-1"
}
homeTeam/awayTeam for these matches are resolved at runtime via resolveBracketTeams().
results.json
Adds support for penalties (knockout stage):
{
"matchId": 73,
"homeScore": 1,
"awayScore": 1,
"penalties": { "home": 4, "away": 3 },
"status": "finished"
}
penalties is optional — only present if a knockout match ended in a draw.
bracket-config.json (new file)
Defines the generic bracket structure. The only file to edit once the 8 best third-place teams are known.
{
"round32": [
{ "id": "R32-1", "home": { "type": "group", "ref": "A", "pos": 1 }, "away": { "type": "group", "ref": "B", "pos": 2 } },
{ "id": "R32-2", "home": { "type": "group", "ref": "C", "pos": 1 }, "away": { "type": "third", "slot": 1 } }
],
"thirdPlaceAssignment": {
"1": null,
"2": null
}
}
round32must have 16 entries, in the actual bracket order (array position defines bracket position).thirdPlaceAssignmentmaps slot → group letter (e.g.,"1": "C"); stillnulluntil defined.- Subsequent rounds (R16, QF, SF, 3rd place, Final) have no config of their own — they're generated by sequential pairing of winners (indices 0-1 → next 0, 2-3 → next 1, etc.).
stadiums.json (update)
Adds timezone (IANA), used by the timezone selector (section 7):
{
"id": 1,
"name": "Estadio Azteca",
"city": "Mexico City",
"capacity": 87000,
"image": "azteca.jpg",
"timezone": "America/Mexico_City"
}
General note: all times in matches.json must be in UTC — formatMatchTime() (section 7) and the .ics export (section 10) depend on this.
localStorage — keys
Keys prefixed with wc2026_, managed via storage.js (thin wrapper: get(key, fallback) / set(key, value), automatic JSON):
| Key | Content |
|---|---|
wc2026_simulation |
{ "R32-1": { "winner": "MEX", "score": "2-1" }, ... } |
wc2026_favorites |
["MEX", "BRA"] |
wc2026_prefs |
{ "timeMode": "local" | "stadium", "lastTab": "bracket", "theme": "dark" | "light" } |
2. Bracket Logic (bracket.js)
- Group standings: from
groups.json+results.json, compute points (3/1/0), goal difference, and goals for; sort each group. - Resolve Round of 32 via
bracket-config.json:type: "group"→standings[ref][pos - 1]type: "third"→standings[thirdPlaceAssignment[slot]][2]- If the group stage isn't finished or the slot isn't defined, show a placeholder ("Group A Winner", "Best 3rd #1").
- Subsequent rounds: winner = higher score, or penalty winner on a draw. Match not played yet → "TBD".
resolveBracketTeams(matchId): function exported frombracket.js, reused byschedule.jsto display the correct teams for knockout matches in the schedule.- Generated ID convention:
{ROUND}-{N}(R16-1,QF-1,SF-1,FINAL,THIRD-PLACE), via sequential pairing — used as keys inwc2026_simulation(sections 8 and 9).
3. Mock Data
Generate data/*.json with fictional/placeholder data (to be replaced with real data later):
- 48 fictional teams, 12 groups (A–L)
- 72 group-stage matches + 32 knockout matches = 104 matches (official 2026 format)
- ~30 stadiums (Mexico/USA/Canada)
- Mix of
"finished"/"scheduled"statuses inresults.jsonto test both bracket states thirdPlaceAssignmentwith all values set tonull
4. Execution Plan (sequential phases)
- File structure + mock data
- Base layout: header, hero, dashboard
- Match schedule: listing, filters, search, sorting
- Group table (computed standings)
- Stadiums page
- Match detail modal
- Bracket: static structure
- Bracket: hover/highlight, animations, zoom, drag
- Simulation mode +
localStorage - Responsiveness + accessibility + entry animations
- README + acceptance criteria checklist
- Extra features:
storage.js, favorites, timezone, .ics, bracket challenge, export/import prediction (sections 6–10)
At the end of each phase, summarize what was done before moving on.
5. Constraints
- Vanilla JS, ES Modules (
type="module"), no bundler/transpiler/CDN - No automated tests, no linter
- CSS mobile-first, variables per the original spec's palette
6. Favorites + "My Matches"
- Star icon next to each team (group table, schedule, modal) →
toggleFavorite(teamId)instorage.js. - Matches involving favorites get a visual highlight (border/badge) in the schedule, groups, and bracket.
- "My Matches" filter in the schedule (
schedule.js): shows only matches wherehomeTeamorawayTeamis inwc2026_favorites. getFavoriteMatches(matches, favorites)shared betweenschedule.jsandbracket.js.
7. Automatic Timezone
- Global toggle "Local time / Stadium time" → saved in
wc2026_prefs.timeMode. formatMatchTime(match, stadium, mode)inapp.js:mode: "local"→Intl.DateTimeFormatwithouttimeZone(uses the browser's timezone)mode: "stadium"→Intl.DateTimeFormatwithtimeZone: stadium.timezone
- Applied in
schedule.js,modal.js, andbracket.jscards.
8. Bracket Challenge (simulation score)
calculateChallengeScore(simulation, results, bracketTree)inbracket.js:- For each knockout match with
status: "finished", compares the actual winner withwc2026_simulation[matchId].winner. - Returns
{ correct, total, byPhase: { "Round of 32": "3/8", ... } }using the IDs/phases from the resolvedbracketTree(section 2).
- For each knockout match with
- Displayed as a card near the bracket: "X out of Y correct".
- No persistence of its own — recalculated on every load from
wc2026_simulation+results.json.
9. Export/Import Prediction (shareable link)
- "Share prediction" →
getShareableLink():base64(JSON.stringify(wc2026_simulation))in?prediction=...on the current URL. - On page load,
loadPredictionFromURL():- decodes and validates (same keys as the current
bracketTree) - if valid, confirms with the user before overwriting the local
wc2026_simulation - if invalid/incompatible, ignores silently
- decodes and validates (same keys as the current
- Functions in
bracket.js.
10. Export Match to Calendar (.ics)
New calendar.js module, imported by modal.js. "Add to calendar" button in the detail modal.
- iCalendar format (RFC 5545) —
VCALENDARwith oneVEVENT. DTSTART/DTENDin UTC (...Z) frommatch.date+match.time; fixed 2h duration.- Knockout matches:
home/awayviaresolveBracketTeams(match). Blob(type: "text/calendar") +<a download>; CRLF (\r\n) required between lines.
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//WorldCup2026Hub//EN
BEGIN:VEVENT
UID:match-{id}@worldcup2026hub
DTSTAMP:{start}
DTSTART:{start}
DTEND:{end}
SUMMARY:{home} x {away} — {phase}
LOCATION:{stadium.name}, {stadium.city}
END:VEVENT
END:VCALENDAR