Highlight.js サンプル(最もシンプルなバージョン)

以下はハイライトされる行の高さを取得する処理を行わない例です。

この場合、ハイライト表示を指定した行が、自動折り返しする場合、折り返し部分はハイライトしません。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hilight.js Sample</title>
  <!-- Hilight.js テーマ CSS の読み込み -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css" integrity="sha512-Jk4AqjWsdSzSWCSuQTfYRIF84Rq/eV0G2+tu07byYwHcbTGfdmLrHjUSwvzp5HvbiqK4ibmNwdcG49Y5RGYPTg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
  <style>
    /* 以下のサンプルの CSS(custom.css)をコピペ */
  </style>
</head>
<body>

  <div class="hljs-wrap">
    <pre><code>ハイライト表示するコードを記述</code></pre>
  </div>

  <!-- Hilight.js JavaScript の読み込み -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" integrity="sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script>
    // 以下のサンプルの JavaScript(custom.js)をコピペ
  </script>
</body>
</html>

長い行のコードを部分的に表示することはできませんが、アコーディオンパネルで表示できます。

JavaScript を表示
/* 行数を指定して表示する機能を削除したバージョン(最もシンプルなバージョン) updated on: 2024/03/31  */
document.addEventListener('DOMContentLoaded', () => {
  // Highlight.js のプラグインをセットアップ
  mySetUpHljsPlugins(myCustomHighlightJsSettings);
  // Highlight.js の初期化とカスタマイズの実行
  mySetupHighlightJs(myCustomHighlightJsSettings);
  // アコーディオンアニメーションの開閉パネルの追加(オプショナル)
  myAddAccordionPanel(myCustomHighlightJsSettings.accordionClassName);
  // アコーディオンアニメーションの呼び出し(オプショナル)
  mySetupToggleDetailsAnimation();
});

const myCustomHighlightJsSettings = {
  // pre code のラッパーのクラス名
  wrapperClassName: 'hljs-wrap',
  // pre 要素なし(インライン)の code 要素でもハイライトするかどうか
  useInlineHighlight: true,
  // インラインの code 要素でハイライトする場合に code 要素に指定するクラス(空の場合、全ての code 要素)
  inlineHighlightClassName: 'highlight',
  // アコーディオンパネルで表示場合にラッパー要素に指定するクラス名
  accordionClassName: 'toggle-accordion'
}

// Hightlight.js のプラグインを定義
function mySetUpHljsPlugins(settings) {
  const { wrapperClassName, useInlineHighlight } = settings;
  // デフォルトで行の自動折り返しを有効にするかどうか
  const preWrapOnInit = false;
  // 初期状態でコピーボタンを非表示
  const noCopyBtnOnInit = false;
  // 初期状態で行番号を非表示(ツールバー使用時のみ有効)
  const noLineNumOnInit = document.body.classList.contains('no-line-num') ? true  : false;
  // ツールバーを表示(使用)するかどうか(全てのページで使用しない場合は false を指定)
  const useToolbar = document.body.classList.contains('no-toolbar') ? false  : true;
  // ツールバーやボタンのテキスト(ラベル)
  const lineAutoWrapLabel = 'wrap';
  const lineNumLabel = 'number';
  const copyBtnLabel = 'Copy';
  const copyBtnCompleteLabel = 'Copied';
  const copyBtnFailedLabel = 'Failed';
  const copyFailedMessage = 'Sorry, can not copy with this browser.';

  // Hightlight.js の addPlugin() でプラグインを定義
  hljs.addPlugin({
    'after:highlightElement': ({ el, result, text }) => {
      // ラッパー要素
      const wrapper = el.closest('.' + wrapperClassName);
      // pre 要素(親要素)
      const pre = el.parentElement;
      if(wrapper && pre) {
        showLanguage(el, result, wrapper);
        copyCode(text, pre);
        addLineNumbers(el, result, wrapper, pre);
        highlightNumbers(el, pre);
        //setMaxHeight(el, wrapper, pre);
        setUpWrapper(el, wrapper, pre);
      }
    }
  });

  // 言語名を表示する関数
  function showLanguage(el, result, wrapper) {
    if (el.classList.contains('show-no-lang')) {
      if (wrapper) wrapper.classList.add('no-lang');
      return;
    }
    if (el.hasAttribute('data-set-lang')) {
      addLanguageSpan(el.getAttribute('data-set-lang'));
      return;
    }
    if (result.language) {
      if (useToolbar) {
        addLanguageSpan(result.language);
      } else {
        el.dataset.language = result.language;
      }
    }
    function addLanguageSpan(language) {
      const languageSpan = document.createElement('span');
      languageSpan.setAttribute('class', 'lng-span');
      languageSpan.textContent = language;
      const wrapper = el.closest('.' + wrapperClassName);
      if (wrapper && !wrapper.classList.contains('no-toolbar')) {
        wrapper.appendChild(languageSpan);
      } else if (wrapper && wrapper.classList.contains('no-toolbar')) {
        el.dataset.language = language;
      }
    }
  }

  // コードをコピーする関数 (addPlugin で呼び出す)
  function copyCode(text, pre) {
    const preClass = pre.classList;
    if (preClass.contains('no-copy-btn')) return;
    if (noCopyBtnOnInit && !preClass.contains('show-copy-btn')) return;
    if (useInlineHighlight && pre.nodeName !== 'PRE') return;
    const copyButton = document.createElement('button');
    copyButton.setAttribute('class', 'hljs-copy-btn');
    copyButton.textContent = copyBtnLabel;
    pre.after(copyButton);
    copyButton.addEventListener('click', () => {
      copyToClipboard(copyButton, text)
    });
    function copyToClipboard(btn, text) {
      if (!navigator.clipboard) {
        alert(copyFailedMessage);
      }
      // data-max-lines 属性と no-scroll クラスが指定されている場合は、表示されている部分のみをコピー
      if(preClass.contains('no-scroll') && pre.hasAttribute('data-max-lines')) {
        let startLine =  1;
        let endLine = parseInt(pre.getAttribute('data-max-lines'));
        if(pre.hasAttribute('data-scroll-to')) {
          const scrollTo = parseInt(pre.getAttribute('data-scroll-to'));
          if(scrollTo) {
            startLine = scrollTo;
            endLine += scrollTo -1;
          }
        }
        if (pre.hasAttribute('data-line-num-start')) {
          const startNumber = parseInt(pre.getAttribute('data-line-num-start'));
          if(startNumber) {
            startLine -= startNumber -1;
            endLine -= startNumber -1;
          }
        }
        const textArray = text.split(/\r?\n/);
        let visibleText = '';
        if(startLine >=1 && endLine < textArray.length){
          for(let i=startLine-1; i<=endLine-1; i++) {
            if(i !== endLine-1) {
              visibleText += textArray[i] + "\n";
            }else{
              visibleText += textArray[i];
            }
          }
        }
        text = visibleText;
      }
      // プロンプト文字($ と %)を除外してコピー
      if (preClass.contains('copy-no-prompt')) {
        text = text.replace(/^\$\s|^%\s/gm, '');
      }
      // シングルラインコメントを除外してコピー
      if (preClass.contains('copy-no-sl-comments') || preClass.contains('copy-no-comments')) {
        // 行の途中の「半角スペース + //」も削除。 [^\S\r\n] は改行を除く空白にマッチ(コメント以外も削除する可能性あり)
        text = text.replace(/^([^\S\r\n]*\/\/).*$\r?\n?/gm, "").replace(/(.*)\s\/\/.*/g, "$1");
      }
      // マルチラインコメントを除外してコピー
      if (preClass.contains('copy-no-ml-comments') || preClass.contains('copy-no-comments')) {
        // replace() の第2引数に関数 replaceComments を指定(正しくマッチしない可能性あり)
        text = text.replace(/^(.*)\/\*[\s\S]*?\*\/($\r?\n?)?/gm, replaceComments)
      }
      // HTML コメントを除外してコピー
      if (preClass.contains('copy-no-html-comments')) {
        // replace() の第2引数に関数 replaceComments を指定
        text = text.replace(/^(.*)<!\-\-[\s\S]*?\-\->($\r?\n?)?/gm, replaceComments)
      }
      function replaceComments(match, p1, p2) {
        // コメントの後に改行がない場合(p2 は undefined)
        if (!p2) p2 = '';
        // コメントの前が空白文字の場合
        if (!p1.trim()) {
          if(p2) {
            return '';
          }
          return p1;
        } else {
          return p1 + p2;
        }
      }
      navigator.clipboard.writeText(text).then(
        () => {
          btn.textContent = copyBtnCompleteLabel;
          resetCopyBtnText(btn, 1500);
        },
        (error) => {
          btn.textContent = copyBtnFailedLabel;
          resetCopyBtnText(btn, 1500);
          console.log(error.message);
        }
      );
    };
    function resetCopyBtnText(btn, delay) {
      setTimeout(() => {
        btn.textContent = copyBtnLabel
      }, delay)
    }
  }

  // 行番号表示する関数
  function addLineNumbers(el, result, wrapper, pre) {
    el.innerHTML = result.value.replace( /^/gm, '<span class="line-num"></span>');
    let startNumOffset = 0;
    if (pre.hasAttribute('data-line-num-start')) {
      const startNumber = parseInt(pre.getAttribute('data-line-num-start'));
      if (startNumber || startNumber === 0) {
        pre.style.setProperty('counter-reset', 'lineNumber ' + (startNumber - 1));
        startNumOffset = startNumber - 1;
      }
    }
  }

  //指定された行をハイライト表示する関数
  function highlightNumbers(el, pre) {
     // pre 要素に data-line-highlight 属性が指定されていれば
    if (pre.hasAttribute('data-line-highlight')) {
      const targetLines = pre.getAttribute('data-line-highlight');
      const highlightCode = pre.classList.contains('no-highlight-code') ? false : true;
      const highlightNumber = pre.classList.contains('no-highlight-number') ? false : true;
      const targets = targetLines.split(',').map((val) => val.trim());
      if (targets.length > 0) {
        const lineNumSpans = el.getElementsByClassName('line-num');
        const lineLength = lineNumSpans.length;
        targets.forEach((target) => {
          let startNumOffset = 0;
          if (pre.hasAttribute('data-line-num-start')) {
            const startNumber = parseInt(pre.getAttribute('data-line-num-start'));
            if (startNumber || startNumber === 0) {
              startNumOffset = startNumber - 1;
            }
          }
          const range = target.split('-');
          if (range.length === 2) {
            if (range[0] !== '') {
              const start = startNumOffset === 0 ? parseInt(range[0]) : parseInt(range[0]) - startNumOffset;
              const end = startNumOffset === 0 ? parseInt(range[1]) : parseInt(range[1]) - startNumOffset;
              if (start && end) {
                if (end >= start) {
                  for (let i = start; i <= end; i++) {
                    addClassToSpan(i);
                  }
                } else {
                  for (let i = end; i <= start; i++) {
                    addClassToSpan(i);
                  }
                }
              }
            } else {
              const negativeNum = (startNumOffset === 0 ? parseInt(range[1]) : parseInt(range[1])) * -1;
              addClassToSpan(negativeNum - startNumOffset);
            }
          } else if (range.length === 1) {
            addClassToSpan(startNumOffset === 0 ? parseInt(target) : parseInt(target) - startNumOffset);
          }
          function addClassToSpan(number) {
            if (number > 0 && number <= lineLength) {
              if (highlightCode) {
                const highlightSpan = document.createElement('span');
                highlightSpan.className = 'line-highlight';
                lineNumSpans.item(number - 1).after(highlightSpan);
              }
              if (highlightNumber) {
                lineNumSpans.item(number - 1).classList.add('line-num-highlight');
              }
            }
          }
        })
      }
    }
  }

  // ラッパー要素にラベルやツールバーを表示する関数
  let index = 0;
  function setUpWrapper(el, wrapper, pre) {
    const preClass = pre.classList;
    const wrapperClass = wrapper.classList;
    if(preWrapOnInit) preClass.add('pre-wrap');
    // ラベルの追加
    if (pre.hasAttribute('data-label')) {
      const label = pre.getAttribute('data-label');
      let element;
      if (pre.hasAttribute('data-label-url')) {
        element = document.createElement('a');
        element.href = pre.getAttribute('data-label-url');
        element.classList.add('hljs-label-url');
        if (preClass.contains('target-blank')) {
          element.target = "_blank";
          element.rel = "noopener";
        }
      } else {
        element = document.createElement('span');
        element.classList.add('hljs-label');
      }
      element.textContent = label;
      wrapper.appendChild(element);
      wrapperClass.add('has-label');
    }
    // no-line-num クラスを指定した要素の行番号を非表示
    if (preClass.contains('no-line-num')) {
      el.classList.add('hide-line-num');
    }

    // ツールバーの追加
    if (useToolbar) {
      if (!wrapperClass.contains('no-toolbar')) {
        const toolbar = document.createElement('div');
        toolbar.setAttribute('class', 'highlight-toolbar');
        const noLineNumChecked = noLineNumOnInit ? '' : ' checked';
        let lineWrapChecked = preWrapOnInit ? ' checked' : '';
        if(preClass.contains('pre')) {
          lineWrapChecked = '';
        }else if(preClass.contains('pre-wrap')){
          lineWrapChecked = ' checked';
        }
        toolbar.innerHTML = `<input type="checkbox" id="line-auto-wrap${index}" name="line-auto-wrap"${lineWrapChecked}>
<label for="line-auto-wrap${index}">${lineAutoWrapLabel}</label>`;
        const noLineNum = preClass.contains('no-line-num');
        if (!noLineNum) {
          toolbar.insertAdjacentHTML('afterbegin', `<input type="checkbox" id="line-num-check${index}" name="line-num-check"${noLineNumChecked}>
<label for="line-num-check${index}">${lineNumLabel}</label>`);
        }
        wrapper.insertBefore(toolbar, wrapper.firstElementChild);
        const lineNumCheck = toolbar.querySelector('[name="line-num-check"]');
        if (lineNumCheck) {
          lineNumCheck.addEventListener('change', (e) => {
            if (e.currentTarget.checked) {
              el.classList.remove('hide-line-num');
            } else {
              el.classList.add('hide-line-num');
            }
          });
          if (noLineNumOnInit) {
            el.classList.add('hide-line-num');
          }
        }
        const lineAutoWrapCheck = toolbar.querySelector('[name="line-auto-wrap"]');
        lineAutoWrapCheck.addEventListener('change', (e) => {
          // white-space プロパティを変更
          if (e.currentTarget.checked) {
            pre.style.setProperty('white-space', 'pre-wrap');
          } else {
            pre.style.setProperty('white-space', 'pre');
          }
        });
        const langSpan = wrapper.querySelector('.lng-span');
        if (langSpan) {
          toolbar.insertBefore(langSpan, toolbar.firstElementChild)
        }
        const copyBtn = wrapper.querySelector('.hljs-copy-btn');
        if (copyBtn) {
          toolbar.appendChild(copyBtn)
        }
      }
    }
    index ++;
  }
}

// Highlight.js の初期化とカスタマイズを適用する関数。
// targetWrapper は単一の要素のみに適用する場合に指定(WordPress のエディタでのプレビュー用に使用する場合に指定)
function mySetupHighlightJs(settings, targetWrapper = false) {
  const { wrapperClassName, useInlineHighlight, inlineHighlightClassName } = settings;
  // 全てのラッパー要素を取得
  const wrappers = document.getElementsByClassName(wrapperClassName);
  // インラインでハイライトする場合
  if (useInlineHighlight) {
    // pre 要素なしの code 要素でハイライト
    const inlineHighlightElems = document.getElementsByClassName(inlineHighlightClassName);
    for (const elem of inlineHighlightElems) {
      if (elem.parentElement.nodeName !== 'PRE') {
        hljs.highlightElement(elem);
      }
    }
  }
  // Highlight.js の初期化とセットアップ
  if (wrappers.length > 0 && !targetWrapper) {
    for (const wrapper of wrappers) {
      hljs.highlightElement(wrapper.querySelector('pre code'));
    }
  }else if (targetWrapper) {
    const code = targetWrapper.querySelector('code');
    if(code) {
      hljs.highlightElement(code);
    }
  }
}

// 開閉パネル(アコーディオンパネル)を指定されたクラスを持つ要素(または第2引数で渡された単一の要素)に追加する関数
function myAddAccordionPanel(targetClassName, elem = null) {
  // details 要素に付与するクラス
  const detailsClass = 'toggle-code-animation';
  // details 要素内のコンテンツを格納する div 要素に付与するクラス
  const detailsContentClass = 'details-content';
  // details 要素内のコンテンツを格納する要素のラッパーに付与するクラス
  const detailsContentWrapperClass = 'details-content-wrapper';
  // 第2引数の elem が指定されていなければ、第1引数のクラス名を使って要素を取得してパネルを追加
  if(!elem) {
    // パネルを追加する要素を取得
    const targetElems = document.getElementsByClassName(targetClassName);
    for (const elem of targetElems) {
      addPanel(elem);
    }
  }else{
    // 第2引数の elem が指定されていれば、その要素にパネルを追加
    addPanel(elem);
  }
  // アコーディオンパネルを受け取った要素に追加する関数
  function addPanel(elem) {
    // アコーディオンパネルを開くボタンのテキスト
    let summaryOpenText = "Open";
    // アコーディオンパネルを閉じるボタンのテキスト
    let summaryCloseText = "Close";
    if (elem) {
      if(elem.hasAttribute('data-open-text')) {
        summaryOpenText = elem.getAttribute('data-open-text');
      }
      if(elem.hasAttribute('data-close-text')) {
        summaryCloseText = elem.getAttribute('data-close-text');
      }
      // details 要素を作成
      const detailsElem = document.createElement('details');
      detailsElem.classList.add(detailsClass);
      // 作成した details 要素の HTML(summary 要素と div 要素)を設定
      detailsElem.innerHTML = `<summary data-close-text="${summaryCloseText}">${summaryOpenText}</summary>
  <div class="${detailsContentWrapperClass}">
    <div class="${detailsContentClass}"></div>
  </div>`;
      // コードブロックのラッパー要素を details 要素でラップする
      elem.insertAdjacentElement('beforebegin', detailsElem);
      detailsElem.querySelector('.' + detailsContentClass).appendChild(elem);
    }
  }
}

// アコーディオンアニメーションの関数の定義(elem は特定の要素のみに適用する場合に指定)
function mySetupToggleDetailsAnimation(elem) {
  // ボタンのラベル(summary 要素のテキストが空の場合)
  const accodionOpenBtnDefaultLabel = 'Open';
  // 閉じるボタンのラベル(summary 要素に data-close-text 属性が指定されていない場合)
  const accodionCloseBtnDefaultLabel = 'Close';
  // toggle-code-animation クラスの details 要素を全て取得
  const details = document.getElementsByClassName('toggle-code-animation');
  // 引数 elem が指定されていればその要素のみを対象に setupAccordion() を呼び出す(WordPress のプレビューモード用)
  if(elem) {
    setupAccordion(elem);
  }else{
    for(const elem of details) {
      setupAccordion(elem);
    }
  }
  // アコーディオンアニメーションを設定
  function setupAccordion(elem) {
    const summary = elem.querySelector('summary');
    const content = elem.querySelector('.details-content');
    const summaryText = summary.textContent.trim() ? summary.textContent : accodionOpenBtnDefaultLabel;
    if (!summary.textContent.trim()) summary.textContent = summaryText;
    const summaryCloseText = summary.dataset.closeText ? summary.dataset.closeText : accodionCloseBtnDefaultLabel;
    let isAnimating = false;
    summary.addEventListener('click', (e) => {
      e.preventDefault();
      if (isAnimating === true) {
        return;
      }
      if (elem.open) {
        summary.textContent = summaryText;
        isAnimating = true;
        const closeDetails = content.animate(
          {
            opacity: [1, 0],
            height: [content.offsetHeight + 'px', 0],
          },
          {
            duration: 300,
            easing: 'ease-in',
          }
        );
        const rotateIcon = summary.animate(
          { rotate: ["90deg", "0deg"] },
          {
            duration: 300,
            pseudoElement: "::before",
            easing: 'ease-in',
            fill: 'forwards',
          }
        );
        closeDetails.onfinish = () => {
          elem.removeAttribute('open');
          isAnimating = false;
        }
      } else {
        elem.setAttribute('open', 'true');
        summary.textContent = summaryCloseText;
        isAnimating = true;
        const openDetails = content.animate(
          {
            opacity: [0, 1],
            height: [0, content.offsetHeight + 'px'],
          },
          {
            duration: 300,
            easing: 'ease-in',
          }
        );
        const rotateIcon = summary.animate(
          { rotate: ["0deg", "90deg"] },
          {
            duration: 300,
            pseudoElement: "::before",
            easing: 'ease-in',
            fill: 'forwards',
          }
        );
        openDetails.onfinish = () => {
          isAnimating = false;
        }
      }
    });
  }
};
.hljs-wrap {
  position: relative;

  pre {
    overflow-wrap: break-word;
    overflow-x: hidden;
    padding: 0;
  }

  pre.pre-wrap {
    white-space: pre-wrap;
  }

  pre.pre {
    white-space: pre;
  }

  pre code {
    padding-left: 3rem;
    position: relative;
    white-space: inherit;
    overflow-y: hidden;
  }

  &.no-toolbar pre code,
  body.no-toolbar & pre code {
    padding-bottom: 1.5rem;
    padding-top: 2.5rem;
  }

  &.no-toolbar pre code.show-no-lang,
  body.no-toolbar & pre code.show-no-lang {
    padding-top: 1rem;
  }

  &.no-toolbar.has-copy-btn pre code.show-no-lang,
  body.no-toolbar.has-copy-btn & pre code.show-no-lang {
    padding-top: 2rem;
  }

  &.no-toolbar pre.no-copy-btn code,
  body.no-toolbar & pre.no-copy-btn code {
    padding-bottom: 1rem;
  }

  .highlight-toolbar {
    height: 2rem;
    background-color: #3a3e4a;
    padding-right: 5px;
    color: #999;
    font-size: 12px;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
  }

  &.no-lang .highlight-toolbar {
    justify-content: flex-end;
  }

  .highlight-toolbar + pre {
    margin-top: 0;
  }

  .highlight-toolbar label {
    color: #888;
    cursor: pointer;
    margin: 0 10px 0 0;
    transition: color 0.3s;
  }

  .highlight-toolbar input[type="checkbox"] {
    background-color: #262b37;
    transition: background-color 0.3s;
    display: none;
  }

  @media screen and (min-width: 640px) {
    .highlight-toolbar label {
      margin: 0 10px 0 3px;
    }

    .highlight-toolbar input[type="checkbox"] {
      display: block;
    }
  }

  .highlight-toolbar input[type="checkbox"]:hover {
    background-color: #0d37a9;
  }

  .highlight-toolbar input[type="checkbox"] {
    border-radius: 0;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
  }

  .highlight-toolbar input[type="checkbox"] {
    position: relative;
    width: 16px;
    height: 16px;
    border: 1px solid #333;
    cursor: pointer;
  }

  .highlight-toolbar input[type="checkbox"]:checked:before {
    content: "";
    position: absolute;
    top: 0px;
    left: 4px;
    transform: rotate(45deg);
    width: 4px;
    height: 8px;
    border-right: 2px solid #bbb;
    border-bottom: 3px solid #bbb;
  }

  .highlight-toolbar input[type="checkbox"]:checked + label {
    color: #b9bfd0;
  }

  code[data-language]::before {
    content: attr(data-language);
    position: absolute;
    top: 0;
    left: 0;
    color: #ccc;
    display: inline-block;
    padding: 0.5rem 1rem;
    background-color: #40547d;
    z-index: 5;
  }

  code[data-language].hide-line-num::before {
    left: 2.5rem;
  }

  .highlight-toolbar .lng-span {
    margin-right: auto;
    margin-left: 10px;
    font-size: 13px;
    color: #ccc;
  }

  .hljs-copy-btn {
    position: absolute;
    top: 0;
    right: 0;
    background-color: #262b37;
    border: none;
    padding: 8px;
    color: #999;
    cursor: pointer;
    transition: color 0.3s, background-color 0.3s;
  }

  .hljs-copy-btn:hover {
    color: #eee;
    background-color: #162858;
  }

  .highlight-toolbar .hljs-copy-btn {
    position: relative;
    margin: 0 10px;
    padding: 2px 4px;
  }

  .hljs-label,
  .hljs-label-url {
    position: absolute;
    top: -2rem;
    right: 10px;
    color: #999;
    display: inline-block;
    padding: 0.5rem 0;
  }

  .hljs-label-url {
    color: #3987c7;
    text-decoration: none;
  }

  .hljs-label-url:hover {
    color: #55924f;
  }

  &.has-label {
    margin-top: 4rem;
  }

  pre {
    counter-reset: lineNumber;
  }

  pre span.line-num::before {
    counter-increment: lineNumber;
    content: counter(lineNumber);
    min-width: 2.5rem;
    display: inline-block;
    color: #777;
    text-align: center;
    position: absolute;
    left: 0;
    background: #282c34;
    /* 行番号のボーダーが不要な場合は以下を削除 */
    border-right: 1px solid #595a60;
  }

  pre code.hide-line-num span.line-num::before {
    left: -2.5rem;
  }

  pre code.hide-line-num {
    margin-left: -2.5rem;
  }

  pre span.line-num.line-num-highlight::before {
    color: #c2c21a;
  }

  /* コード部分のハイライトが不要な場合は以下を削除 */
  .line-highlight {
    position: absolute;
    left: 2.5rem;
    width: calc(100% - 2.5rem);
    margin-left: -2.5rem;
    width: 100%;
    height: 1.1rem;
    background: linear-gradient(
      to right,
      hsla(254, 15%, 51%, 0.2) 50%,
      hsla(254, 15%, 51%, 0.01)
    );
    pointer-events: none;
  }

  & .hljs-comment {
    color: #6b788f;
  }
}

details.toggle-code-animation {
  border: none;
  margin: 2rem 0;

  .details-content-wrapper {
    padding: 1rem 0;
  }

  .details-content {
    overflow: hidden;
  }

  summary {
    display: inline-block;
    cursor: pointer;
    position: relative;
    padding: 0.5rem 0.5rem 0.5rem 36px;
    border: 1px solid #aaa;
    font-size: 13px;
  }

  summary::-webkit-details-marker {
    display: none;
  }

  summary::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 10px;
    margin: auto 0;
    width: 8px;
    height: 8px;
    border-top: 3px solid #097b27;
    border-right: 3px solid #097b27;
    transform: rotate(45deg);
  }
}

行番号のボーダーを表示しない

CSS で span.line-num::before の border を削除すると、以下のように行番号のボーダーを表示しません。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hilight.js Sample</title>
  <!-- Hilight.js テーマ CSS の読み込み -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css" integrity="sha512-Jk4AqjWsdSzSWCSuQTfYRIF84Rq/eV0G2+tu07byYwHcbTGfdmLrHjUSwvzp5HvbiqK4ibmNwdcG49Y5RGYPTg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
  <style>
    /* 以下のサンプルの CSS(custom.css)をコピペ */
  </style>
</head>
<body>

  <div class="hljs-wrap">
    <pre><code>ハイライト表示するコードを記述</code></pre>
  </div>

  <!-- Hilight.js JavaScript の読み込み -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" integrity="sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script>
    // 以下のサンプルの JavaScript(custom.js)をコピペ
  </script>
</body>
</html>

コード部分をハイライトしない

CSS で .line-highlight の高さを削除すると、以下のように行番号部分のみがハイライトされます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hilight.js Sample</title>
  <!-- Hilight.js テーマ CSS の読み込み -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css" integrity="sha512-Jk4AqjWsdSzSWCSuQTfYRIF84Rq/eV0G2+tu07byYwHcbTGfdmLrHjUSwvzp5HvbiqK4ibmNwdcG49Y5RGYPTg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
  <style>
    /* 以下のサンプルの CSS(custom.css)をコピペ */
  </style>
</head>
<body>

  <div class="hljs-wrap">
    <pre><code>ハイライト表示するコードを記述</code></pre>
  </div>

  <!-- Hilight.js JavaScript の読み込み -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" integrity="sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script>
    // 以下のサンプルの JavaScript(custom.js)をコピペ
  </script>
</body>
</html>

Highlight.js のカスタマイズ サンプル ページへ