第24回 コードブロックにコピーボタンを追加―TOPの連載一覧もカード表示へ変更
記事本文のコードブロックへコピー操作を追加し、TOPページの「連載から読む」を3連載のカード表示へ変更しました。Clipboard APIのフォールバック、複数コードブロック、既存の連載カード再利用まで記録します。

第23回では、カテゴリ・サブカテゴリと連載に固有の説明文を追加し、/series/の連載一覧をカード表示へ変更した。
そのあと記事本文を見返すと、技術記事でよく使うコードブロックにはまだコピー操作がない。コマンドや設定をそのまま使いたい場合でも、コードを選択してコピーする必要がある。
TOPページの「連載から読む」も、連載が3本になったことで表示を見直したくなった。/series/ではすでに説明文付きのカードを使っているが、TOPはタイトルと記事数だけの表示が残っていた。
今回はこの2点を変更する。
コードブロックは表示だけで、コピー操作がなかった#
第5回のテスト記事には、FrontmatterやMarkdown本文をまとめた長いコードブロックがある。

既存のコードブロックにはシンタックスハイライトと横スクロールがあり、長い行も表示できる。
ただ、内容をそのまま使う場合はコードをドラッグして選択する必要がある。短いコマンドなら大きな問題にならないが、複数行の設定やMarkdownを丸ごとコピーしたいときは操作が増える。
同じ記事には、短いコードブロックが複数並ぶ箇所もある。

今回の追加では、インラインコードには手を入れず、Markdownから生成された<pre><code>だけを対象にする。
Markdown生成後のpre codeへボタンを追加する#
現在の記事は、render(post)でMarkdownを描画し、ArticleLayout.astroの.prose内へ出力している。
そのため、Markdownの記述方法を変えたり、記事ごとにコピーボタン用のHTMLを書いたりする必要はない。
今回はArticleLayout.astro側で、ページ内に出力された<pre><code>をJavaScriptから取得し、それぞれのコードブロックへボタンを追加する方法にした。
流れは次のようになる。
Markdown
↓
Astroが <pre><code> を生成
↓
ArticleLayoutのスクリプトで対象を取得
↓
各コードブロックへ「コピー」ボタンを追加
記事本文のMarkdownはそのままなので、既存記事を一括修正する作業も発生しない。
Codexで生成HTMLを確認したところ、<pre><code>を含む39記事すべてに拡張用スクリプトが出力されている。確認に使った記事ではコードブロックが5個あり、実行時に5個のボタンが追加された。
Clipboard APIを使い、使えない場合も処理を残す#
コピー処理はnavigator.clipboard.writeText()を優先している。
ブラウザでClipboard APIを利用できる場合は、そのコードブロックの文字列をそのままクリップボードへ渡す。
一方、Clipboard APIを使えない場合にボタンが何もしなくなる構成にはせず、一時的なtextareaを使うフォールバックも追加した。
コピーを押す
↓
navigator.clipboard.writeText()を試す
↓
利用できない場合はtextareaを使う
新しいnpmパッケージは追加していない。Astroの記事レイアウトに小さなクライアントJavaScriptを加えるだけにしている。
JavaScript自体が動かない場合も、コード本文は従来どおりpreとして表示される。コピー操作がなくなるだけで、記事を読めなくなることはない。
コードブロック右上に「コピー」を表示する#
修正後は、コードブロックの右上に「コピー」ボタンが表示される。

既存の横スクロールは残している。
長いコードでは下部のスクロールバーを使って横へ移動でき、コピーボタンはコードブロックの枠内に配置される。コード表示そのものを作り直したわけではない。
ボタンにはaria-labelも設定した。キーボードから操作でき、状態変化を読み上げ側へ伝えるためのライブリージョンも追加している。
コピー後は「コピー済み」へ切り替える#
ボタンを押してコピーに成功すると、表示を「コピー済み」へ変更する。

見た目だけを変えるのではなく、aria-labelも同じ状態に合わせて変更する。
コピー処理には成功時だけでなく失敗時の表示も追加している。今回のローカル確認ではコピーに成功し、実際にコード文字列がクリップボードへ入ることを確認した。
公開後はHTTPS環境でも同じ操作を確認する。Clipboard APIは実際のブラウザ環境にも影響されるため、公開ページでの確認は残しておく。
複数のコードブロックにも個別に付く#
1つの記事に複数のコードブロックがある場合も、それぞれにボタンを追加する。

画像では、
cd C:\000pj\astro\yumaru-site
npm run dev
のブロックと、
http://localhost:4321/blog/cloudflare-test/
のブロックに、それぞれ別の「コピー」が表示されている。
ボタンはページ全体に1つではなく、pre codeごとに追加される。どのコードをコピーするかを選ぶための別UIは必要ない。
第5回で行ったMarkdown記事のローカル確認については、当時の記事にも作業手順を残している。
第5回 Astroへテスト記事を投稿してCloudflareの自動デプロイを確認する
TOPの連載表示は3本になると情報が少なく見える#
もう1つ変更したのがTOPページの「連載から読む」。
修正前は、連載タイトルと記事数を縦に並べていた。

この時点では、
- Astro+Cloudflareでサイトを作る
- サイトを作ったら ― 運用・SEO・SNS
- サイトを収益化する ― AdSense・広告運用
の3連載がある。
タイトルだけでも移動はできるが、初めてTOPを見た場合、それぞれの連載が何を扱うのかはタイトルから判断するしかない。
第23回で/series/には説明文・記事数・導線を持つカード表示を追加しているので、TOPだけ別の簡素な表示を残す必要もなくなっていた。
第23回 カテゴリと連載に説明文を追加―連載一覧もカード表示へ変更
/series/のカードをTOPでも再利用する#
TOP用に別のカードを新しく作るのではなく、既存のTaxonomyBrowse.astroを再利用した。
TOPページ側では既存コンポーネントへseriesCardsを指定するだけにし、連載タイトル、説明文、記事数はこれまでのtaxonomy・記事集計から取得する。
記事数を、
全24記事
全4記事
全2記事
のようにHTMLへ手入力してはいない。記事が増えれば既存の集計結果から表示件数も変わる。
/series/側で使っているカード表示も維持している。
TOPでは周囲の見出し階層に合わせてカードタイトルをh3にし、/series/側の既存構造へ不要な影響を出さないようにした。
TOPでは3列、狭い画面では1列にする#
修正後の「連載から読む」は3枚のカードを横に並べる。

各カードには次を表示している。
- 連載タイトル
- 連載の説明
- 全○記事
- この連載を読む →
1本目と2本目は第23回で追加した説明をそのまま利用し、3本目の「サイトを収益化する ― AdSense・広告運用」も同じ構造で表示されている。
PCでは3列。
狭い画面では1列へ切り替える。Codexでは390px幅相当のiframeを使い、カードが1列になることと、カード自体に横スクロールが発生しないことを確認した。
TOP専用に大きな装飾は追加していない。白背景、薄いborder、既存の余白とリンク表示を使い、/series/と近い見え方を維持している。
変更したファイルは3つ#
今回変更したファイルは次の3件。
src/layouts/ArticleLayout.astro
src/components/TaxonomyBrowse.astro
src/pages/index.astro
ArticleLayout.astroには、
pre codeへのコピーボタン追加- Clipboard APIによるコピー
textareaを使ったフォールバック- 「コピー」「コピー済み」などの状態表示
aria-label- ライブリージョン
- ボタン配置用CSS
を追加した。
TaxonomyBrowse.astroでは、TOPで連載カードを表示するときの見出し階層とレイアウトを調整した。
index.astroでは、新しいカードを直接記述せず、既存コンポーネントへseriesCardsを指定している。
package.jsonとnpm依存関係は変更していない。
ビルドと表示を確認する#
変更後に実行したのは次のコマンド。
npm run build
node scripts/verify.mjs
git diff --check
npm run buildは成功。
158ページを生成し、Pagefindの索引作成とsitemap出力まで完了した。
生成HTML、ローカル静的プレビュー、ヘッドレスChromeでも確認している。
コードブロックについては、
pre codeを含む39記事へスクリプトが出力される- 確認記事の5コードブロックに5ボタンが追加される
- コピー後に「コピー済み」へ変わる
- 長いコードでも既存の横スクロールが残る
- ボタンがコードブロックの枠内に収まる
ことを確認した。
TOPでは、
- 3つの連載カードが表示される
- タイトル、説明、記事数、リンクが出る
- 広い画面では3列
- 390px相当では1列
- カードで横スクロールが発生しない
ことを確認した。
今回の変更に起因するビルドエラーは発生していない。既存記事や同時作業中の記事に由来する未登録タグの警告、Pagefindの日本語ステミング通知、Nodeの実験的機能通知はそのまま残している。
コピーボタンと連載カードの変更点#
今回は記事本文とTOPページの2か所を変更した。
- Markdownから生成されたコードブロックへ「コピー」を追加
- コピー成功後は「コピー済み」へ変更
- Clipboard APIが使えない場合のフォールバックを追加
- 1記事内の複数コードブロックへ個別にボタンを追加
- 既存の横スクロール表示を維持
- TOPの「連載から読む」を3列カードへ変更
/series/で使っているカード表示とtaxonomy集計を再利用- 連載の記事数を手入力せず既存の集計から表示
記事本文のMarkdownや既存URL、package.json、Cloudflare Workersの構成には変更を入れていない。


