Plan: Responsive Design & Mobile UX
Vision
pal-e-docs is on a resume and shown in interviews. The browse frontend must be properly responsive — readable, well-spaced, and user-friendly on any screen size. Not flashy, just correct. Nothing should overflow, nothing should feel cramped, and tables should be navigable on a phone.
Projects & Repos Touched
| Project/Repo | Platform | Role in this plan |
|---|---|---|
| pal-e-docs (app) | Forgejo | Templates, CSS, server-side HTML processing, playwright tests |
Context
The Browse Frontend Polish plan shipped typography, table styling, and pre blocks (PR #43). But visual QA revealed the site is still broken on mobile. The root cause isn't missing CSS — it's structural:
- Zero
@mediaqueries — one layout for all screen sizes - Tables in note content have no wrapper div — the CSS
display: block; overflow-x: autohack breaks table layout semantics - Nav overflows on mobile — brand + 5 links + login all compete for one flex row with no breakpoint
- Doc lists on landing page dump tags inline with titles — chaotic wrapping on narrow screens
- No automated mobile QA — the existing 3 playwright tests only cover mermaid/lightbox
Key architectural insight: We don't need to touch the 75+ notes in the database. The rendering pipeline is:
We control three layers: server-side HTML processing (table wrapper step), Jinja2 templates (structural divs, nav), and CSS (breakpoints, mobile-first rules). All presentation concerns, zero content changes.
Previous Plan
plan-2026-02-26-browse-frontend-polish — completed. Shipped font, table CSS, pre blocks, XSS sanitization, auto-link slugs. This plan addresses the structural gaps that CSS alone can't fix.Depends On
None.
Decisions Made
| Decision | Rationale |
|---|---|
| Server-side table wrapping, not client-side JS | Clean approach. Same HTMLParser pattern as autolink_slugs(). No layout shift on page load. Consistent with existing pipeline. |
| CSS-only responsive nav, no hamburger menu | 5 nav links + login is not enough to justify a hamburger. Flexbox + one @media breakpoint: brand on its own row on mobile, links wrap into a compact second row. Simple, no JS. |
| Playwright tests define "done" before code changes | Tests written first, fail on current state, pass after fixes. CI catches regressions forever. |
| New plan, not extending the old one | Browse Frontend Polish was "make it look decent." This plan is "make the HTML structure correct for responsive design." Different problem, different scope. |
| Pre-commit framework for ruff enforcement | Agents repeatedly failed CI with unformatted code (PRs #37, #46). <code>.pre-commit-config.yaml</code> with ruff-format + ruff check catches this at git commit time. |
Phases
Phase 1: Server-side table wrapper + playwright mobile test suite ✓ COMPLETE
Slug:
Goal: Tables in note content are wrapped in scroll containers server-side. Playwright tests define the mobile responsiveness contract.
Owner: Agent (worktree, pal-e-docs repo)
Status: COMPLETE — PR #45 merged
phase-2026-02-27-1-table-wrapper-testsGoal: Tables in note content are wrapped in scroll containers server-side. Playwright tests define the mobile responsiveness contract.
Owner: Agent (worktree, pal-e-docs repo)
Status: COMPLETE — PR #45 merged
Delivered:
- PR #45 —
wrap_tables.pyHTMLParser wrapping outermost<table>elements in<div class="table-scroll">. Nested table depth tracking (no double-wrap). Safety flush for unclosed tables.str | Nonetype signature matchingautolink_slugs(). - Pipeline wired:
sanitize → autolink → wrap_tables → template - CSS:
.table-scrollwithoverflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%. Removeddisplay: block; overflow-x: autohack from.note-content table. - 16 unit tests + 5 integration tests + 5 playwright mobile tests (151 total passing)
- All 5 playwright mobile tests pass — no horizontal overflow on landing page, tables scrollable in wrapper, nav fits viewport, readable font sizes
Review-fix loop: 2 rounds. Round 1: unclosed table safety flush (blocking), type signature consistency, entity-encoded table-in-pre test, import placement, max-width CSS. Round 2: all fixes verified, clean approval.
Phase 2: Template restructuring + CSS overhaul ✓ COMPLETE
Slug:
Goal: All pages render correctly at 375px. No horizontal overflow. Nav is usable on mobile. Proper @media breakpoints.
Owner: Agent (worktree, pal-e-docs repo)
Status: COMPLETE — PR #48 merged
phase-2026-02-27-2-template-css-overhaulGoal: All pages render correctly at 375px. No horizontal overflow. Nav is usable on mobile. Proper @media breakpoints.
Owner: Agent (worktree, pal-e-docs repo)
Status: COMPLETE — PR #48 merged
Delivered:
- PR #48 — Nav restructured with CSS classes (
.nav-auth,.nav-links,.user-email,.logout-btn,.login-link). All inline styles removed from all 9 templates. @media (max-width: 600px)breakpoint: brand on own row, nav links on own row, auth on own row right-aligned. Reduced main padding/margin. Card grid forced to single column. h1 size reduced.landing.html: tags wrapped in<div class="tag-row">below titles. Section headers moved to.section-headerclass.login.html: all inline styles replaced with CSS classes.tag-rowclass applied consistently acrossnote.html,tag_notes.html,project_notes.html,landing.html.- Form elements inherit Atkinson Hyperlegible via
font-family: inheritrule. .pre-commit-config.yamlwith ruff-format + ruff check (rev v0.15.2).- 151 tests passing, all 5 playwright mobile tests green.
Review-fix loop: 3 rounds. Round 1: remaining inline style in landing.html (blocking), ruff version outdated (blocking), nav-links wrapper suggestion, tag-row consistency, font inheritance. Round 2: note.html tag-row, nav-links mobile gap. Round 3: clean approval.
Phase 3: Visual QA + polish ✓ COMPLETE
Slug:
Goal: Final visual pass. Fix the nav wrap bug, improve mobile spacing. Everything looks intentional, not just "not broken."
Owner: Agent (worktree, pal-e-docs repo)
Status: COMPLETE — PR #50 merged
phase-2026-02-27-3-visual-qa-polishGoal: Final visual pass. Fix the nav wrap bug, improve mobile spacing. Everything looks intentional, not just "not broken."
Owner: Agent (worktree, pal-e-docs repo)
Status: COMPLETE — PR #50 merged
Delivered:
- PR #50 —
flex-basis: 100%→flex: 0 0 100%for.brand,.nav-links,.nav-authin mobile breakpoint. Root cause:flex-shrink: 1default allowed items to compress below 100% width, preventing proper row stacking. - Mobile section spacing:
.section { margin-bottom: 3rem; }in mobile breakpoint (up from 2.5rem default). - New playwright test
test_nav_three_row_layout— validates brand/nav-links/nav-auth have strictly increasing Y positions with 10px minimum gap assertions. - 152 total tests passing, all 9 playwright browser tests green.
Review-fix loop: 2 rounds. Round 1: clean approval on CSS fix. Round 2: strengthened test assertions from simple
> to > + 10 minimum gap (catches zero-height edge case). Clean approval.Key Files
| Phase | File | Repo | Change |
|---|---|---|---|
| 1 ✓ | <code>src/pal_e_docs/wrap_tables.py</code> | pal-e-docs | New — HTMLParser table wrapper |
| 1 ✓ | <code>src/pal_e_docs/routes/frontend.py</code> | pal-e-docs | Wire wrap_tables into rendering pipeline |
| 1 ✓ | <code>tests/test_wrap_tables.py</code> | pal-e-docs | New — 16 unit tests for table wrapping |
| 1 ✓ | <code>tests/test_wrap_tables_integration.py</code> | pal-e-docs | New — 5 integration tests |
| 1 ✓ | <code>tests/test_mobile_responsive.py</code> | pal-e-docs | New — 5 playwright mobile viewport tests |
| 1 ✓ | <code>src/pal_e_docs/templates/base.html</code> | pal-e-docs | CSS: .table-scroll styles, remove display:block hack |
| 2 ✓ | <code>src/pal_e_docs/templates/base.html</code> | pal-e-docs | Nav restructure, @media breakpoints, CSS overhaul, form font inherit |
| 2 ✓ | <code>src/pal_e_docs/templates/landing.html</code> | pal-e-docs | Doc list tag-row layout, section-header class, view-all-link class |
| 2 ✓ | <code>src/pal_e_docs/templates/login.html</code> | pal-e-docs | All inline styles moved to CSS classes |
| 2 ✓ | <code>src/pal_e_docs/templates/note.html</code> | pal-e-docs | tag-row class consistency |
| 2 ✓ | <code>src/pal_e_docs/templates/tag_notes.html</code> | pal-e-docs | tag-row class consistency |
| 2 ✓ | <code>src/pal_e_docs/templates/project_notes.html</code> | pal-e-docs | tag-row class consistency |
| 2 ✓ | <code>.pre-commit-config.yaml</code> | pal-e-docs | New — ruff-format + ruff check hooks (v0.15.2) |
| 3 ✓ | <code>src/pal_e_docs/templates/base.html</code> | pal-e-docs | Nav wrap fix (flex: 0 0 100%), mobile section spacing |
| 3 ✓ | <code>tests/test_mobile_responsive.py</code> | pal-e-docs | New test — test_nav_three_row_layout with 10px gap assertions |
Verification
- [x] Phase 1: wrap_tables() unit tests pass. Table wrapper in pipeline. Playwright mobile tests exist and run in CI. 151 total tests passing. PR #45 merged.
- [x] Phase 2: ALL playwright mobile tests pass. No horizontal overflow at 375px on any page. Nav usable on mobile. All inline styles removed. Pre-commit config added. PR #48 merged. 3-round review-fix loop.
- [x] Phase 3: Nav renders as 3 rows on mobile. Mobile spacing improved. 152 total tests passing. PR #50 merged. 2-round review-fix loop.
Next Plan Seeds
plan-2026-02-27-browse-ux-enhancements— sort by recency, project detail page redesign, mermaid diagram revision- Screenshot regression tests in CI
- Dark mode (if there's ever demand)
Related
project-pal-e-docs— parent projectplan-2026-02-26-browse-frontend-polish— predecessor (completed). This plan addresses structural gaps that CSS alone couldn't fix.issue-pal-e-docs-table-wrapper-mobile-tests— resolved, Phase 1 (PR #45)issue-pal-e-docs-template-css-overhaul— resolved, Phase 2 (PR #48)issue-pal-e-docs-visual-qa-polish— resolved, Phase 3 (PR #50)