<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Animated Calendar</title>
<style>
/* Base Styles */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: #eef1f5; /* Light background */
transition: background 0.5s ease-in-out;
}
/* Calendar Container */
.calendar {
width: 380px;
background: #ffffff;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
border-radius: 15px;
overflow: hidden;
transition: box-shadow 0.3s ease-in-out, background 0.5s ease-in-out;
position: relative; /* For transition animation */
}
/* Dark Mode Styles */
.dark-mode body {
background: #1e1e2d;
}
.dark-mode .calendar {
background: #2a2a3d;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.4);
color: #f1f1f1;
}
.dark-mode .header {
background: #3c3c5a;
}
.dark-mode .header button,
.dark-mode #modeToggle {
background: #4a4a6c;
color: #f1f1f1;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}
.dark-mode .weekdays {
background: #3a3a50;
}
.dark-mode .day:hover {
background: #4a4a6c !important; /* Ensure hover works in dark mode */
}
.dark-mode .day.today {
background: #ff7043; /* Orange for today in dark mode */
color: #fff;
}
.dark-mode .day.active {
background: #5c6bc0; /* Selected day in dark mode */
color: #fff;
}
/* Header */
.header {
display: flex;
justify-content: space-between;
align-items: center;
background: #4a69bd; /* Blue header */
color: #fff;
padding: 15px 20px;
}
.header button,
#modeToggle {
background: #5c7cda;
color: #fff;
border: none;
padding: 8px 15px;
cursor: pointer;
border-radius: 8px;
font-size: 16px;
transition: background 0.3s, transform 0.1s;
}
.header button:hover,
#modeToggle:hover {
background: #4768b5;
transform: translateY(-1px);
}
/* Weekdays */
.weekdays {
display: grid;
grid-template-columns: repeat(7, 1fr);
background:black;
padding: 10px;
text-align: center;
font-weight: 600;
color:whitesmoke;
border-bottom: 1px solid #eee;
transition: background 0.5s ease-in-out, color 0.5s ease-in-out;
}
.dark-mode .weekdays {
border-bottom: 1px solid #3a3a50;
}
/* Days Grid */
.days-wrapper {
position: relative;
overflow: hidden;
min-height: 250px; /* To prevent collapse during transition */
}
.days {
display: grid;
grid-template-columns: repeat(7, 1fr);
padding: 10px;
position: absolute;
width: 100%;
top: 0;
left: 0;
transition: transform 0.4s ease-in-out, opacity 0.4s ease-in-out;
}
/* Transition States */
.days.slide-out-left {
transform: translateX(-100%);
opacity: 0;
}
.days.slide-out-right {
transform: translateX(100%);
opacity: 0;
}
.days.slide-in-left {
transform: translateX(0);
opacity: 1;
}
.days.slide-in-right {
transform: translateX(0);
opacity: 1;
}
/* Day Cell */
.day {
text-align: center;
padding: 12px;
margin: 3px;
border-radius: 50%; /* Circle shape */
cursor: pointer;
font-size: 14px;
transition: background 0.2s, color 0.2s, transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); /* Bounce transition */
font-weight: 500;
}
/* Bounce Effect on Hover */
.day:hover {
background: #e0e0e0;
transform: translateY(-5px) scale(1.05); /* Bounce up and slightly scale */
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
}
/* Today's Date */
.day.today {
background: #ff9800; /* Vibrant Orange */
color: #fff;
font-weight: bold;
box-shadow: 0 4px 8px rgba(255, 152, 0, 0.4);
}
.day.today:hover {
background: #e68900;
transform: translateY(-5px) scale(1.05);
}
/* Active/Selected Day (Optional, for future feature) */
.day.active {
background: #4CAF50; /* Green */
color: #fff;
}
</style>
</head>
<body>
<div class="calendar">
<div class="header">
<button onclick="changeMonth(-1)">◀</button>
<div id="monthYear" style="font-size: 1.5em;"></div>
<button onclick="changeMonth(1)">▶</button>
</div>
<div class="header" style="justify-content: center; padding-top: 5px;">
<button id="modeToggle">☀️ Light Mode</button>
</div>
<div class="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="days-wrapper">
<div class="days" id="days"></div>
</div>
</div>
<script>
const monthYear = document.getElementById("monthYear");
const daysContainer = document.getElementById("days");
const modeToggle = document.getElementById("modeToggle");
const body = document.body;
let date = new Date();
let isDarkMode = false;
// --- Dark/Light Mode Toggle ---
function toggleDarkMode() {
isDarkMode = !isDarkMode;
body.classList.toggle('dark-mode', isDarkMode);
modeToggle.textContent = isDarkMode ? '🌙 Dark Mode' : '☀️ Light Mode';
}
modeToggle.addEventListener('click', toggleDarkMode);
// --- Calendar Rendering ---
function renderCalendar(direction = 0) {
const year = date.getFullYear();
const month = date.getMonth();
const today = new Date();
// Store current month content for smooth transition
const oldDaysContainer = daysContainer.cloneNode(true);
const newDaysContainer = document.createElement('div');
newDaysContainer.className = 'days';
newDaysContainer.id = 'tempDays'; // Use a temporary ID for the new grid
monthYear.textContent = date.toLocaleString("default", { month: "long" }) + " " + year;
const firstDay = new Date(year, month, 1).getDay();
const lastDate = new Date(year, month + 1, 0).getDate();
let daysHTML = "";
// Empty slots before first day
for (let i = 0; i < firstDay; i++) {
daysHTML += `<div></div>`;
}
// Days of month
for (let d = 1; d <= lastDate; d++) {
const isToday = d === today.getDate() && month === today.getMonth() && year === today.getFullYear();
daysHTML += `<div class="day ${isToday ? "today" : ""}" data-date="${year}-${month + 1}-${d}">${d}</div>`;
}
newDaysContainer.innerHTML = daysHTML;
const daysWrapper = document.querySelector('.days-wrapper');
// Apply initial transition classes to the new container
if (direction !== 0) {
oldDaysContainer.classList.add(direction > 0 ? 'slide-out-left' : 'slide-out-right');
newDaysContainer.classList.add(direction > 0 ? 'slide-out-right' : 'slide-out-left');
daysWrapper.appendChild(newDaysContainer);
} else {
// Initial load
daysContainer.innerHTML = daysHTML;
return;
}
// Start the transition
setTimeout(() => {
// Animate old container out
daysContainer.remove();
// Animate new container in
newDaysContainer.classList.remove(direction > 0 ? 'slide-out-right' : 'slide-out-left');
newDaysContainer.classList.add(direction > 0 ? 'slide-in-left' : 'slide-in-right');
newDaysContainer.id = 'days';
}, 50); // Small delay for effect
// Clean up the old container
setTimeout(() => {
if (oldDaysContainer.parentNode) {
oldDaysContainer.parentNode.removeChild(oldDaysContainer);
}
}, 450); // Wait for animation to finish
}
// --- Month Change Logic ---
function changeMonth(step) {
const currentDaysContainer = document.getElementById('days');
const direction = step > 0 ? 1 : -1;
// 1. Prepare old container for slide-out
currentDaysContainer.classList.add(direction > 0 ? 'slide-out-left' : 'slide-out-right');
currentDaysContainer.style.opacity = 0; // Hide it quickly
// 2. Change the date model
date.setMonth(date.getMonth() + step);
// 3. Render the new month with slide-in effect
// We use a timeout to let the slide-out animation start before rendering the next month
setTimeout(() => {
// The renderCalendar function handles replacing the old container with a new, sliding-in one.
renderCalendar(direction);
}, 300); // Time needs to be slightly less than the CSS transition duration (0.4s)
}
// --- Initial Load ---
renderCalendar(0); // Load initially without animation
</script>
</body>
</html>
