Foundry Foundry

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 rootMargin to match header height exactly
  • Add matching background-color to 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:

  1. If FOUNDRY_READ_TOKEN is not set as a Fly secret, all pages are accessible (dev mode fallback)
  2. Nav sidebar filtering happens client-side via localStorage token, but the FOUNDRY_WRITE_TOKEN (for annotations) is different from FOUNDRY_READ_TOKEN (for page access) — users authenticate with the write token but the page gating checks the read token

Fix approach:

  • Verify FOUNDRY_READ_TOKEN is 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/selectionchange event 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-width on the content area (e.g., max-width: 48rem or 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-toggle button from .header into .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-primary and 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-secondary and --bg-surface to 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

ItemTypeSizeComplexity
B1: Sticky heading twitch + H2-onlyBugSCSS tuning + observer logic
B2: Private docs visibleBugMAccess control logic
B3: Header text blueBugXSOne-line CSS
B4: Doc path mismatchBugSNormalizer update
B5: Text highlight annotationsBugMEvent handler debugging
E1: Content widthEnhancementSCSS/layout
E2: Hamburger placementEnhancementMLayout restructure
E3: Reply button bottomEnhancementSComponent update
E4: Midnight blue tintEnhancementXSCSS custom properties

Total: 5 bugs + 4 enhancements. Estimated: one good sub-agent batch session.

Review

🔒

Enter your access token to view annotations