diff --git a/dashboard/habits.html b/dashboard/habits.html
new file mode 100644
index 0000000..b7480b6
--- /dev/null
+++ b/dashboard/habits.html
@@ -0,0 +1,267 @@
+
+
+
+
+
+
+ Echo · Habits
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dashboard/index.html b/dashboard/index.html
index e15431c..bdf20cd 100644
--- a/dashboard/index.html
+++ b/dashboard/index.html
@@ -1071,6 +1071,10 @@
KB
+
+
+ Habits
+
Files
diff --git a/dashboard/tests/test_habits_frontend.py b/dashboard/tests/test_habits_frontend.py
new file mode 100644
index 0000000..b4f7340
--- /dev/null
+++ b/dashboard/tests/test_habits_frontend.py
@@ -0,0 +1,191 @@
+"""
+Test suite for Habits frontend page structure and navigation
+Story US-006: Frontend - Page structure, layout, and navigation link
+"""
+
+import sys
+import os
+from pathlib import Path
+
+# Add parent directory to path for imports
+sys.path.insert(0, str(Path(__file__).parent.parent))
+
+def test_habits_html_exists():
+ """Test 1: habits.html exists in dashboard/"""
+ habits_path = Path(__file__).parent.parent / 'habits.html'
+ assert habits_path.exists(), "habits.html should exist in dashboard/"
+ print("✓ Test 1: habits.html exists")
+
+def test_habits_html_structure():
+ """Test 2: Page includes common.css, Lucide icons, and swipe-nav.js"""
+ habits_path = Path(__file__).parent.parent / 'habits.html'
+ content = habits_path.read_text()
+
+ assert 'href="/echo/common.css"' in content, "Should include common.css"
+ assert 'lucide@latest/dist/umd/lucide.min.js' in content, "Should include Lucide icons"
+ assert 'src="/echo/swipe-nav.js"' in content, "Should include swipe-nav.js"
+ print("✓ Test 2: Page includes required CSS and JS")
+
+def test_page_has_header():
+ """Test 3: Page has header with 'Habits' title and 'Add Habit' button"""
+ habits_path = Path(__file__).parent.parent / 'habits.html'
+ content = habits_path.read_text()
+
+ assert 'class="page-title"' in content, "Should have page-title element"
+ assert '>Habits' in content, "Should have 'Habits' title"
+ assert 'Add Habit' in content, "Should have 'Add Habit' button"
+ assert 'showAddHabitModal()' in content, "Add Habit button should have onclick handler"
+ print("✓ Test 3: Page has header with title and Add Habit button")
+
+def test_empty_state():
+ """Test 4: Empty state message shown when no habits exist"""
+ habits_path = Path(__file__).parent.parent / 'habits.html'
+ content = habits_path.read_text()
+
+ assert 'No habits yet' in content, "Should have empty state message"
+ assert 'Create your first habit' in content, "Should have call-to-action"
+ assert 'empty-state' in content, "Should have empty-state class"
+ print("✓ Test 4: Empty state message present")
+
+def test_grid_container():
+ """Test 5: Grid container uses CSS grid with responsive breakpoints (1/2/3 columns)"""
+ habits_path = Path(__file__).parent.parent / 'habits.html'
+ content = habits_path.read_text()
+
+ assert 'habits-grid' in content, "Should have habits-grid class"
+ assert 'display: grid' in content, "Should use CSS grid"
+ assert 'grid-template-columns' in content, "Should define grid columns"
+
+ # Check responsive breakpoints
+ assert '@media (max-width: 768px)' in content or '@media (max-width:768px)' in content, \
+ "Should have mobile breakpoint"
+ assert 'grid-template-columns: 1fr' in content or 'grid-template-columns:1fr' in content, \
+ "Should have 1 column on mobile"
+
+ # Check for 2 or 3 column layouts
+ assert ('grid-template-columns: repeat(2, 1fr)' in content or
+ 'grid-template-columns:repeat(2,1fr)' in content or
+ 'grid-template-columns: repeat(3, 1fr)' in content or
+ 'grid-template-columns:repeat(3,1fr)' in content), \
+ "Should have multi-column layout for larger screens"
+
+ print("✓ Test 5: Grid container with responsive breakpoints")
+
+def test_index_navigation_link():
+ """Test 6: index.html navigation includes 'Habits' link with dumbbell icon"""
+ index_path = Path(__file__).parent.parent / 'index.html'
+ content = index_path.read_text()
+
+ assert '/echo/habits.html' in content, "Should link to /echo/habits.html"
+ assert 'dumbbell' in content, "Should have dumbbell icon"
+ assert '>Habits' in content, "Should have 'Habits' label"
+
+ # Check that Habits link is in the nav
+ nav_start = content.find('', nav_start)
+ nav_section = content[nav_start:nav_end]
+
+ assert '/echo/habits.html' in nav_section, "Habits link should be in navigation"
+ assert 'dumbbell' in nav_section, "Dumbbell icon should be in navigation"
+
+ print("✓ Test 6: index.html includes Habits navigation link")
+
+def test_page_fetches_habits():
+ """Test 7: Page fetches GET /echo/api/habits on load"""
+ habits_path = Path(__file__).parent.parent / 'habits.html'
+ content = habits_path.read_text()
+
+ assert "fetch('/echo/api/habits')" in content or 'fetch("/echo/api/habits")' in content, \
+ "Should fetch from /echo/api/habits"
+ assert 'loadHabits' in content, "Should have loadHabits function"
+
+ # Check that loadHabits is called on page load
+ # (either in inline script or as last statement)
+ assert content.count('loadHabits()') > 0, "loadHabits should be called"
+
+ print("✓ Test 7: Page fetches habits on load")
+
+def test_habit_card_rendering():
+ """Test 8: Placeholder habit card rendering exists"""
+ habits_path = Path(__file__).parent.parent / 'habits.html'
+ content = habits_path.read_text()
+
+ assert 'renderHabitCard' in content, "Should have renderHabitCard function"
+ assert 'habit-card' in content, "Should have habit-card class"
+ assert 'renderHabits' in content, "Should have renderHabits function"
+
+ print("✓ Test 8: Habit card rendering functions exist")
+
+def test_no_console_errors_structure():
+ """Test 9: No obvious console error sources (basic structure check)"""
+ habits_path = Path(__file__).parent.parent / 'habits.html'
+ content = habits_path.read_text()
+
+ # Check for basic script structure
+ assert '')
+ assert script_open == script_close, f"Script tags should match (found {script_open} opens, {script_close} closes)"
+
+ print("✓ Test 10: HTML structure is well-formed")
+
+def run_all_tests():
+ """Run all tests in sequence"""
+ tests = [
+ test_habits_html_exists,
+ test_habits_html_structure,
+ test_page_has_header,
+ test_empty_state,
+ test_grid_container,
+ test_index_navigation_link,
+ test_page_fetches_habits,
+ test_habit_card_rendering,
+ test_no_console_errors_structure,
+ test_typecheck,
+ ]
+
+ print(f"\nRunning {len(tests)} frontend tests for US-006...\n")
+
+ failed = []
+ for test in tests:
+ try:
+ test()
+ except AssertionError as e:
+ print(f"✗ {test.__name__}: {e}")
+ failed.append((test.__name__, str(e)))
+ except Exception as e:
+ print(f"✗ {test.__name__}: Unexpected error: {e}")
+ failed.append((test.__name__, str(e)))
+
+ print(f"\n{'='*60}")
+ if failed:
+ print(f"FAILED: {len(failed)} test(s) failed:")
+ for name, error in failed:
+ print(f" - {name}: {error}")
+ sys.exit(1)
+ else:
+ print(f"SUCCESS: All {len(tests)} tests passed!")
+ sys.exit(0)
+
+if __name__ == '__main__':
+ run_all_tests()