・luxla・

Reactで「目次を眺めて1日が終わるUI」を作る

3-year-old-builds-a-castle-with-the-help-of-ChatGPT
Posted on 2024/11/9

※この記事は、「今日も1日何もしなかったな…」という罪悪感を、Reactコンポーネントの数で正当化する試みです。

はじめに:暇と戦うエンジニアたちへ

「時間を無駄にしてる気がする」と感じたことはありませんか?
大丈夫、それはまだ目次を固定していないだけです。

人類は古来より、暇と戦ってきました。
火を起こし、車輪を発明し、そして今──スクロール連動型目次をReactで書いています。

そんな高度に退屈な現代文明の最前線から、今日は「全力で暇つぶし」していきましょう。

目次という哲学装置

本来の役割(と現代的誤用)

目次とは本来、読む前に全体像を知るための道具です。
でも現実はこうです。

読み始める → 忘れる → スクロールする → 「あれ、どこだっけ?」 → そのまま閉じる。

つまり、目次は「親切な案内役」から「誰も見ない装飾」へと進化しました。
まるで年末にしか開かれない家計簿アプリのように。

そこで今回は、この悲劇を救うため、目次を画面に“貼り付け”ました。
読まれないなら、見せつければいいのです。

邪魔にならない位置に張り付ける(という自己矛盾)

当初、私は考えました。

「Tailwindだけでできないかな?」

結果はシンプルです。

「よくわからん。」

そう、人間にはできることとできないことがある。
今回は素直にJavaScriptに助けを求めました。

 

const [tocPosition, setTocPosition] = useState({ left: 0, top: 0 })
const centeredElementRef = useRef(null)
const tocRef = useRef(null)

const alignElements = () => {
  if (centeredElementRef.current && tocRef.current) {
    const centeredElementRect = centeredElementRef.current.getBoundingClientRect()
    const tocRect = tocRef.current.getBoundingClientRect()
    const tocLeft = centeredElementRect.right
    const tocTop = window.innerHeight / 2 - tocRect.height / 2
    setTocPosition({ left: tocLeft, top: tocTop })
  }
}

useEffect(() => {
  alignElements()
  window.addEventListener('resize', alignElements)
  return () => window.removeEventListener('resize', alignElements)
}, [])

つまりこれは、「目次を真ん中に寄せたがる人類の記録」です。
Tailwindで無理ならReactで、Reactで無理なら…
ChatGPTを頼ればいい。

どこを読んでいるのかを“なんとなく”把握する機能

人は自分がどこにいるのかを知りたがる生き物です。
地図、コンパス、GPS、そして──IntersectionObserver。

ありがたい時代になりました。
これさえ知らなくても、記事のどの章を読んでるのか“それっぽく”ハイライトできるのです。

 

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    const id = entry.target.getAttribute('id');
    const link = document.querySelector(`.toc a[href="#${id}"]`);
    const listItem = link?.closest('li');

    if (entry.isIntersecting) listItem?.classList.add('active');
    else listItem?.classList.remove('active');
  });
}, { threshold: 0.5 });

読者が「この章、もう読んだっけ?」と迷うその瞬間、
小さく光る目次の項目が囁くのです。

「君は今、ここにいる。」

まるで現代版のデジタル悟りです。

トップへ戻るボタンは、下へも行く

ボタンを押せばページ最上部へ──そんなUI、見飽きましたよね?
私たちの暇は、上下に広がるべきです。

 

<button onClick={() => scrollPage(0)}>▲</button>
<button onClick={() => scrollPage(document.documentElement.scrollHeight)}>▼</button>

▲:やる気のある時
▼:もうだめな時

人生と同じく、どちらの方向にも意義があるのです。

実装は終わるが、暇は終わらない

ここまでで、あなたも立派な暇つぶしエンジニアです。
TailwindとReactでUIをいじりながら、「自分、何やってんだろ…」と思うその瞬間──
あなたの中に芽生えているのは、創造の喜びです。

Google Analyticsの導入? まだ迷ってます。
でもいいんです。迷う時間こそ、最高の暇つぶしだから。

結論:暇とは、最も贅沢なリソースである

全力で暇をつぶすとは、
つまり「自分の時間を、笑いながら燃やす」ということ。

Reactが教えてくれたのは、
コードの美しさでも、UIの最適化でもありません。

“無駄の中にこそ、創造の芽がある。”

🧩まとめ

  • 暇は、バグではない。
  • Tailwindで解決できないことは、だいたい哲学。
  • スクロール位置より大事なのは、自分の位置。

今日もまた、全力で暇をつぶそう。
それが、一番効率のいい人生かもしれません。