E10.5: UI Bugfixes & Polish — Design Doc
Status: Step 0 — Ready for Execution Created: April 6, 2026 Authors: Dan Hannah & Clay Parent: E10 UI/UX Polish Part 2
Overview
E10 shipped 9 stories of UI polish across 10 PRs. Dan's first visual review surfaced several fit-and-finish issues plus a couple of real bugs. This cleanup epic captures everything that needs fixing before (or in parallel with) E11.
This is intentionally lightweight — no sub-systems, no batching. Just a flat list of targeted fixes. All items are independent and can be parallelized.
Bugs
B1: Sticky Heading Twitch & Gap
Observed: When scrolling between sections, the sticky heading transition is not smooth. A visible gap appears between the heading and the top bar, and the heading swap feels "twitchy."
Root Cause (suspected): The IntersectionObserver sentinel height doesn't perfectly match the header bar height. The sentinel trigger point may be off by a few pixels, causing a brief flash where neither heading is stuck.
Fix approach:
- Adjust sentinel
rootMarginto match header height exactly - Add matching
background-colorto the sticky heading container to hide any gap - Consider a CSS transition on the heading swap to smooth the visual change
- Limit sticky headings to H2 only — H2s are major sections, H3s are subsections that don't need to persist in view. Fewer transitions = less twitch.
Source: E10-S3 (PR #73)
B2: Private Docs Visible When Unauthenticated
Observed: Private docs (under projects/) are visible in the sidebar nav and accessible via direct URL when unauthenticated. Only search correctly filters private results.
Expected: Unauthenticated users should not see private docs in the nav or be able to access them.
Root Cause: The access control check in [...slug].astro gates on FOUNDRY_READ_TOKEN, but:
- If
FOUNDRY_READ_TOKENis not set as a Fly secret, all pages are accessible (dev mode fallback) - Nav sidebar filtering happens client-side via localStorage token, but the
FOUNDRY_WRITE_TOKEN(for annotations) is different fromFOUNDRY_READ_TOKEN(for page access) — users authenticate with the write token but the page gating checks the read token
Fix approach:
- Verify
FOUNDRY_READ_TOKENis set in Fly secrets - Consider unifying read/write tokens (or deriving read access from write token)
- Ensure nav filtering checks the same token the user entered
Source: E6 access control + E10 interaction
B3: "Foundry" Header Text Highlights Blue
Observed: The "Foundry" text in the top header bar turns blue (link active/visited color) instead of staying neutral.
Root Cause: The header title is an <a href="/"> tag that inherits default link styling.
Fix approach: Set explicit color: inherit or color: var(--text-primary) on the .header-title anchor element. One-line CSS fix.
Source: E10-S5 (PR #68 — logo/branding)
B4: Doc Path Mismatch (MCP vs Frontend)
Observed: Annotations created via MCP tools with doc_path /foundry/docs/... don't appear in the frontend, which queries with /docs/....
Root Cause: The Foundry skill file documented the wrong path format. The docPathVariants() normalizer strips /docs/ prefix but not /foundry/docs/ prefix. Astro has no base path configured, so URLs are /docs/... not /foundry/docs/....
Fix approach:
- Update
normalizeDocPath()to also strip/foundry/and/foundry/docs/prefixes - Already fixed in skill file, need code fix for robustness
- Note: SS4 in E11 replaces this entirely with canonical format normalization on write
Source: PR #71 normalizer + Foundry skill doc
B5: Text Highlight → Annotation Creation Broken
Observed: Dan cannot add comments on highlighted/selected text. The selection → annotation popup flow doesn't work.
Root Cause (suspected): E10's layout changes (sticky headings, TOC, content area restructuring) may have broken the selection/popup positioning or event handling.
Fix approach:
- Debug the text selection event handler in the annotation components
- Check if the sticky heading or TOC z-index/overflow changes interfere with the selection popup
- Verify the
mouseup/selectionchangeevent listeners are still firing correctly
Source: E4 annotation highlight feature + E10 layout changes
Enhancements
E1: Content Column Width When Thread Panel Hidden
Observed: When the review/thread panel is collapsed, the main content column stretches too wide, causing text to overlap with the Table of Contents on the right side.
Fix approach:
- Set a
max-widthon the content area (e.g.,max-width: 48remor similar) - Or add a right margin that accounts for the TOC width even when the thread panel is hidden
- The TOC component should have its own reserved space in the grid/flex layout
Source: E10-S4 (PR #74 — Table of Contents) + E10-S8 (PR #77 — thread panel)
E2: Move Hamburger Menu Inline with Nav Tree
Observed: The hamburger menu icon sits in the top header bar next to the logo. Dan suggests moving it inline with the nav sidebar for a cleaner header layout.
Proposed: Logo + "Foundry" text flush left in the corner. Search + Settings icons on the right. Hamburger becomes a sidebar collapse/expand toggle positioned at the top of the sidebar, not in the header.
Fix approach:
- Move
#sidebar-togglebutton from.headerinto.sidebar(top of nav) - Adjust header layout to remove the gap where the hamburger was
- Consider a collapse arrow (‹/›) instead of hamburger lines for sidebar toggle
Source: Dan's visual review feedback
E3: Reply Button at Bottom of Comment Threads
Observed: The reply button is only at the top of each annotation thread. Dan finds it more natural to have the reply action at the bottom of the thread (after reading all replies).
Proposed:
- Keep reply button on top for parent comments (it's the primary action when no thread exists yet)
- Add reply button at the bottom of child comment threads (natural flow: read thread → reply)
- Both locations wire to the same reply handler
Fix approach:
- Add a reply trigger at the bottom of the replies list in
AnnotationThread.tsx - Style consistently with the existing reply button
Source: Dan's review panel UX feedback
E4: Midnight Blue Background Tint (Dark Mode)
Observed: The dark mode background is pure dark gray. Dan suggests adding a subtle hint of dark blue for a more premium feel.
Proposed: Change dark mode background from pure gray (e.g., #1a1a1a) to a subtle midnight blue (e.g., hsl(220, 15%, 8%) or hsl(220, 20%, 10%)). The kind of change that makes people say "this looks premium" without knowing exactly why.
Fix approach:
- Update
--bg-primaryand related background CSS custom properties in dark mode - Keep the saturation low (10-20%) so it reads as "dark with personality" not "blue"
- May need to adjust
--bg-secondaryand--bg-surfaceto complement
Source: Dan's visual polish feedback
Dependencies
- E10 shipped ✅ (all 9 stories, PRs #68-#77)
- All items are independent — no ordering required
- Can be parallelized in one batch or run alongside E11 Batch 1
Sizing
| Item | Type | Size | Complexity |
|---|---|---|---|
| B1: Sticky heading twitch + H2-only | Bug | S | CSS tuning + observer logic |
| B2: Private docs visible | Bug | M | Access control logic |
| B3: Header text blue | Bug | XS | One-line CSS |
| B4: Doc path mismatch | Bug | S | Normalizer update |
| B5: Text highlight annotations | Bug | M | Event handler debugging |
| E1: Content width | Enhancement | S | CSS/layout |
| E2: Hamburger placement | Enhancement | M | Layout restructure |
| E3: Reply button bottom | Enhancement | S | Component update |
| E4: Midnight blue tint | Enhancement | XS | CSS custom properties |
Total: 5 bugs + 4 enhancements. Estimated: one good sub-agent batch session.