Skip to content

feat: default theme layout with tab bar + page nav types#37

Open
rsbh wants to merge 27 commits intomainfrom
feat_default_theme_changes
Open

feat: default theme layout with tab bar + page nav types#37
rsbh wants to merge 27 commits intomainfrom
feat_default_theme_changes

Conversation

@rsbh
Copy link
Copy Markdown
Member

@rsbh rsbh commented Apr 17, 2026

Figma: https://www.figma.com/design/HYWvap3F9zHURz4MwNHb2d/Aurora-Docs

Summary

Layout + sidebar

  • Rewire default theme: sidebar holds content-dir + API cells at top, then nested page tree, footer holds version switcher
  • Nested sidebar supports 2 levels — top-level Sidebar.Group, nested folder as collapsible with right-aligned chevron
  • Data-depth based styling for label color + height differences per level
  • Hide scrollbar on sidebar main
  • Sidebar header: logo + search + theme toggle only (no divider, no version)
  • Drop tabBar; top nav (prev/next + breadcrumb) stays in subNav inside content card; content card border-left only, no radius

Config + content features

  • config.logo.light / config.logo.dark drive sidebar logo, book-open fallback
  • Optional icon per content dir and API entry — URL or inline SVG; dangerouslySetInnerHTML for SVG so currentColor works
  • Drop config.llms.enabled — raw .md route and llms.txt always on
  • Drop footer config + Footer component
  • <link rel="alternate" type="text/markdown"> injected on docs pages for AI/crawler discovery

Open in AI

  • New subNav dropdown: Copy as MD, View MD, Open in ChatGPT, Open in Claude

Breadcrumbs + search

  • Breadcrumb.Item uses render={<RouterLink>} so SPA navigation works
  • Bump @raystack/apsara to 1.0.0-rc.4 and migrate Search to Command.Dialog + Command.DialogContent + Command.Content + Command.Label

TODO

Tracked in TODO.md at repo root.

Test plan

  • bun run dev:docs — sidebar groups expand, active state correct, nested folders show chevron right, rotate down when open
  • bun run build:docs && bun run start:docs — /docs, /apis, 404 render; /docs/*.md and /llms.txt serve
  • View page source: <link rel="alternate" type="text/markdown" href="/slug.md" title="{Page} (Markdown)">
  • Open in AI: Copy as MD places markdown on clipboard; View MD opens raw; ChatGPT/Claude open with Read prefix
  • Search dialog: Cmd+K opens, results highlight, arrow keys + enter navigate

🤖 Generated with Claude Code

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chronicle Ready Ready Preview, Comment Apr 24, 2026 9:49am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds prev/next page navigation to types, source, API, SSR, and client hydration; updates page context and components to carry nav links; overhauls default theme layout, TOC, and page styles; simplifies search/theme UI; switches docs theme and adds a new package dependency.

Changes

Cohort / File(s) Summary
Types & Page Model
packages/chronicle/src/types/content.ts
Add PageNavLink and PageNav; extend exported Page with prev/next.
Source & Nav Logic
packages/chronicle/src/lib/source.ts
Add exported getPageNav(slug, tree?) to compute adjacent page links from flattened tree and derive titles.
API, SSR & Hydration
packages/chronicle/src/server/api/page.ts, packages/chronicle/src/server/entry-server.tsx, packages/chronicle/src/server/entry-client.tsx
Compute and include prev/next in API responses, SSR payload, and client hydration; client accepts nullable nav fields.
Page Context & Consumption
packages/chronicle/src/lib/page-context.tsx, packages/chronicle/src/pages/DocsPage.tsx
Switch internal state/props to exported Page (now includes prev/next); forward full page object to theme Page.
Layout / Theme UI
packages/chronicle/src/themes/default/Layout.tsx, packages/chronicle/src/themes/default/Layout.module.css
Replace top navbar with sidebar-driven layout, move controls into sidebar, add prev/next controls and breadcrumb derivation, and introduce substantial CSS/layout changes.
TOC & Page Styling
packages/chronicle/src/themes/default/Toc.tsx, packages/chronicle/src/themes/default/Toc.module.css, packages/chronicle/src/themes/default/Page.module.css, packages/chronicle/src/themes/default/Page.tsx
Replace TOC with marker+panel UI, update panel interaction and typography, remove Breadcrumbs from Page props.
UI Components & Search
packages/chronicle/src/components/ui/search.tsx, packages/chronicle/src/components/ui/search.module.css, packages/chronicle/src/components/ui/client-theme-switcher.tsx
Switch search trigger to IconButton (remove kbd hint/styles); simplify theme switcher to IconButton using useTheme.
Misc docs & config
docs/chronicle.yaml, package.json, docs/content/docs/components.mdx
Change docs theme from paperdefault; add dependency std-env@^4.0.0; update an example to use Tabs.Tab.
Breadcrumbs
packages/chronicle/src/components/ui/breadcrumbs.tsx
Render current breadcrumb without link and use react-router Link for non-current items.
Small CSS updates
packages/chronicle/src/components/ui/search.module.css, packages/chronicle/src/themes/default/Toc.module.css
Remove unused search trigger/kbd classes; rename and add TOC classes for new marker/panel UI.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Server
  participant PageTree
  participant ClientApp as Client App
  participant PageAPI as /api/page

  Browser->>Server: Request page (SSR)
  Server->>PageTree: getPage(slug) and getPageNav(slug)
  PageTree-->>Server: pageData + nav(prev/next)
  Server-->>Browser: HTML + __PAGE_DATA__ (includes prev/next)

  Browser->>ClientApp: Hydrate (reads __PAGE_DATA__)
  ClientApp->>PageContext: setPage(page + prev/next)
  alt user navigates to another doc
    ClientApp->>PageAPI: fetch /api/page?slug
    PageAPI->>PageTree: getPage + getPageNav
    PageTree-->>PageAPI: page + nav
    PageAPI-->>ClientApp: payload with prev/next
    ClientApp-->>Layout: render prev/next IconButtons
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • rohilsurana
  • rohanchkrabrty
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: restructuring the default theme layout with a tab bar and introducing page navigation types.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly relates to the changeset, detailing layout restructuring, sidebar implementation, page navigation features, and configuration changes that align with the file modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat_default_theme_changes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
packages/chronicle/src/themes/default/Layout.module.css (1)

36-67: Tab styles look good; minor consideration on border: 0.5px.

Sub-pixel borders (0.5px) render inconsistently across browsers/DPRs — on 1x displays some browsers will round to 0 and the border disappears. If the intent is a thinner-looking divider, consider 1px with a lighter border color token, or rely on transform: scaleY(0.5) patterns. Not blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Layout.module.css` around lines 36 -
67, Replace the sub-pixel border in the .tab rule: avoid using "0.5px" (in
Layout.module.css .tab) because it can disappear on some DPRs; change it to
"1px" and pair it with a lighter border token (e.g., use the existing
var(--rs-color-border-*-secondary) style) or alternatively implement a visual
thinner divider using transform scaling on a pseudo-element (apply to .tab or
.tabActive as appropriate) so the thin visual is preserved across browsers and
DPRs.
packages/chronicle/src/server/entry-server.tsx (1)

38-60: prev/next hardcoded to null — track the follow-up.

Per the PR description this is intentional placeholder wiring until the backend computes adjacent-page links from the page tree. Worth leaving a TODO here (and symmetric sites) referencing the tracking issue so this doesn't silently ship to consumers expecting real navigation data.

Happy to open a follow-up issue to compute prev/next from getPageTree() + current slug if you'd like.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/server/entry-server.tsx` around lines 38 - 60, The
pageData currently sets prev and next to null and embeddedData mirrors those
nulls; add a clear TODO comment in the pageData/embeddedData block noting these
are intentional placeholders and reference the tracking issue (e.g. "TODO:
compute prev/next from getPageTree() using current slug — see tracking issue
#<ID>") so consumers and future maintainers know to replace the hardcoded nulls
with adjacent-page links computed from getPageTree() + slug.
packages/chronicle/src/server/entry-client.tsx (1)

13-22: EmbeddedData drifts from Page shape — consider reusing types.

EmbeddedData duplicates fields that now exist on Page/PageNav. As more nav metadata lands (e.g., section, breadcrumbs), keeping two hand-maintained shapes in sync is error-prone. Consider typing as Pick<Page, 'slug' | 'frontmatter' | 'prev' | 'next'> & { config; tree; relativePath; originalPath? } or defining a shared EmbeddedPagePayload in @/types.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/server/entry-client.tsx` around lines 13 - 22, The
EmbeddedData interface duplicates fields already defined on Page/PageNav (slug,
frontmatter, prev, next); replace the handwritten shape by reusing existing
types—e.g., change EmbeddedData to compose Pick<Page, 'slug' | 'frontmatter' |
'prev' | 'next'> & { config: ChronicleConfig; tree: Root; relativePath: string;
originalPath?: string } or create/use a shared EmbeddedPagePayload type in the
types package and import it; update imports to pull Page (or
EmbeddedPagePayload) and PageNavLink as needed and remove the duplicated field
declarations from EmbeddedData.
packages/chronicle/src/themes/default/Layout.tsx (2)

1-9: Nit: merge duplicate @heroicons/react/24/outline imports.

Lines 1 and 2–5 import from the same module in two separate statements.

♻️ Proposed tidy-up
-import { RectangleStackIcon } from '@heroicons/react/24/outline';
 import {
+  RectangleStackIcon,
   DocumentTextIcon,
   CodeBracketSquareIcon
 } from '@heroicons/react/24/outline';
-import {
+import {
   Flex,
   Sidebar
 } from '@raystack/apsara';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Layout.tsx` around lines 1 - 9, There
are two import statements pulling icons from the same module; merge them into a
single import from '@heroicons/react/24/outline' that includes
RectangleStackIcon, DocumentTextIcon, and CodeBracketSquareIcon to avoid
duplicate imports (leave the existing imports for Flex and Sidebar from
'@raystack/apsara' unchanged); update the import block in Layout.tsx so all
three icon symbols are imported in one statement.

77-100: Accessibility: tabs semantics and focus.

The tab bar uses <nav> + RouterLinks styled as tabs. If these are genuinely tabs (mutually exclusive sections), consider role="tablist" / role="tab" + aria-current="page" on the active link — at minimum add aria-current="page" to the active tab so assistive tech announces selection. Verify keyboard focus states are visible under the new .tab/.tabActive rules (no :focus-visible styles are defined in the module).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Layout.tsx` around lines 77 - 100, The
tab bar currently renders RouterLink elements as tabs but lacks proper ARIA and
focus semantics; update the nav and links in Layout.tsx to add role="tablist" on
the nav, role="tab" on each RouterLink, and set aria-current="page" on the
active tab (use the existing isApiRoute logic for the API links and the root tab
check for '/'), and ensure the styles.tab / styles.tabActive CSS module includes
visible focus styles (or add a :focus-visible rule) so keyboard focus is clearly
visible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/chronicle/src/lib/page-context.tsx`:
- Around line 108-113: The API response may omit prev/next so normalize them to
match Page['prev'] (PageNavLink | null) before calling setPage: when handling
the promise result in the .then callback (the function that calls loadMdx,
setErrorStatus and setPage with slug and data.frontmatter), coerce data.prev and
data.next to null if they are undefined (e.g., const prev = data.prev ?? null;
const next = data.next ?? null) and pass those normalized values into setPage to
preserve the SSR-hydrated shape and avoid undefined prev/next on client
navigation.

In `@packages/chronicle/src/themes/default/Layout.tsx`:
- Around line 86-95: The API tabs all use the shared boolean isApiRoute causing
every API entry from config.api to be marked active; update the tab active logic
to compute per-entry activity by checking the current pathname against each
api.basePath (e.g., replace the shared isApiRoute in the RouterLink className
expression with a per-item check like pathname.startsWith(api.basePath) or a
helper like isApiBasePath(api.basePath)); ensure the RouterLink rendering for
each api uses that per-api active boolean when toggling styles.tabActive (and
revisit the Docs tab's !isApiRoute usage if you later add non-/apis sections).

---

Nitpick comments:
In `@packages/chronicle/src/server/entry-client.tsx`:
- Around line 13-22: The EmbeddedData interface duplicates fields already
defined on Page/PageNav (slug, frontmatter, prev, next); replace the handwritten
shape by reusing existing types—e.g., change EmbeddedData to compose Pick<Page,
'slug' | 'frontmatter' | 'prev' | 'next'> & { config: ChronicleConfig; tree:
Root; relativePath: string; originalPath?: string } or create/use a shared
EmbeddedPagePayload type in the types package and import it; update imports to
pull Page (or EmbeddedPagePayload) and PageNavLink as needed and remove the
duplicated field declarations from EmbeddedData.

In `@packages/chronicle/src/server/entry-server.tsx`:
- Around line 38-60: The pageData currently sets prev and next to null and
embeddedData mirrors those nulls; add a clear TODO comment in the
pageData/embeddedData block noting these are intentional placeholders and
reference the tracking issue (e.g. "TODO: compute prev/next from getPageTree()
using current slug — see tracking issue #<ID>") so consumers and future
maintainers know to replace the hardcoded nulls with adjacent-page links
computed from getPageTree() + slug.

In `@packages/chronicle/src/themes/default/Layout.module.css`:
- Around line 36-67: Replace the sub-pixel border in the .tab rule: avoid using
"0.5px" (in Layout.module.css .tab) because it can disappear on some DPRs;
change it to "1px" and pair it with a lighter border token (e.g., use the
existing var(--rs-color-border-*-secondary) style) or alternatively implement a
visual thinner divider using transform scaling on a pseudo-element (apply to
.tab or .tabActive as appropriate) so the thin visual is preserved across
browsers and DPRs.

In `@packages/chronicle/src/themes/default/Layout.tsx`:
- Around line 1-9: There are two import statements pulling icons from the same
module; merge them into a single import from '@heroicons/react/24/outline' that
includes RectangleStackIcon, DocumentTextIcon, and CodeBracketSquareIcon to
avoid duplicate imports (leave the existing imports for Flex and Sidebar from
'@raystack/apsara' unchanged); update the import block in Layout.tsx so all
three icon symbols are imported in one statement.
- Around line 77-100: The tab bar currently renders RouterLink elements as tabs
but lacks proper ARIA and focus semantics; update the nav and links in
Layout.tsx to add role="tablist" on the nav, role="tab" on each RouterLink, and
set aria-current="page" on the active tab (use the existing isApiRoute logic for
the API links and the root tab check for '/'), and ensure the styles.tab /
styles.tabActive CSS module includes visible focus styles (or add a
:focus-visible rule) so keyboard focus is clearly visible.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 324e5e64-cd4d-411d-9155-dc48131055f8

📥 Commits

Reviewing files that changed from the base of the PR and between d35822e and fcbe6c0.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • docs/chronicle.yaml
  • package.json
  • packages/chronicle/src/lib/page-context.tsx
  • packages/chronicle/src/pages/DocsPage.tsx
  • packages/chronicle/src/server/entry-client.tsx
  • packages/chronicle/src/server/entry-server.tsx
  • packages/chronicle/src/themes/default/Layout.module.css
  • packages/chronicle/src/themes/default/Layout.tsx
  • packages/chronicle/src/types/content.ts

Comment thread packages/chronicle/src/lib/page-context.tsx Outdated
Comment thread packages/chronicle/src/themes/default/Layout.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
packages/chronicle/src/themes/default/Layout.module.css (1)

103-124: Hardcoded shadow color and near-no-op backdrop-filter.

  • Lines 110–111: rgba(0, 0, 0, 0.04) won't render meaningfully in dark themes; consider a token-based shadow (e.g., var(--rs-shadow-...)) to stay theme-aware.
  • Lines 123 / 31: backdrop-filter: blur(1px) is visually imperceptible. Either increase the radius or drop it to avoid paying for an unused GPU pass.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Layout.module.css` around lines 103 -
124, Update the theme-unaware hardcoded shadow and the near-no-op backdrop
filter: replace the rgba(...) shadow used in the .card rule with a theme token
(e.g., var(--rs-shadow-*, or an appropriate existing shadow variable) so the
elevation responds to dark/light themes and accessibility settings, and for
.subNav either increase the blur radius (e.g., to a perceptible value) or remove
the backdrop-filter property to avoid unnecessary GPU work—change the rules in
the .card and .subNav selectors accordingly.
packages/chronicle/src/themes/default/Page.module.css (1)

33-45: Minor: redundant/odd values in content typography.

  • Line 34: margin-top: 0 on .content h1 is redundant with the :first-child reset above and also removes top spacing for any non-first h1 (uncommon, but worth a conscious call).
  • Line 44: line-height: 171.429% is a computed literal — prefer a design token (e.g., var(--rs-line-height-regular)) or a clean unitless value like 1.71 for readability and consistency with the rest of the file.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Page.module.css` around lines 33 - 45,
Remove the redundant margin-top: 0 from the .content h1 rule (or replace it with
a comment/intent if you explicitly need to override the :first-child reset) to
avoid unintentionally removing top spacing for non-first h1s; and change the
.content p rule's line-height: 171.429% to a design token or a unitless value
(e.g., var(--rs-line-height-regular) or 1.71) so it matches the file's styling
conventions and improves readability.
packages/chronicle/src/themes/default/Layout.tsx (1)

121-150: Consider hiding the sub-nav on pages without navigation data.

On routes where page is null (404, non-content routes, API index) both IconButtons will be disabled and Breadcrumbs may render empty — producing a visually empty sub-nav bar. Low priority, but you may want to conditionally render <nav className={styles.subNav}> when there is meaningful content to show, or collapse it to avoid an empty 48px bar.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Layout.tsx` around lines 121 - 150, The
sub-nav should be omitted when there's no navigation to display; update the
Layout component to only render the <nav className={styles.subNav}> block when
there is meaningful content (e.g. prev or next exist, or Breadcrumbs would
render). Compute a boolean like hasNav = Boolean(prev || next || (slug && tree
&& tree.length)) and wrap the existing nav (including the IconButton group and
<Breadcrumbs slug={slug} tree={tree} />) in a conditional render using that flag
so the empty 48px sub-nav is not shown on pages with no page data.
packages/chronicle/src/components/ui/client-theme-switcher.tsx (1)

19-34: Minor: hydration placeholder causes a brief layout shift.

Returning null until isClient flips avoids SSR/CSR theme mismatch, but the sidebar navbar's right-side actions row visibly reflows on mount as this button appears. Consider rendering a fixed-size placeholder (or using visibility: hidden on a sized wrapper) to reserve space, e.g.:

💡 Suggestion
-  if (!isClient) return null
-
-  const isDark = resolvedTheme === 'dark'
+  const isDark = resolvedTheme === 'dark'
+  if (!isClient) {
+    return <IconButton size={3} aria-hidden tabIndex={-1} style={{ visibility: 'hidden' }} />
+  }

Also note: the size prop only controls the inner icon dimensions; the outer IconButton is pinned to size={3}. If that's intentional, it may be worth documenting or dropping the prop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/components/ui/client-theme-switcher.tsx` around lines
19 - 34, When isClient is false the component returns null causing a layout
shift on mount; instead render a fixed-size placeholder that matches the
IconButton's outer dimensions so space is reserved (for example render the same
IconButton structure with visibility:hidden or a sized wrapper) inside the
ClientThemeSwitcher (use the existing isClient check and IconButton/size props
to mirror size), and either align the outer IconButton's size with the incoming
size prop or document why IconButton uses size={3} if you intentionally keep the
inner icon size independent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/chronicle/src/components/ui/search.tsx`:
- Around line 55-62: The keyboard shortcut (Cmd/Ctrl+K) is still wired in the
useEffect but the visual hint was removed; add a discoverability hint to the
IconButton by setting a title attribute (e.g., title="Search (⌘K)") on the
IconButton component used in this file so users see the shortcut on hover; keep
the existing props (size, aria-label, onClick => setOpen(true), className) and
only add the title string (you can include both Mac and Win hints if desired).

In `@packages/chronicle/src/lib/source.ts`:
- Around line 115-118: toLink currently forces PageNavLink.title by doing
String(p.name) which will stringify ReactNode names to "[object Object]";
instead guard and produce a proper string title: if p.name is a string use it,
otherwise resolve the page by URL from source.getPages() (or the pages array)
and use extractFrontmatter(match).title as the fallback (and finally fallback to
an empty string); update the toLink mapping (referencing toLink, pages,
PageNavLink.title, Item.name, source.getPages(), and extractFrontmatter) to
implement this lookup and type-guard so non-string ReactNode names do not
produce "[object Object]".

In `@packages/chronicle/src/themes/default/Layout.tsx`:
- Around line 47-50: Breadcrumbs is always fed the docs tree so on API routes
getBreadcrumbItems can't find the path; update the Layout component to use the
isApiRoute flag (detected at isApiRoute) to either hide the Breadcrumbs or pass
the API tree instead of the docs tree: when isApiRoute is true, supply the API
page tree (the API tree variable) to the Breadcrumbs call (or skip rendering
Breadcrumbs entirely on API routes), ensuring getBreadcrumbItems receives the
correct tree for the slug computed by useMemo.

In `@packages/chronicle/src/themes/default/Page.tsx`:
- Around line 7-15: The theme no longer renders the table of contents because
the Toc component is not wired into the default layout; reintegrate Toc into the
layout so pages render a TOC (or document its removal). Update the default
theme’s Layout component (where Breadcrumbs was added) to import and render the
Toc component alongside or within the same container used for Breadcrumbs
(reference Toc and Breadcrumbs components and the Layout and Page/ThemePageProps
rendering flow), pass the page or page.toc data as needed, and ensure any
CSS/classes (styles.toc or styles.sidebar) are applied so the TOC appears
without breaking existing layout.

---

Nitpick comments:
In `@packages/chronicle/src/components/ui/client-theme-switcher.tsx`:
- Around line 19-34: When isClient is false the component returns null causing a
layout shift on mount; instead render a fixed-size placeholder that matches the
IconButton's outer dimensions so space is reserved (for example render the same
IconButton structure with visibility:hidden or a sized wrapper) inside the
ClientThemeSwitcher (use the existing isClient check and IconButton/size props
to mirror size), and either align the outer IconButton's size with the incoming
size prop or document why IconButton uses size={3} if you intentionally keep the
inner icon size independent.

In `@packages/chronicle/src/themes/default/Layout.module.css`:
- Around line 103-124: Update the theme-unaware hardcoded shadow and the
near-no-op backdrop filter: replace the rgba(...) shadow used in the .card rule
with a theme token (e.g., var(--rs-shadow-*, or an appropriate existing shadow
variable) so the elevation responds to dark/light themes and accessibility
settings, and for .subNav either increase the blur radius (e.g., to a
perceptible value) or remove the backdrop-filter property to avoid unnecessary
GPU work—change the rules in the .card and .subNav selectors accordingly.

In `@packages/chronicle/src/themes/default/Layout.tsx`:
- Around line 121-150: The sub-nav should be omitted when there's no navigation
to display; update the Layout component to only render the <nav
className={styles.subNav}> block when there is meaningful content (e.g. prev or
next exist, or Breadcrumbs would render). Compute a boolean like hasNav =
Boolean(prev || next || (slug && tree && tree.length)) and wrap the existing nav
(including the IconButton group and <Breadcrumbs slug={slug} tree={tree} />) in
a conditional render using that flag so the empty 48px sub-nav is not shown on
pages with no page data.

In `@packages/chronicle/src/themes/default/Page.module.css`:
- Around line 33-45: Remove the redundant margin-top: 0 from the .content h1
rule (or replace it with a comment/intent if you explicitly need to override the
:first-child reset) to avoid unintentionally removing top spacing for non-first
h1s; and change the .content p rule's line-height: 171.429% to a design token or
a unitless value (e.g., var(--rs-line-height-regular) or 1.71) so it matches the
file's styling conventions and improves readability.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 26555773-af54-4944-8cd9-4f4fc0f16ecd

📥 Commits

Reviewing files that changed from the base of the PR and between fcbe6c0 and 6cd486f.

📒 Files selected for processing (10)
  • packages/chronicle/src/components/ui/client-theme-switcher.tsx
  • packages/chronicle/src/components/ui/search.module.css
  • packages/chronicle/src/components/ui/search.tsx
  • packages/chronicle/src/lib/source.ts
  • packages/chronicle/src/server/api/page.ts
  • packages/chronicle/src/server/entry-server.tsx
  • packages/chronicle/src/themes/default/Layout.module.css
  • packages/chronicle/src/themes/default/Layout.tsx
  • packages/chronicle/src/themes/default/Page.module.css
  • packages/chronicle/src/themes/default/Page.tsx
💤 Files with no reviewable changes (1)
  • packages/chronicle/src/components/ui/search.module.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/chronicle/src/server/entry-server.tsx

Comment thread packages/chronicle/src/components/ui/search.tsx
Comment thread packages/chronicle/src/lib/source.ts
Comment thread packages/chronicle/src/themes/default/Layout.tsx
Comment thread packages/chronicle/src/themes/default/Page.tsx
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Restructures Chronicle’s default theme layout to introduce a section tab bar + updated sidebar/nav chrome, and adds typed prev/next navigation data to the Page model (with server/client wiring stubbed).

Changes:

  • Introduce PageNavLink / PageNav types and extend Page with backend-provided prev/next links.
  • Redesign the default theme layout (remove top navbar, add tab bar + card wrapper + new TOC UI).
  • Switch docs to the default theme and add std-env as a root dependency to unblock Nitro runtime resolution.

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/chronicle/src/types/content.ts Adds PageNavLink/PageNav and extends Page with prev/next.
packages/chronicle/src/lib/source.ts Adds getPageNav() implementation using the page tree.
packages/chronicle/src/server/entry-server.tsx Embeds prev/next into SSR page payload; calls getPageNav.
packages/chronicle/src/server/entry-client.tsx Hydrates prev/next from embedded payload into client page model.
packages/chronicle/src/server/api/page.ts Extends /api/page response with prev/next.
packages/chronicle/src/lib/page-context.tsx Updates context page typing to Page and populates prev/next on client fetch.
packages/chronicle/src/pages/DocsPage.tsx Passes the full page object through to the theme page component.
packages/chronicle/src/themes/default/Layout.tsx Replaces top navbar with sidebar header + tab bar + subnav containing prev/next + breadcrumbs.
packages/chronicle/src/themes/default/Layout.module.css Styles the new layout structure (sidebar header, tab bar, card/subnav).
packages/chronicle/src/themes/default/Page.tsx Removes breadcrumbs from page body (now rendered in layout).
packages/chronicle/src/themes/default/Page.module.css Adds typography/layout tweaks for headings and paragraphs.
packages/chronicle/src/themes/default/Toc.tsx Replaces text TOC with marker rail + hover panel TOC.
packages/chronicle/src/themes/default/Toc.module.css Styles marker rail + hover-revealed panel.
packages/chronicle/src/components/ui/search.tsx Switches search trigger from button to icon button and removes inline shortcut UI.
packages/chronicle/src/components/ui/search.module.css Removes trigger/kbd styles; keeps dialog styling.
packages/chronicle/src/components/ui/client-theme-switcher.tsx Replaces ThemeSwitcher with a custom icon toggle using useTheme().
docs/chronicle.yaml Switches docs example theme from paper to default.
package.json Adds std-env root dependency for Nitro runtime compatibility.
bun.lock Lockfile update for std-env.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/chronicle/src/server/entry-client.tsx
Comment on lines +18 to +67
.toc:hover .markers {
opacity: 0;
pointer-events: none;
}

.marker {
display: block;
height: 2px;
background: var(--rs-color-border-base-secondary);
border-radius: 1px;
transition:
width 0.15s ease,
background 0.15s ease;
}

.marker:hover {
background: var(--rs-color-foreground-base-secondary);
}

.markerActive {
background: var(--rs-color-border-base-emphasis);
}


.panel {
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%) translateX(8px);
display: flex;
flex-direction: column;
gap: var(--rs-space-2);
min-width: 200px;
padding: var(--rs-space-3) 0;
background: var(--rs-color-background-base-primary);
border: 0.5px solid var(--rs-color-border-base-primary);
border-radius: var(--rs-radius-4);
box-shadow: var(--rs-shadow-soft);
opacity: 0;
pointer-events: none;
transition:
opacity 150ms ease,
transform 150ms ease;
}

.toc:hover .panel {
opacity: 1;
pointer-events: auto;
transform: translateY(-50%) translateX(0);
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TOC panel/markers are only toggled on :hover, so keyboard users who tab into the TOC won’t ever see the expanded panel. Consider adding :focus-within (and/or :focus-visible) variants that mirror the hover behavior so the panel becomes visible when any TOC link receives focus.

Copilot uses AI. Check for mistakes.
Comment thread packages/chronicle/src/components/ui/search.tsx Outdated
Comment thread packages/chronicle/src/lib/source.ts Outdated
Comment thread packages/chronicle/src/lib/source.ts Outdated
Comment thread packages/chronicle/src/server/entry-server.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
packages/chronicle/src/themes/default/Toc.tsx (1)

39-46: markerWidth assumes monospace character metrics.

Using length * 1px + 8px as a width is a very rough proxy for text width with proportional fonts and unicode (CJK, emoji, combining marks); the effect is mostly cosmetic but results can look off. If the visual intent is "longer heading → longer marker," consider bucketing by depth or clamping to a small set of widths instead of per-character math.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Toc.tsx` around lines 39 - 46,
markerWidth currently multiplies character count by MARKER_PER_CHAR (using
MARKER_BASE, MARKER_PER_CHAR, MARKER_MAX) which assumes monospace and yields
poor results for proportional/unicode text; change it to compute a bucketed
width instead (e.g., map nodeToText(title).length to a small set of discrete
widths or derive from heading depth) so that widths step through a few clamped
sizes rather than per-character pixels; update the markerWidth(title: ReactNode)
implementation to produce one of a few preset widths (using MARKER_BASE and
MARKER_MAX as min/max and a small set of increments) and keep references to
MARKER_BASE, MARKER_PER_CHAR, MARKER_MAX or replace MARKER_PER_CHAR with a
BUCKET_STEP/thresholds constant for clarity.
packages/chronicle/src/lib/page-context.tsx (1)

57-59: isApisRoute is defined but never referenced.

The helper looks intended to classify API routes but isn't wired up here. Either drop it or use it in place of the ad-hoc pathname.startsWith('/apis') check in Layout.tsx (line 48), which additionally matches unrelated paths like /apispecs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/lib/page-context.tsx` around lines 57 - 59, The helper
isApisRoute(pathname: string) is defined but unused; replace the ad-hoc check
pathname.startsWith('/apis') in Layout.tsx (the conditional around
rendering/layout logic) with a call to isApisRoute(pathname) so only true API
routes like '/apis' or '/apis/*' match (or alternatively delete the unused
isApisRoute if you prefer to keep the existing matching behavior). Update
imports/exports if needed so isApisRoute from page-context.tsx is referenced by
Layout.tsx, and run tests to confirm no other callers break.
packages/chronicle/src/themes/default/Layout.module.css (1)

22-46: Four declared CSS classes aren't referenced in Layout.tsx.

.sidebarNavbar (22-32), .sidebarNavLogo (34-36), .sidebarNavActions (38-42), and .sidebarMain (44-46) appear unused — the current Layout.tsx uses .sidebarHeader + inline Flex gap for the sidebar top bar, and Sidebar.Main is rendered without a custom class. Consider removing them to keep the stylesheet lean, or wire them up if they're intended for a follow-up commit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Layout.module.css` around lines 22 -
46, The stylesheet defines .sidebarNavbar, .sidebarNavLogo, .sidebarNavActions,
and .sidebarMain but Layout.tsx currently uses Sidebar.Header (or a
.sidebarHeader) and inline Flex gap instead, so either remove those four unused
classes from Layout.module.css or wire them up: in Layout.tsx replace the
current Sidebar.Header wrapper with className={styles.sidebarNavbar}, give the
logo element className={styles.sidebarNavLogo}, wrap the header action buttons
in an element with className={styles.sidebarNavActions}, and pass
className={styles.sidebarMain} to Sidebar.Main so the declared CSS is actually
applied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/chronicle/src/components/ui/search.tsx`:
- Line 61: The title string "Search (⌘K)" is Mac-specific; update the Search
component's title prop (currently set as title='Search (⌘K)') to be
platform-aware by computing the shortcut label on mount and using that value, or
replace it with a platform-agnostic hint like "Search (Ctrl/⌘+K)"; look for the
Search component in this file (title='Search (⌘K)') and either derive the label
via navigator.platform or userAgentData in a useEffect/useMemo and set
title={shortcutLabel} or change the literal to "Search (Ctrl/⌘+K)" so
Windows/Linux users see the correct hint.

In `@packages/chronicle/src/themes/default/Layout.tsx`:
- Line 48: The isApiRoute check currently uses pathname.startsWith('/apis')
which incorrectly matches paths like '/apispecs'; update the logic in Layout.tsx
(the isApiRoute definition) to use the same semantic as isApiBase (i.e., match
either pathname === '/apis' or pathname.startsWith('/apis/')) or import and
reuse the existing isApisRoute helper exported from page-context.tsx so the
Breadcrumbs suppression (the conditional that uses isApiRoute) only fires for
the actual /apis base and its true subroutes.

In `@packages/chronicle/src/themes/default/Toc.module.css`:
- Around line 1-7: The .toc class is fixed to the viewport with no responsive
rules so it overlaps content on narrow viewports; update Toc.module.css to add
responsive handling by adding a media query (e.g., for max-width breakpoints)
that hides or repositions .toc on small screens, or change the positioning
strategy for .toc to anchor to the layout (e.g., use relative/sticky within the
page/container) and lower/remove the z-index as needed; target the .toc selector
and adjust the display/position/z-index inside the new media query or alternate
positioning rule so the TOC does not overlap article content on narrow
viewports.

In `@packages/chronicle/src/themes/default/Toc.tsx`:
- Around line 48-97: The TOC panel is currently only revealed on hover which
hides labels from keyboard and touch users; update the TocContent component and
CSS so the panel is also revealed on focus and can be toggled on touch: add
tabIndex={0} to the aside rendered by TocContent (so it can receive keyboard
focus) and implement a simple toggle state (e.g., isOpen) with a clickable
button/indicator that sets aria-expanded on the toggle and adds an "open" class
when active; in Toc.module.css update the reveal rule from .toc:hover to also
match .toc:focus-within and .toc.open (and ensure focus styles are preserved) so
keyboard users (focus-within) and touch users (toggle adds .open) can see the
panel.
- Around line 53-95: The marker column is creating duplicate focusable anchors;
update the marker anchors rendered in the styles.markers map (the <a> produced
inside items.map that uses styles.marker and markerWidth) to be non-interactive
by adding aria-hidden="true" and tabIndex={-1} (or set the wrapper div
styles.markers to aria-hidden="true") so they are ignored by assistive tech and
keyboard navigation, and keep the panel <nav> (styles.panel / styles.panelList
and its panelItem anchors) as the single accessible navigation.

---

Nitpick comments:
In `@packages/chronicle/src/lib/page-context.tsx`:
- Around line 57-59: The helper isApisRoute(pathname: string) is defined but
unused; replace the ad-hoc check pathname.startsWith('/apis') in Layout.tsx (the
conditional around rendering/layout logic) with a call to isApisRoute(pathname)
so only true API routes like '/apis' or '/apis/*' match (or alternatively delete
the unused isApisRoute if you prefer to keep the existing matching behavior).
Update imports/exports if needed so isApisRoute from page-context.tsx is
referenced by Layout.tsx, and run tests to confirm no other callers break.

In `@packages/chronicle/src/themes/default/Layout.module.css`:
- Around line 22-46: The stylesheet defines .sidebarNavbar, .sidebarNavLogo,
.sidebarNavActions, and .sidebarMain but Layout.tsx currently uses
Sidebar.Header (or a .sidebarHeader) and inline Flex gap instead, so either
remove those four unused classes from Layout.module.css or wire them up: in
Layout.tsx replace the current Sidebar.Header wrapper with
className={styles.sidebarNavbar}, give the logo element
className={styles.sidebarNavLogo}, wrap the header action buttons in an element
with className={styles.sidebarNavActions}, and pass
className={styles.sidebarMain} to Sidebar.Main so the declared CSS is actually
applied.

In `@packages/chronicle/src/themes/default/Toc.tsx`:
- Around line 39-46: markerWidth currently multiplies character count by
MARKER_PER_CHAR (using MARKER_BASE, MARKER_PER_CHAR, MARKER_MAX) which assumes
monospace and yields poor results for proportional/unicode text; change it to
compute a bucketed width instead (e.g., map nodeToText(title).length to a small
set of discrete widths or derive from heading depth) so that widths step through
a few clamped sizes rather than per-character pixels; update the
markerWidth(title: ReactNode) implementation to produce one of a few preset
widths (using MARKER_BASE and MARKER_MAX as min/max and a small set of
increments) and keep references to MARKER_BASE, MARKER_PER_CHAR, MARKER_MAX or
replace MARKER_PER_CHAR with a BUCKET_STEP/thresholds constant for clarity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fa4772fe-f929-4218-b29e-e9b312e5d206

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd486f and cd86f5e.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • docs/chronicle.yaml
  • package.json
  • packages/chronicle/src/components/ui/client-theme-switcher.tsx
  • packages/chronicle/src/components/ui/search.module.css
  • packages/chronicle/src/components/ui/search.tsx
  • packages/chronicle/src/lib/page-context.tsx
  • packages/chronicle/src/lib/source.ts
  • packages/chronicle/src/pages/DocsPage.tsx
  • packages/chronicle/src/server/api/page.ts
  • packages/chronicle/src/server/entry-client.tsx
  • packages/chronicle/src/server/entry-server.tsx
  • packages/chronicle/src/themes/default/Layout.module.css
  • packages/chronicle/src/themes/default/Layout.tsx
  • packages/chronicle/src/themes/default/Page.module.css
  • packages/chronicle/src/themes/default/Page.tsx
  • packages/chronicle/src/themes/default/Toc.module.css
  • packages/chronicle/src/themes/default/Toc.tsx
  • packages/chronicle/src/types/content.ts
💤 Files with no reviewable changes (1)
  • packages/chronicle/src/components/ui/search.module.css
✅ Files skipped from review due to trivial changes (2)
  • docs/chronicle.yaml
  • package.json
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/chronicle/src/pages/DocsPage.tsx
  • packages/chronicle/src/server/entry-client.tsx
  • packages/chronicle/src/server/api/page.ts
  • packages/chronicle/src/themes/default/Page.tsx
  • packages/chronicle/src/lib/source.ts
  • packages/chronicle/src/server/entry-server.tsx
  • packages/chronicle/src/types/content.ts
  • packages/chronicle/src/components/ui/client-theme-switcher.tsx

Comment thread packages/chronicle/src/components/ui/search.tsx Outdated
Comment thread packages/chronicle/src/themes/default/Layout.tsx Outdated
Comment thread packages/chronicle/src/themes/default/Toc.module.css
Comment on lines 48 to 97
function TocContent({ items }: { items: TOCItemType[] }) {
const activeAnchor = useActiveAnchor();

return (
<aside className={styles.toc}>
<Text size={1} weight='medium' className={styles.title}>
On this page
</Text>
<nav className={styles.nav}>
<aside className={styles.toc} aria-label='Table of contents'>
<div className={styles.markers}>
{items.map(item => {
const id = item.url.replace('#', '');
const isActive = activeAnchor === id;
const isNested = item.depth > 2;
return (
<a
key={item.url}
href={item.url}
className={`${styles.link} ${isActive ? styles.active : ''} ${isNested ? styles.nested : ''}`}
aria-label={nodeToText(item.title)}
className={cx(styles.marker, isActive && styles.markerActive)}
style={{ width: `${markerWidth(item.title)}px` }}
>
{item.title}
<span />
</a>
);
})}
</nav>
</div>
<div className={styles.panel} role='presentation'>
<div className={styles.panelHeader}>
<Bars3BottomLeftIcon width={16} height={16} />
<span className={styles.panelHeaderLabel}>On this page</span>
</div>
<nav className={styles.panelList}>
{items.map(item => {
const id = item.url.replace('#', '');
const isActive = activeAnchor === id;
const isNested = item.depth > 2;
return (
<a
key={item.url}
href={item.url}
className={cx(
styles.panelItem,
isNested && styles.panelItemNested,
isActive && styles.panelItemActive
)}
>
{nodeToText(item.title)}
</a>
);
})}
</nav>
</div>
</aside>
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hover-only disclosure hides the panel from keyboard and touch users.

The panel is revealed only via .toc:hover in CSS. Keyboard users tabbing through the marker links never see the text labels (no :focus-within rule), and touch devices have no hover at all, so the TOC effectively becomes an unlabeled set of colored bars for those users. Consider revealing the panel on :focus-within as well, and/or making it tap-to-open on touch.

Suggested CSS addition in Toc.module.css
-.toc:hover .markers {
+.toc:hover .markers,
+.toc:focus-within .markers {
   opacity: 0;
   pointer-events: none;
 }
@@
-.toc:hover .panel {
+.toc:hover .panel,
+.toc:focus-within .panel {
   opacity: 1;
   pointer-events: auto;
   transform: translateY(-50%) translateX(0);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Toc.tsx` around lines 48 - 97, The TOC
panel is currently only revealed on hover which hides labels from keyboard and
touch users; update the TocContent component and CSS so the panel is also
revealed on focus and can be toggled on touch: add tabIndex={0} to the aside
rendered by TocContent (so it can receive keyboard focus) and implement a simple
toggle state (e.g., isOpen) with a clickable button/indicator that sets
aria-expanded on the toggle and adds an "open" class when active; in
Toc.module.css update the reveal rule from .toc:hover to also match
.toc:focus-within and .toc.open (and ensure focus styles are preserved) so
keyboard users (focus-within) and touch users (toggle adds .open) can see the
panel.

Comment thread packages/chronicle/src/themes/default/Toc.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (1)
packages/chronicle/src/themes/default/Toc.module.css (1)

1-7: ⚠️ Potential issue | 🟡 Minor

Add responsive handling for the fixed TOC.

The fixed right-anchored TOC still has no breakpoint or layout-aware positioning, so it can overlap article content on narrower viewports.

Suggested responsive guard
 .toc {
   position: fixed;
   right: calc(var(--rs-space-5) + var(--rs-space-3));
   top: 50%;
   transform: translateY(-50%);
   z-index: 10;
 }
+
+@media (max-width: 1024px) {
+  .toc {
+    display: none;
+  }
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Toc.module.css` around lines 1 - 7, The
.toc rule in Toc.module.css uses a fixed right-anchored position that can
overlap content on narrow viewports; update the stylesheet to add a responsive
media query (e.g., max-width: 768px or a site breakpoint) that changes .toc from
position: fixed to a layout-friendly state (position: static or position: sticky
with transform: none, top: auto, right: auto) or hides it (display: none) and
adjusts z-index/margins accordingly so the TOC no longer overlaps article
content on small screens; target the .toc selector to implement this breakpoint
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/chronicle/src/themes/default/Layout.module.css`:
- Around line 9-15: The .sidebar rule can shrink in flex layouts and its fixed
100vh can clip content; update the .sidebar declaration to prevent shrinking and
enable internal scrolling by using a non-shrinking flex basis (e.g., add flex: 0
0 262px and/or min-width: 262px) instead of a plain width, and add overflow-y:
auto with max-height: 100vh (keep position: sticky; top: 0) so long nav trees
scroll inside the sidebar; apply the same changes to the other sidebar rule
further down that mirrors this styling.
- Around line 119-127: The .card rule in Layout.module.css uses overflow: clip
which trims floating panels and focus rings; update the .card CSS (card class)
to allow overflow to be visible (replace overflow: clip with overflow: visible)
so TOC hover/focus panels and outlines can extend outside the card boundaries,
keeping the existing flex, border, radius and box-shadow intact; if any child
relies on clipping for layout, instead scope clipping to that specific child
rather than the .card.
- Around line 69-98: The tab row can overflow horizontally and needs scroll
handling: update the .tabs (or .tabBar) styles to enable horizontal scrolling by
adding overflow-x: auto and overflow-y: hidden, enable smooth scrolling on touch
with -webkit-overflow-scrolling: touch, and prevent individual tabs from
shrinking by setting .tab to flex: 0 0 auto; also keep white-space: nowrap on
the container so tabs don’t wrap. This ensures many .tab items remain reachable
via horizontal scroll while preserving the existing layout and spacing.
- Around line 129-138: The .subNav rule combines a fixed height with vertical
padding which can alter its rendered height; update the .subNav selector in
Layout.module.css to include box-sizing: border-box (same as .sidebarHeader) so
the 48px height remains stable and padding is included within that height.

In `@packages/chronicle/src/themes/default/Toc.module.css`:
- Around line 43-62: The .panel styles allow the TOC to grow beyond the
viewport; update the .panel rule (in Toc.module.css) to constrain its height and
enable internal scrolling by adding a max-height computed from the viewport
(e.g., using calc(100vh - <offset>)) and overflow-y: auto so long TOC lists
remain reachable; ensure pointer-events and transition behavior remain unchanged
and apply the same change to the other panel variant referenced in the file
(lines ~88-93).
- Around line 18-22: The CSS currently hides .markers when .toc receives
focus-within which makes keyboard-focused controls invisible; change the
behavior so markers are hidden only when the panel is hovered AND not when a
child has keyboard focus: replace the .toc:focus-within .markers rule with a
selector that targets .toc:not(:focus-within) .markers (or alternatively add an
overriding rule .toc:focus-within .markers { opacity: 1; pointer-events: auto })
so .markers remain visible and interactive for keyboard users; update the same
pattern covering the other occurrences of .toc/.markers in the file.

---

Duplicate comments:
In `@packages/chronicle/src/themes/default/Toc.module.css`:
- Around line 1-7: The .toc rule in Toc.module.css uses a fixed right-anchored
position that can overlap content on narrow viewports; update the stylesheet to
add a responsive media query (e.g., max-width: 768px or a site breakpoint) that
changes .toc from position: fixed to a layout-friendly state (position: static
or position: sticky with transform: none, top: auto, right: auto) or hides it
(display: none) and adjusts z-index/margins accordingly so the TOC no longer
overlaps article content on small screens; target the .toc selector to implement
this breakpoint behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 51759f2d-0465-45d0-a0f6-5d258d5619fa

📥 Commits

Reviewing files that changed from the base of the PR and between cd86f5e and 3ab607a.

📒 Files selected for processing (7)
  • packages/chronicle/src/components/ui/search.tsx
  • packages/chronicle/src/lib/source.ts
  • packages/chronicle/src/server/App.tsx
  • packages/chronicle/src/server/entry-client.tsx
  • packages/chronicle/src/server/entry-server.tsx
  • packages/chronicle/src/themes/default/Layout.module.css
  • packages/chronicle/src/themes/default/Toc.module.css
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/chronicle/src/server/entry-client.tsx
  • packages/chronicle/src/components/ui/search.tsx
  • packages/chronicle/src/server/entry-server.tsx
  • packages/chronicle/src/lib/source.ts

Comment thread packages/chronicle/src/themes/default/Layout.module.css
Comment on lines 69 to 98
.tabBar {
display: flex;
align-items: center;
height: 48px;
padding: 0 var(--rs-space-2);
background: var(--rs-color-background-base-secondary);
}

.folder > .sidebarList {
margin-top: var(--rs-space-2);
padding-left: var(--rs-space-4);
.tabs {
display: flex;
align-items: center;
gap: var(--rs-space-3);
}

.navButton {
.tab {
display: flex;
align-items: center;
height: 32px;
padding: 0 var(--rs-space-4);
border: 1px solid var(--rs-color-border-base-primary);
gap: var(--rs-space-2);
padding: var(--rs-space-2) var(--rs-space-3);
border: 0.5px solid var(--rs-color-border-base-primary);
border-radius: var(--rs-radius-2);
font-size: var(--rs-font-size-small);
font-size: var(--rs-font-size-mini);
font-weight: var(--rs-font-weight-medium);
color: var(--rs-color-foreground-base-primary);
letter-spacing: var(--rs-letter-spacing-mini);
line-height: var(--rs-line-height-mini);
color: var(--rs-color-foreground-base-secondary);
text-decoration: none;
white-space: nowrap;
cursor: pointer;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add horizontal overflow handling for the tab bar.

The tabs are non-wrapping links, so multiple APIs can spill outside the main area and become hard to reach.

Proposed fix
 .tabBar {
   display: flex;
   align-items: center;
   height: 48px;
   padding: 0 var(--rs-space-2);
   background: var(--rs-color-background-base-secondary);
+  min-width: 0;
+  overflow-x: auto;
 }

 .tabs {
   display: flex;
   align-items: center;
   gap: var(--rs-space-3);
+  min-width: max-content;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.tabBar {
display: flex;
align-items: center;
height: 48px;
padding: 0 var(--rs-space-2);
background: var(--rs-color-background-base-secondary);
}
.folder > .sidebarList {
margin-top: var(--rs-space-2);
padding-left: var(--rs-space-4);
.tabs {
display: flex;
align-items: center;
gap: var(--rs-space-3);
}
.navButton {
.tab {
display: flex;
align-items: center;
height: 32px;
padding: 0 var(--rs-space-4);
border: 1px solid var(--rs-color-border-base-primary);
gap: var(--rs-space-2);
padding: var(--rs-space-2) var(--rs-space-3);
border: 0.5px solid var(--rs-color-border-base-primary);
border-radius: var(--rs-radius-2);
font-size: var(--rs-font-size-small);
font-size: var(--rs-font-size-mini);
font-weight: var(--rs-font-weight-medium);
color: var(--rs-color-foreground-base-primary);
letter-spacing: var(--rs-letter-spacing-mini);
line-height: var(--rs-line-height-mini);
color: var(--rs-color-foreground-base-secondary);
text-decoration: none;
white-space: nowrap;
cursor: pointer;
}
.tabBar {
display: flex;
align-items: center;
height: 48px;
padding: 0 var(--rs-space-2);
background: var(--rs-color-background-base-secondary);
min-width: 0;
overflow-x: auto;
}
.tabs {
display: flex;
align-items: center;
gap: var(--rs-space-3);
min-width: max-content;
}
.tab {
display: flex;
align-items: center;
gap: var(--rs-space-2);
padding: var(--rs-space-2) var(--rs-space-3);
border: 0.5px solid var(--rs-color-border-base-primary);
border-radius: var(--rs-radius-2);
font-size: var(--rs-font-size-mini);
font-weight: var(--rs-font-weight-medium);
letter-spacing: var(--rs-letter-spacing-mini);
line-height: var(--rs-line-height-mini);
color: var(--rs-color-foreground-base-secondary);
text-decoration: none;
white-space: nowrap;
cursor: pointer;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Layout.module.css` around lines 69 -
98, The tab row can overflow horizontally and needs scroll handling: update the
.tabs (or .tabBar) styles to enable horizontal scrolling by adding overflow-x:
auto and overflow-y: hidden, enable smooth scrolling on touch with
-webkit-overflow-scrolling: touch, and prevent individual tabs from shrinking by
setting .tab to flex: 0 0 auto; also keep white-space: nowrap on the container
so tabs don’t wrap. This ensures many .tab items remain reachable via horizontal
scroll while preserving the existing layout and spacing.

Comment thread packages/chronicle/src/themes/default/Layout.module.css
Comment on lines +129 to +138
.subNav {
display: flex;
align-items: center;
justify-content: space-between;
height: 48px;
padding: var(--rs-space-4) var(--rs-space-7);
background: var(--rs-color-background-base-primary);
border-bottom: 0.5px solid var(--rs-color-border-base-primary);
backdrop-filter: blur(1px);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Keep the subnav’s 48px height border-boxed.

This selector combines fixed height with vertical padding; add box-sizing here like .sidebarHeader to keep the rendered height stable.

Proposed fix
 .subNav {
   display: flex;
   align-items: center;
   justify-content: space-between;
   height: 48px;
+  box-sizing: border-box;
   padding: var(--rs-space-4) var(--rs-space-7);
   background: var(--rs-color-background-base-primary);
   border-bottom: 0.5px solid var(--rs-color-border-base-primary);
   backdrop-filter: blur(1px);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.subNav {
display: flex;
align-items: center;
justify-content: space-between;
height: 48px;
padding: var(--rs-space-4) var(--rs-space-7);
background: var(--rs-color-background-base-primary);
border-bottom: 0.5px solid var(--rs-color-border-base-primary);
backdrop-filter: blur(1px);
}
.subNav {
display: flex;
align-items: center;
justify-content: space-between;
height: 48px;
box-sizing: border-box;
padding: var(--rs-space-4) var(--rs-space-7);
background: var(--rs-color-background-base-primary);
border-bottom: 0.5px solid var(--rs-color-border-base-primary);
backdrop-filter: blur(1px);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Layout.module.css` around lines 129 -
138, The .subNav rule combines a fixed height with vertical padding which can
alter its rendered height; update the .subNav selector in Layout.module.css to
include box-sizing: border-box (same as .sidebarHeader) so the 48px height
remains stable and padding is included within that height.

Comment on lines +18 to +22
.toc:hover .markers,
.toc:focus-within .markers {
opacity: 0;
pointer-events: none;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t hide the focused marker for keyboard users.

With .toc:focus-within .markers { opacity: 0 }, tabbing to a marker makes the focused control invisible before focus moves into the panel.

Suggested keyboard-safe adjustment
-.toc:hover .markers,
-.toc:focus-within .markers {
+.toc:hover .markers {
   opacity: 0;
   pointer-events: none;
 }
 
 .marker {
   display: block;
   height: 2px;
   background: var(--rs-color-border-base-secondary);
   border-radius: 1px;
   transition:
     width 0.15s ease,
     background 0.15s ease;
 }
+
+.marker:focus-visible {
+  outline: 2px solid var(--rs-color-border-base-emphasis);
+  outline-offset: var(--rs-space-2);
+}

Also applies to: 24-40

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Toc.module.css` around lines 18 - 22,
The CSS currently hides .markers when .toc receives focus-within which makes
keyboard-focused controls invisible; change the behavior so markers are hidden
only when the panel is hovered AND not when a child has keyboard focus: replace
the .toc:focus-within .markers rule with a selector that targets
.toc:not(:focus-within) .markers (or alternatively add an overriding rule
.toc:focus-within .markers { opacity: 1; pointer-events: auto }) so .markers
remain visible and interactive for keyboard users; update the same pattern
covering the other occurrences of .toc/.markers in the file.

Comment on lines +43 to +62
.panel {
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%) translateX(8px);
display: flex;
flex-direction: column;
gap: var(--rs-space-2);
min-width: 200px;
padding: var(--rs-space-3) 0;
background: var(--rs-color-background-base-primary);
border: 0.5px solid var(--rs-color-border-base-primary);
border-radius: var(--rs-radius-4);
box-shadow: var(--rs-shadow-soft);
opacity: 0;
pointer-events: none;
transition:
opacity 150ms ease,
transform 150ms ease;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Constrain long TOC panels to the viewport.

The centered fixed panel can grow past the viewport for pages with many headings, making some links unreachable. Add a max-height and scroll the list.

Suggested overflow handling
 .panel {
   position: absolute;
   top: 50%;
   right: 0;
   transform: translateY(-50%) translateX(8px);
   display: flex;
   flex-direction: column;
   gap: var(--rs-space-2);
   min-width: 200px;
+  max-height: calc(100vh - (2 * var(--rs-space-5)));
   padding: var(--rs-space-3) 0;
   background: var(--rs-color-background-base-primary);
   border: 0.5px solid var(--rs-color-border-base-primary);
   border-radius: var(--rs-radius-4);
   box-shadow: var(--rs-shadow-soft);
+  overflow: hidden;
   opacity: 0;
   pointer-events: none;
   transition:
     opacity 150ms ease,
     transform 150ms ease;
 }
 
 .panelList {
   display: flex;
   flex-direction: column;
   gap: var(--rs-space-1);
   padding-left: var(--rs-space-8);
+  min-height: 0;
+  overflow-y: auto;
 }

Also applies to: 88-93

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chronicle/src/themes/default/Toc.module.css` around lines 43 - 62,
The .panel styles allow the TOC to grow beyond the viewport; update the .panel
rule (in Toc.module.css) to constrain its height and enable internal scrolling
by adding a max-height computed from the viewport (e.g., using calc(100vh -
<offset>)) and overflow-y: auto so long TOC lists remain reachable; ensure
pointer-events and transition behavior remain unchanged and apply the same
change to the other panel variant referenced in the file (lines ~88-93).

rsbh and others added 9 commits April 23, 2026 10:15
Prepare Page type for backend-provided prev/next links used by the
docs sub-navbar. Adds PageNavLink/PageNav types, extends Page via
extends PageNav, unifies PageData with Page in page context, and
threads null placeholders through SSR + hydration until backend
computes real values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nitro@3.0.260311-beta lists std-env in devDependencies but its
runtime imports it. Node ESM resolution from nitro's isolated
cache path fails without std-env at top-level node_modules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the top Navbar; move navigation into a per-section tab bar
alongside the sidebar. Resize sidebar to 262px, swap button-based
nav links for pill-style tabs using mini typography tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add getPageNav helper that flattens the page tree and returns the
adjacent PageNavLinks for a slug. Both the /api/page handler and SSR
pageData now populate prev/next so the client can render navigation
without re-flattening the tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Default theme now renders the main content inside a rounded, bordered
card with a sub-navbar at the top containing prev/next IconButtons
driven by the server-provided page nav, followed by breadcrumbs.
Breadcrumbs move out of Page.tsx since the Layout owns the chrome.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add sidebar top navbar with placeholder logo, icon-only search, and
  IconButton-based theme switcher using resolvedTheme so system mode
  lands on the correct icon.
- Simplify Search component to IconButton only, drop cmdk kbd trigger.
- Wrap content in a bordered card, remove TOC from Page, align heading
  + paragraph typography with Aurora tokens.
- Secondary bg on sidebar + card wrapper, primary bg on content area.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TOC renders as short horizontal markers floating right-center, each
width scaled from the heading text length (base + per char, clamped).
Hovering the aside fades the markers out and reveals a panel with the
full heading list, active state highlighted. Shadows switch to the
Apsara --rs-shadow-soft token.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- page-context: coerce prev/next from API response to null so the
  state always matches the Page type instead of carrying undefined.
- source.getPageNav: guard PageNavLink.title to string-only page names
  so ReactNode names don't stringify to "[object Object]".
- Layout tabs: compute per-entry active state from api.basePath instead
  of the shared isApiRoute boolean, so only the matching API tab
  highlights when multiple APIs are configured.
- Layout: skip breadcrumbs on API routes since the docs tree doesn't
  include API paths.
- Search: add title="Search (⌘K)" so the keyboard shortcut stays
  discoverable after the kbd hint was removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rsbh and others added 8 commits April 23, 2026 10:15
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- search shortcut tooltip: platform-neutral "Ctrl/⌘K"
- TOC: mirror :hover with :focus-within for keyboard users
- getPageNav: derive PageNavLink.title from URL when page name is non-string

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- getPageNav: accept pre-loaded tree to avoid redundant tree rebuild
- entry-server: skip getPageNav when page is null; share tree with nav
- entry-client: nullable frontmatter/relativePath/originalPath in EmbeddedData; narrow at usage

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- SidebarNode recurses to depth 1 with data-depth attr on section
- Depth 0: non-collapsible section header
- Depth 1: collapsible sub-group with right-aligned chevron
- Grandchildren folders (depth > 1) ignored
- Hide sidebar scrollbar
- Style navGroup header/trigger/label/chevron via apsara classNames slots

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop tabBar (ContentDirButtons + API tabs) and subNav card-chrome
- Sidebar header: logo + search + theme only (version switcher moved out)
- Sidebar footer: holds VersionSwitcher, hidden when no versions
- Sidebar bg uses --rs-color-background-base-secondary
- Card: border-left only, no border-radius
- cardWrapper: drop horizontal padding
- TOC: drop extra right offset

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Render content-dir entries and API entries as Sidebar.Item cells
  at the top of Sidebar.Main, above the page tree
- Hidden when total (content + API) count is ≤ 1
- Drop Apsara's default header margin-bottom

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Top content links: tertiary text by default, primary when active, transparent bg
- Nav group label uses secondary for section headers, primary for sub-groups
- Nav group: zero out default margin-top, apply space-7 only to top-level (data-depth=0)
- Zero out gap on Sidebar.Main and nav-group-items
- Breadcrumbs: use render={<RouterLink>} so SPA navigation works (not-current items)
- Wrap top content/API links in .topLinks div with bottom margin

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rsbh and others added 7 commits April 24, 2026 10:56
- New SidebarLogo component reads config.logo.light/dark
- Falls back to BookOpenIcon when no logo is configured
- Picks src based on resolvedTheme from Apsara useTheme()

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Schema accepts icon string on content entries and api entries
- Icon can be a URL or an inline SVG string (uses currentColor)
- renderConfigIcon helper picks <img> for URL or inlines SVG
- Fallback to DocumentTextIcon / CodeBracketSquareIcon when absent

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop llms config section; routes serve unconditionally
- Remove llms.enabled gating from .md slug route and llms.txt routes
- Clean up llms: enabled: true from docs and versioned example yamls

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove footerSchema + FooterConfig from config schema
- Delete Footer component and CSS
- Remove Footer usage from paper theme Layout
- Clean footer: sections from docs and basic example yamls

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Head accepts optional markdownHref; DocsPage passes `/${slug}.md` so
crawlers and AI assistants can discover the raw markdown source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds OpenInAI menu in the subNav with four actions:
- Copy as MD (fetches the page's .md and writes to clipboard)
- View MD (opens the raw .md in a new tab)
- Open in ChatGPT (chatgpt.com/?q=Read <mdUrl>)
- Open in Claude (claude.ai/new?q=Read <mdUrl>)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bump @raystack/apsara to 1.0.0-rc.4
- Drop manual Dialog wrapper; use Command.Dialog + Command.DialogContent
- Command.List -> Command.Content
- Command.Group heading -> <Command.Label>
- Item onSelect -> onClick; Input leadingIcon replaces external icon
- Style dialogContent (width/radius/top) to position at 20% from top

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace four 48px usages (sidebar header, sidebar footer, subNav, etc)
with var(--navbar-height) defined on .layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inline SVG icons using currentColor so they inherit the text color.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Layout: tighten isApiRoute match (exact /apis or /apis/ prefix) so
  breadcrumb suppression doesn't catch unrelated paths
- Sidebar: flex: 0 0 262px + flex-column so it doesn't shrink; Main
  gets overflow-y: auto + min-height: 0 for long trees
- Card: overflow visible so floating TOC + focus rings aren't clipped
- Toc markers: aria-hidden + tabIndex=-1 so the panel nav is the
  single accessible entry
- Toc: hide below 900px viewport via media query

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants