docs: add project specification documents

This commit is contained in:
Lucas Kalil 2026-06-12 16:11:49 -03:00
commit 5ee42abb48
2 changed files with 734 additions and 0 deletions

View file

@ -0,0 +1,209 @@
# 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`
```json
[
{ "id": "MEX", "name": "Mexico", "flag": "mexico.svg" }
]
```
### `groups.json`
```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`:
```json
{
"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):
```json
{
"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.**
```json
{
"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):
```json
{
"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`)
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`.
---
## 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
- 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.
```text
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
```

View file

@ -0,0 +1,525 @@
# World Cup 2026 Hub
## 1. Overview
Develop a static web application for hosting on GitHub Pages that displays complete information about the FIFA World Cup 2026.
The system should consume JSON files to load match, results, and standings information, with no backend required.
The main focus is:
* Modern interface
* Excellent visual experience
* Responsiveness
* Interactive bracket
* Easy maintenance via JSON
---
# 2. Objectives
Create a portal that allows:
* Browsing all World Cup matches
* Viewing the group stage
* Viewing the complete knockout stage
* Tracking results
* Viewing stadiums and match information
* Simulating knockout stage winners (optional mode)
* Working fully offline after loading
---
# 3. Technologies
## Required
* HTML5
* CSS3
* JavaScript ES2022+
## Allowed
* CSS Variables
* CSS Grid
* Flexbox
* Web Components (Optional)
## Not allowed
* Backend
* Database
* Heavy frameworks
---
# 4. File Structure
```text
/
├── index.html
├── assets/
│ ├── css/
│ │ ├── style.css
│ │ ├── bracket.css
│ │ └── animations.css
│ │
│ ├── js/
│ │ ├── app.js
│ │ ├── bracket.js
│ │ ├── groups.js
│ │ ├── schedule.js
│ │ └── modal.js
│ │
│ └── images/
├── data/
│ ├── matches.json
│ ├── results.json
│ ├── stadiums.json
│ ├── teams.json
│ └── groups.json
└── README.md
```
---
# 5. Visual Theme
## Style
Visual inspired by:
* FIFA World Cup
* UEFA Champions League
* Apple Design Language
## Characteristics
* Glassmorphism
* Soft shadows
* Rounded corners
* Smooth transitions
* Gradient backgrounds
## Palette
```css
--bg-primary: #081421;
--bg-secondary: #10243b;
--accent-gold: #d4af37;
--accent-blue: #1e88e5;
--text-primary: #ffffff;
--text-secondary: #cfd8dc;
```
---
# 6. Main Layout
## Header
Display:
* World Cup 2026 logo
* Navigation
Menu:
* Home
* Matches
* Groups
* Knockout Stage
* Stadiums
---
## Hero Section
Display:
* Next match
* Countdown
* Highlights
---
## Dashboard
Cards:
* Total matches
* Completed matches
* Upcoming matches
* Participating teams
---
# 7. Match System
## Data Source
matches.json
Example:
```json
{
"id": 1,
"phase": "Group A",
"date": "2026-06-11",
"time": "17:00",
"stadium": "Azteca",
"city": "Mexico City",
"homeTeam": "Mexico",
"awayTeam": "Canada"
}
```
---
## Features
### Filters
Filter by:
* Date
* Group
* Phase
* Team
* Stadium
### Search
Search by:
* Country
* City
* Stadium
### Sorting
* Date ascending
* Date descending
---
# 8. Results System
File:
results.json
Example:
```json
{
"matchId": 1,
"homeScore": 2,
"awayScore": 1,
"status": "finished"
}
```
Possible statuses:
```text
scheduled
live
finished
```
---
# 9. Knockout Bracket
## Goal
Create a highly interactive visualization.
---
## Rounds
* Round of 32
* Round of 16
* Round of 16
* Quarterfinals
* Semifinals
* Third Place
* Final
---
## Layout
Horizontal format.
Example:
```text
Round of 16 -> Quarterfinals -> Semifinals -> Final -> Champion
```
---
## Features
### Hover
On mouse hover:
* Highlight the full path
### Animations
* Fade-in
* Slide-in
* Glow
### Zoom
Allow:
* Mouse wheel
* Pinch on mobile devices
### Drag
Freely move the bracket.
---
## Simulation Mode
User can:
* Choose winner
* Enter score
* Update next rounds
Without modifying the original JSON.
State saved in:
```javascript
localStorage
```
---
# 10. Match Info Modal
When clicking on a match.
Open a modal containing:
## Information
* Teams
* Date
* Time
* Stadium
* City
* Capacity
* Result
## Future statistics
Prepare space for:
* Possession
* Shots
* Cards
---
# 11. Stadiums Page
Data from:
stadiums.json
Example:
```json
{
"id": 1,
"name": "Estadio Azteca",
"city": "Mexico City",
"capacity": 87000,
"image": "azteca.jpg"
}
```
---
## Display
Responsive cards containing:
* Photo
* Name
* City
* Capacity
* Matches held
---
# 12. Responsiveness
## Desktop
1440px+
Full layout.
---
## Tablet
768px1439px
Reduced menu.
---
## Mobile
Up to 767px
Bracket with:
* Horizontal scroll
* Zoom
* Drag
---
# 13. Performance
Goals:
* Lighthouse > 90
* First render < 2s
* JS bundle < 300KB
---
# 14. Accessibility
Implement:
* ARIA labels
* Keyboard navigation
* Adequate contrast
* Visible focus states
---
# 15. Animations
## Entry
```css
fade-in
slide-up
slide-left
```
## Interaction
```css
hover-scale
hover-glow
pulse
```
## Bracket
```css
line-draw
winner-highlight
path-highlight
```
---
# 16. Local Persistence
Save:
* Simulations
* Visual preferences
* Last opened tab
Use:
```javascript
localStorage
```
---
# 17. Code Requirements
## JavaScript
* Modular
* No duplicated code
* Pure functions when possible
## CSS
* Organized by component
* Global variables
* Mobile First
---
# 18. Acceptance Criteria
The project will be considered complete when:
* All matches are loaded via JSON
* All results are loaded via JSON
* The bracket is generated dynamically
* It works on GitHub Pages
* It works on desktop and mobile
* It allows knockout stage simulation
* It has smooth animations
* It does not depend on a backend
---
# 19. Future Improvements
* PWA
* Dark/light mode
* Real-time statistics
* Results API
* FIFA ranking
* World Cup history
* Team comparison
* Push notifications
---
# 20. Deliverables
The final system must provide:
1. Single page (SPA)
2. Complete dynamic bracket
3. Group standings tables
4. Match schedule
5. Stadiums page
6. Detailed match modal
7. Simulation system
8. Architecture ready for expansion
9. Compatibility with GitHub Pages
10. Clean, documented code