From 51e1ef32aba75c7abedfcf9ef96b4f60d428ee84 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sat, 29 Aug 2026 18:39:51 -0700 Subject: [PATCH] =?UTF-8?q?fix(website):=20docs=20polish=202/3=20=E2=80=94?= =?UTF-8?q?=20breadcrumb,=20tables,=20rails,=20and=20mdx=20details?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The detail third of the docs polish arc (findings §3, §4, §6-rail, §7, §9). - Breadcrumb typography moves to the list so the separators inherit it - they were siblings of the links and rendered 3px larger and 3px higher on the first two crumbs. Measured after: one font size, one baseline. - Tables get a 560px floor inside .docs-table-scroll so the scroller finally scrolls instead of crushing (375px rendered `agent` across three lines in a 49px column, rows 227px tall). Edge fades signal scrollability; the wrapper is tabIndex=0 role=region so keyboard users can reach the clipped columns (WCAG 2.1.1). ApiDocRenderer and ApiRefTable tables gain the same wrapper. - Inline code chips: word-break normal + overflow-wrap break-word - chips wrap whole instead of splitting @threadplane/langgraph into two pills. break-word, not anywhere: `anywhere` shrinks min-content to one character, which let table columns squeeze below chip width and wrap `agent` as agen/t (caught by measurement mid-implementation). - One horizontal rail: header, API block, and prev/next align down to the article's padding scale, ending the 8px left-edge mismatch below 640px. - Steps' dangling connector below the last step is gone (:last-child - the selector inline styles could never express). - The dead .shiki and [data-rehype-pretty-code-title] rules are deleted (findings §9: zero matching elements site-wide), with breadcrumb comments pointing at what to do if either ever comes back. Co-Authored-By: Claude Opus 5 --- .../docs/[library]/[section]/[slug]/page.tsx | 6 +- .../src/components/docs/ApiDocRenderer.tsx | 2 + .../src/components/docs/ApiRefTable.tsx | 2 + .../src/components/docs/MdxRenderer.tsx | 5 +- apps/website/src/styles/docs.css | 83 ++++++++++++------- 5 files changed, 65 insertions(+), 33 deletions(-) diff --git a/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx b/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx index c4ba04c91..d1dbf2af9 100644 --- a/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx +++ b/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx @@ -92,7 +92,7 @@ export default async function DocsPage({ params }: DocsRouteProps) {
-
+
Boolean(e)); return rendered.length > 0 ? ( -
+
{rendered.map((entry) => ( ))}
) : null; })()} -
+
diff --git a/apps/website/src/components/docs/ApiDocRenderer.tsx b/apps/website/src/components/docs/ApiDocRenderer.tsx index 723feedc2..7f9dea3eb 100644 --- a/apps/website/src/components/docs/ApiDocRenderer.tsx +++ b/apps/website/src/components/docs/ApiDocRenderer.tsx @@ -32,6 +32,7 @@ function KindBadge({ kind }: { kind: string }) { function ParamTable({ params }: { params: ApiParam[] }) { return ( +
@@ -50,6 +51,7 @@ function ParamTable({ params }: { params: ApiParam[] }) { ))}
+
); } diff --git a/apps/website/src/components/docs/ApiRefTable.tsx b/apps/website/src/components/docs/ApiRefTable.tsx index bc2839fa4..2468d01f2 100644 --- a/apps/website/src/components/docs/ApiRefTable.tsx +++ b/apps/website/src/components/docs/ApiRefTable.tsx @@ -24,6 +24,7 @@ export function ApiRefTable({ entries }: { entries: ApiEntry[] }) {

{entry.description}

{entry.params && entry.params.length > 0 && ( +
@@ -46,6 +47,7 @@ export function ApiRefTable({ entries }: { entries: ApiEntry[] }) { ))}
+
)}
))} diff --git a/apps/website/src/components/docs/MdxRenderer.tsx b/apps/website/src/components/docs/MdxRenderer.tsx index 193ac3b7d..e32d049a6 100644 --- a/apps/website/src/components/docs/MdxRenderer.tsx +++ b/apps/website/src/components/docs/MdxRenderer.tsx @@ -62,7 +62,10 @@ const mdxComponents = { ); }, table: ({ children, ...rest }: React.HTMLAttributes) => ( -
+ // tabIndex + role: a scrollable region must be keyboard-reachable + // (WCAG 2.1.1) — without it, keyboard users can never see the clipped + // columns the scroller hides. +
{children}
), diff --git a/apps/website/src/styles/docs.css b/apps/website/src/styles/docs.css index 4bad69ac6..5c05f19b2 100644 --- a/apps/website/src/styles/docs.css +++ b/apps/website/src/styles/docs.css @@ -38,17 +38,12 @@ --docs-accent-tint-line: color-mix(in srgb, var(--color-accent) 10%, transparent); } -/* Shiki code blocks — tokyo-night theme */ -.shiki { - padding: 1.5rem; - background: var(--docs-code-bg) !important; - overflow-x: auto; -} -.shiki code { - font-family: var(--font-mono), monospace; - font-size: 0.75rem; - line-height: 1.7; -} +/* NOTE: the old `.shiki` rules were deleted here — rehype-pretty-code runs + * with keepBackground:true, which writes the theme background INLINE on the + *
 and never emits a .shiki class. Zero elements matched, on docs and
+ * blog (findings §9). If keepBackground is ever turned off, style the code
+ * background via [data-rehype-pretty-code-figure] pre, not a .shiki rule. */
+
 
 /* rehype-pretty-code — docs code blocks */
 .docs-prose [data-rehype-pretty-code-figure] {
@@ -80,21 +75,21 @@
   padding: 0 0.25rem;
 }
 
-.docs-prose [data-rehype-pretty-code-figure] [data-rehype-pretty-code-title] {
-  font-family: var(--font-mono), monospace;
-  font-size: 0.7rem;
-  color: var(--docs-code-title-fg);
-  padding: 0.5rem 1.5rem;
-  background: var(--docs-code-bg);
-  border-bottom: 1px solid var(--docs-code-title-rule);
-  border-radius: 0.75rem 0.75rem 0 0;
-}
+/* NOTE: the [data-rehype-pretty-code-title] rules were deleted — no code
+ * fence in the repo uses the `title=` meta, so the element is never generated
+ * (findings §9). Re-add title styling if a fence ever gains one; the
+ * foreground for a dark title bar lives with the --docs-code-* constants. */
 
-.docs-prose [data-rehype-pretty-code-figure]:has([data-rehype-pretty-code-title]) pre {
-  border-radius: 0 0 0.75rem 0.75rem;
-}
 
 .docs-prose :not(pre) > code {
+  /* .docs-prose sets word-break:break-word, which split tokens like
+   * @threadplane/langgraph into two separately-backgrounded pills mid-token.
+   * Prefer moving the whole chip to the next line; break inside only when a
+   * single token alone exceeds the line (findings §4). break-word, NOT
+   * anywhere: `anywhere` also shrinks min-content to one character, which let
+   * table columns squeeze below the chip width and wrap `agent` as agen/t. */
+  word-break: normal;
+  overflow-wrap: break-word;
   font-family: var(--font-mono), monospace;
   font-size: 0.85em;
   background: var(--color-accent-surface);
@@ -176,7 +171,30 @@
 .docs-prose > p:has(> .docs-diagram) { overflow-x: auto; }
 .docs-prose > p > img.docs-diagram { max-width: none; }
 
-.docs-table-scroll { max-width: 100%; overflow-x: auto; margin: 1.5rem 0; }
+.docs-table-scroll {
+  max-width: 100%;
+  overflow-x: auto;
+  margin: 1.5rem 0;
+  /* Edge fades signal that the table scrolls; background-attachment:local
+   * pins the covers to the content so they vanish at the ends. */
+  background:
+    linear-gradient(to right, var(--color-surface), var(--color-surface)) left / 24px 100%,
+    linear-gradient(to right, var(--color-surface), var(--color-surface)) right / 24px 100%,
+    linear-gradient(to right, rgba(0, 0, 0, 0.08), transparent) left / 16px 100%,
+    linear-gradient(to left, rgba(0, 0, 0, 0.08), transparent) right / 16px 100%;
+  background-repeat: no-repeat;
+  background-attachment: local, local, scroll, scroll;
+}
+.docs-table-scroll:focus-visible {
+  outline: none;
+  box-shadow: var(--shadow-focus);
+  border-radius: var(--radius-sm);
+}
+/* width:100% alone meant the table always fit its container, so the scroller
+ * had nothing to scroll — at 375px the props table crushed `agent` into a
+ * 49px column across three lines (findings §4). A floor makes narrow
+ * viewports scroll instead of crush; wide viewports are unaffected. */
+.docs-table-scroll table { min-width: 560px; }
 .docs-prose table { width: 100%; border-collapse: collapse; font-size: 0.875rem; margin: 0; }
 .docs-prose th { text-align: left; padding: 0.5rem 0.75rem; font-family: var(--font-mono); font-size: 0.75rem; text-transform: uppercase; color: var(--color-text-muted); border-bottom: 1px solid var(--color-accent-border); }
 .docs-prose td { padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--docs-accent-tint-soft); color: var(--color-text-secondary); }
@@ -455,6 +473,11 @@
   background: var(--color-border);
   margin-top: 4px;
 }
+/* The connector is per-step, so the last step trailed a dangling line into
+ * the whitespace below (findings §7). */
+.mdx-step:last-child .mdx-step-connector {
+  display: none;
+}
 .mdx-step-content {
   flex: 1;
   /* flex:1 leaves min-width:auto; the item refuses to shrink below its
@@ -934,11 +957,16 @@
   margin: 0;
   display: flex;
   flex-wrap: wrap;
-}
-.docs-crumb-link {
+  align-items: center;
+  row-gap: 4px;
+  /* Typography on the LIST, not the links: the separators are siblings of the
+   * links inside each li, and when only the links carried 13px the first two
+   * separators inherited body's 16px/24px and floated 3px high (findings §3). */
   font-family: Inter, system-ui, sans-serif;
   font-size: 13px;
   line-height: 1.5;
+}
+.docs-crumb-link {
   color: var(--color-text-muted);
   text-decoration: none;
 }
@@ -947,9 +975,6 @@
   color: var(--color-text-muted);
 }
 .docs-crumb-current {
-  font-family: Inter, system-ui, sans-serif;
-  font-size: 13px;
-  line-height: 1.5;
   text-decoration: none;
   color: var(--color-text-primary);
   font-weight: 600;