Created comprehensive planning documentation following Spec-Kit standards: Phase 0 - Research: - Analyzed 10 technical decisions (HTML5, ASCII art, countdown, responsive design) - Resolved edge cases (post-countdown, post-event, JS disabled, small screens) - Documented rationale and alternatives for each decision Phase 1 - Design: - Defined 4 data entities (Event, Countdown, VisualTheme, Content) - Created JSON schema contract for page content validation - Mapped 35 functional requirements to implementation approach - Generated quickstart testing checklist with 10 test categories Technical decisions: - Single-file HTML architecture (no external dependencies) - Pure CSS animations (blinking cursor) - JavaScript countdown with GMT+8 timezone handling - Responsive ASCII art (desktop/mobile versions) - WCAG 2.1 AA compliance (21:1 contrast ratio) Constitution compliance: PASS - No violations detected - Follows user-centric, professional excellence principles - Minimal technical stack aligns with simplicity requirement Artifacts created: - spec.md: 35 functional requirements - plan.md: Implementation strategy and phases - research.md: 10 technical research decisions - data-model.md: 4 entity definitions with validation - contracts/page-content.schema.json: JSON Schema - quickstart.md: Comprehensive testing checklist Ready for: /tasks command to generate tasks.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
10 KiB
Data Model: McDonald's IT Halloween Event Page
Feature: Halloween event announcement webpage Date: 2025-10-04 Status: Complete
Overview
This document defines the data entities and their relationships for the Halloween event page. While this is a static page with no backend storage, defining the data model helps structure the HTML content and JavaScript logic.
Entities
1. Event
Represents the Halloween event information.
Purpose: Store and display all event details
Fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
title |
String | Yes | - | Event title: "2025 MITA Halloween" |
date |
Date | Yes | - | Event date: October 31, 2025 |
startTime |
Time | Yes | - | Event start: 18:00 GMT+8 |
endTime |
Time | Yes | - | Event end: 21:00 GMT+8 |
timezone |
String | Yes | "GMT+8" | Timezone for event |
location |
String | Yes | - | "MITA Building 2F Pantry" |
organizer |
Object | Yes | - | Organizer details |
organizer.name |
String | Yes | - | "Jessi Pan" |
organizer.email |
String | Yes | - | "jessi.pan@cn.mcd.com" |
activities |
Array | Yes | - | List of activities |
notes |
Array | Yes | - | Additional information |
Validation Rules:
datemust be valid ISO date formatstartTimemust be beforeendTime- Times must be in 24-hour format (HH:MM)
organizer.emailmust be valid email formatactivitiesmust contain at least 1 item
Example:
{
"title": "2025 MITA Halloween",
"date": "2025-10-31",
"startTime": "18:00",
"endTime": "21:00",
"timezone": "GMT+8",
"location": "MITA Building 2F Pantry",
"organizer": {
"name": "Jessi Pan",
"email": "jessi.pan@cn.mcd.com"
},
"activities": [
"Cosplay (encouraged)",
"Bug Debugging Games",
"Lucky Draw"
],
"notes": [
"Costumes encouraged",
"Food and drinks provided"
]
}
State Transitions: None (static data)
2. Countdown
Represents the countdown timer state and logic.
Purpose: Calculate and display time remaining until event
Fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
targetDateTime |
DateTime | Yes | - | Oct 31 2025 18:00:00 GMT+8 (in UTC: 10:00:00 UTC) |
currentDateTime |
DateTime | Yes | - | User's current time (updated every second) |
timeRemaining |
Object | No | null | Calculated time remaining |
timeRemaining.days |
Integer | No | 0 | Days remaining |
timeRemaining.hours |
Integer | No | 0 | Hours remaining (0-23) |
timeRemaining.minutes |
Integer | No | 0 | Minutes remaining (0-59) |
timeRemaining.seconds |
Integer | No | 0 | Seconds remaining (0-59) |
status |
Enum | Yes | "before" | Current event status |
Status Values:
before: Current time is before event start (show countdown)active: Event has started, current time is between start and end (show "EVENT STARTING NOW!")ended: Event has ended, current time is after event end (show "EVENT COMPLETED")
Validation Rules:
targetDateTimemust be a valid Date object- All time values must be >= 0
statusmust be one of: "before", "active", "ended"timeRemainingis null when status is not "before"
Calculation Logic:
// Convert GMT+8 to UTC for calculation
// Oct 31 2025 18:00 GMT+8 = Oct 31 2025 10:00 UTC
const targetDate = new Date('2025-10-31T10:00:00Z');
const eventEnd = new Date('2025-10-31T13:00:00Z'); // 21:00 GMT+8
function updateCountdown() {
const now = new Date();
if (now >= eventEnd) {
return { status: 'ended', timeRemaining: null };
}
if (now >= targetDate) {
return { status: 'active', timeRemaining: null };
}
const diff = targetDate - now;
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
return {
status: 'before',
timeRemaining: { days, hours, minutes, seconds }
};
}
State Transitions:
before → active (when current time >= targetDateTime)
active → ended (when current time >= eventEnd)
3. VisualTheme
Represents the visual design configuration.
Purpose: Define and apply consistent styling across the page
Fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
backgroundColor |
Color | Yes | "#000000" | Pure black background |
primaryColor |
Color | Yes | "#00FF00" | Terminal green for main text |
accentColor |
Color | Yes | "#FFBF00" | Amber for key information |
fontFamily |
String | Yes | See below | Monospace font stack |
fontSize |
Object | Yes | - | Font sizes for different elements |
fontSize.base |
String | Yes | "16px" | Base font size |
fontSize.heading |
String | Yes | "20px" | Heading size |
fontSize.small |
String | Yes | "14px" | Small text size |
asciiArt |
Object | Yes | - | ASCII art content |
asciiArt.logoDesktop |
String | Yes | - | McDonald's logo for desktop |
asciiArt.logoMobile |
String | Yes | - | McDonald's logo for mobile |
asciiArt.decorations |
Array | Yes | - | Halloween ASCII decorations |
Font Stack:
"Courier New", Monaco, Consolas, "Liberation Mono", Menlo, monospace
Validation Rules:
- All color values must be valid hex format (#RRGGBB)
backgroundColormust be #000000 (black)primaryColorandaccentColormust meet WCAG AA contrast ratio with backgroundfontFamilymust include at least one monospace fallback- ASCII art strings must contain only ASCII characters (0-127)
Example:
{
"backgroundColor": "#000000",
"primaryColor": "#00FF00",
"accentColor": "#FFBF00",
"fontFamily": "\"Courier New\", Monaco, Consolas, Menlo, monospace",
"fontSize": {
"base": "16px",
"heading": "20px",
"small": "14px"
},
"asciiArt": {
"logoDesktop": "[Multi-line ASCII art]",
"logoMobile": "[Simplified ASCII art]",
"decorations": ["🎃", "👻", "🦇"]
}
}
State Transitions: None (static configuration)
4. Content (Bilingual)
Represents bilingual text content structure.
Purpose: Store and display English/Chinese content pairs
Fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
section |
String | Yes | - | Content section identifier |
english |
String | Yes | - | English text |
chinese |
String | Yes | - | Chinese text (Simplified) |
order |
Integer | Yes | - | Display order |
Example Content Pairs:
[
{
"section": "date",
"english": "Date: October 31, 2025",
"chinese": "日期:2025年10月31日",
"order": 1
},
{
"section": "time",
"english": "Time: 18:00 - 21:00 (6:00 PM - 9:00 PM)",
"chinese": "时间:18:00 - 21:00",
"order": 2
},
{
"section": "location",
"english": "Location: MITA Building 2F Pantry",
"chinese": "地点:MITA大厦2楼茶水间",
"order": 3
},
{
"section": "organizer",
"english": "Organizer: Jessi Pan (jessi.pan@cn.mcd.com)",
"chinese": "组织者:Jessi Pan (jessi.pan@cn.mcd.com)",
"order": 4
}
]
Display Pattern:
<p lang="en">{english}</p>
<p lang="zh-CN">{chinese}</p>
Relationships
Event (1)
├── has timezone (1) → Countdown
└── has theme (1) → VisualTheme
Countdown (1)
└── uses targetDateTime from Event
VisualTheme (1)
└── styles all Content
Content (many)
└── derived from Event
HTML Representation
The data model maps to HTML structure as follows:
<main>
<!-- VisualTheme.asciiArt.logoDesktop -->
<header>
<pre class="ascii-desktop">[McDonald's ASCII Logo]</pre>
<pre class="ascii-mobile">[McDonald's ASCII Logo Simplified]</pre>
<p class="tagline">i'm lovin' IT<span class="cursor">_</span></p>
</header>
<!-- Countdown -->
<section class="countdown">
<div id="countdown-display">
<span id="days">0</span>d
<span id="hours">0</span>h
<span id="minutes">0</span>m
<span id="seconds">0</span>s
</div>
</section>
<!-- Event + Content (bilingual) -->
<section class="event-info">
<p lang="en">Date: {Event.date}</p>
<p lang="zh-CN">日期:{Event.date}</p>
<!-- ... more bilingual pairs -->
</section>
<!-- Event.activities -->
<section class="activities">
<p lang="en">Activities:</p>
<p lang="zh-CN">活动:</p>
<ul>
<li lang="en">{activity}</li>
<li lang="zh-CN">{activity_zh}</li>
</ul>
</section>
<!-- VisualTheme.asciiArt.decorations -->
<section class="decorations">
<pre>{decoration}</pre>
</section>
</main>
JavaScript Data Structure
const eventData = {
event: {
title: "2025 MITA Halloween",
date: "2025-10-31",
startTime: "18:00",
endTime: "21:00",
timezone: "GMT+8",
location: "MITA Building 2F Pantry",
organizer: {
name: "Jessi Pan",
email: "jessi.pan@cn.mcd.com"
},
activities: ["Cosplay", "Bug Debugging Games", "Lucky Draw"],
notes: ["Costumes encouraged", "Food and drinks provided"]
},
countdown: {
targetDateTime: new Date('2025-10-31T10:00:00Z'), // GMT+8 = UTC+8
eventEnd: new Date('2025-10-31T13:00:00Z'),
status: 'before',
timeRemaining: null
},
theme: {
backgroundColor: "#000000",
primaryColor: "#00FF00",
accentColor: "#FFBF00",
fontFamily: '"Courier New", Monaco, Consolas, Menlo, monospace'
}
};
Validation Contract
All data must conform to the schema defined in /contracts/page-content.schema.json.
Notes
- This is a static page, so data is hardcoded in HTML/JS
- No database or API calls required
- Data updates only via code changes
- Countdown is the only dynamic data (calculated client-side)
Next Steps
- Create contracts/page-content.schema.json (JSON Schema validation)
- Create quickstart.md (testing checklist)
- Update CLAUDE.md (agent context)