| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>ZenHabit | Minimal Habit Tracker</title> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| :root { |
| --primary: #6366f1; |
| --primary-light: #818cf8; |
| --text: #1e293b; |
| --text-light: #64748b; |
| --bg: #f8fafc; |
| --card: #ffffff; |
| --border: #e2e8f0; |
| --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
| --success: #10b981; |
| --warning: #f59e0b; |
| --danger: #ef4444; |
| } |
| |
| .dark-mode { |
| --primary: #818cf8; |
| --primary-light: #a5b4fc; |
| --text: #e2e8f0; |
| --text-light: #94a3b8; |
| --bg: #0f172a; |
| --card: #1e293b; |
| --border: #334155; |
| --shadow: 0 1px 3px rgba(0, 0, 0, 0.3); |
| } |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| transition: background-color 0.3s, color 0.3s; |
| } |
| |
| body { |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| background-color: var(--bg); |
| color: var(--text); |
| line-height: 1.6; |
| } |
| |
| .container { |
| max-width: 800px; |
| margin: 0 auto; |
| padding: 2rem; |
| } |
| |
| header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 2rem; |
| } |
| |
| h1 { |
| font-size: 1.8rem; |
| font-weight: 700; |
| color: var(--primary); |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| } |
| |
| .theme-toggle { |
| background: none; |
| border: none; |
| color: var(--text-light); |
| font-size: 1.2rem; |
| cursor: pointer; |
| transition: transform 0.3s; |
| } |
| |
| .theme-toggle:hover { |
| transform: rotate(30deg); |
| color: var(--primary); |
| } |
| |
| .stats { |
| display: grid; |
| grid-template-columns: repeat(3, 1fr); |
| gap: 1rem; |
| margin-bottom: 2rem; |
| } |
| |
| .stat-card { |
| background-color: var(--card); |
| border-radius: 0.5rem; |
| padding: 1rem; |
| box-shadow: var(--shadow); |
| text-align: center; |
| } |
| |
| .stat-card h3 { |
| font-size: 0.9rem; |
| color: var(--text-light); |
| margin-bottom: 0.5rem; |
| } |
| |
| .stat-card p { |
| font-size: 1.5rem; |
| font-weight: 700; |
| color: var(--primary); |
| } |
| |
| .habits { |
| margin-bottom: 2rem; |
| } |
| |
| .habits-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 1rem; |
| } |
| |
| .habits-header h2 { |
| font-size: 1.3rem; |
| } |
| |
| .add-habit { |
| background-color: var(--primary); |
| color: white; |
| border: none; |
| border-radius: 0.3rem; |
| padding: 0.5rem 1rem; |
| font-size: 0.9rem; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| transition: background-color 0.3s; |
| } |
| |
| .add-habit:hover { |
| background-color: var(--primary-light); |
| } |
| |
| .habit-list { |
| display: flex; |
| flex-direction: column; |
| gap: 0.5rem; |
| } |
| |
| .habit-item { |
| background-color: var(--card); |
| border-radius: 0.5rem; |
| padding: 1rem; |
| box-shadow: var(--shadow); |
| display: flex; |
| align-items: center; |
| gap: 1rem; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .habit-item::before { |
| content: ''; |
| position: absolute; |
| top: 0; |
| left: 0; |
| height: 100%; |
| width: 0.3rem; |
| background-color: var(--primary); |
| } |
| |
| .habit-check { |
| width: 1.5rem; |
| height: 1.5rem; |
| border: 2px solid var(--border); |
| border-radius: 0.3rem; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| transition: all 0.3s; |
| } |
| |
| .habit-check.checked { |
| background-color: var(--primary); |
| border-color: var(--primary); |
| color: white; |
| } |
| |
| .habit-info { |
| flex: 1; |
| } |
| |
| .habit-name { |
| font-weight: 600; |
| margin-bottom: 0.2rem; |
| } |
| |
| .habit-streak { |
| font-size: 0.8rem; |
| color: var(--text-light); |
| display: flex; |
| align-items: center; |
| gap: 0.3rem; |
| } |
| |
| .habit-streak i { |
| color: var(--warning); |
| } |
| |
| .habit-progress { |
| width: 100px; |
| height: 0.3rem; |
| background-color: var(--border); |
| border-radius: 1rem; |
| overflow: hidden; |
| } |
| |
| .progress-bar { |
| height: 100%; |
| background-color: var(--primary); |
| border-radius: 1rem; |
| transition: width 0.5s ease; |
| } |
| |
| .calendar { |
| background-color: var(--card); |
| border-radius: 0.5rem; |
| padding: 1rem; |
| box-shadow: var(--shadow); |
| } |
| |
| .calendar-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 1rem; |
| } |
| |
| .calendar-nav { |
| display: flex; |
| gap: 1rem; |
| } |
| |
| .calendar-nav button { |
| background: none; |
| border: none; |
| color: var(--text-light); |
| cursor: pointer; |
| font-size: 1rem; |
| } |
| |
| .calendar-nav button:hover { |
| color: var(--primary); |
| } |
| |
| .calendar-grid { |
| display: grid; |
| grid-template-columns: repeat(7, 1fr); |
| gap: 0.5rem; |
| } |
| |
| .calendar-day-header { |
| text-align: center; |
| font-size: 0.8rem; |
| color: var(--text-light); |
| padding: 0.5rem 0; |
| } |
| |
| .calendar-day { |
| aspect-ratio: 1; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| border-radius: 0.3rem; |
| cursor: pointer; |
| position: relative; |
| } |
| |
| .calendar-day:hover { |
| background-color: var(--border); |
| } |
| |
| .calendar-day.today { |
| background-color: var(--primary); |
| color: white; |
| } |
| |
| .day-number { |
| font-size: 0.9rem; |
| font-weight: 500; |
| } |
| |
| .day-habits { |
| position: absolute; |
| bottom: 0.2rem; |
| display: flex; |
| gap: 0.2rem; |
| } |
| |
| .day-habit-dot { |
| width: 0.3rem; |
| height: 0.3rem; |
| border-radius: 50%; |
| background-color: var(--success); |
| } |
| |
| .modal { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-color: rgba(0, 0, 0, 0.5); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| z-index: 1000; |
| opacity: 0; |
| pointer-events: none; |
| transition: opacity 0.3s; |
| } |
| |
| .modal.active { |
| opacity: 1; |
| pointer-events: all; |
| } |
| |
| .modal-content { |
| background-color: var(--card); |
| border-radius: 0.5rem; |
| padding: 1.5rem; |
| width: 90%; |
| max-width: 400px; |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); |
| transform: translateY(-20px); |
| transition: transform 0.3s; |
| } |
| |
| .modal.active .modal-content { |
| transform: translateY(0); |
| } |
| |
| .modal-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 1rem; |
| } |
| |
| .modal-header h3 { |
| font-size: 1.2rem; |
| } |
| |
| .close-modal { |
| background: none; |
| border: none; |
| font-size: 1.2rem; |
| color: var(--text-light); |
| cursor: pointer; |
| } |
| |
| .form-group { |
| margin-bottom: 1rem; |
| } |
| |
| .form-group label { |
| display: block; |
| margin-bottom: 0.5rem; |
| font-size: 0.9rem; |
| color: var(--text-light); |
| } |
| |
| .form-group input, |
| .form-group select { |
| width: 100%; |
| padding: 0.5rem; |
| border: 1px solid var(--border); |
| border-radius: 0.3rem; |
| background-color: var(--bg); |
| color: var(--text); |
| } |
| |
| .modal-actions { |
| display: flex; |
| justify-content: flex-end; |
| gap: 0.5rem; |
| margin-top: 1rem; |
| } |
| |
| .btn { |
| padding: 0.5rem 1rem; |
| border-radius: 0.3rem; |
| cursor: pointer; |
| font-size: 0.9rem; |
| border: none; |
| transition: background-color 0.3s; |
| } |
| |
| .btn-primary { |
| background-color: var(--primary); |
| color: white; |
| } |
| |
| .btn-primary:hover { |
| background-color: var(--primary-light); |
| } |
| |
| .btn-secondary { |
| background-color: var(--border); |
| color: var(--text); |
| } |
| |
| .btn-secondary:hover { |
| background-color: #d1d5db; |
| } |
| |
| @media (max-width: 600px) { |
| .stats { |
| grid-template-columns: 1fr; |
| } |
| |
| .container { |
| padding: 1rem; |
| } |
| } |
| |
| |
| @keyframes pulse { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.1); } |
| 100% { transform: scale(1); } |
| } |
| |
| .habit-check.checked { |
| animation: pulse 0.3s ease; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <header> |
| <h1><i class="fas fa-leaf"></i> ZenHabit</h1> |
| <button class="theme-toggle" id="themeToggle"> |
| <i class="fas fa-moon"></i> |
| </button> |
| </header> |
|
|
| <div class="stats"> |
| <div class="stat-card"> |
| <h3>Current Streak</h3> |
| <p id="currentStreak">5</p> |
| </div> |
| <div class="stat-card"> |
| <h3>Habits Tracked</h3> |
| <p id="habitsTracked">8</p> |
| </div> |
| <div class="stat-card"> |
| <h3>Completion Rate</h3> |
| <p id="completionRate">72%</p> |
| </div> |
| </div> |
|
|
| <div class="habits"> |
| <div class="habits-header"> |
| <h2>Today's Habits</h2> |
| <button class="add-habit" id="addHabitBtn"> |
| <i class="fas fa-plus"></i> Add Habit |
| </button> |
| </div> |
| <div class="habit-list" id="habitList"> |
| |
| </div> |
| </div> |
|
|
| <div class="calendar"> |
| <div class="calendar-header"> |
| <h3 id="currentMonth">June 2023</h3> |
| <div class="calendar-nav"> |
| <button id="prevMonth"><i class="fas fa-chevron-left"></i></button> |
| <button id="nextMonth"><i class="fas fa-chevron-right"></i></button> |
| </div> |
| </div> |
| <div class="calendar-grid" id="calendarGrid"> |
| |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="modal" id="addHabitModal"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h3>Add New Habit</h3> |
| <button class="close-modal" id="closeModal">×</button> |
| </div> |
| <form id="habitForm"> |
| <div class="form-group"> |
| <label for="habitName">Habit Name</label> |
| <input type="text" id="habitName" placeholder="e.g. Drink water" required> |
| </div> |
| <div class="form-group"> |
| <label for="habitFrequency">Frequency</label> |
| <select id="habitFrequency" required> |
| <option value="daily">Daily</option> |
| <option value="weekly">Weekly</option> |
| <option value="monthly">Monthly</option> |
| </select> |
| </div> |
| <div class="modal-actions"> |
| <button type="button" class="btn btn-secondary" id="cancelHabit">Cancel</button> |
| <button type="submit" class="btn btn-primary">Add Habit</button> |
| </div> |
| </form> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let habits = [ |
| { id: 1, name: "Morning Meditation", streak: 7, frequency: "daily", progress: 85, checked: false }, |
| { id: 2, name: "Drink 2L water", streak: 14, frequency: "daily", progress: 60, checked: true }, |
| { id: 3, name: "Read 30 minutes", streak: 21, frequency: "daily", progress: 90, checked: false }, |
| { id: 4, name: "Workout", streak: 5, frequency: "weekly", progress: 75, checked: false }, |
| { id: 5, name: "Journaling", streak: 10, frequency: "daily", progress: 50, checked: true } |
| ]; |
| |
| |
| const themeToggle = document.getElementById('themeToggle'); |
| const addHabitBtn = document.getElementById('addHabitBtn'); |
| const addHabitModal = document.getElementById('addHabitModal'); |
| const closeModal = document.getElementById('closeModal'); |
| const cancelHabit = document.getElementById('cancelHabit'); |
| const habitForm = document.getElementById('habitForm'); |
| const habitList = document.getElementById('habitList'); |
| const currentStreak = document.getElementById('currentStreak'); |
| const habitsTracked = document.getElementById('habitsTracked'); |
| const completionRate = document.getElementById('completionRate'); |
| const currentMonth = document.getElementById('currentMonth'); |
| const calendarGrid = document.getElementById('calendarGrid'); |
| const prevMonthBtn = document.getElementById('prevMonth'); |
| const nextMonthBtn = document.getElementById('nextMonth'); |
| |
| |
| let currentDate = new Date(); |
| let currentYear = currentDate.getFullYear(); |
| let currentMonthIndex = currentDate.getMonth(); |
| |
| |
| document.addEventListener('DOMContentLoaded', () => { |
| renderHabits(); |
| renderCalendar(); |
| updateStats(); |
| |
| |
| if (localStorage.getItem('theme') === 'dark') { |
| document.body.classList.add('dark-mode'); |
| themeToggle.innerHTML = '<i class="fas fa-sun"></i>'; |
| } |
| }); |
| |
| |
| themeToggle.addEventListener('click', () => { |
| document.body.classList.toggle('dark-mode'); |
| if (document.body.classList.contains('dark-mode')) { |
| localStorage.setItem('theme', 'dark'); |
| themeToggle.innerHTML = '<i class="fas fa-sun"></i>'; |
| } else { |
| localStorage.setItem('theme', 'light'); |
| themeToggle.innerHTML = '<i class="fas fa-moon"></i>'; |
| } |
| }); |
| |
| |
| addHabitBtn.addEventListener('click', () => { |
| addHabitModal.classList.add('active'); |
| }); |
| |
| closeModal.addEventListener('click', () => { |
| addHabitModal.classList.remove('active'); |
| }); |
| |
| cancelHabit.addEventListener('click', () => { |
| addHabitModal.classList.remove('active'); |
| }); |
| |
| |
| habitForm.addEventListener('submit', (e) => { |
| e.preventDefault(); |
| |
| const name = document.getElementById('habitName').value; |
| const frequency = document.getElementById('habitFrequency').value; |
| |
| const newHabit = { |
| id: habits.length + 1, |
| name: name, |
| streak: 0, |
| frequency: frequency, |
| progress: 0, |
| checked: false |
| }; |
| |
| habits.push(newHabit); |
| renderHabits(); |
| updateStats(); |
| renderCalendar(); |
| |
| |
| habitForm.reset(); |
| addHabitModal.classList.remove('active'); |
| }); |
| |
| |
| function renderHabits() { |
| habitList.innerHTML = ''; |
| |
| habits.forEach(habit => { |
| const habitItem = document.createElement('div'); |
| habitItem.className = 'habit-item'; |
| |
| habitItem.innerHTML = ` |
| <div class="habit-check ${habit.checked ? 'checked' : ''}" data-id="${habit.id}"> |
| ${habit.checked ? '<i class="fas fa-check"></i>' : ''} |
| </div> |
| <div class="habit-info"> |
| <div class="habit-name">${habit.name}</div> |
| <div class="habit-streak"> |
| <i class="fas fa-fire"></i> ${habit.streak} day streak |
| </div> |
| </div> |
| <div class="habit-progress"> |
| <div class="progress-bar" style="width: ${habit.progress}%"></div> |
| </div> |
| `; |
| |
| habitList.appendChild(habitItem); |
| }); |
| |
| |
| document.querySelectorAll('.habit-check').forEach(checkbox => { |
| checkbox.addEventListener('click', function() { |
| const habitId = parseInt(this.getAttribute('data-id')); |
| const habit = habits.find(h => h.id === habitId); |
| |
| habit.checked = !habit.checked; |
| |
| if (habit.checked) { |
| habit.streak += 1; |
| habit.progress = Math.min(habit.progress + 20, 100); |
| } else { |
| habit.streak = Math.max(habit.streak - 1, 0); |
| habit.progress = Math.max(habit.progress - 20, 0); |
| } |
| |
| renderHabits(); |
| updateStats(); |
| }); |
| }); |
| } |
| |
| |
| function updateStats() { |
| const totalHabits = habits.length; |
| const completedHabits = habits.filter(h => h.checked).length; |
| const completionPercentage = Math.round((completedHabits / totalHabits) * 100); |
| |
| |
| const longestStreak = habits.reduce((max, habit) => Math.max(max, habit.streak), 0); |
| |
| currentStreak.textContent = longestStreak; |
| habitsTracked.textContent = totalHabits; |
| completionRate.textContent = `${completionPercentage}%`; |
| } |
| |
| |
| function renderCalendar() { |
| |
| calendarGrid.innerHTML = ''; |
| |
| |
| const monthNames = ["January", "February", "March", "April", "May", "June", |
| "July", "August", "September", "October", "November", "December"]; |
| currentMonth.textContent = `${monthNames[currentMonthIndex]} ${currentYear}`; |
| |
| |
| const firstDay = new Date(currentYear, currentMonthIndex, 1).getDay(); |
| const daysInMonth = new Date(currentYear, currentMonthIndex + 1, 0).getDate(); |
| |
| |
| const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; |
| dayNames.forEach(day => { |
| const dayHeader = document.createElement('div'); |
| dayHeader.className = 'calendar-day-header'; |
| dayHeader.textContent = day; |
| calendarGrid.appendChild(dayHeader); |
| }); |
| |
| |
| for (let i = 0; i < firstDay; i++) { |
| const emptyDay = document.createElement('div'); |
| emptyDay.className = 'calendar-day'; |
| calendarGrid.appendChild(emptyDay); |
| } |
| |
| |
| for (let i = 1; i <= daysInMonth; i++) { |
| const dayElement = document.createElement('div'); |
| dayElement.className = 'calendar-day'; |
| |
| |
| const today = new Date(); |
| if (i === today.getDate() && currentMonthIndex === today.getMonth() && currentYear === today.getFullYear()) { |
| dayElement.classList.add('today'); |
| } |
| |
| |
| const dayNumber = document.createElement('div'); |
| dayNumber.className = 'day-number'; |
| dayNumber.textContent = i; |
| dayElement.appendChild(dayNumber); |
| |
| |
| const habitsCompleted = Math.floor(Math.random() * 3); |
| if (habitsCompleted > 0) { |
| const dayHabits = document.createElement('div'); |
| dayHabits.className = 'day-habits'; |
| |
| for (let j = 0; j < habitsCompleted; j++) { |
| const habitDot = document.createElement('div'); |
| habitDot.className = 'day-habit-dot'; |
| dayHabits.appendChild(habitDot); |
| } |
| |
| dayElement.appendChild(dayHabits); |
| } |
| |
| calendarGrid.appendChild(dayElement); |
| } |
| } |
| |
| |
| prevMonthBtn.addEventListener('click', () => { |
| currentMonthIndex--; |
| if (currentMonthIndex < 0) { |
| currentMonthIndex = 11; |
| currentYear--; |
| } |
| renderCalendar(); |
| }); |
| |
| nextMonthBtn.addEventListener('click', () => { |
| currentMonthIndex++; |
| if (currentMonthIndex > 11) { |
| currentMonthIndex = 0; |
| currentYear++; |
| } |
| renderCalendar(); |
| }); |
| |
| |
| window.addEventListener('click', (e) => { |
| if (e.target === addHabitModal) { |
| addHabitModal.classList.remove('active'); |
| } |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> |
| </html> |