Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions examples/interactive-calendar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# 📅 Interactive Calendar

A lightweight, responsive calendar widget built with vanilla JavaScript. This project demonstrates DOM manipulation, Date object handling, and local state management without external libraries.

## 🚀 Features

- **Dynamic Rendering:** Automatically generates the correct grid for any month and year.
- **Navigation:** Browse through past and future months.
- **Current Date Highlighting:** visual indicator for today's date.
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lowercase sentence beginning. The sentence should start with a capital letter: "Visual indicator for today's date."

Suggested change
- **Current Date Highlighting:** visual indicator for today's date.
- **Current Date Highlighting:** Visual indicator for today's date.

Copilot uses AI. Check for mistakes.
- **Event Management (Bonus):** Click any date to add, view, or delete notes.
- **Data Persistence:** Events are saved to the browser's `localStorage`, so they remain after refreshing the page.
- **Responsive Design:** Built with CSS Grid to adapt to different screen sizes.

## 🛠️ Technologies Used

- **HTML5:** Semantic structure.
- **CSS3:** Flexbox and Grid layout; CSS Variables for theming.
- **JavaScript (ES6+):** Logic for date calculation and event handling.

## 📂 Project Structure

```text
interactive-calendar/
├── index.html # Main HTML structure
├── style.css # Styling and Grid layout
├── script.js # Calendar logic and Event handling
└── README.md # Project documentation

Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing closing triple backticks for the code block. The code block starting at line 22 should be properly closed before the "How It Works" section begins.

Copilot uses AI. Check for mistakes.
##💡 How It Works
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after ## in the markdown heading. Should be "## 💡 How It Works" to follow proper markdown syntax.

Suggested change
##💡 How It Works
## 💡 How It Works

Copilot uses AI. Check for mistakes.
1. Date Calculation
The calendar grid is calculated using the native Date object:

Start Day: new Date(year, month, 1).getDay() determines which day of the week the month starts on (0=Sunday, 1=Monday).

Total Days: new Date(year, month + 1, 0).getDate() retrieves the exact number of days in the current month.

2. Rendering the Grid
We use a loop to generate <div> elements.

First, we insert empty placeholder divs to align the 1st of the month with the correct weekday column.

Then, we generate the actual numbered days.

3. State Management
Comment on lines +29 to +44
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect markdown heading syntax. The "Date Calculation" and subsequent numbered sections should use proper markdown headings (### ) rather than plain text followed by descriptions.

Suggested change
##💡 How It Works
1. Date Calculation
The calendar grid is calculated using the native Date object:
Start Day: new Date(year, month, 1).getDay() determines which day of the week the month starts on (0=Sunday, 1=Monday).
Total Days: new Date(year, month + 1, 0).getDate() retrieves the exact number of days in the current month.
2. Rendering the Grid
We use a loop to generate <div> elements.
First, we insert empty placeholder divs to align the 1st of the month with the correct weekday column.
Then, we generate the actual numbered days.
3. State Management
## 💡 How It Works
### 1. Date Calculation
The calendar grid is calculated using the native Date object:
Start Day: new Date(year, month, 1).getDay() determines which day of the week the month starts on (0=Sunday, 1=Monday).
Total Days: new Date(year, month + 1, 0).getDate() retrieves the exact number of days in the current month.
### 2. Rendering the Grid
We use a loop to generate <div> elements.
First, we insert empty placeholder divs to align the 1st of the month with the correct weekday column.
Then, we generate the actual numbered days.
### 3. State Management

Copilot uses AI. Check for mistakes.
Events are stored in a simple JSON object and saved to LocalStorage:

JavaScript
// Data Structure Example
{
"2023-10-25": "Meeting with team",
"2023-10-31": "Halloween Party"
}
Comment on lines +47 to +52
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown formatting issue. The code block for JavaScript data structure example is incomplete. It starts with "JavaScript" as a language identifier but lacks proper code fencing (triple backticks). The example should be wrapped in proper markdown code fences.

Copilot uses AI. Check for mistakes.

## 🏃‍♂️ How to Run
Clone the repository.

Navigate to the interactive-calendar folder.

Open index.html in your browser.

# 🔮 Future Improvements
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent heading level. This should be "## 🔮 Future Improvements" (level 2) to match the other main sections in the document, not "# 🔮 Future Improvements" (level 1).

Suggested change
# 🔮 Future Improvements
## 🔮 Future Improvements

Copilot uses AI. Check for mistakes.
Add drag-and-drop functionality for events.

Support for multiple events per day.

Add specific time slots for events.
39 changes: 39 additions & 0 deletions examples/interactive-calendar/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Calendar</title>
<link rel="stylesheet" href="style.css">
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent file naming convention. Other examples in the repository use 'styles.css' (plural), but this example uses 'style.css' (singular). For consistency with existing examples like Weather-app and Stopwatch-app, consider renaming to 'styles.css'.

Copilot uses AI. Check for mistakes.
</head>
<body>

<div class="calendar-container">
<header class="calendar-header">
<div class="calendar-navigation">
<span id="month-prev" class="nav-btn">&lt;</span>
<h2 id="month-year"></h2>
<span id="month-next" class="nav-btn">&gt;</span>
Comment on lines +14 to +16
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessibility issue: The navigation buttons use symbols (< and >) without accessible labels. Screen reader users won't know what these buttons do. Add aria-label attributes to the spans, such as aria-label="Previous month" and aria-label="Next month".

Suggested change
<span id="month-prev" class="nav-btn">&lt;</span>
<h2 id="month-year"></h2>
<span id="month-next" class="nav-btn">&gt;</span>
<span id="month-prev" class="nav-btn" aria-label="Previous month">&lt;</span>
<h2 id="month-year"></h2>
<span id="month-next" class="nav-btn" aria-label="Next month">&gt;</span>

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +16
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessibility issue: Interactive elements (navigation buttons and day cells) lack proper keyboard accessibility. The .nav-btn spans should be buttons or have role="button" with tabindex="0" and keyboard event handlers. Similarly, day cells should be keyboard navigable. Users who cannot use a mouse will not be able to interact with the calendar.

Copilot uses AI. Check for mistakes.
</div>
</header>

<div class="calendar-weekdays">
<div>Sun</div><div>Mon</div><div>Tue</div><div>Wed</div><div>Thu</div><div>Fri</div><div>Sat</div>
</div>

<div class="calendar-dates" id="calendar-dates"></div>
</div>

<div id="event-modal" class="modal hidden">
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessibility issue: The modal lacks proper ARIA attributes. The modal should have role="dialog", aria-modal="true", and aria-labelledby pointing to the heading element. This helps screen readers understand the modal's purpose and behavior.

Suggested change
<div id="event-modal" class="modal hidden">
<div id="event-modal" class="modal hidden" role="dialog" aria-modal="true" aria-labelledby="selected-date">

Copilot uses AI. Check for mistakes.
<div class="modal-content">
<span class="close-btn">&times;</span>
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessibility issue: The close button uses the × symbol without an accessible label. Add an aria-label="Close" attribute to help screen reader users understand the button's purpose.

Suggested change
<span class="close-btn">&times;</span>
<span class="close-btn" aria-label="Close">&times;</span>

Copilot uses AI. Check for mistakes.
<h3 id="selected-date"></h3>
<textarea id="event-input" placeholder="Add an event/note..."></textarea>
<button id="save-event-btn">Save Event</button>
<button id="delete-event-btn" class="secondary">Clear Event</button>
</div>
</div>

<script src="script.js"></script>
</body>
</html>
121 changes: 121 additions & 0 deletions examples/interactive-calendar/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
const monthYearElement = document.getElementById('month-year');
const datesElement = document.getElementById('calendar-dates');
const prevBtn = document.getElementById('month-prev');
const nextBtn = document.getElementById('month-next');

// Modal Elements
const modal = document.getElementById('event-modal');
const closeModalBtn = document.querySelector('.close-btn');
const selectedDateTitle = document.getElementById('selected-date');
const eventInput = document.getElementById('event-input');
const saveEventBtn = document.getElementById('save-event-btn');
const deleteEventBtn = document.getElementById('delete-event-btn');

let currentDate = new Date();
let clickedDate = null;
let events = JSON.parse(localStorage.getItem('events')) || {};

const months = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];

// --- Core Functions ---

function renderCalendar() {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();

monthYearElement.innerText = `${months[month]} ${year}`;
datesElement.innerHTML = '';

// First day of the month (0 = Sunday, 1 = Monday, etc.)
const firstDay = new Date(year, month, 1).getDay();
// Total days in the current month
const totalDays = new Date(year, month + 1, 0).getDate();

// Add empty divs for days before the first day of the month
for (let i = 0; i < firstDay; i++) {
const emptyDiv = document.createElement('div');
datesElement.appendChild(emptyDiv);
}

// Render days
for (let day = 1; day <= totalDays; day++) {
const dayDiv = document.createElement('div');
dayDiv.classList.add('day');
dayDiv.innerText = day;

// Check if this day is "Today"
const today = new Date();
if (day === today.getDate() && month === today.getMonth() && year === today.getFullYear()) {
dayDiv.classList.add('current-date');
}

// Check for Events
const dateString = `${year}-${month + 1}-${day}`;
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential bug with date string format. The date string format used here (e.g., "2026-1-15" vs "2026-01-15") is inconsistent when month or day is less than 10. This can cause issues when comparing or storing events. Consider padding the month and day with leading zeros for consistency: ${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}

Suggested change
const dateString = `${year}-${month + 1}-${day}`;
const dateString = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;

Copilot uses AI. Check for mistakes.
if (events[dateString]) {
dayDiv.classList.add('has-event');
dayDiv.title = events[dateString]; // Tooltip
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security consideration: User input from eventInput is stored in localStorage and later displayed as a tooltip (title attribute) without sanitization. While title attributes are generally safe from XSS, this could be a concern if the data is used elsewhere in the future. Consider documenting this or adding a comment about input handling if event display methods change.

Copilot uses AI. Check for mistakes.
}

// Add Click Event for Modal
dayDiv.addEventListener('click', () => openModal(dateString));
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event listener is attached to each day cell individually, which creates many event listeners. For better performance, especially with larger calendars, consider using event delegation by attaching a single listener to the parent datesElement and checking the event target. This reduces memory usage and improves performance.

Copilot uses AI. Check for mistakes.

datesElement.appendChild(dayDiv);
}
}

function openModal(dateStr) {
clickedDate = dateStr;
selectedDateTitle.innerText = `Event for: ${dateStr}`;
eventInput.value = events[dateStr] || '';
modal.classList.remove('hidden');
}

function closeModal() {
modal.classList.add('hidden');
clickedDate = null;
}

// --- Event Listeners ---

prevBtn.addEventListener('click', () => {
currentDate.setMonth(currentDate.getMonth() - 1);
renderCalendar();
});

nextBtn.addEventListener('click', () => {
currentDate.setMonth(currentDate.getMonth() + 1);
renderCalendar();
});

closeModalBtn.addEventListener('click', closeModal);

saveEventBtn.addEventListener('click', () => {
if (eventInput.value.trim()) {
events[clickedDate] = eventInput.value;
localStorage.setItem('events', JSON.stringify(events));
}
closeModal();
renderCalendar();
});
Comment on lines +95 to +102
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential data loss issue. When saving an event, if the user enters only whitespace and then clicks save, the event is not saved (due to the trim check), but the modal closes and the calendar re-renders. However, if an existing event is present and the user clears it to whitespace and clicks save, the old event remains. Consider also deleting the event when the trimmed value is empty to provide consistent behavior.

Copilot uses AI. Check for mistakes.

deleteEventBtn.addEventListener('click', () => {
if (events[clickedDate]) {
delete events[clickedDate];
localStorage.setItem('events', JSON.stringify(events));
}
closeModal();
renderCalendar();
});

// Close modal if clicking outside
window.addEventListener('click', (e) => {
if (e.target === modal) {
closeModal();
}
});

// Initial Render
renderCalendar();
145 changes: 145 additions & 0 deletions examples/interactive-calendar/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
:root {
--primary-color: #4a90e2;
--bg-color: #f4f4f9;
--text-color: #333;
--highlight-color: #e0f7fa;
}

body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
}

.calendar-container {
background: white;
width: 90%;
max-width: 400px;
border-radius: 10px;
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
overflow: hidden;
padding: 20px;
}

.calendar-header {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}

.calendar-navigation {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}

.nav-btn {
cursor: pointer;
font-size: 1.5rem;
-webkit-user-select: none;
user-select: none;
padding: 0 10px;
}

.nav-btn:hover {
color: var(--primary-color);
}

/* Grid System */
.calendar-weekdays, .calendar-dates {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
}

.calendar-weekdays div {
font-weight: bold;
color: #888;
margin-bottom: 10px;
}

.day {
padding: 10px;
cursor: pointer;
border-radius: 5px;
transition: background 0.2s;
position: relative;
}

.day:hover {
background-color: #f0f0f0;
}

.day.current-date {
background-color: var(--primary-color);
color: white;
font-weight: bold;
}

.day.has-event::after {
content: '';
position: absolute;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
width: 5px;
height: 5px;
background-color: red;
border-radius: 50%;
}

.day.inactive {
color: #ccc;
pointer-events: none;
}

/* Modal Styles */
.modal {
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
}

.hidden { display: none; }

.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 300px;
position: relative;
}

.close-btn {
position: absolute;
top: 10px; right: 15px;
cursor: pointer;
font-size: 1.2rem;
}

textarea {
width: 100%;
height: 60px;
margin: 10px 0;
padding: 5px;
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The box-sizing property is not set, which can cause unexpected width calculations with the textarea element that has width: 100% and padding. Consider adding 'box-sizing: border-box;' to the textarea rule or universally with a reset rule to prevent the element from exceeding its container width.

Suggested change
padding: 5px;
padding: 5px;
box-sizing: border-box;

Copilot uses AI. Check for mistakes.
}

button {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
cursor: pointer;
border-radius: 4px;
}

button.secondary { background: #ccc; }
Loading