--- title: このポートフォリオブログの技術的な工夫点について description: Next.jsで構築したこのブログにおける,アクセシビリティ,パフォーマンス,UX向上のための細かな実装やこだわりを紹介します. date: 2025-12-08 tags: ["frontend", "nextjs", "react", "ui/ux"] emoji: 🌏 --- このポートフォリオ兼ブログサイトは,単なる情報の羅列ではなく,閲覧者にとって快適で,かつ技術的な遊び心を詰め込んだ場所になるよう設計しました. 開発にあたって工夫した点や,見えないところでのこだわりについて紹介します. ## コンテンツ運用を支えるための工夫 ブログを継続的に運用するためには,書く側の体験も重要です. ### 効率的な OGP 画像の自動生成 記事に画像を設定しなくても,記事の markdown で絵文字を指定するだけで,記事タイトルと著者情報を組み合わせた専用の OGP 画像を自動生成する仕組みを構築しました. Twemoji の API から絵文字データを取得し,Satori を使って SVG を生成,さらに Resvg で PNG 化するというパイプラインをビルド時に走らせています. 例えばこの記事のカバー画像は以下のようになります.  ### 絵文字ショートリンク 各記事には,通常の URL とは別に,短い絵文字ショートリンクが割り当てられています. 記事に設定した絵文字を `yantan.dev/`の後に配置するとそのブログのリンクになります. - 例: `yantan.dev/🌏` -> `www.yantan.dev/space/portfolio-features` 実はこれ,DB レスで実装しています. 仕組みとしては,ビルド時に Next.js の SSG で各絵文字に対応した以下のような静的ページを生成し,アクセス時に対応するslugを解決してリダイレクトする仕組みです. ```tsx // app/[...slug]/page.tsx // ... // ここで絵文字に対応する記事を探す let post = emojiMap.get(slugJoined); if (!post) { // 絵文字がURLエンコードされている可能性があるので試してみる try { const decoded = decodeURIComponent(slugJoined); post = emojiMap.get(decoded); } catch (e) { console.log("Decode error:", e); } } // 見つかった場合はリダイレクトする if (post) { return permanentRedirect(`/space/${post.slugAsParams}`); } ``` PC 版は記事ページのサイドバーから,スマホ版はハーフモーダル内のボタンからワンクリックでコピー可能です. ### 外部記事のシームレスな統合 Zenn などの外部プラットフォームで書いた記事も,このポートフォリオの一部として扱えるよう工夫しています. 専用のスクリプトに URL を渡すだけで,そのページのメタデータを自動取得し,ここへのリンク用記事ファイルを一瞬で生成できます. これにより,発信媒体が分散してもポートフォリオ上で一覧表示しやすくなります.  ## UX とパフォーマンスの追求 「使いやすさ」と「速さ」は,ユーザー体験における最も重要な要素です. ### タグフィルタリングの非同期処理とアニメーション 記事一覧のフィルタリング機能では,操作のレスポンスを最優先に考えました. フィルタリングの際,タグを選択してからブログ一覧が表示されるまで若干遅延があったので,タグをクリックした瞬間データ取得を待たず即座にボタンの選択状態を反映させるようにしました. その後,実際のリスト更新処理は非同期で行い,連続押下対策のデバウンス処理を組み合わせることで,スムーズな操作感を実現しました. 一覧の切り替え時にはフェードアニメーションを行い,唐突な画面変化を防いでいます.  ### レイアウトシフトの防止 一覧のフィルタリングによってコンテンツの高さが変わると,スクロールバーが出たり消えたりして画面全体がガクッと横にずれることがあります. これを防ぐため,scrollbar-gutter: stable を設定し,スクロールバーの有無に関わらず常に領域を確保することで,安定した見た目を維持しています. ### GitHub API と ISR による最新情報の同期 GitHub の issues には read の api が提供されています. 以下のエンドポイントを叩くことで issues に登録した様々な情報を取得することができます. ``` https://api.github.com/repos/syuya2036/syuya2036/issues ``` これを利用して[Projects ページ](/projects)のプロジェクト一覧を取得していますが,毎回リクエストを送ると遅延の原因になります. そこで Next.js の ISR を使用し,結果を一定時間キャッシュすることで,情報の新しさとページの表示速度のバランスを最適化しています. - [syuya2036 Issues](https://github.com/syuya2036/syuya2036/issues)   ## アクセシビリティと構造への配慮 モバイル全盛の現代において,スマートフォンでの操作性や,スクリーンリーダー利用者への配慮は欠かせません. ### 構造化されたモバイルメニュー モバイル版のナビゲーションは,よくあるハンバーガーメニューではなく下から出現するハーフモーダルを使用しました. スマホは片手で操作する人が多く,ハンバーガーメニューだとボタンが上に寄るのでこのような対応にしました. また,ブログを開いた状態でハーフモーダルを開くと目次が表示されるようにしました. 目次セクションでは,記事の構造をそのままリストとしてメニュー内に展開しており,目次を押したらモーダルが閉じるようにしました. ← 通常のハーフモーダル → ブログ記事の目次表示