Skip to content

Commit 3ad29c2

Browse files
simonhampclaude
andcommitted
Collapse TOC into sticky popover button on plugin and docs pages
Replaces inline table of contents with a Flux dropdown/popover button that sticks to the top-right of the content area. Applied to both plugin detail pages (Alpine-driven dynamic headings) and docs pages (server-side TOC data). Adds scrollbar width compensation CSS for modal and popover overlay states. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 40b1a5e commit 3ad29c2

File tree

5 files changed

+91
-103
lines changed

5 files changed

+91
-103
lines changed

resources/css/app.css

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,16 @@
7979
}
8080
}
8181

82-
/* Prevents scrollbars from appearing when a popover is open */
82+
/* Compensate for custom scrollbar width when overflow is hidden */
83+
/* Browser UA sets overflow:hidden on html:has(:modal); Flux JS sets it for menus */
84+
/* Neither adds padding-right to prevent layout shift with custom 8px scrollbar */
85+
html:has(dialog:modal) {
86+
padding-right: 8px !important;
87+
}
88+
8389
html:has(#mobile-menu-popover:popover-open) {
8490
overflow: hidden;
91+
padding-right: 8px;
8592
}
8693

8794
/* Scrollbar width */

resources/views/components/docs/toc-and-sponsors.blade.php

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,6 @@
11
{{-- Copy as Markdown Button --}}
22
<x-docs.copy-markdown-button />
33

4-
{{-- On this page --}}
5-
<h3 class="flex items-center gap-1.5 text-sm opacity-60">
6-
{{-- Icon --}}
7-
<x-icons.stacked-lines class="size-[18px]" />
8-
9-
{{-- Label --}}
10-
<div>On this page</div>
11-
</h3>
12-
13-
{{-- Table of contents --}}
14-
@if (count($tableOfContents) > 0)
15-
<div
16-
class="mt-4 flex min-h-20 flex-col space-y-2 overflow-y-auto overflow-x-hidden border-l text-xs dark:border-l-white/15"
17-
>
18-
@foreach ($tableOfContents as $item)
19-
<a
20-
href="#{{ $item['anchor'] }}"
21-
@class([
22-
'transition duration-300 ease-in-out will-change-transform hover:translate-x-0.5 hover:text-violet-400 hover:opacity-100 dark:text-white/80',
23-
'pb-1 pl-3' => $item['level'] == 2,
24-
'py-1 pl-6' => $item['level'] == 3,
25-
])
26-
>
27-
{{ $item['title'] }}
28-
</a>
29-
@endforeach
30-
</div>
31-
@endif
32-
334
<div
345
class="mt-3 max-w-52 border-t border-t-black/20 pt-5 dark:border-t-white/15"
356
>

resources/views/components/plugin-toc.blade.php

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,24 @@
1515
}"
1616
x-show="headings.length > 0"
1717
x-cloak
18-
class="mb-6"
1918
>
20-
<h3 class="flex items-center gap-1.5 text-sm opacity-60">
21-
<x-icons.stacked-lines class="size-[18px]" />
22-
<div>On this page</div>
23-
</h3>
19+
<flux:dropdown position="bottom" align="end">
20+
<flux:button variant="filled" size="sm" class="!rounded-full">
21+
<x-icons.stacked-lines class="size-4" />
22+
On this page
23+
</flux:button>
2424

25-
<div class="mt-4 flex flex-col space-y-2 overflow-y-auto overflow-x-hidden border-l text-xs dark:border-l-white/15">
26-
<template x-for="heading in headings" :key="heading.id">
27-
<a
28-
:href="'#' + heading.id"
29-
:class="heading.level === 2 ? 'pb-1 pl-3' : 'py-1 pl-6'"
30-
class="transition duration-300 ease-in-out will-change-transform hover:translate-x-0.5 hover:text-violet-400 hover:opacity-100 dark:text-white/80"
31-
x-text="heading.text"
32-
></a>
33-
</template>
34-
</div>
25+
<flux:popover class="w-64">
26+
<nav class="flex max-h-80 flex-col gap-0.5 overflow-y-auto">
27+
<template x-for="heading in headings" :key="heading.id">
28+
<a
29+
:href="'#' + heading.id"
30+
:class="heading.level === 2 ? 'pl-2' : 'pl-5'"
31+
class="rounded-md px-2 py-1.5 text-xs transition hover:bg-zinc-100 dark:text-white/80 dark:hover:bg-zinc-700"
32+
x-text="heading.text"
33+
></a>
34+
</template>
35+
</nav>
36+
</flux:popover>
37+
</flux:dropdown>
3538
</div>

resources/views/docs/index.blade.php

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -53,37 +53,40 @@
5353
{{-- Copy as Markdown Button --}}
5454
<x-docs.copy-markdown-button />
5555

56-
<div>
57-
<h3 class="inline-flex items-center gap-1.5 text-sm opacity-50">
58-
{{-- Icon --}}
59-
<x-icons.stacked-lines class="size-[18px]" />
60-
{{-- Label --}}
61-
<div>On this page</div>
62-
</h3>
63-
@if (count($tableOfContents) > 0)
64-
<div
65-
class="mt-2 flex flex-col space-y-2 border-l text-xs dark:border-l-white/15"
66-
>
67-
@foreach ($tableOfContents as $item)
68-
<a
69-
href="#{{ $item['anchor'] }}"
70-
@class([
71-
'transition duration-300 ease-in-out will-change-transform hover:translate-x-0.5 hover:text-violet-400 hover:opacity-100 dark:text-white/80',
72-
'pb-1 pl-3' => $item['level'] == 2,
73-
'py-1 pl-6' => $item['level'] == 3,
74-
])
75-
>
76-
{{ $item['title'] }}
77-
</a>
78-
@endforeach
79-
</div>
80-
@endif
81-
</div>
82-
8356
</div>
8457

58+
@if (count($tableOfContents) > 0)
59+
<div class="sticky top-20 z-10 mt-8 mb-4 flex justify-end">
60+
<div class="rounded-full bg-white shadow-sm dark:bg-zinc-800">
61+
<flux:dropdown position="bottom" align="end">
62+
<flux:button variant="filled" size="sm" class="!rounded-full">
63+
<x-icons.stacked-lines class="size-4" />
64+
On this page
65+
</flux:button>
66+
67+
<flux:popover class="w-64">
68+
<nav class="flex max-h-80 flex-col gap-0.5 overflow-y-auto">
69+
@foreach ($tableOfContents as $item)
70+
<a
71+
href="#{{ $item['anchor'] }}"
72+
@class([
73+
'rounded-md px-2 py-1.5 text-xs transition hover:bg-zinc-100 dark:text-white/80 dark:hover:bg-zinc-700',
74+
'pl-2' => $item['level'] == 2,
75+
'pl-5' => $item['level'] == 3,
76+
])
77+
>
78+
{{ $item['title'] }}
79+
</a>
80+
@endforeach
81+
</nav>
82+
</flux:popover>
83+
</flux:dropdown>
84+
</div>
85+
</div>
86+
@endif
87+
8588
<div
86-
class="prose dark:prose-invert prose-headings:scroll-mt-20 prose-headings:text-gray-800 sm:prose-headings:scroll-mt-32 dark:prose-headings:text-gray-50 mt-8 max-w-none"
89+
class="prose dark:prose-invert prose-headings:scroll-mt-20 prose-headings:text-gray-800 sm:prose-headings:scroll-mt-32 dark:prose-headings:text-gray-50 max-w-none"
8790
>
8891
{!! $content !!}
8992
</div>

resources/views/plugin-show.blade.php

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -95,38 +95,42 @@ class="font-mono text-2xl font-bold sm:text-3xl"
9595
<x-divider />
9696

9797
<div class="mt-2 flex flex-col-reverse gap-8 lg:flex-row lg:items-start">
98-
{{-- Main content - README --}}
99-
<div class="min-w-0 grow">
100-
@if ($plugin->readme_html)
101-
<x-plugin-toc />
102-
@endif
103-
104-
<article
105-
x-init="
106-
() => {
107-
motion.inView($el, () => {
108-
gsap.fromTo(
109-
$el,
110-
{ autoAlpha: 0, y: 5 },
111-
{ autoAlpha: 1, y: 0, duration: 0.7, ease: 'power1.out' },
112-
)
113-
})
114-
}
115-
"
116-
class="prose min-w-0 max-w-none grow text-gray-600 dark:text-gray-400 dark:prose-headings:text-white"
117-
aria-labelledby="plugin-title"
118-
>
98+
{{-- Main content - README --}}
99+
<div class="min-w-0 grow">
119100
@if ($plugin->readme_html)
120-
{!! $plugin->readme_html !!}
121-
@else
122-
<div class="rounded-xl border border-gray-200 bg-gray-50 p-8 text-center dark:border-gray-700 dark:bg-slate-800/50">
123-
<p class="text-gray-500 dark:text-gray-400">
124-
README not available yet.
125-
</p>
126-
</div>
101+
<div class="sticky top-20 z-10 mb-4 flex justify-end">
102+
<div class="rounded-full bg-white shadow-sm dark:bg-zinc-800">
103+
<x-plugin-toc />
104+
</div>
105+
</div>
127106
@endif
128-
</article>
129-
</div>
107+
108+
<article
109+
x-init="
110+
() => {
111+
motion.inView($el, () => {
112+
gsap.fromTo(
113+
$el,
114+
{ autoAlpha: 0, y: 5 },
115+
{ autoAlpha: 1, y: 0, duration: 0.7, ease: 'power1.out' },
116+
)
117+
})
118+
}
119+
"
120+
class="prose min-w-0 max-w-none grow text-gray-600 dark:text-gray-400 dark:prose-headings:text-white"
121+
aria-labelledby="plugin-title"
122+
>
123+
@if ($plugin->readme_html)
124+
{!! $plugin->readme_html !!}
125+
@else
126+
<div class="rounded-xl border border-gray-200 bg-gray-50 p-8 text-center dark:border-gray-700 dark:bg-slate-800/50">
127+
<p class="text-gray-500 dark:text-gray-400">
128+
README not available yet.
129+
</p>
130+
</div>
131+
@endif
132+
</article>
133+
</div>
130134

131135
{{-- Sidebar - Plugin details --}}
132136
<aside

0 commit comments

Comments
 (0)