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
22 changes: 9 additions & 13 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,24 @@
</head>
<body>
<div id="app">
<!-- Components will be injected here by other members -->
<header id="main-header"></header>

<header id="main-header">
<h1>My To-Do List</h1>
<button id="theme-toggle" aria-label="Toggle dark mode">
<span class="toggle-icon" aria-hidden="true">🌙</span>
<span class="toggle-label">Dark Mode</span>
</button>
</header>

<main id="main-content">
<!-- Search Bar will go here (Issue #19) -->
<div id="search-container"></div>

<!-- Input Section (Issue #5 & #11) -->
<div id="input-section"></div>

<!-- Task List (Issue #10) -->
<ul id="task-list"></ul>

<!-- Empty State (Issue #17) -->
<div id="empty-state"></div>

<!-- Filter Controls (Issue #13) -->
<div id="filter-controls"></div>
</main>

<footer id="main-footer"></footer>
</div>
<script src="script.js"></script>
</body>
</html>
</html>
88 changes: 81 additions & 7 deletions public/script.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,86 @@
// State Management
let tasks = [];

// DOM Elements (Will be populated as elements are added)
const taskList = document.getElementById('task-list');
// DOM Elements
const taskList = document.getElementById("task-list");

// Initial Render
document.addEventListener('DOMContentLoaded', () => {
console.log("App Initialized");
// Load tasks will be called here later (Issue #9)
});
// ============================================
// THEME MANAGEMENT
// ============================================

const THEME_KEY = "todo-app-theme";
const DARK_THEME = "dark";
const LIGHT_THEME = "light";

/**
* Applies a theme by setting data-theme on <html> and
* updating the toggle button's label + icon.
*/
function applyTheme(theme) {
const html = document.documentElement;
const btn = document.getElementById("theme-toggle");
const icon = btn?.querySelector(".toggle-icon");
const label = btn?.querySelector(".toggle-label");

if (theme === DARK_THEME) {
html.setAttribute("data-theme", DARK_THEME);
if (icon) icon.textContent = "☀️";
if (label) label.textContent = "Light Mode";
btn?.setAttribute("aria-label", "Switch to light mode");
} else {
html.removeAttribute("data-theme");
if (icon) icon.textContent = "🌙";
if (label) label.textContent = "Dark Mode";
btn?.setAttribute("aria-label", "Switch to dark mode");
}
}

/**
* Reads the user's saved preference, falling back to the
* OS-level prefers-color-scheme setting.
*/
function getInitialTheme() {
const saved = localStorage.getItem(THEME_KEY);
if (saved) return saved;

const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
return prefersDark ? DARK_THEME : LIGHT_THEME;
}

/**
* Toggles between light and dark and persists the choice.
*/
function toggleTheme() {
const current = document.documentElement.getAttribute("data-theme");
const next = current === DARK_THEME ? LIGHT_THEME : DARK_THEME;

applyTheme(next);
localStorage.setItem(THEME_KEY, next);
}

// ============================================
// INITIALISATION
// ============================================

document.addEventListener("DOMContentLoaded", () => {
console.log("App Initialized");

// Apply the right theme immediately (prevents flash of wrong theme)
applyTheme(getInitialTheme());

// Wire up the toggle button
const themeToggleBtn = document.getElementById("theme-toggle");
themeToggleBtn?.addEventListener("click", toggleTheme);

// Keep in sync if the OS theme changes while the app is open
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (e) => {
// Only follow the OS if the user hasn't overridden it manually
if (!localStorage.getItem(THEME_KEY)) {
applyTheme(e.matches ? DARK_THEME : LIGHT_THEME);
}
});

// Load tasks will be called here later (Issue #9)
});
161 changes: 156 additions & 5 deletions public/style.css
Original file line number Diff line number Diff line change
@@ -1,22 +1,173 @@
/* ============================================
DESIGN TOKENS — Global CSS Custom Properties
============================================ */

:root {
/* --- Brand Colors --- */
--color-primary: #6366f1; /* Indigo — main actions */
--color-primary-hover: #4f46e5;
--color-danger: #ef4444; /* Delete / destructive */
--color-danger-hover: #dc2626;
--color-success: #22c55e; /* Completed tasks */

/* --- Light Mode Surface Colors (default) --- */
--color-bg: #f4f7f9;
--color-surface: #ffffff;
--color-surface-alt: #f0f2f5; /* Subtle secondary panels */
--color-border: #e2e8f0;

/* --- Light Mode Text Colors --- */
--color-text-primary: #1e293b;
--color-text-secondary: #64748b;
--color-text-muted: #94a3b8;
--color-text-on-primary:#ffffff; /* Text placed on --color-primary */

/* --- Typography --- */
--font-sans: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
--font-mono: 'Courier New', Courier, monospace;

--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */

--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-bold: 700;

/* --- Spacing Scale --- */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-12: 3rem;

/* --- Shape & Depth --- */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--radius-full: 9999px;

--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.07), 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.05);

/* --- Transitions --- */
--transition-fast: 150ms ease;
--transition-normal: 250ms ease;
}

/* ============================================
DARK MODE OVERRIDES
Applied by toggling data-theme="dark" on <html>
============================================ */

[data-theme="dark"] {
--color-bg: #0f172a;
--color-surface: #1e293b;
--color-surface-alt: #273549;
--color-border: #334155;

--color-text-primary: #f1f5f9;
--color-text-secondary: #94a3b8;
--color-text-muted: #64748b;

--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4), 0 2px 4px rgba(0, 0, 0, 0.2);
}

/* ============================================
BASE STYLES — consume the tokens above
============================================ */

/* Basic Reset */
* {
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f4f4f4;
color: #333;
font-family: var(--font-sans);
font-size: var(--text-base);
font-weight: var(--font-weight-normal);
background-color: var(--color-bg);
color: var(--color-text-primary);
line-height: 1.6;
/* Smooth the bg/text transition when toggling themes */
transition: background-color var(--transition-normal),
color var(--transition-normal);
}

#app {
max-width: 600px;
max-width: 640px;
margin: 0 auto;
min-height: 100vh;
display: flex;
flex-direction: column;
padding: var(--space-4);
gap: var(--space-6);
}

/* ============================================
HEADER & THEME TOGGLE BUTTON
============================================ */

#main-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-4) var(--space-6);
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
transition: background-color var(--transition-normal),
border-color var(--transition-normal);
}

#main-header h1 {
font-size: var(--text-xl);
font-weight: var(--font-weight-bold);
color: var(--color-text-primary);
}

/* Theme toggle button */
#theme-toggle {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-4);
font-family: var(--font-sans);
font-size: var(--text-sm);
font-weight: var(--font-weight-medium);
color: var(--color-text-secondary);
background-color: var(--color-surface-alt);
border: 1px solid var(--color-border);
border-radius: var(--radius-full);
cursor: pointer;
transition: background-color var(--transition-fast),
color var(--transition-fast),
border-color var(--transition-fast),
box-shadow var(--transition-fast);
}

#theme-toggle:hover {
background-color: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-text-on-primary);
box-shadow: var(--shadow-sm);
}

#theme-toggle:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 3px;
}

#theme-toggle .toggle-icon {
font-size: var(--text-base);
line-height: 1;
}