diff --git a/_includes/css/copy-btn.css b/_includes/css/copy-btn.css
new file mode 100644
index 0000000..14855c7
--- /dev/null
+++ b/_includes/css/copy-btn.css
@@ -0,0 +1,24 @@
+/* Code block container */
+pre {
+ position: relative;
+ background: #002b36;
+ color: #FFFFFF;
+ padding: 0.75rem 1rem !important;
+ border-radius: 6px;
+ overflow: auto;
+ min-height: 2.5rem; /* <-- ensures one-liners have room */
+}
+
+/* Copy button */
+.copy-button {
+ position: absolute;
+ top: 0.4rem;
+ right: 0.5rem;
+ background: none;
+ border: none;
+ color: #fff;
+ cursor: pointer;
+ opacity: 0.7;
+ transition: opacity 0.2s ease;
+ line-height: 1; /* prevent vertical misalignment */
+}
diff --git a/combo.css b/combo.css
index c64e1a6..7ff5083 100644
--- a/combo.css
+++ b/combo.css
@@ -3,3 +3,4 @@
{% include css/base.css %}
{% include css/skeleton.css %}
{% include css/main.css %}
+{% include css/copy-btn.css%}
diff --git a/site.js b/site.js
index 09f5e7d..7d1582c 100644
--- a/site.js
+++ b/site.js
@@ -1 +1,26 @@
[...document.getElementById("install").getElementsByTagName("code")].forEach(x => x.tabIndex = 0);
+document.addEventListener('DOMContentLoaded', () => {
+ document.querySelectorAll('pre > code').forEach((codeBlock) => {
+ const button = document.createElement('button');
+ button.className = 'copy-button';
+ button.innerHTML = '';
+ button.addEventListener('click', async () => {
+ try {
+ await navigator.clipboard.writeText(codeBlock.innerText);
+ button.classList.add('copied');
+ // button.innerHTML = '';
+ button.innerHTML = '';
+ setTimeout(() => {
+ button.classList.remove('copied');
+ button.innerHTML = '';
+ }, 1500);
+ } catch {
+ button.textContent = 'Error';
+ }
+ });
+
+ const pre = codeBlock.parentNode;
+ pre.style.position = 'relative';
+ pre.appendChild(button);
+ });
+});
\ No newline at end of file