Code Blocks in WordPress

by web
Updated On:
word press- code blocks

Many developers and tech bloggers want their code blocks to look clean, modern, and easy to read. The WebNQ code blocks are popular because they not only provide beautiful syntax highlighting but also come with a handy “Copy code” button.

In this guide, I’ll show you how to add the exact same style to your WordPress site using a simple child theme with functions.php and style.css.

Installation (Step-by-Step)

wordpress code blocks functions.php

Copy the provided functions.php code. and goto your WordPress Dashboard > Appearance > Theme File Editor > Theme Functions file and paste that code at bottom and click Update File.


function mychild_enqueue_assets() {
    // Enqueue parent theme stylesheet (adjust if parent uses a different handle)
    $parent_style = 'parent-style';
    if ( ! wp_style_is( $parent_style, 'enqueued' ) ) {
        wp_enqueue_style( $parent_style, get_template_directory_uri() . '/style.css' );
    }

    // Child theme stylesheet (this file)
    wp_enqueue_style(
        'child-style',
        get_stylesheet_uri(),
        array( $parent_style ),
        filemtime( get_stylesheet_directory() . '/style.css' )
    );

    // Fira Code font from Google Fonts
    wp_enqueue_style( 'fira-code-font', 'https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;600&display=swap', array(), null );

    // Prism VSC Dark+ theme CSS (unpkg CDN)
    wp_enqueue_style(
        'prism-vsc-dark-plus',
        'https://unpkg.com/prismjs@1.29.0/themes/prism-vsc-dark-plus.css',
        array(),
        '1.29.0'
    );

    // Prism core
    wp_enqueue_script(
        'prism-core',
        'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js',
        array(),
        '1.29.0',
        true
    );

    // Prism Autoloader (loads languages on demand)
    wp_enqueue_script(
        'prism-autoloader',
        'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-autoloader.min.js',
        array( 'prism-core' ),
        '1.29.0',
        true
    );

    // Our small initializer and copy-button script (in footer)
    wp_add_inline_script(
        'prism-autoloader',
        mychild_prism_init_js()
    );
}
add_action( 'wp_enqueue_scripts', 'mychild_enqueue_assets' );

/**
 * Inline JS that:
 * - configures Prism Autoloader languages path
 * - wraps code blocks (defensive duplication check)
 * - inserts the "Copy code" button, handles copying and button text change
 *
 * Note: we return the JS as a string from a PHP function so we can put it inline and ensure it runs after Prism scripts.
 */
function mychild_prism_init_js() {
    // Use unpkg components path for autoloader to fetch language components automatically
    $js = <<<JS
(function(){
  // Ensure DOM ready
  function ready(fn) {
    if (document.readyState !== 'loading') {
      fn();
    } else {
      document.addEventListener('DOMContentLoaded', fn);
    }
  }

  ready(function() {
    // Configure Prism Autoloader language components path.
    // Autoloader expects path to directory containing components/prism-<lang>.js
    if (window.Prism && Prism.plugins && Prism.plugins.autoloader) {
      Prism.plugins.autoloader.languages_path = 'https://unpkg.com/prismjs@1.29.0/components/';
    }

    // Utility: wrap <pre><code> with .code-block if not already wrapped
    // Use a robust DOM-based approach to avoid breaking HTML.
    var pres = document.querySelectorAll('pre > code');
    pres.forEach(function(codeEl){
      var pre = codeEl.parentElement;
      if (!pre) return;
      // If already wrapped by .code-block, skip
      if (pre.parentElement && pre.parentElement.classList && pre.parentElement.classList.contains('code-block')) {
        return;
      }
      // Create wrapper
      var wrapper = document.createElement('div');
      wrapper.className = 'code-block';
      // Insert wrapper before <pre>, then move <pre> inside wrapper
      pre.parentElement.insertBefore(wrapper, pre);
      wrapper.appendChild(pre);
    });

    // After wrapping, enhance each .code-block with a copy button
    function createCopyButton() {
      var blocks = document.querySelectorAll('.code-block');
      blocks.forEach(function(block){
        // don't add button twice
        if (block.querySelector('.copy-code-btn')) return;

        // Create button
        var btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'copy-code-btn';
        btn.textContent = 'Copy code';
        // Ensure the block is positioned for absolute button placement
        // (CSS also sets position:relative; this is just an extra safety)
        block.style.position = block.style.position || '';

        // Attach click handler
        btn.addEventListener('click', function(ev){
          ev.preventDefault();
          var code = block.querySelector('pre > code');
          if (!code) return;
          var text = code.innerText;

          // Try navigator.clipboard first
          if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(text).then(function(){
              showCopied(btn);
            }).catch(function(){
              fallbackCopy(text, btn);
            });
          } else {
            fallbackCopy(text, btn);
          }
        });

        // Insert button into block (top-right)
        block.appendChild(btn);
      });
    }

    function fallbackCopy(text, btn) {
      try {
        var textarea = document.createElement('textarea');
        textarea.value = text;
        // Prevent scrolling to bottom
        textarea.style.position = 'fixed';
        textarea.style.left = '-9999px';
        document.body.appendChild(textarea);
        textarea.select();
        var successful = document.execCommand('copy');
        document.body.removeChild(textarea);
        if (successful) {
          showCopied(btn);
        } else {
          // fallback fail: briefly notify
          btn.textContent = 'Unable to copy';
          setTimeout(function(){ btn.textContent = 'Copy code'; }, 2000);
        }
      } catch (e) {
        btn.textContent = 'Unable to copy';
        setTimeout(function(){ btn.textContent = 'Copy code'; }, 2000);
      }
    }

    function showCopied(btn) {
      var original = btn.textContent;
      btn.textContent = 'Copied!';
      btn.classList.add('copied');
      setTimeout(function(){
        btn.textContent = original || 'Copy code';
        btn.classList.remove('copied');
      }, 2000);
    }

    // Run createCopyButton initially
    createCopyButton();

    // Re-run after Prism highlights (in case content gets highlighted later or via AJAX)
    // Prism exposes a hook 'complete' when highlights finish; listen to it if available
    if (window.Prism && Prism.hooks) {
      Prism.hooks.add('complete', function(env) {
        // ensure copy button added for newly highlighted blocks
        createCopyButton();
      });
    }

    // Also run when new nodes are inserted (handles AJAX/shortcodes that add code)
    if (window.MutationObserver) {
      var observer = new MutationObserver(function(mutations){
        mutations.forEach(function(m){
          if (m.addedNodes && m.addedNodes.length) {
            // small debounce to avoid thrashing
            setTimeout(function(){ createCopyButton(); }, 50);
          }
        });
      });
      observer.observe(document.body, { childList: true, subtree: true });
    }

    // Force Prism to highlight everything initially
    if (window.Prism && Prism.highlightAll) {
      Prism.highlightAll();
    }
  }); // ready
})(); // IIFE
JS;
    return $js;
}

/**
 * Wrap <pre><code> blocks in .code-block container at the server side
 * so they exist even if JS is off. This is a defensive measure.
 */
function mychild_wrap_codeblocks_in_content( $content ) {
    // Quick check: if there is no <pre><code> anywhere, return early
    if ( false === strpos( $content, '<pre' ) || false === strpos( $content, '<code' ) ) {
        return $content;
    }

    // Use a callback-based regex replacement for robust handling
    // Pattern matches <pre ...><code ...> ... </code></pre> (including attributes)
    $pattern = '/(<pre\b[^>]*>\s*<code\b[^>]*>)([\\s\\S]*?)(<\/code>\s*<\/pre>)/i';

    $content = preg_replace_callback( $pattern, function( $matches ) {
        // If it's already wrapped by .code-block, skip
        // We'll inspect a small snippet before the <pre> to detect wrapper (safe)
        // But the callback only receives the match; to keep it simple, we won't double-wrap
        // We'll return wrapper + contents.
        return '<div class="code-block">' . $matches[1] . $matches[2] . $matches[3] . '</div>';
    }, $content );

    return $content;
}
add_filter( 'the_content', 'mychild_wrap_codeblocks_in_content', 20 );

Next copy this style.css code. and goto WordPress Dashboard > Appearance > Theme File Editor > Stylesheet file and paste that code at bottom then click Update File.

wordpress code blocks styles.css

/* Code Blocks */
:root {
  --code-bg: #1e1e1e; /* ChatGPT-like background */
  --code-border: rgba(255,255,255,0.06);
  --code-radius: 8px;
  --button-bg: rgba(255,255,255,0.06);
  --button-bg-hover: rgba(255,255,255,0.10);
  --button-color: #e6e6e6;
  --accent: #0ea5e9;
  --font-mono: "Fira Code", ui-monospace, SFMono-Regular, Menlo, Monaco, "Roboto Mono", "Courier New", monospace;
}

/* Ensure body text uses default theme, but code blocks use Fira Code */
.code-block,
.code-block pre,
.code-block code {
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.45;
  direction: ltr; /* important for horizontal scroll */
}

/* Outer wrapper around each code block (ChatGPT-like card) */
.code-block {
  position: relative; /* for absolute-positioned button */
  background: var(--code-bg);
  border: 1px solid var(--code-border);
  border-radius: var(--code-radius);
  padding: 14px; /* spacing inside wrapper */
  margin: 1.2em 0;
  overflow: hidden; /* the pre element will handle scrolling */
  box-shadow: 0 1px 0 rgba(255,255,255,0.02) inset;
}

/* The <pre> element: allow both vertical and horizontal scrolling */
.code-block pre {
  margin: 0; /* remove default pre margin */
  background: transparent !important; /* use wrapper's background */
  padding: 0; /* wrapper handles padding */
  max-height: 480px; /* sensible max-height; adjust as needed */
  overflow: auto; /* both axis scroll when needed */
  white-space: pre; /* preserve whitespace; allow horizontal scroll */
  -webkit-overflow-scrolling: touch;
  border-radius: calc(var(--code-radius) - 2px);
}

/* Ensure code element takes full width and uses block display for Prism compatibility */
.code-block pre > code {
  display: block;
  min-width: 100%;
  padding: 10px 12px;
  box-sizing: border-box;
  color: #dcdcdc; /* fallback text color */
  tab-size: 4;
  -moz-tab-size: 4;
  font-feature-settings: "liga" 1; /* allow Fira Code ligatures */
}

/* Make sure long lines don't wrap and horizontal scroll shows */
.code-block pre::-webkit-scrollbar {
  height: 10px;
  width: 10px;
}
.code-block pre::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.06);
  border-radius: 10px;
}
.code-block pre::-webkit-scrollbar-track {
  background: transparent;
}

/* Copy button styling (top-right, sticky inside code-block) */
.copy-code-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 20;
  padding: 6px 10px;
  font-size: 12px;
  line-height: 1;
  background: var(--button-bg);
  color: var(--button-color);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 6px;
  cursor: pointer;
  transition: background 160ms ease, transform 120ms ease;
  -webkit-tap-highlight-color: transparent;
  backdrop-filter: blur(6px);
}

/* Smaller button on mobile to avoid overlap */
@media (max-width: 480px) {
  .copy-code-btn {
    top: 8px;
    right: 8px;
    padding: 5px 8px;
    font-size: 11px;
  }
}

/* hover and active states */
.copy-code-btn:hover {
  background: var(--button-bg-hover);
  transform: translateY(-1px);
}

/* Visual state when copied */
.copy-code-btn.copied {
  background: rgba(16,185,129,0.12); /* subtle green tint */
  color: #10b981; /* green */
  border-color: rgba(16,185,129,0.18);
}

/* Make sure Prism theme styling's tokens have room to breathe inside code */
.code-block .token.comment,
.code-block .token.prolog,
.code-block .token.doctype,
.code-block .token.cdata {
  opacity: 0.6;
}

/* Ensure code blocks are responsive and don't overflow the page */
@media (max-width: 768px) {
  .code-block {
    padding: 10px;
    border-radius: 6px;
  }
  .code-block pre {
    max-height: 360px;
  }
}

/* Optional: small style tweak to match ChatGPT subtle rounded border marks */
.code-block:focus-within {
  box-shadow: 0 0 0 3px rgba(14,165,233,0.08);
  outline: none;
}

/* If your theme applies default code styles, make sure Prism tokens remain visible */
pre code[class*="language-"] {
  /* take theme's colors, but ensure transparent background not override */
  background: transparent !important;
}

/* Accessibility: ensure button is focus-visible */
.copy-code-btn:focus {
  outline: 2px solid rgba(14,165,233,0.18);
  outline-offset: 2px;
}

/* End of styles */

That’s it. then add a code block in any post/page.

Output:
  • Beautiful VSCode Dark+ highlighted block.
  • “Copy code” button appears top-right.
  • Clicking the button copies code to clipboard and shows “Copied!” for 1s.

Key Features

  • Prism.js for syntax highlighting (VSCode Dark+ theme).
  • Background color: #1e1e1e (same as VSCode ).
  • Prism Autoloader for automatic support of all programming languages.
  • Each <pre><code> wrapped in a .code-block container (even if JavaScript is off).
  • “Copy code” button at the top-right, changes to “Copied!” for 2 seconds.
  • Full vertical and horizontal scrolling, no cutoff.
  • Fira Code monospace font with ligatures.

Why Use This Code Blocks?

  • Professional look – VSCode Dark+ colors are easy on the eyes and highly readable.
  • Better user experience – Readers can copy entire code blocks with one click.
  • Scalable – Autoloader automatically supports new programming languages.
  • Graceful fallback – Even if JavaScript is disabled, code blocks still display properly.

With just two files (functions.php and style.css), you can transform your WordPress site into a developer-friendly platform with ChatGPT-style code blocks. Readers will enjoy cleaner code presentation, and you’ll enjoy fewer “How do I copy this code?” questions.

Author: Sai Gopal Gunturu

Goto Home Page

{ }
</>
function()
;
//
Author: Sai Gopal Gunturu | Founder of WebNQ

Leave a Comment