Files
wordsearch/docs/superpowers/plans/2026-05-06-website-redesign.md
Mathew Sir Guest the bestandClaude Opus 4.6 36c3c712fe feat: add scoreboard with player names, per-category leaderboards, and themed tables
Add player_name column to games (migration included), with regex-based
validation and anti-abuse sanitization. New /scoreboard page shows recent
games from localStorage, popular categories with play counts, and top-10
per-category leaderboards sorted by completion time. Also includes
two-click reverse word matching, base URL prefix support for reverse-proxy
hosting, BasePageHandler refactoring, themed table CSS for all 5 themes,
and comprehensive test coverage for player names and scoreboard API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-07 10:38:19 -06:00

764 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Website Redesign Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Redesign the word search frontend with a dark indigo palette, pill-style navbar, footer, two new content pages (How to Play, About), and emoji/icon accents.
**Architecture:** Enhance existing Tornado template hierarchy in place. `base.html` gets navbar restructure, Bootstrap Icons CDN, semantic HTML, and a footer. Child templates set an `active_page` block for nav highlighting. Two new handlers + templates for the new pages. CSS swaps teal variables for indigo palette.
**Tech Stack:** Python/Tornado, Bootstrap 5.3 (dark mode), Bootstrap Icons (CDN), pytest + pytest-tornasync
**Spec:** `docs/superpowers/specs/2026-05-06-website-redesign-design.md`
---
## File Map
### Modified
| File | Responsibility |
|------|---------------|
| `tests/e2e/test_hello.py` | E2e tests for all routes including new ones |
| `app.py` | Route registration — add `/howtoplay` and `/about` |
| `static/css/theme.css` | Full palette swap + new component styles |
| `templates/base.html` | Shared layout: navbar, footer, Bootstrap Icons, active page logic |
| `templates/main.html` | Homepage hero with emoji and coral CTA |
| `templates/game.html` | Game board card restyled for new palette |
### Created
| File | Responsibility |
|------|---------------|
| `handlers/howtoplay.py` | HowToPlayHandler — renders howtoplay.html |
| `handlers/about.py` | AboutHandler — renders about.html |
| `templates/howtoplay.html` | How to Play instructions page |
| `templates/about.html` | About page with app description and authors |
---
### Task 1: Add e2e tests for new routes
**Files:**
- Modify: `tests/e2e/test_hello.py`
- [ ] **Step 1: Add tests for `/howtoplay` and `/about` routes**
Replace the contents of `tests/e2e/test_hello.py` with:
```python
import pytest
from app import make_app
@pytest.fixture
def app():
return make_app()
async def test_homepage_returns_200(http_server_client):
response = await http_server_client.fetch("/")
assert response.code == 200
async def test_game_returns_200(http_server_client):
response = await http_server_client.fetch("/game")
assert response.code == 200
async def test_howtoplay_returns_200(http_server_client):
response = await http_server_client.fetch("/howtoplay")
assert response.code == 200
async def test_about_returns_200(http_server_client):
response = await http_server_client.fetch("/about")
assert response.code == 200
```
- [ ] **Step 2: Run tests to verify new tests fail**
Run: `uv run pytest tests/e2e/ -v`
Expected: `test_homepage_returns_200` PASS, `test_game_returns_200` PASS, `test_howtoplay_returns_200` FAIL (404), `test_about_returns_200` FAIL (404)
---
### Task 2: Create handlers and register routes
**Files:**
- Create: `handlers/howtoplay.py`
- Create: `handlers/about.py`
- Create: `templates/howtoplay.html` (minimal placeholder)
- Create: `templates/about.html` (minimal placeholder)
- Modify: `app.py`
- [ ] **Step 1: Create HowToPlayHandler**
Create `handlers/howtoplay.py`:
```python
import tornado.web
class HowToPlayHandler(tornado.web.RequestHandler):
def get(self):
self.render("howtoplay.html")
```
- [ ] **Step 2: Create AboutHandler**
Create `handlers/about.py`:
```python
import tornado.web
class AboutHandler(tornado.web.RequestHandler):
def get(self):
self.render("about.html")
```
- [ ] **Step 3: Create placeholder templates**
Create `templates/howtoplay.html`:
```html
{% extends "base.html" %}
{% block title %}Word Search — How to Play{% end %}
{% block content %}
<h2>How to Play</h2>
<p>Instructions coming soon.</p>
{% end %}
```
Create `templates/about.html`:
```html
{% extends "base.html" %}
{% block title %}Word Search — About{% end %}
{% block content %}
<h2>About</h2>
<p>About page coming soon.</p>
{% end %}
```
- [ ] **Step 4: Register routes in app.py**
In `app.py`, add imports after the existing handler imports:
```python
from handlers.howtoplay import HowToPlayHandler
from handlers.about import AboutHandler
```
Add routes to the `make_app()` route list, after the `/game` route:
```python
(r"/howtoplay", HowToPlayHandler),
(r"/about", AboutHandler),
```
- [ ] **Step 5: Run tests to verify all pass**
Run: `uv run pytest tests/e2e/ -v`
Expected: All 4 tests PASS
- [ ] **Step 6: Lint and commit**
Run: `uv run black . && uv run flake8`
```bash
git add handlers/howtoplay.py handlers/about.py templates/howtoplay.html templates/about.html app.py tests/e2e/test_hello.py
git commit -m "feat: add howtoplay and about routes with placeholder templates"
```
---
### Task 3: Swap CSS palette and add component styles
**Files:**
- Modify: `static/css/theme.css`
- [ ] **Step 1: Replace full contents of `static/css/theme.css`**
```css
:root {
--indigo-darkest: #1a1a2e;
--indigo-dark: #16213e;
--indigo-mid: #0f3460;
--coral: #e94560;
--coral-light: #ff6b81;
--gold: #f5c518;
--text-primary: #e0e0e0;
--text-muted: #a0aec0;
}
body {
background-color: var(--indigo-darkest);
color: var(--text-primary);
}
/* Navbar */
.navbar {
background-color: var(--indigo-dark);
border-bottom: 1px solid var(--indigo-mid);
}
.navbar-brand {
color: var(--coral);
font-weight: 700;
display: flex;
align-items: center;
gap: 0.4rem;
}
.navbar-brand:hover {
color: var(--coral-light);
}
.navbar-nav .nav-link {
color: var(--text-muted);
border-radius: 6px;
padding: 0.4rem 1rem;
margin: 0 0.15rem;
font-weight: 500;
transition: background-color 0.2s, color 0.2s;
}
.navbar-nav .nav-link:hover {
color: #ffffff;
background-color: rgba(15, 52, 96, 0.5);
}
.navbar-nav .nav-link.active {
color: #ffffff;
background-color: var(--indigo-mid);
}
/* Hero */
.hero {
text-align: center;
padding: 3rem 1rem;
}
.hero-emoji {
font-size: 3rem;
margin-bottom: 0.75rem;
}
.hero h1 {
color: #ffffff;
font-weight: 800;
}
.hero .lead {
color: var(--text-muted);
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
/* Buttons */
.btn-accent {
background-color: var(--coral);
border-color: var(--coral);
color: #ffffff;
font-weight: 600;
}
.btn-accent:hover {
background-color: var(--coral-light);
border-color: var(--coral-light);
color: #ffffff;
}
/* Cards */
.card {
background-color: var(--indigo-dark);
border-color: var(--indigo-mid);
}
/* Content pages */
.content-section {
background-color: var(--indigo-dark);
border: 1px solid var(--indigo-mid);
border-radius: 8px;
padding: 2rem;
margin-bottom: 1.5rem;
}
.step-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
background-color: var(--indigo-mid);
color: var(--gold);
border-radius: 50%;
font-weight: 700;
font-size: 0.875rem;
flex-shrink: 0;
}
.step-item {
display: flex;
gap: 1rem;
align-items: flex-start;
margin-bottom: 1.25rem;
}
.step-item p {
margin: 0;
}
.tip-card {
background-color: rgba(15, 52, 96, 0.4);
border-left: 3px solid var(--gold);
border-radius: 4px;
padding: 1rem 1.25rem;
margin-bottom: 0.75rem;
}
/* Footer */
.site-footer {
border-top: 1px solid var(--indigo-mid);
padding: 1.25rem 0;
text-align: center;
color: var(--text-muted);
font-size: 0.85rem;
margin-top: 3rem;
}
```
- [ ] **Step 2: Verify the app still renders**
Run: `uv run pytest tests/e2e/ -v`
Expected: All 4 tests PASS (CSS changes don't break templates)
- [ ] **Step 3: Commit**
```bash
git add static/css/theme.css
git commit -m "style: swap teal palette for dark indigo with coral accents"
```
---
### Task 4: Restructure base.html
**Files:**
- Modify: `templates/base.html`
- [ ] **Step 1: Replace full contents of `templates/base.html`**
```html
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Word Search{% end %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YcnS/1WR6zNiELmFp5mGg7gCjSP54e0TpoS"
crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
rel="stylesheet">
<link rel="stylesheet" href="{{ static_url('css/theme.css') }}">
</head>
<body>
{% block active_page %}{% end %}
<nav class="navbar navbar-expand-lg">
<div class="container">
<a class="navbar-brand" href="/">
<i class="bi bi-search"></i> Word Search
</a>
<button class="navbar-toggler" type="button"
data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link {% if active_page == 'home' %}active{% end %}"
href="/">
<i class="bi bi-house-door"></i> Home
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_page == 'play' %}active{% end %}"
href="/game">
<i class="bi bi-controller"></i> Play
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_page == 'howtoplay' %}active{% end %}"
href="/howtoplay">
<i class="bi bi-question-circle"></i> How to Play
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_page == 'about' %}active{% end %}"
href="/about">
<i class="bi bi-info-circle"></i> About
</a>
</li>
</ul>
</div>
</div>
</nav>
<main class="container py-4">
{% block content %}{% end %}
</main>
<footer class="site-footer">
<div class="container">
&copy; 2026 Word Search
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
</body>
</html>
```
**Note on active page logic:** Tornado templates don't support `{% set %}` like Jinja2. The `{% block active_page %}{% end %}` block is intentionally empty in the base. Each child template overrides it with `{% block active_page %}{% set active_page = "home" %}{% end %}`. Tornado's `{% set %}` creates a template variable accessible in the rest of the template including the base.
- [ ] **Step 2: Update child templates to set active_page**
In `templates/main.html`, add after the `{% extends %}` line:
```html
{% block active_page %}{% set active_page = "home" %}{% end %}
```
In `templates/game.html`, add after the `{% extends %}` line:
```html
{% block active_page %}{% set active_page = "play" %}{% end %}
```
In `templates/howtoplay.html`, add after the `{% extends %}` line:
```html
{% block active_page %}{% set active_page = "howtoplay" %}{% end %}
```
In `templates/about.html`, add after the `{% extends %}` line:
```html
{% block active_page %}{% set active_page = "about" %}{% end %}
```
- [ ] **Step 3: Run tests**
Run: `uv run pytest tests/e2e/ -v`
Expected: All 4 tests PASS
- [ ] **Step 4: Commit**
```bash
git add templates/base.html templates/main.html templates/game.html templates/howtoplay.html templates/about.html
git commit -m "feat: restructure base template with navbar pills, footer, and active page logic"
```
---
### Task 5: Restyle homepage hero
**Files:**
- Modify: `templates/main.html`
- [ ] **Step 1: Replace content block in `templates/main.html`**
Full file contents:
```html
{% extends "base.html" %}
{% block active_page %}{% set active_page = "home" %}{% end %}
{% block title %}Word Search — Home{% end %}
{% block content %}
<header class="hero">
<div class="hero-emoji">🧩</div>
<h1 class="display-4 fw-bold mb-3">Word Search</h1>
<p class="lead mb-4">
Find hidden words in a grid of letters. Words can appear horizontally,
vertically, or diagonally. How fast can you find them all?
</p>
<a href="/game" class="btn btn-accent btn-lg px-5">🎮 Start Game</a>
</header>
{% end %}
```
- [ ] **Step 2: Run tests**
Run: `uv run pytest tests/e2e/ -v`
Expected: All 4 tests PASS
- [ ] **Step 3: Commit**
```bash
git add templates/main.html
git commit -m "style: restyle homepage hero with emoji and coral CTA"
```
---
### Task 6: Update game page for new palette
**Files:**
- Modify: `templates/game.html`
- [ ] **Step 1: Replace full contents of `templates/game.html`**
```html
{% extends "base.html" %}
{% block active_page %}{% set active_page = "play" %}{% end %}
{% block title %}Word Search — Play{% end %}
{% block content %}
<h2 class="mb-4">🎯 Game Board</h2>
<div class="card">
<div class="card-body text-center py-5">
<p class="text-muted">Game board coming soon.</p>
</div>
</div>
{% end %}
```
- [ ] **Step 2: Run tests**
Run: `uv run pytest tests/e2e/ -v`
Expected: All 4 tests PASS
- [ ] **Step 3: Commit**
```bash
git add templates/game.html
git commit -m "style: update game page with active nav and emoji heading"
```
---
### Task 7: Create How to Play page content
**Files:**
- Modify: `templates/howtoplay.html`
- [ ] **Step 1: Replace full contents of `templates/howtoplay.html`**
```html
{% extends "base.html" %}
{% block active_page %}{% set active_page = "howtoplay" %}{% end %}
{% block title %}Word Search — How to Play{% end %}
{% block content %}
<h2 class="mb-4">📖 How to Play</h2>
<div class="content-section">
<h3 class="mb-4">Step-by-Step Instructions</h3>
<div class="step-item">
<span class="step-number">1</span>
<p>
📋 <strong>Check the word list</strong> — Look at the list of words
displayed alongside the grid. These are the words you need to find.
</p>
</div>
<div class="step-item">
<span class="step-number">2</span>
<p>
🔍 <strong>Scan for the first letter</strong> — Pick a word from the
list and scan the grid for its first letter. This gives you a starting
point.
</p>
</div>
<div class="step-item">
<span class="step-number">3</span>
<p>
↗️ <strong>Look in all directions</strong> — Words can run
horizontally, vertically, or diagonally — and they can go forward or
backward. Check all eight directions from your starting letter.
</p>
</div>
<div class="step-item">
<span class="step-number">4</span>
<p>
👆 <strong>Select the word</strong> — Click or tap the first letter of
the word, then click the last letter to highlight the entire word.
</p>
</div>
<div class="step-item">
<span class="step-number">5</span>
<p>
<strong>Words get crossed off</strong> — When you correctly find a
word, it gets crossed off the list so you can track your progress.
</p>
</div>
<div class="step-item">
<span class="step-number">6</span>
<p>
🏆 <strong>Find them all to win!</strong> — The puzzle is complete when
every word on the list has been found. Try to beat your best time!
</p>
</div>
</div>
<h3 class="mb-3">💡 Tips</h3>
<div class="tip-card">
<strong>Start with uncommon letters</strong> — Letters like Q, X, Z, and J
appear less often in the grid, making words with those letters easier to spot.
</div>
<div class="tip-card">
<strong>Scan systematically</strong> — Work through the grid row by row or
column by column rather than jumping around randomly.
</div>
<div class="tip-card">
<strong>Look for word shapes</strong> — Sometimes you can spot diagonal words
by the distinctive slanted pattern they make in the grid.
</div>
{% end %}
```
- [ ] **Step 2: Run tests**
Run: `uv run pytest tests/e2e/ -v`
Expected: All 4 tests PASS
- [ ] **Step 3: Commit**
```bash
git add templates/howtoplay.html
git commit -m "feat: add How to Play page with step-by-step instructions and tips"
```
---
### Task 8: Create About page content
**Files:**
- Modify: `templates/about.html`
- [ ] **Step 1: Replace full contents of `templates/about.html`**
```html
{% extends "base.html" %}
{% block active_page %}{% set active_page = "about" %}{% end %}
{% block title %}Word Search — About{% end %}
{% block content %}
<h2 class="mb-4">️ About</h2>
<div class="content-section">
<h3>🧩 What is Word Search?</h3>
<p>
Word Search is a browser-based puzzle game where you find hidden words in a
grid of letters. Words can be placed horizontally, vertically, or
diagonally — forwards or backwards. The backend generates puzzles,
validates your selections, and tracks your progress in real time.
</p>
</div>
<div class="content-section">
<h3>🛠️ Built With</h3>
<ul>
<li><strong>Python</strong> — core language</li>
<li><strong>Tornado</strong> — async web framework</li>
<li><strong>Bootstrap 5</strong> — responsive UI</li>
<li><strong>Bootstrap Icons</strong> — interface icons</li>
</ul>
</div>
<div class="content-section">
<h3>✍️ Authors</h3>
<ul>
<li><strong>Mathew Guest</strong> — creator and developer</li>
<li><strong>🤖 Claude</strong> (Anthropic) — AI co-author</li>
</ul>
</div>
<div class="content-section">
<h3>📂 Source Code</h3>
<p>
The source code for this project will be available on GitHub.
<em>Link coming soon.</em>
</p>
</div>
{% end %}
```
- [ ] **Step 2: Run tests**
Run: `uv run pytest tests/e2e/ -v`
Expected: All 4 tests PASS
- [ ] **Step 3: Commit**
```bash
git add templates/about.html
git commit -m "feat: add About page with app description, tech stack, and authors"
```
---
### Task 9: Final verification
- [ ] **Step 1: Run full test suite**
Run: `uv run pytest -v`
Expected: All tests PASS (unit + e2e)
- [ ] **Step 2: Lint and format**
Run: `uv run black . && uv run flake8`
Expected: No errors
- [ ] **Step 3: Start the server and visually verify with Playwright**
Start the app: `uv run python app.py &`
Use Playwright MCP tools to:
1. Navigate to `http://localhost:8888` — verify indigo palette, navbar with 4 pill-style links, "Home" is active, hero section with puzzle emoji, coral "Start Game" button, footer with copyright
2. Navigate to `http://localhost:8888/game` — verify "Play" is active in navbar, game card renders
3. Navigate to `http://localhost:8888/howtoplay` — verify "How to Play" is active, 6 numbered steps visible, 3 tip cards visible
4. Navigate to `http://localhost:8888/about` — verify "About" is active, 4 content sections visible (What is, Built With, Authors, Source Code)
5. Resize browser to mobile width — verify navbar collapses to hamburger menu
- [ ] **Step 4: Stop the server and commit any fixes**
Kill the background server process. If any visual issues were found and fixed, commit the fixes.