Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Neon Exam Countdown</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background: linear-gradient(135deg, #0A0A0B 0%, #141419 100%); | |
| min-height: 100vh; | |
| color: #fff; | |
| overflow-x: hidden; | |
| position: relative; | |
| } | |
| /* Animated background */ | |
| .particles { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| overflow: hidden; | |
| z-index: 1; | |
| } | |
| .particle { | |
| position: absolute; | |
| width: 4px; | |
| height: 4px; | |
| background: radial-gradient(circle, rgba(0, 212, 255, 0.5) 0%, transparent 70%); | |
| border-radius: 50%; | |
| animation: float 20s infinite linear; | |
| } | |
| @keyframes float { | |
| from { | |
| transform: translateY(100vh) translateX(0); | |
| opacity: 0; | |
| } | |
| 10% { | |
| opacity: 1; | |
| } | |
| 90% { | |
| opacity: 1; | |
| } | |
| to { | |
| transform: translateY(-100vh) translateX(100px); | |
| opacity: 0; | |
| } | |
| } | |
| /* Main container */ | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 2rem; | |
| position: relative; | |
| z-index: 2; | |
| } | |
| /* Header */ | |
| .header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 3rem; | |
| } | |
| h1 { | |
| font-size: 2.5rem; | |
| font-weight: 700; | |
| background: linear-gradient(135deg, #00D4FF 0%, #FF0080 50%, #00FF88 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| text-shadow: 0 0 30px rgba(0, 212, 255, 0.5); | |
| } | |
| /* Settings button */ | |
| .settings-btn { | |
| width: 50px; | |
| height: 50px; | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 12px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .settings-btn:hover { | |
| background: rgba(255, 255, 255, 0.1); | |
| border-color: #00D4FF; | |
| box-shadow: 0 0 20px rgba(0, 212, 255, 0.5); | |
| transform: scale(1.05); | |
| } | |
| .settings-btn svg { | |
| width: 24px; | |
| height: 24px; | |
| fill: #00D4FF; | |
| } | |
| /* Exam cards grid */ | |
| .exams-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | |
| gap: 2rem; | |
| } | |
| /* Exam card */ | |
| .exam-card { | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 20px; | |
| padding: 2rem; | |
| text-align: center; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .exam-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: -2px; | |
| left: -2px; | |
| right: -2px; | |
| bottom: -2px; | |
| background: linear-gradient(45deg, #00D4FF, #FF0080, #00FF88, #00D4FF); | |
| border-radius: 20px; | |
| opacity: 0; | |
| z-index: -1; | |
| transition: opacity 0.3s ease; | |
| } | |
| .exam-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 30px rgba(0, 212, 255, 0.3); | |
| } | |
| .exam-card:hover::before { | |
| opacity: 0.5; | |
| } | |
| .exam-name { | |
| font-size: 1.5rem; | |
| font-weight: 600; | |
| margin-bottom: 1rem; | |
| color: #fff; | |
| } | |
| .countdown { | |
| font-size: 3rem; | |
| font-weight: 700; | |
| color: #00D4FF; | |
| text-shadow: 0 0 20px rgba(0, 212, 255, 0.8); | |
| margin-bottom: 0.5rem; | |
| } | |
| .countdown-label { | |
| font-size: 1rem; | |
| color: rgba(255, 255, 255, 0.7); | |
| } | |
| .exam-date { | |
| font-size: 0.9rem; | |
| color: rgba(255, 255, 255, 0.5); | |
| margin-top: 1rem; | |
| } | |
| .delete-btn { | |
| position: absolute; | |
| top: 1rem; | |
| right: 1rem; | |
| width: 30px; | |
| height: 30px; | |
| background: rgba(255, 0, 128, 0.2); | |
| border: 1px solid rgba(255, 0, 128, 0.5); | |
| border-radius: 8px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| opacity: 0; | |
| } | |
| .exam-card:hover .delete-btn { | |
| opacity: 1; | |
| } | |
| .delete-btn:hover { | |
| background: rgba(255, 0, 128, 0.4); | |
| transform: scale(1.1); | |
| } | |
| /* Modal */ | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.8); | |
| z-index: 1000; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 2rem; | |
| } | |
| .modal.active { | |
| display: flex; | |
| } | |
| .modal-content { | |
| background: rgba(20, 20, 25, 0.95); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 20px; | |
| padding: 2rem; | |
| width: 100%; | |
| max-width: 400px; | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5); | |
| } | |
| .modal-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 2rem; | |
| } | |
| .modal-title { | |
| font-size: 1.5rem; | |
| font-weight: 600; | |
| } | |
| .close-btn { | |
| width: 30px; | |
| height: 30px; | |
| background: transparent; | |
| border: none; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: transform 0.3s ease; | |
| } | |
| .close-btn:hover { | |
| transform: rotate(90deg); | |
| } | |
| .form-group { | |
| margin-bottom: 1.5rem; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 0.5rem; | |
| color: rgba(255, 255, 255, 0.8); | |
| font-size: 0.9rem; | |
| } | |
| input { | |
| width: 100%; | |
| padding: 0.75rem 1rem; | |
| background: rgba(255, 255, 255, 0.05); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 10px; | |
| color: #fff; | |
| font-size: 1rem; | |
| transition: all 0.3s ease; | |
| } | |
| input:focus { | |
| outline: none; | |
| border-color: #00D4FF; | |
| box-shadow: 0 0 10px rgba(0, 212, 255, 0.5); | |
| } | |
| .add-btn { | |
| width: 100%; | |
| padding: 1rem; | |
| background: linear-gradient(135deg, #00D4FF 0%, #FF0080 100%); | |
| border: none; | |
| border-radius: 10px; | |
| color: #fff; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .add-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 20px rgba(0, 212, 255, 0.5); | |
| } | |
| /* Empty state */ | |
| .empty-state { | |
| text-align: center; | |
| padding: 4rem 2rem; | |
| } | |
| .empty-state p { | |
| font-size: 1.2rem; | |
| color: rgba(255, 255, 255, 0.5); | |
| margin-bottom: 2rem; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 768px) { | |
| h1 { | |
| font-size: 2rem; | |
| } | |
| .exams-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| .countdown { | |
| font-size: 2.5rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Animated particles background --> | |
| <div class="particles" id="particles"></div> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>Exam Countdown</h1> | |
| <div class="settings-btn" onclick="openModal()"> | |
| <svg viewBox="0 0 24 24"> | |
| <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/> | |
| </svg> | |
| </div> | |
| </div> | |
| <div class="exams-grid" id="examsGrid"> | |
| <!-- Exam cards will be inserted here --> | |
| </div> | |
| <div class="empty-state" id="emptyState" style="display: none;"> | |
| <p>No exams scheduled yet. Click the + button to add your first exam!</p> | |
| </div> | |
| </div> | |
| <!-- Add Exam Modal --> | |
| <div class="modal" id="modal"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h2 class="modal-title">Add New Exam</h2> | |
| <button class="close-btn" onclick="closeModal()"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="#fff"> | |
| <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/> | |
| </svg> | |
| </button> | |
| </div> | |
| <form onsubmit="addExam(event)"> | |
| <div class="form-group"> | |
| <label for="examName">Exam Name</label> | |
| <input type="text" id="examName" required placeholder="e.g., Physics Final"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="examDate">Exam Date</label> | |
| <input type="date" id="examDate" required> | |
| </div> | |
| <button type="submit" class="add-btn">Add Exam</button> | |
| </form> | |
| </div> | |
| </div> | |
| <script> | |
| // Initialize particles | |
| function createParticles() { | |
| const particlesContainer = document.getElementById('particles'); | |
| for (let i = 0; i < 50; i++) { | |
| const particle = document.createElement('div'); | |
| particle.className = 'particle'; | |
| particle.style.left = Math.random() * 100 + '%'; | |
| particle.style.animationDelay = Math.random() * 20 + 's'; | |
| particle.style.animationDuration = (15 + Math.random() * 10) + 's'; | |
| particlesContainer.appendChild(particle); | |
| } | |
| } | |
| // Load exams from localStorage | |
| let exams = JSON.parse(localStorage.getItem('exams')) || []; | |
| // Modal functions | |
| function openModal() { | |
| document.getElementById('modal').classList.add('active'); | |
| } | |
| function closeModal() { | |
| document.getElementById('modal').classList.remove('active'); | |
| document.getElementById('examName').value = ''; | |
| document.getElementById('examDate').value = ''; | |
| } | |
| // Add exam | |
| function addExam(event) { | |
| event.preventDefault(); | |
| const name = document.getElementById('examName').value; | |
| const date = document.getElementById('examDate').value; | |
| const exam = { | |
| id: Date.now(), | |
| name, | |
| date | |
| }; | |
| exams.push(exam); | |
| localStorage.setItem('exams', JSON.stringify(exams)); | |
| closeModal(); | |
| renderExams(); | |
| } | |
| // Delete exam | |
| function deleteExam(id) { | |
| exams = exams.filter(exam => exam.id !== id); | |
| localStorage.setItem('exams', JSON.stringify(exams)); | |
| renderExams(); | |
| } | |
| // Calculate days until exam | |
| function calculateDaysUntil(examDate) { | |
| const today = new Date(); | |
| const exam = new Date(examDate); | |
| const diffTime = exam - today; | |
| const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); | |
| return diffDays; | |
| } | |
| // Render exams | |
| function renderExams() { | |
| const grid = document.getElementById('examsGrid'); | |
| const emptyState = document.getElementById('emptyState'); | |
| grid.innerHTML = ''; | |
| if (exams.length === 0) { | |
| emptyState.style.display = 'block'; | |
| return; | |
| } | |
| emptyState.style.display = 'none'; | |
| // Sort exams by date | |
| const sortedExams = [...exams].sort((a, b) => new Date(a.date) - new Date(b.date)); | |
| sortedExams.forEach(exam => { | |
| const daysUntil = calculateDaysUntil(exam.date); | |
| const card = document.createElement('div'); | |
| card.className = 'exam-card'; | |
| let countdownColor = '#00D4FF'; | |
| if (daysUntil <= 3) countdownColor = '#FF0080'; | |
| else if (daysUntil <= 7) countdownColor = '#00FF88'; | |
| card.innerHTML = ` | |
| <div class="delete-btn" onclick="deleteExam(${exam.id})"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="#FF0080"> | |
| <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/> | |
| </svg> | |
| </div> | |
| <h3 class="exam-name">${exam.name}</h3> | |
| <div class="countdown" style="color: ${countdownColor}; text-shadow: 0 0 20px ${countdownColor}80;"> | |
| ${daysUntil} | |
| </div> | |
| <div class="countdown-label">days remaining</div> | |
| <div class="exam-date">${new Date(exam.date).toLocaleDateString('en-US', { | |
| weekday: 'long', | |
| year: 'numeric', | |
| month: 'long', | |
| day: 'numeric' | |
| })}</div> | |
| `; | |
| grid.appendChild(card); | |
| }); | |
| } | |
| // Set minimum date to today | |
| document.getElementById('examDate').min = new Date().toISOString().split('T')[0]; | |
| // Initialize | |
| createParticles(); | |
| renderExams(); | |
| // Update countdown every day | |
| setInterval(renderExams, 86400000); | |
| // Close modal on outside click | |
| window.onclick = function(event) { | |
| const modal = document.getElementById('modal'); | |
| if (event.target === modal) { | |
| closeModal(); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |