Reactで「目次を眺めて1日が終わるUI」を作る
※この記事は、「今日も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で解決できないことは、だいたい哲学。
- スクロール位置より大事なのは、自分の位置。
今日もまた、全力で暇をつぶそう。
それが、一番効率のいい人生かもしれません。