Skip to content

Commit 3cf15d5

Browse files
committed
refactor: replace details/summary with split button + Popover API
Fixes the CSS collision with .markdown-body details rules by switching CopyPage from <details>/<summary> to a proper split button. Left half copies the URL directly; right half opens a floating panel via the native Popover API (light-dismiss, Escape-to-close, top-layer stacking — no z-index needed).
1 parent a55630c commit 3cf15d5

File tree

3 files changed

+64
-48
lines changed

3 files changed

+64
-48
lines changed

_components/CopyPage.tsx

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ export default function CopyPage({ file }: { file: string | undefined }) {
99
}`;
1010

1111
return (
12-
<details class="copy-page-dropdown relative">
13-
<summary class="btn list-none [&::-webkit-details-marker]:hidden flex items-center gap-2 cursor-pointer select-none">
12+
<div class="copy-page-split inline-flex shrink-0 rounded-full border-2 border-foreground-primary dark:border-background-tertiary">
13+
{/* Primary: directly copies the URL */}
14+
<button
15+
type="button"
16+
class="copy-page-main-btn flex items-center gap-2 px-4 py-2 text-sm font-semibold text-foreground-primary bg-transparent hover:bg-header-highlight dark:hover:text-background-primary rounded-l-full transition-colors cursor-pointer select-none"
17+
>
1418
<svg
1519
xmlns="http://www.w3.org/2000/svg"
1620
aria-hidden="true"
@@ -22,9 +26,26 @@ export default function CopyPage({ file }: { file: string | undefined }) {
2226
<path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z" />
2327
<path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z" />
2428
</svg>
25-
Copy page
29+
<span class="copy-page-main-label">Copy page</span>
30+
</button>
31+
32+
{/* Visual divider between the two halves */}
33+
<span
34+
class="self-stretch w-0.5 bg-foreground-primary dark:bg-background-tertiary shrink-0"
35+
aria-hidden="true"
36+
>
37+
</span>
38+
39+
{/* Chevron: opens the popover panel */}
40+
<button
41+
type="button"
42+
class="copy-page-toggle-btn flex items-center px-3 py-2 text-foreground-primary bg-transparent hover:bg-header-highlight dark:hover:text-background-primary rounded-r-full transition-colors cursor-pointer select-none"
43+
popovertarget="copy-page-menu"
44+
aria-label="More copy options"
45+
aria-haspopup="menu"
46+
>
2647
<svg
27-
class="copy-page-chevron ml-1 transition-transform duration-200"
48+
class="copy-page-chevron transition-transform duration-200"
2849
xmlns="http://www.w3.org/2000/svg"
2950
aria-hidden="true"
3051
fill="currentColor"
@@ -34,35 +55,14 @@ export default function CopyPage({ file }: { file: string | undefined }) {
3455
>
3556
<path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z" />
3657
</svg>
37-
</summary>
38-
39-
<div class="absolute right-0 z-50 mt-1 w-72 border border-foreground-tertiary rounded-md overflow-hidden bg-white dark:bg-gray-900 shadow-lg">
40-
<button class="copy-page-link-btn flex items-start gap-3 w-full px-4 py-3 text-left hover:bg-background-secondary dark:hover:bg-gray-800 transition-colors">
41-
<svg
42-
xmlns="http://www.w3.org/2000/svg"
43-
aria-hidden="true"
44-
fill="currentColor"
45-
width="16"
46-
height="16"
47-
viewBox="0 0 16 16"
48-
class="mt-0.5 shrink-0"
49-
>
50-
<path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 2 2 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a2.002 2.002 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 2 2 0 0 0-2.83 0l-2.5 2.5a2.002 2.002 0 0 0 0 2.83Z" />
51-
</svg>
52-
<span>
53-
<span class="copy-page-link-label block text-sm font-medium">
54-
Copy page link
55-
</span>
56-
<span class="block text-xs text-foreground-secondary mt-0.5">
57-
Copy the current page URL to clipboard
58-
</span>
59-
</span>
60-
</button>
58+
</button>
6159

60+
{/* Popover panel — lives in top layer, positioned via JS */}
61+
<div id="copy-page-menu" popover="auto" class="copy-page-panel">
6262
<a
6363
href={file}
6464
target="_blank"
65-
class="no-underline flex items-start gap-3 px-4 py-3 border-t border-foreground-tertiary hover:bg-background-secondary dark:hover:bg-gray-800 transition-colors"
65+
class="no-underline flex items-start gap-3 px-4 py-3 hover:bg-background-secondary dark:hover:bg-gray-800 transition-colors"
6666
>
6767
<svg
6868
xmlns="http://www.w3.org/2000/svg"
@@ -82,7 +82,6 @@ export default function CopyPage({ file }: { file: string | undefined }) {
8282
</span>
8383
</span>
8484
</a>
85-
8685
<a
8786
href={claudeUrl}
8887
target="_blank"
@@ -107,6 +106,6 @@ export default function CopyPage({ file }: { file: string | undefined }) {
107106
</span>
108107
</a>
109108
</div>
110-
</details>
109+
</div>
111110
);
112111
}

js/copy-page.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
1-
// Rotate chevron when dropdown opens/closes
2-
document.querySelectorAll<HTMLDetailsElement>(".copy-page-dropdown").forEach(
3-
(dropdown) => {
4-
dropdown.addEventListener("toggle", () => {
5-
const chevron = dropdown.querySelector<SVGElement>(".copy-page-chevron");
6-
if (chevron) {
7-
chevron.style.transform = dropdown.open ? "rotate(180deg)" : "";
8-
}
9-
});
10-
},
11-
);
12-
13-
// Handle "Copy page link" button
14-
document.querySelectorAll<HTMLButtonElement>(".copy-page-link-btn").forEach(
1+
// Primary "Copy page" button — directly copies URL
2+
document.querySelectorAll<HTMLButtonElement>(".copy-page-main-btn").forEach(
153
(btn) => {
164
btn.addEventListener("click", () => {
175
navigator?.clipboard?.writeText(window.location.href).then(() => {
18-
const label = btn.querySelector<HTMLElement>(".copy-page-link-label");
6+
const label = btn.querySelector<HTMLElement>(".copy-page-main-label");
197
if (label) {
208
const original = label.textContent;
219
label.textContent = "Copied!";
2210
setTimeout(() => {
2311
label.textContent = original;
2412
}, 2000);
2513
}
26-
const dropdown = btn.closest<HTMLDetailsElement>(".copy-page-dropdown");
27-
if (dropdown) dropdown.open = false;
2814
});
2915
});
3016
},
3117
);
18+
19+
// Popover panel — position below the chevron button + rotate chevron
20+
const panel = document.getElementById("copy-page-menu") as HTMLElement | null;
21+
const toggleBtn = document.querySelector<HTMLButtonElement>(
22+
".copy-page-toggle-btn",
23+
);
24+
25+
panel?.addEventListener("toggle", (event) => {
26+
const e = event as ToggleEvent;
27+
const chevron = toggleBtn?.querySelector<SVGElement>(".copy-page-chevron");
28+
29+
if (e.newState === "open" && toggleBtn) {
30+
const rect = toggleBtn.getBoundingClientRect();
31+
panel.style.top = `${rect.bottom + 4}px`;
32+
panel.style.right = `${window.innerWidth - rect.right}px`;
33+
if (chevron) chevron.style.transform = "rotate(180deg)";
34+
} else {
35+
if (chevron) chevron.style.transform = "";
36+
}
37+
});

style.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,17 @@
12261226
}
12271227
}
12281228

1229+
.copy-page-panel {
1230+
/* Reset browser popover defaults */
1231+
inset: unset;
1232+
margin: 0;
1233+
padding: 0;
1234+
border: none;
1235+
/* Styled via Tailwind */
1236+
@apply w-72 rounded-md overflow-hidden bg-white dark:bg-gray-900
1237+
border border-foreground-tertiary shadow-lg;
1238+
}
1239+
12291240
#feedback-no:focus + label,
12301241
#feedback-yes:focus + label {
12311242
@apply shadow-[inset_0_0_0_1px];

0 commit comments

Comments
 (0)