feat: add dark code blocks and copy buttons to documentation

- Fixed white background in code blocks by adding custom CSS
  - Code blocks now use dark background (#1a1b26 for pre, #16161e for code)
  - Inline code matches theme colors
- Added copy buttons to all code blocks
  - Button appears on hover (top-right corner)
  - Shows 'Copy' → 'Copied!' feedback
  - Uses Clipboard API for copying code
- Copy buttons styled to match documentation theme

This improves the dark mode documentation experience with better
code block visibility and usability.
This commit is contained in:
2026-02-02 21:03:39 -05:00
parent 7d717e32a2
commit 0b979d698e
2 changed files with 169 additions and 30 deletions
+139
View File
@@ -15,6 +15,19 @@ templ DocsLayout(nav Navigation, doc Document, user User) {
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lunr@2.3.9/lunr.min.js"></script>
<style>
/* Fix code block backgrounds for dark theme */
.prose pre {
background-color: #1a1b26 !important;
}
.prose code {
background-color: #16161e !important;
color: #a9b1d6 !important;
}
.prose pre code {
background-color: transparent !important;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/lunr-flex@1.0.5/lunr.flex.min.js"></script>
<script>
tailwind.config = {
@@ -182,9 +195,72 @@ templ DocsLayout(nav Navigation, doc Document, user User) {
// Initialize syntax highlighting
if (typeof hljs !== 'undefined') {
hljs.highlightAll();
// Add copy buttons to all code blocks
document.querySelectorAll('pre code').forEach((block) => {
const pre = block.parentElement;
const wrapper = document.createElement('div');
wrapper.className = 'relative group';
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
const button = document.createElement('button');
button.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';
button.textContent = 'Copy';
button.onclick = async () => {
await navigator.clipboard.writeText(block.textContent);
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy';
}, 2000);
};
wrapper.appendChild(button);
});
}
// Add copy buttons to all code blocks
document.querySelectorAll('pre code').forEach((block) => {
const pre = block.parentElement;
const wrapper = document.createElement('div');
wrapper.className = 'relative group';
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
const button = document.createElement('button');
button.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';
button.textContent = 'Copy';
button.onclick = async () => {
await navigator.clipboard.writeText(block.textContent);
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy';
}, 2000);
};
wrapper.appendChild(button);
});
});
// Add copy buttons to all code blocks
document.querySelectorAll('pre code').forEach((block) => {
const pre = block.parentElement;
const wrapper = document.createElement('div');
wrapper.className = 'relative group';
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
const button = document.createElement('button');
button.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';
button.textContent = 'Copy';
button.onclick = async () => {
await navigator.clipboard.writeText(block.textContent);
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy';
}, 2000);
};
wrapper.appendChild(button);
});
// Search state
let idx;
let searchDocs = [];
@@ -472,9 +548,72 @@ templ DocsLayoutWithExplorer(nav Navigation, doc Document, user User, explorer A
// Initialize syntax highlighting
if (typeof hljs !== 'undefined') {
hljs.highlightAll();
// Add copy buttons to all code blocks
document.querySelectorAll('pre code').forEach((block) => {
const pre = block.parentElement;
const wrapper = document.createElement('div');
wrapper.className = 'relative group';
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
const button = document.createElement('button');
button.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';
button.textContent = 'Copy';
button.onclick = async () => {
await navigator.clipboard.writeText(block.textContent);
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy';
}, 2000);
};
wrapper.appendChild(button);
});
}
// Add copy buttons to all code blocks
document.querySelectorAll('pre code').forEach((block) => {
const pre = block.parentElement;
const wrapper = document.createElement('div');
wrapper.className = 'relative group';
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
const button = document.createElement('button');
button.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';
button.textContent = 'Copy';
button.onclick = async () => {
await navigator.clipboard.writeText(block.textContent);
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy';
}, 2000);
};
wrapper.appendChild(button);
});
});
// Add copy buttons to all code blocks
document.querySelectorAll('pre code').forEach((block) => {
const pre = block.parentElement;
const wrapper = document.createElement('div');
wrapper.className = 'relative group';
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
const button = document.createElement('button');
button.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';
button.textContent = 'Copy';
button.onclick = async () => {
await navigator.clipboard.writeText(block.textContent);
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy';
}, 2000);
};
wrapper.appendChild(button);
});
// Search state
let idx;
let searchDocs = [];
+30 -30
View File
File diff suppressed because one or more lines are too long