diff --git a/src/format/html/format-html-shared.ts b/src/format/html/format-html-shared.ts index 6d63e73abd5..dbba131dc03 100644 --- a/src/format/html/format-html-shared.ts +++ b/src/format/html/format-html-shared.ts @@ -317,7 +317,8 @@ export const quartoDefaults = (format: Format) => { "code-copy-selector", format.metadata[kCodeCopy] === undefined || format.metadata[kCodeCopy] === "hover" - ? '"pre.sourceCode:hover > "' + // ? '"div.sourceCode:hover > "' + ? '"div.code-copy-outer-scaffold:hover > "' : '""', ), ), diff --git a/src/format/html/format-html.ts b/src/format/html/format-html.ts index 0116419ce82..305de562c8c 100644 --- a/src/format/html/format-html.ts +++ b/src/format/html/format-html.ts @@ -750,12 +750,25 @@ function htmlFormatPostprocessor( // insert code copy button (with specfic attribute when inside a modal) if (codeCopy) { - code.classList.add("code-with-copy"); + // the interaction of code copy button fixed position + // and scrolling overflow behavior requires a scaffold div to be inserted + // as a parent of the code block and the copy button both + // (see #13009, #5538, and #12787) + const outerScaffold = doc.createElement("div"); + outerScaffold.classList.add("code-copy-outer-scaffold"); + const copyButton = createCodeCopyButton(doc, format); if (EmbedSourceModal && EmbedSourceModal.contains(code)) { copyButton.setAttribute("data-in-quarto-modal", ""); } - code.appendChild(copyButton); + code.classList.add("code-with-copy"); + + const sourceCodeDiv = code.parentElement!; + const sourceCodeDivParent = code.parentElement?.parentElement; + sourceCodeDivParent!.replaceChild(outerScaffold, sourceCodeDiv); + + outerScaffold.appendChild(sourceCodeDiv); + outerScaffold.appendChild(copyButton); } // insert example iframe diff --git a/src/resources/formats/html/_quarto-rules.scss b/src/resources/formats/html/_quarto-rules.scss index 4a4e9959f5e..f10cadb5a86 100644 --- a/src/resources/formats/html/_quarto-rules.scss +++ b/src/resources/formats/html/_quarto-rules.scss @@ -282,7 +282,7 @@ details > summary > p:only-child { } // codeCopy -div.sourceCode { +div.code-copy-outer-scaffold { position: relative; } diff --git a/src/resources/formats/html/bootstrap/_bootstrap-rules.scss b/src/resources/formats/html/bootstrap/_bootstrap-rules.scss index ed6dd280b9b..07d982073db 100644 --- a/src/resources/formats/html/bootstrap/_bootstrap-rules.scss +++ b/src/resources/formats/html/bootstrap/_bootstrap-rules.scss @@ -877,7 +877,8 @@ pre.sourceCode { border: none; } font-size: $code-block-font-size; - overflow-y: auto !important; + overflow-y: visible !important; + // overflow-y: auto !important; @if $code-block-bg { padding: $code-block-bg-padding; } @@ -890,7 +891,8 @@ pre.sourceCode > code.sourceCode { } div.sourceCode { - position: relative; + overflow-y: hidden; + // position: relative; } .callout div.sourceCode {