agent-pages

Plan: HTML Toolbar Auto-Hide and Markdown Content Spacing

Download

Plan: HTML Toolbar Auto-Hide and Markdown Content Spacing

Summary

Fix two viewer UX issues:

  1. The HTML viewer toolbar consumes permanent vertical space because it cannot observe scroll direction inside the sandboxed artifact iframe.
  2. Markdown content begins too close to the shared toolbar because its main container has no top padding.

Use a secure, format-specific interaction model:

Current Behavior and Root Cause

HTML toolbar

The HTML host deliberately prevents its outer document from scrolling. The artifact scrolls inside an iframe using sandbox="".

Scroll and wheel events inside that nested browsing context do not bubble to the parent document. Because the iframe has an opaque sandboxed origin, the outer viewer cannot inspect scrollTop or attach listeners to the artifact document.

The existing viewer-scroll.js also exits unless the body has viewer-markdown-host, and the HTML host CSP retains script-src 'none'. The fixed HTML toolbar is therefore expected behavior under decision D36, not a regression in the Markdown scroll state machine.

Markdown spacing

The shared Markdown main container currently uses:

.viewer-main {
  padding: 0 1.25rem 2rem;
}

The zero top padding places the content card immediately below the sticky toolbar.

Chosen UX

Markdown

Keep the existing behavior unchanged:

HTML

Use auto-collapse rather than pretending to detect iframe scroll direction:

This recovers viewport space without changing the iframe sandbox, mutating published HTML, or enabling author scripts.

Implementation Plan

1. Generalize the trusted toolbar controller

Rename app/static/viewer-scroll.js to app/static/viewer-toolbar.js, or retain the filename while restructuring it into two explicit modes.

The controller should detect:

Keep the Markdown state calculation pure and preserve its existing thresholds.

Add a separate HTML state model with named constants, for example:

Avoid sharing scroll-specific variables with the HTML timer behavior.

2. Add an HTML reveal control

Update app/templates/viewer/html_host.html or the shared toolbar partial to render an HTML-only reveal control adjacent to the toolbar in the trusted parent document.

Requirements:

The activation zone should intercept pointer input only at the very top edge; it must not create a large transparent overlay over the artifact.

3. Add HTML auto-collapse behavior

In the trusted controller:

Do not automatically hide while a visitor is operating the version or theme menu.

4. Extend shared toolbar CSS safely

Refactor the hidden-state CSS so both host types can use the same visual transition:

.viewer-toolbar {
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.viewer-toolbar.viewer-toolbar-hidden {
  opacity: 0;
  transform: translateY(-100%);
  pointer-events: none;
}

Then add format-specific positioning:

Recommended behavior: expand the visible iframe to the top without resizing its document on every transition. Move the iframe host upward and increase its visible height using a class on body or .viewer-main-iframe, with the same transition duration as the toolbar. Verify this does not trigger disruptive iframe reflow.

Style the reveal control so:

Under prefers-reduced-motion: reduce, remove transitions but retain immediate show/hide state changes.

5. Restore Markdown breathing room

Add a body-scoped override after the shared .viewer-main rule:

body.viewer-markdown-host .viewer-main {
  padding-top: 1.25rem;
}

Do not change the HTML iframe host padding and do not apply the gap globally to error pages.

Verify that the gap remains visually balanced at desktop and mobile widths. If 1.25rem feels excessive below the 3rem toolbar on small screens, use a responsive token such as clamp(0.875rem, 2vw, 1.25rem).

6. Keep the security boundary intact

The HTML auto-collapse controller runs only in the trusted outer shell.

To load it on HTML host pages:

This permits trusted shell behavior without granting script execution to the artifact.

Record this as an explicit update to D36: trusted same-origin shell JavaScript is permitted for Markdown and HTML host chrome, while /raw and published HTML remain script-blocked.

7. Update templates and asset fingerprinting

Update:

Markdown and HTML should still share the same toolbar markup. Only the reveal control and fullscreen action remain HTML-specific.

8. Automated tests

State-machine tests

Extend tests/test_viewer_scroll.py or rename it to tests/test_viewer_toolbar.py.

Preserve Markdown tests for:

Add HTML tests for:

Use fake timers where possible; do not make the suite sleep for real timeout durations.

Template and CSP tests

Verify:

CSS tests

Verify:

9. Browser verification

Perform visual QA in a clean/incognito Chromium session at representative desktop and mobile widths.

Markdown checklist

HTML checklist

Verify System, Light, and Dark shell themes in both formats.

10. Documentation and decision updates

Update:

Document that:

Explicit Non-Goals

Do not:

Acceptance Criteria