world-2026-hub/complement-spec-worldcup2026-en.md

7.6 KiB
Raw Permalink Blame History

World Cup 2026 Hub — Spec Complement (Claude Code Prompt)

This document complements World-Cup-2026-Hub-Spec.md and 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 (AL), 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
  }
}
  • round32 must have 16 entries, in the actual bracket order (array position defines bracket position).
  • thirdPlaceAssignment maps slot → group letter (e.g., "1": "C"); still null until 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 UTCformatMatchTime() (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)

  1. Group standings: from groups.json + results.json, compute points (3/1/0), goal difference, and goals for; sort each group.
  2. 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").
  3. Subsequent rounds: winner = higher score, or penalty winner on a draw. Match not played yet → "TBD".
  4. resolveBracketTeams(matchId): function exported from bracket.js, reused by schedule.js to display the correct teams for knockout matches in the schedule.
  5. Generated ID convention: {ROUND}-{N} (R16-1, QF-1, SF-1, FINAL, THIRD-PLACE), via sequential pairing — used as keys in wc2026_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 (AL)
  • 72 group-stage matches + 32 knockout matches = 104 matches (official 2026 format)
  • ~30 stadiums (Mexico/USA/Canada)
  • Mix of "finished"/"scheduled" statuses in results.json to test both bracket states
  • thirdPlaceAssignment with all values set to null

4. Execution Plan (sequential phases)

  1. File structure + mock data
  2. Base layout: header, hero, dashboard
  3. Match schedule: listing, filters, search, sorting
  4. Group table (computed standings)
  5. Stadiums page
  6. Match detail modal
  7. Bracket: static structure
  8. Bracket: hover/highlight, animations, zoom, drag
  9. Simulation mode + localStorage
  10. Responsiveness + accessibility + entry animations
  11. README + acceptance criteria checklist
  12. Extra features: storage.js, favorites, timezone, .ics, bracket challenge, export/import prediction (sections 610)

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) in storage.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 where homeTeam or awayTeam is in wc2026_favorites.
  • getFavoriteMatches(matches, favorites) shared between schedule.js and bracket.js.

7. Automatic Timezone

  • Global toggle "Local time / Stadium time" → saved in wc2026_prefs.timeMode.
  • formatMatchTime(match, stadium, mode) in app.js:
    • mode: "local"Intl.DateTimeFormat without timeZone (uses the browser's timezone)
    • mode: "stadium"Intl.DateTimeFormat with timeZone: stadium.timezone
  • Applied in schedule.js, modal.js, and bracket.js cards.

8. Bracket Challenge (simulation score)

  • calculateChallengeScore(simulation, results, bracketTree) in bracket.js:
    • For each knockout match with status: "finished", compares the actual winner with wc2026_simulation[matchId].winner.
    • Returns { correct, total, byPhase: { "Round of 32": "3/8", ... } } using the IDs/phases from the resolved bracketTree (section 2).
  • 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.

  • "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
  • 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) — VCALENDAR with one VEVENT.
  • DTSTART/DTEND in UTC (...Z) from match.date + match.time; fixed 2h duration.
  • Knockout matches: home/away via resolveBracketTeams(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