Skip to content

Commit 779bb10

Browse files
committed
fix(shell-docs): move docs scrollbar to viewport edge
Previously <main> was both the scroll container AND width-capped (`flex-1 max-w-4xl px-8 py-10 overflow-y-auto`). The scrollbar rendered at the capped column's right edge, parking it mid-viewport with a blank gutter beside it. Separate the concerns: <main> is now full-width with the scroll, and an inner <div> caps the content width and owns the padding. Scrollbar now lands at the viewport edge (or TOC's left edge on pages that render the right-rail TOC). Applies to the four docs entry points that shared this pattern: the root overview, the /<framework> landing + scoped pages, the /ag-ui route, and the shared DocsPageView used by scoped docs.
1 parent 8fc1260 commit 779bb10

4 files changed

Lines changed: 294 additions & 264 deletions

File tree

showcase/shell-docs/src/app/[[...slug]]/page.tsx

Lines changed: 104 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -128,112 +128,124 @@ function DocsOverview() {
128128
))}
129129
</SidebarNav>
130130

131-
<main className="flex-1 max-w-4xl px-8 py-10 overflow-y-auto">
132-
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-2">
133-
Documentation
134-
</div>
135-
<h1 className="text-[2.25rem] font-bold text-[var(--text)] tracking-tight mb-3 leading-tight">
136-
Build AI-powered apps with CopilotKit
137-
</h1>
138-
<p className="text-base text-[var(--text-secondary)] leading-relaxed mb-10 max-w-2xl">
139-
CopilotKit ships deep integrations across every major agent framework
140-
and SDK. Pick your <em>agentic backend</em> below — the rest of the
141-
docs adapt every snippet and code sample to that framework.
142-
</p>
143-
144-
{/* Framework picker — big grid of all integrations, grouped by category */}
145-
<section className="mb-12">
131+
{/* <main> is the full-width scroll container so the scrollbar
132+
* lands at the viewport edge. Content width is capped by the
133+
* inner wrapper below. Previously `max-w-4xl` sat on <main>
134+
* directly, which parked the scrollbar mid-page. */}
135+
<main className="flex-1 overflow-y-auto">
136+
<div className="max-w-4xl px-8 py-10">
146137
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-2">
147-
Step 1
138+
Documentation
148139
</div>
149-
<h2 className="text-xl font-semibold text-[var(--text)] mb-1">
150-
Pick an agentic backend
151-
</h2>
152-
<p className="text-sm text-[var(--text-secondary)] mb-5">
153-
We store your choice locally so every page renders the right code.
140+
<h1 className="text-[2.25rem] font-bold text-[var(--text)] tracking-tight mb-3 leading-tight">
141+
Build AI-powered apps with CopilotKit
142+
</h1>
143+
<p className="text-base text-[var(--text-secondary)] leading-relaxed mb-10 max-w-2xl">
144+
CopilotKit ships deep integrations across every major agent
145+
framework and SDK. Pick your <em>agentic backend</em> below — the
146+
rest of the docs adapt every snippet and code sample to that
147+
framework.
154148
</p>
155149

156-
{[...buckets.entries()].map(([catId, items]) => {
157-
if (items.length === 0) return null;
158-
const label = catId === "other" ? "Other" : getCategoryLabel(catId);
159-
return (
160-
<div key={catId} className="mb-6">
161-
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-3">
162-
{label}
163-
</div>
164-
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
165-
{items.map((i) => (
166-
<Link
167-
key={i.slug}
168-
href={`/${i.slug}`}
169-
className={`group relative flex items-center gap-2 p-3 rounded-lg border transition-all ${
170-
i.deployed
171-
? "border-[var(--border)] bg-[var(--bg-surface)] hover:border-[var(--accent)] hover:shadow-sm"
172-
: "border-[var(--border-dim)] bg-[var(--bg-elevated)] opacity-70"
173-
}`}
174-
>
175-
{i.logo ? (
176-
// eslint-disable-next-line @next/next/no-img-element
177-
<img src={i.logo} alt="" className="w-5 h-5 shrink-0" />
178-
) : (
179-
<span className="w-5 h-5 shrink-0" />
180-
)}
181-
<span className="flex-1 min-w-0 truncate text-sm font-medium text-[var(--text)] group-hover:text-[var(--accent)]">
182-
{i.name}
183-
</span>
184-
{!i.deployed && (
185-
<span className="text-[9px] font-mono uppercase tracking-widest text-[var(--text-faint)]">
186-
soon
150+
{/* Framework picker — big grid of all integrations, grouped by category */}
151+
<section className="mb-12">
152+
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-2">
153+
Step 1
154+
</div>
155+
<h2 className="text-xl font-semibold text-[var(--text)] mb-1">
156+
Pick an agentic backend
157+
</h2>
158+
<p className="text-sm text-[var(--text-secondary)] mb-5">
159+
We store your choice locally so every page renders the right code.
160+
</p>
161+
162+
{[...buckets.entries()].map(([catId, items]) => {
163+
if (items.length === 0) return null;
164+
const label =
165+
catId === "other" ? "Other" : getCategoryLabel(catId);
166+
return (
167+
<div key={catId} className="mb-6">
168+
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-3">
169+
{label}
170+
</div>
171+
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
172+
{items.map((i) => (
173+
<Link
174+
key={i.slug}
175+
href={`/${i.slug}`}
176+
className={`group relative flex items-center gap-2 p-3 rounded-lg border transition-all ${
177+
i.deployed
178+
? "border-[var(--border)] bg-[var(--bg-surface)] hover:border-[var(--accent)] hover:shadow-sm"
179+
: "border-[var(--border-dim)] bg-[var(--bg-elevated)] opacity-70"
180+
}`}
181+
>
182+
{i.logo ? (
183+
// eslint-disable-next-line @next/next/no-img-element
184+
<img
185+
src={i.logo}
186+
alt=""
187+
className="w-5 h-5 shrink-0"
188+
/>
189+
) : (
190+
<span className="w-5 h-5 shrink-0" />
191+
)}
192+
<span className="flex-1 min-w-0 truncate text-sm font-medium text-[var(--text)] group-hover:text-[var(--accent)]">
193+
{i.name}
187194
</span>
188-
)}
189-
{/* Marks the framework currently stored in
195+
{!i.deployed && (
196+
<span className="text-[9px] font-mono uppercase tracking-widest text-[var(--text-faint)]">
197+
soon
198+
</span>
199+
)}
200+
{/* Marks the framework currently stored in
190201
localStorage so repeat visitors can spot "their"
191202
choice at a glance without an auto-redirect. */}
192-
<StoredFrameworkHighlight slug={i.slug} />
193-
</Link>
194-
))}
203+
<StoredFrameworkHighlight slug={i.slug} />
204+
</Link>
205+
))}
206+
</div>
195207
</div>
196-
</div>
197-
);
198-
})}
199-
</section>
208+
);
209+
})}
210+
</section>
200211

201-
{/* Section cards — grouped by broad topic. Clicking any of
212+
{/* Section cards — grouped by broad topic. Clicking any of
202213
these before picking a framework lands on the per-feature
203214
pivot; once a framework is stored the destination's
204215
<RouterPivot /> redirects into the scoped view. */}
205-
<section>
206-
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-2">
207-
Step 2
208-
</div>
209-
<h2 className="text-xl font-semibold text-[var(--text)] mb-5">
210-
Or jump into a topic
211-
</h2>
216+
<section>
217+
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-2">
218+
Step 2
219+
</div>
220+
<h2 className="text-xl font-semibold text-[var(--text)] mb-5">
221+
Or jump into a topic
222+
</h2>
212223

213-
{[...sectionsByCategory.entries()].map(([catLabel, sections]) => (
214-
<div key={catLabel} className="mb-6">
215-
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-3">
216-
{catLabel}
217-
</div>
218-
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
219-
{sections.map((s) => (
220-
<SidebarLink
221-
key={s.href}
222-
slug={s.href.slice(1)}
223-
className="group p-4 rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] hover:border-[var(--accent)] transition-all"
224-
>
225-
<div className="text-sm font-semibold text-[var(--text)] group-hover:text-[var(--accent)] mb-1">
226-
{s.title}
227-
</div>
228-
<div className="text-xs text-[var(--text-muted)] leading-relaxed">
229-
{s.description}
230-
</div>
231-
</SidebarLink>
232-
))}
224+
{[...sectionsByCategory.entries()].map(([catLabel, sections]) => (
225+
<div key={catLabel} className="mb-6">
226+
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-3">
227+
{catLabel}
228+
</div>
229+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
230+
{sections.map((s) => (
231+
<SidebarLink
232+
key={s.href}
233+
slug={s.href.slice(1)}
234+
className="group p-4 rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] hover:border-[var(--accent)] transition-all"
235+
>
236+
<div className="text-sm font-semibold text-[var(--text)] group-hover:text-[var(--accent)] mb-1">
237+
{s.title}
238+
</div>
239+
<div className="text-xs text-[var(--text-muted)] leading-relaxed">
240+
{s.description}
241+
</div>
242+
</SidebarLink>
243+
))}
244+
</div>
233245
</div>
234-
</div>
235-
))}
236-
</section>
246+
))}
247+
</section>
248+
</div>
237249
</main>
238250
</div>
239251
);

showcase/shell-docs/src/app/[framework]/[[...slug]]/page.tsx

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -208,56 +208,62 @@ function FrameworkLandingPage({ framework }: { framework: string }) {
208208
))}
209209
</aside>
210210

211-
<main className="flex-1 max-w-3xl px-8 py-8 overflow-y-auto">
212-
<div className="flex items-center gap-3 mb-6">
213-
{integration.logo && (
214-
// eslint-disable-next-line @next/next/no-img-element
215-
<img src={integration.logo} alt="" className="w-10 h-10" />
216-
)}
217-
<h1 className="text-[2rem] font-bold text-[var(--text)] tracking-tight">
218-
{integration.name}
219-
</h1>
220-
</div>
211+
{/* <main> is the full-width scroll container so the scrollbar
212+
* lands at the viewport edge. Content width is capped by the
213+
* inner wrapper. Previously `max-w-3xl` sat on <main> directly,
214+
* which parked the scrollbar mid-page. */}
215+
<main className="flex-1 overflow-y-auto">
216+
<div className="max-w-3xl px-8 py-8">
217+
<div className="flex items-center gap-3 mb-6">
218+
{integration.logo && (
219+
// eslint-disable-next-line @next/next/no-img-element
220+
<img src={integration.logo} alt="" className="w-10 h-10" />
221+
)}
222+
<h1 className="text-[2rem] font-bold text-[var(--text)] tracking-tight">
223+
{integration.name}
224+
</h1>
225+
</div>
221226

222-
<p className="text-base text-[var(--text-secondary)] leading-relaxed mb-8">
223-
{integration.description}
224-
</p>
227+
<p className="text-base text-[var(--text-secondary)] leading-relaxed mb-8">
228+
{integration.description}
229+
</p>
225230

226-
<div className="rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] p-4 mb-6">
227-
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-2">
228-
You're viewing docs scoped to
229-
</div>
230-
<div className="text-sm font-medium text-[var(--text)]">
231-
{integration.name}
231+
<div className="rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] p-4 mb-6">
232+
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-faint)] mb-2">
233+
You're viewing docs scoped to
234+
</div>
235+
<div className="text-sm font-medium text-[var(--text)]">
236+
{integration.name}
237+
</div>
238+
<p className="text-[13px] text-[var(--text-muted)] mt-2 leading-relaxed">
239+
Every code snippet on these pages is pulled from the live{" "}
240+
<code>{framework}</code> cells. Pick a topic from the sidebar to
241+
start reading.
242+
</p>
232243
</div>
233-
<p className="text-[13px] text-[var(--text-muted)] mt-2 leading-relaxed">
234-
Every code snippet on these pages is pulled from the live{" "}
235-
<code>{framework}</code> cells. Pick a topic from the sidebar to
236-
start reading.
237-
</p>
238-
</div>
239244

240-
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
241-
<LandingCard
242-
href={`/${framework}/agentic-chat-ui`}
243-
title="Chat UI"
244-
desc="Pre-built chat components wired to the agent"
245-
/>
246-
<LandingCard
247-
href={`/${framework}/generative-ui/tool-rendering`}
248-
title="Tool Rendering"
249-
desc="Render agent tool calls as UI components"
250-
/>
251-
<LandingCard
252-
href={`/${framework}/frontend-tools`}
253-
title="Frontend Tools"
254-
desc="Expose client-side actions to the agent"
255-
/>
256-
<LandingCard
257-
href={`/${framework}/human-in-the-loop`}
258-
title="Human-in-the-Loop"
259-
desc="Intercept tool calls for approval"
260-
/>
245+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
246+
<LandingCard
247+
href={`/${framework}/agentic-chat-ui`}
248+
title="Chat UI"
249+
desc="Pre-built chat components wired to the agent"
250+
/>
251+
<LandingCard
252+
href={`/${framework}/generative-ui/tool-rendering`}
253+
title="Tool Rendering"
254+
desc="Render agent tool calls as UI components"
255+
/>
256+
<LandingCard
257+
href={`/${framework}/frontend-tools`}
258+
title="Frontend Tools"
259+
desc="Expose client-side actions to the agent"
260+
/>
261+
<LandingCard
262+
href={`/${framework}/human-in-the-loop`}
263+
title="Human-in-the-Loop"
264+
desc="Intercept tool calls for approval"
265+
/>
266+
</div>
261267
</div>
262268
</main>
263269
</div>

showcase/shell-docs/src/app/ag-ui/[[...slug]]/page.tsx

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -442,22 +442,26 @@ export default async function AgUiDocPage({
442442
))}
443443
</SidebarNav>
444444

445-
{/* Content */}
446-
<main className="flex-1 max-w-3xl px-8 py-8 overflow-y-auto">
447-
<h1 className="text-2xl font-semibold text-[var(--text)] tracking-tight mb-6">
448-
{title}
449-
</h1>
450-
<div className="reference-content">
451-
<MDXRemote
452-
source={content}
453-
components={components}
454-
options={{
455-
mdxOptions: {
456-
remarkPlugins: [remarkGfm],
457-
rehypePlugins: [rehypeHighlight],
458-
},
459-
}}
460-
/>
445+
{/* Content — <main> is the full-width scroll container so the
446+
* scrollbar lands at the viewport edge; content width is capped
447+
* by the inner wrapper. */}
448+
<main className="flex-1 overflow-y-auto">
449+
<div className="max-w-3xl px-8 py-8">
450+
<h1 className="text-2xl font-semibold text-[var(--text)] tracking-tight mb-6">
451+
{title}
452+
</h1>
453+
<div className="reference-content">
454+
<MDXRemote
455+
source={content}
456+
components={components}
457+
options={{
458+
mdxOptions: {
459+
remarkPlugins: [remarkGfm],
460+
rehypePlugins: [rehypeHighlight],
461+
},
462+
}}
463+
/>
464+
</div>
461465
</div>
462466
</main>
463467
</div>

0 commit comments

Comments
 (0)