Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export default async function DocsPage({ params }: DocsRouteProps) {
]);

return (
<div className="flex min-h-screen overflow-x-hidden docs-shell-page">
<div className="flex min-h-screen docs-shell-page">
<JsonLd data={articleData} />
<JsonLd data={breadcrumbs} />
<DocsSearch library={library as LibraryId} />
Expand Down
12 changes: 11 additions & 1 deletion apps/website/src/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

html {
overflow-x: hidden;
/* Anchor jumps clear the fixed nav (--nav-h defined in styles/chrome.css). */
scroll-padding-top: calc(var(--nav-h) + 16px);
}

body {
Expand All @@ -29,7 +31,15 @@ body {
font-family: var(--font-inter);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
/* overflow-x: CLIP, never hidden. `hidden` makes <body> a scroll container
* and every position:sticky descendant pins against it — which never
* scrolls — so the docs rails were dead site-wide. `clip` clips identically
* but creates no scroll container, so sticky works AND the mobile layout
* viewport stays pinned to the device width (without any body guard,
* min-content overflow forces phones to zoom out — measured vw 421 on a
* 320 device). Real overflow sources are still fixed at the source; this
* is the belt, not the fix. */
overflow-x: clip;
}

/* Code blocks scroll internally; never expand the layout viewport. */
Expand Down
17 changes: 16 additions & 1 deletion apps/website/src/styles/chrome.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@
* 'var(--font-garamond,"EB Garamond",Georgia,serif)') are intentional.
*/

/* One nav height. The fixed nav measures 58px (px-6 py-4) and 81px at the md
* breakpoint (md:px-8 md:py-5, 1px border included). Everything that offsets
* against the nav — the docs shell's top padding, both sticky rails, the
* mobile overlay, and html's scroll-padding for anchor jumps — reads this one
* variable instead of hardcoding its own guess (the old hardcoded 80px left
* 22px of dead space on phones; anchors landed 81px under the nav). */
:root {
--nav-h: 58px;
}
@media (min-width: 768px) {
:root {
--nav-h: 81px;
}
}

/* Footer */
.footer-root {
background: var(--color-surface);
Expand Down Expand Up @@ -132,7 +147,7 @@
margin: -12px;
}
.nav-mobile-overlay {
top: 57px;
top: calc(var(--nav-h) - 1px);
z-index: 9999;
background: var(--color-surface);
border-top: 1px solid var(--color-border);
Expand Down
16 changes: 12 additions & 4 deletions apps/website/src/styles/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@
}
.mdx-step-content {
flex: 1;
/* flex:1 leaves min-width:auto; the item refuses to shrink below its
* content's min-content width and blew the quickstart pages 512px past a
* 320px viewport (findings §2). */
min-width: 0;
padding-bottom: 8px;
}
.mdx-step-title {
Expand Down Expand Up @@ -701,9 +705,13 @@
.docs-sidebar {
border-right: 1px solid var(--color-border);
background: var(--color-surface);
min-height: calc(100vh - 5rem);
position: sticky;
top: 5rem;
top: var(--nav-h);
/* Without align-self the flex row stretches the aside to the article's full
* height (measured 10,030px), so its overflow-y:auto never engaged. */
align-self: flex-start;
min-height: calc(100vh - var(--nav-h));
max-height: calc(100vh - var(--nav-h));
}
.docs-sidebar-lib-trigger {
background: var(--color-surface);
Expand Down Expand Up @@ -890,9 +898,9 @@
/* DocsTOC */
.docs-toc {
position: sticky;
top: 5rem;
top: var(--nav-h);
align-self: flex-start;
max-height: calc(100vh - 6rem);
max-height: calc(100vh - var(--nav-h) - 1rem);
overflow-y: auto;
}
.docs-toc-label {
Expand Down
52 changes: 42 additions & 10 deletions apps/website/src/styles/landing.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@
@keyframes blink { to { visibility: hidden; } }
@media (max-width: 900px) {
.hero-grid {
grid-template-columns: 1fr !important;
/* minmax(0,…): bare 1fr keeps an auto minimum, so one wide child pushed
* the stacked column past the viewport (findings §2). */
grid-template-columns: minmax(0, 1fr) !important;
gap: 40px !important;
}
}
Expand Down Expand Up @@ -190,7 +192,7 @@
}
.feature-block-card-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr));
gap: 8px;
margin-bottom: 24px;
}
Expand Down Expand Up @@ -219,7 +221,9 @@
}
@media (max-width: 900px) {
.feature-block-grid {
grid-template-columns: 1fr !important;
/* minmax(0,…): bare 1fr keeps an auto minimum, so one wide child pushed
* the stacked column past the viewport (findings §2). */
grid-template-columns: minmax(0, 1fr) !important;
gap: 32px !important;
}
.feature-block-grid > div {
Expand Down Expand Up @@ -277,6 +281,9 @@
}
.why-row__body {
flex: 1;
/* min-width:auto refused to shrink past the nowrap primitive chip and
* pushed the row 52px past a 320px viewport (findings §2). */
min-width: 0;
display: flex;
gap: 16px;
align-items: baseline;
Expand Down Expand Up @@ -421,8 +428,27 @@
.wp-cover-wrap {
display: flex;
justify-content: center;
/* Grid items have min-width:auto too — without this the wrap sat at its
* content's 392px inside a 280px track. */
min-width: 0;
}
/* The frame is shrink-to-fit, so the cover's `100%` was indefinite and its
* aspect-ratio kept deriving a fixed ~392px width from content height. A
* definite width (capped by the frame's own inline maxWidth) lets the cover
* track the viewport. Scoped to the stacked breakpoint: at desktop the grid
* track is wider than the card, and an unconditional 100% would grow it. */
@media (max-width: 640px) {
.wp-cover-wrap > [data-ui="browser-frame"] {
width: 100%;
}
}
.wp-cover {
/* aspect-ratio alone derives width from the content's height, which makes
* the cover a fixed ~392px regardless of container — the audit's 392px
* card forcing phones to clip. Pin the width; aspect-ratio then drives
* height, and content taller than the ratio still stretches it (min-height
* auto). */
width: min(100%, 392px);
aspect-ratio: 8.5 / 11;
background: linear-gradient(135deg, #fafbfc 0%, #eaf3ff 100%);
padding: 48px 36px;
Expand Down Expand Up @@ -462,7 +488,9 @@
}
@media (max-width: 900px) {
.wp-grid {
grid-template-columns: 1fr !important;
/* minmax(0,…): bare 1fr keeps an auto minimum, so one wide child pushed
* the stacked column past the viewport (findings §2). */
grid-template-columns: minmax(0, 1fr) !important;
gap: 40px !important;
}
}
Expand Down Expand Up @@ -569,7 +597,9 @@
}
@media (max-width: 900px) {
.pilot-block-grid {
grid-template-columns: 1fr !important;
/* minmax(0,…): bare 1fr keeps an auto minimum, so one wide child pushed
* the stacked column past the viewport (findings §2). */
grid-template-columns: minmax(0, 1fr) !important;
gap: 40px !important;
}
}
Expand Down Expand Up @@ -662,7 +692,7 @@
}
.ecosystem-row-tiles {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
gap: 10px;
}
[data-ui="ecosystem-tile"]:hover {
Expand All @@ -672,7 +702,9 @@
}
@media (max-width: 760px) {
.ecosystem-row {
grid-template-columns: 1fr !important;
/* minmax(0,…): bare 1fr keeps an auto minimum, so one wide child pushed
* the stacked column past the viewport (findings §2). */
grid-template-columns: minmax(0, 1fr) !important;
gap: 8px !important;
}
.ecosystem-row > div:first-child {
Expand Down Expand Up @@ -712,7 +744,7 @@
}
.promises-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
gap: 16px;
max-width: 1100px;
margin: 0 auto;
Expand Down Expand Up @@ -752,7 +784,7 @@
}
.recent-articles-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
gap: 16px;
}
.recent-articles-footer {
Expand Down Expand Up @@ -829,7 +861,7 @@
/* BackendsGrid — components/landing/ag-ui/BackendsGrid.tsx */
.backends-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr));
gap: 10px;
padding: 16px;
background: var(--color-surface-tinted);
Expand Down
7 changes: 5 additions & 2 deletions apps/website/src/styles/pages.css
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@
* not this migration. */
.docs-shell-page {
background: var(--color-canvas);
padding-top: 80px;
padding-top: var(--nav-h);
}
.docs-shell-body {
background: var(--color-surface);
Expand Down Expand Up @@ -410,6 +410,9 @@
margin-top: 32px;
}
.about-link {
/* The full repo URL is one unbreakable token; without this it pushed the
* page 9px past a 320px viewport (findings §2). */
overflow-wrap: anywhere;
color: var(--color-accent);
}

Expand Down Expand Up @@ -1139,7 +1142,7 @@
}
.blog-index-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
gap: 16px;
}

Expand Down
7 changes: 7 additions & 0 deletions apps/website/src/styles/ui.css
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,13 @@
}
[data-ui="browser-frame-url"] {
flex: 1;
/* An unbreakable mono URL is the frame's min-content; without these three
* it propagated a ~390px floor up to the page and forced phones to zoom out
* (findings §2). Real browser chrome ellipsizes — so do we. */
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
font-family: "JetBrains Mono", monospace;
font-size: 11px;
Expand Down
56 changes: 56 additions & 0 deletions docs/superpowers/plans/2026-08-30-docs-polish-arc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Docs Polish Arc Implementation Plan (Project 3)

> **For agentic workers:** This plan is executed by the controller directly with
> live browser measurement — the implement-measure-adjust loop each fix needs.
> Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Fix every defect in `docs/superpowers/audits/2026-08-29-docs-visual-review-findings.md` — the review that started this whole arc. Unlike Projects 1–2, these PRs **deliberately change what readers see**; each change is bounded by a measured before-state from the audit and a measured after-assertion here.

**Prerequisites (all merged):** #845 (tokens), #848–#858 (substrate). Every fix below is now a stylesheet edit.

**Verification stance:** the migration's "signature-identical to prod" gate inverts — for each defect the audit's measured *broken* value must flip to the specified *fixed* value, and surfaces a PR does not target must stay signature-identical to prod at 1280 (blast-radius check).

---

## PR 1 — structural: overflow truth, sticky rails, one nav height

Findings §1, §2, §5, §6(partial).

- [ ] **`--nav-h`** custom property on `:root` in `chrome.css`: `58px`, `81px` at `min-width: 768px` (measured nav heights). Consumers: docs shell `padding-top` (was hardcoded 80px → 22px mobile dead space), sidebar/TOC sticky `top` + `max-height`, mobile overlay `top` (`calc(var(--nav-h) - 1px)`, was 57px), and `html { scroll-padding-top: calc(var(--nav-h) + 16px) }` — which fixes every deep link landing under the nav (§5) without touching a heading.
- [ ] **Revive sticky** (§1): delete `overflow-x: hidden` from `body` (it makes `<body>` a scroll container; sticky descendants pin against it and never stick) and the `overflow-x-hidden` utility from the docs shell div. `html`'s guard **stays** — verified it propagates to the viewport without breaking sticky. Sidebar additionally needs `align-self: flex-start` + `max-height: calc(100vh - var(--nav-h))` so its own `overflow-y: auto` finally engages (it was 10,030px tall).
- [ ] **Fix the real overflow sources the guards were masking** (§2), then prove no page scrolls horizontally at 320/375/768 *with the guard gone*:
- `<Step>` body: `min-width: 0` on `.mdx-step-body` (flex item refused to shrink; +512px on quickstarts at 320).
- landing `.why-row__body` / hero demo box / whitepaper card: responsive max-widths.
- blog index cards (`width: 300px` fixed) → fluid with a 300px cap.
- about page GitHub URL → `overflow-wrap: anywhere`.
- anything else the 320px probe finds — the probe is the authority, not this list.
- [ ] **Assertions:** at `scrollY=3000` on a docs page, sidebar and TOC `getBoundingClientRect().top === nav-h`; hash-jump to a mid-page heading lands its top ≥ nav bottom; docs shell top padding equals actual nav height at 375 and 1280; `documentElement.scrollWidth === innerWidth` on all 15 probe pages at 320 AND 375; non-targeted pages signature-identical to prod at 1280.

## PR 2 — details: breadcrumb, tables, rails, mdx

Findings §3, §4, §6(rail), §9.

- [ ] **Breadcrumb** (§3): typography moves to the `<ol>` (13px), `align-items: center`, separators become `li + li::before` (or equivalent) so they can never diverge from link size again; wrapped-row gap. Assertion: separator and link share font-size and vertical center on every crumb.
- [ ] **Tables** (§4): `min-width` on tables inside `.docs-table-scroll` so the scroller scrolls (at 375 the props table rendered `agent` as three lines in a 49px column, rows 227px tall); edge-fade affordance; `tabindex="0"` + `role="region"` + aria-label on the wrapper (keyboard reachability); same wrapper treatment for `ApiDocRenderer`/`ApiRefTable` tables. Inline `code` chips get `word-break: normal` so `@threadplane/langgraph` stops splitting into two pills.
- [ ] **One horizontal rail** (§6): breadcrumb/header wrapper, article, API block, and prev/next all share one padding scale (the 8px misalignment at <640px).
- [ ] **`<Steps>` connector**: `:last-child` kills the dangling line below the final step.
- [ ] **Dead CSS** (§9): delete the `.shiki` and `[data-rehype-pretty-code-title]` rules (zero matching elements site-wide; documented in the audit).
- [ ] **Assertions:** table wrapper `scrollWidth > clientWidth` at 375 with single-line `agent` cell; breadcrumb metrics; rail lefts equal at 375; docs pages signature vs prod shows deltas ONLY on the targeted elements.

## PR 3 — a11y + interaction

Findings §6(anchors/targets), §7.

- [ ] Shared `:focus-visible` ring (`var(--shadow-focus)`) on interactive docs chrome.
- [ ] **DocsSearch**: `role="dialog"` + `aria-modal`, focus trap + restore, `listbox`/`option` semantics, selected item scrolled into view, and a **mobile entry point** (the ⌘K trigger lives in the desktop-only sidebar).
- [ ] **Tabs/TabGroup**: `role="tablist"`/`tab`/`tabpanel`, arrow keys, horizontal scroll on narrow screens.
- [ ] **PageActions**: menu keyboard nav + item hover/focus styles.
- [ ] Heading anchors get a mobile-visible affordance (currently `display: none` below 768px — no way to deep-link from a phone).
- [ ] Touch targets ≥44px (PageActions 32px, code-copy 28px) via hit-area padding, not visual growth.
- [ ] `prefers-reduced-motion` guards `scroll-behavior: smooth`.
- [ ] **Assertions:** axe-style manual checks per control; targets measured ≥44; keyboard walkthrough of search + tabs + menu.

## Deferred, explicitly

- **Font unification onto next/font** (findings §10): a real sitewide visual change; needs its own decision.
- **Toast width on small screens**; **`AnnouncementToast` focus management** — candidates for a later pass.
Loading