Skip to main content
Settings
Color Mode
Theme Skin
Background

Appearance preferences are saved in this browser only.

Environment
Current Environment Production

Built with JEKYLL_ENV=production. Changes require deployment.

Theme & Build
Jekyll v3.10.0
Last Build Jul 07, 04:03
Page Location
Page Info
Layout default
Collection docs
Path _docs/features/code-copy.md
URL /docs/features/code-copy/
Date 2026-07-07

Code Copy Button

One-click copy functionality for code blocks with visual feedback and clipboard API integration.

Code Copy Button

Automatic copy buttons on all code blocks for easy clipboard copying.

A highlighted JavaScript code block with a "Copy" button in its top-right corner, as rendered across the docs

The button is injected automatically into every fenced code block — there’s no front matter to set and nothing to import. Hover a block and click Copy to put its contents on the clipboard, with a brief “Copied!” confirmation.

Overview

  • Automatic Injection: Buttons added to all code blocks
  • Clipboard API: Modern async clipboard access
  • Visual Feedback: “Copied!” confirmation
  • Accessible: ARIA labels and keyboard support

Implementation

JavaScript

document.addEventListener('DOMContentLoaded', function() {
  document.querySelectorAll('pre.highlight, pre code').forEach(function(pre) {
    // Skip if already has button
    if (pre.querySelector('.copy')) return;
    
    var preElement = pre.tagName === 'PRE' ? pre : pre.closest('pre');
    if (!preElement) return;
    
    var button = document.createElement('button');
    button.className = 'copy';
    button.type = 'button';
    button.setAttribute('aria-label', 'Copy code to clipboard');
    button.innerHTML = '<i class="bi bi-clipboard me-1"></i>Copy';
    
    button.addEventListener('click', function(e) {
      e.preventDefault();
      var code = preElement.querySelector('code');
      if (!code) return;
      
      navigator.clipboard.writeText(code.textContent).then(function() {
        button.innerHTML = '<i class="bi bi-check me-1"></i>Copied!';
        setTimeout(function() {
          button.innerHTML = '<i class="bi bi-clipboard me-1"></i>Copy';
        }, 2000);
      });
    });
    
    preElement.appendChild(button);
  });
});

Styling

Button Positioning

pre.highlight {
  position: relative;
}

pre .copy {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.75rem;
  background: var(--bs-secondary);
  color: white;
  border: none;
  border-radius: var(--bs-border-radius);
  opacity: 0;
  transition: opacity 0.2s;
}

pre:hover .copy {
  opacity: 1;
}

pre .copy:hover {
  background: var(--bs-primary);
}

Success State

pre .copy.copied {
  background: var(--bs-success);
}

Customization

Button Text

var copyText = 'Copy';
var copiedText = 'Copied!';

Icons

// Bootstrap Icons
button.innerHTML = '<i class="bi bi-clipboard"></i>';

// Text only
button.textContent = 'Copy';

// SVG icon
button.innerHTML = '<svg>...</svg>';

Always Visible

pre .copy {
  opacity: 1;
}

Different Position

/* Bottom right */
pre .copy {
  top: auto;
  bottom: 0.5rem;
}

/* Top left */
pre .copy {
  right: auto;
  left: 0.5rem;
}

Clipboard API

Modern Approach

navigator.clipboard.writeText(text)
  .then(() => console.log('Copied!'))
  .catch(err => console.error('Failed to copy:', err));

Fallback for Older Browsers

function copyToClipboard(text) {
  if (navigator.clipboard) {
    return navigator.clipboard.writeText(text);
  }
  
  // Fallback
  var textarea = document.createElement('textarea');
  textarea.value = text;
  textarea.style.position = 'fixed';
  document.body.appendChild(textarea);
  textarea.select();
  document.execCommand('copy');
  document.body.removeChild(textarea);
  return Promise.resolve();
}

Accessibility

ARIA Labels

<button aria-label="Copy code to clipboard"
        title="Copy code to clipboard">
  Copy
</button>

Focus Styles

pre .copy:focus {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

pre .copy:focus-visible {
  opacity: 1;
}

Screen Reader Feedback

// Announce copy success
button.setAttribute('aria-label', 'Copied to clipboard');
setTimeout(() => {
  button.setAttribute('aria-label', 'Copy code to clipboard');
}, 2000);

Language-Specific

Skip Certain Languages

// Don't add to terminal output
if (pre.classList.contains('language-output')) return;
if (pre.classList.contains('language-console')) return;

Custom Label by Language

var lang = pre.className.match(/language-(\w+)/);
if (lang) {
  button.setAttribute('aria-label', `Copy ${lang[1]} code`);
}

Troubleshooting

Button Not Appearing

  1. Check code block has pre.highlight or pre code
  2. Verify JavaScript is loaded
  3. Check CSS isn’t hiding button
  4. Inspect for duplicate buttons

Copy Not Working

  1. Check browser clipboard permissions
  2. Verify HTTPS (required for Clipboard API)
  3. Test fallback method
  4. Check for JavaScript errors

Styling Issues

  1. Verify position: relative on pre
  2. Check z-index conflicts
  3. Test hover states
  4. Verify button is inside pre

See also

  • [[Features]]
  • [[Code Highlighting]]