tsukurun-Lab

📑 Blogページに追従目次とNotionブロック、グラフ表示を足していく

前回は、しばらく止まっていた tsukurun-Lab のサイトを、Codexと一緒にもう一度動かし直したところまで書きました。

AstroとNode.jsを新しくして、古い astro-notion-blog を今の構成に寄せていく、という内容です。

🌱 止まっていたブログを、Codexと動かし直した話(Astro × Notion)

今回はその続きで、「書いた記事を、どう読みやすく見せるか」「Notionで作ったものを、どこまでBlog側でも自然に見せられるか」に手を入れていきました。

せっかくNotionで書くなら、できるだけNotionで見ている感覚に近いままBlogにも出したい。

長い記事になっても迷子にならずに読めるようにしたい。

今回は、そのための目次機能、Notionブロック対応、テーブル、そしてデータベースのグラフ表示まで進めた記録です。

今回やったこと

今回の作業は、ざっくり分けると5つでした。

  • Blog詳細ページに、右側で追従する目次を足す
  • 目次が長くなったときの内部スクロールに対応する
  • H4・PDF・Tab・Link to page など、対応できるNotionブロックを増やす
  • シンプルテーブルのセル結合や列幅を整える
  • NotionデータベースのチャートビューをBlog側でも表示する

見た目が変わる部分もありますが、どちらかというと今回は「これからBlogを書き続けやすくするための土台づくり」に近い作業だったなと思います。

右側に追従する目次をつくる

まず手をつけたのは、Blog詳細ページの目次です。

Notionでページを読んでいると、右側に見出しの一覧が出てきますよね。

いま自分がどのあたりを読んでいるのかが分かって、クリックすればその見出しまで飛べる、あの感じです。

tsukurun-Lab のBlogでも、長い記事を読むときに同じ体験がほしいなと思っていました。

そこで、Blog詳細ページを3カラムの構成にしてみました。

  • 左:関連記事、おすすめ、新着記事、カテゴリ
  • 中央:記事本文
  • 右:追従する目次

画面が広いときは、右側の目次をそのまま出し続けます。

少し狭くなると、目次は細い線と丸だけのシンプルな表示に切り替わります。

さらに狭いときは、左側の関連記事を本文の下へ送るようにしました。

右側の目次は、できるだけ最後まで残るように優先しています。

現在位置の判定を見直した

最初の実装では、目次の現在位置を「画面の中央に見えている見出し」で判定していました。

でも、目次のリンクをクリックすると、その見出しは画面の上のほうに来ます。

このズレが、地味に気になってしまって。

そこで、判定の仕方を変えました。

IntersectionObserverで中央を見るのではなく、スクロール位置と見出しの位置を比べる方式にしています。

おかげで、クリックした移動先と、スクロール中に光る現在位置が、だいぶ近くなりました。

目次の見た目も整えた

細かいところも、少しずつ直しました。

たとえば「目次」という見出しの文字。

最初は小さくて、左側の関連記事などの見出しと比べると弱く見えていたので、それに近いサイズにそろえました。

縦の線と丸の位置も、丸の中心を線が通るように調整しています。

見出しレベルごとの丸の色は、Homeのロゴの背後にある色味から取りました。

  • H1:黄色系
  • H2:紫系
  • H3:ミント系
  • H4:コーラル系

最初はH4も紫系にしていたのですが、H2との差がほとんど分からなかったので、少し暖色寄りに変更しました。

色を入れすぎるとうるさくなりそうだったので、丸のアクセントだけにとどめています。

長い目次は内部スクロールにした

実際にページが長くなってくると、別の問題も見えてきました。

目次の項目数が増えると、追従している目次ブロックそのものが画面から見切れてしまいます。

追従しているのに、目次の下のほうが読めない。

これはちょっと惜しい状態でした。

そこで、目次ブロック全体の高さに上限をつけて、中のリスト部分だけスクロールするようにしました。

タイトルの「目次」はそのまま見える状態にして、見出し一覧の部分だけが内部スクロールします。

さらに、記事を読み進めて現在位置が変わったとき、アクティブな見出しが目次内で見切れていたら、自動で内部スクロールするようにしました。

ユーザーが目次の中を自分でスクロールしなくても、基本的には現在位置を追いかけてくれます。

ここは scrollIntoView() ではなく、内部のスクロール領域だけ scrollTop を調整しています。

ページ全体のスクロールを勝手に動かしたくなかったからです。

Blog一覧ページの崩れも直した

目次レイアウトを入れたあとで、 /blog の一覧ページが崩れているのに気づきました。

詳細ページ用のCSSが、一覧ページにも効いてしまっていたのが原因です。

詳細ページは左・中央・右の3カラムにしたい。

でも一覧ページは、記事一覧と右サイドバーの2カラムで十分です。

なのでCSSを整理して、ページごとにレイアウトの役割が混ざらないようにしました。

今はこんなふうに分けています。

  • Blog一覧ページ:左に記事一覧、右におすすめ・カテゴリ
  • Blog詳細ページ:左に関連記事など、中央に本文、右に追従目次

対応できるNotionブロックを増やす

目次のまわりが落ち着いたので、次はNotionブロックの表示そのものに手を広げていきました。

書いたものがそのまま出てこないと、やっぱり書く気持ちも少し削がれてしまうので。

H4見出しとH4 Toggle

Notionの見出しも、だんだん種類が増えてきました。

これから記事を書いていくと、きっとH4も使いたくなるはずです。

もとの実装では heading_1heading_3 までしか扱っていなかったので、 heading_4 に対応しました。

触ったのは、主にこのあたりです。

  • Notion APIレスポンスの型
  • 内部のBlock型
  • APIから内部Blockへの変換
  • H4 Toggleの子ブロックの取得
  • Heading4.astro の追加
  • 本文中のNotion目次
  • 右側の追従目次

これで、通常のH4も、トグルになったH4も表示できるようになりました。

トグル見出しの の位置も、NotionのHTMLを見ながら何度か直しました。

見出しのフォントサイズが大きいほど、三角の位置が上に見えやすかったので、H1/H2/H3/H4ごとに少しずつ調整しています。

PDFとTabブロック

PDFブロックにも対応しました。

既存のFileブロックと構造が近かったので、PDF用のコンポーネントを作って、リンクとプレビューを出せるようにしています。

Notion内部のファイルの場合は、ファイルの保存処理にもPDFを含めました。

Tabブロックも追加しています。

これがNotion APIだと少し変わっていて、 tab ブロック自体にはほとんど情報がなく、その直下の paragraph がそれぞれのタブになっています。

paragraphのテキストがタブのラベルになり、そのparagraphの子ブロックがタブの中身、という構造です。

今回はこれに合わせて、CSSだけで切り替えられるTab表示を作りました。

今のところ、最大8タブまで対応しています。

Link to page

Link to pageも見直しました。

最初は、本家と同じように link_to_page ブロックとして扱えばいいと思っていました。

でも、今回確認したNotion APIのレスポンスでは、Link to pageは link_to_page ではなく、 paragraph の中の mention: page として返ってきていました。

なので LinkToPage.astro だけ直しても表示されません。

そこで、 RichText 側にページメンションの表示を足しました。

これで、Blog上でもページ名のリンクとして出せるようになっています。

サイト内のBlog記事に対応するページならサイト内リンクへ、見つからない場合でもNotion側のリンクをたどれるようにしました。

未対応ブロックを見えるようにした

Notion APIでは、まだ対応していないブロックが unsupported として返ってくることがあります。

これまでは、そういうブロックがあると何も表示されず、「あれ、ここ何か書いたはずなのに」と分かりにくい状態でした。

そこで、未対応ブロックを見えるように表示することにしました。

サイト側でまだ扱えないブロックがあると、Blog上ではこんなふうに出ます。

未対応ブロック form

未知のブロック型が来たときも、空白で消してしまうのではなく、この未対応表示に落とすようにしました。

地味ですが、これからNotion側で新しいブロックを試すときに、きっと役立つはずです。

テーブルまわりを整える

セル結合に対応した

Notionのシンプルテーブルでは、セルの結合ができます。

Blog側でも、できるだけ同じように見せたいところでした。

ただ、ここはちょっと悩みました。

Notion APIの table_row.cells は、基本的には rich text 配列の配列で返ってきます。

今回見たレスポンスでは、 colspanrowspan のような結合情報が明示的には取れませんでした。

なので、2段階で対応しています。

まず、もしレスポンスに結合情報が含まれていれば、それをHTMLの colspan / rowspan に反映できるように。

そして、結合情報がない場合は、空白セルの並びから結合を推測するようにしました。

結合したくないセルは、空白のままにしない運用にします。

- など何か値を入れておけば、結合の対象になりません。

Notion APIの情報だけで完全に再現できるわけではないのですが、今のBlogで扱う範囲なら、かなり近い見え方になりました。

列幅を見直した

列幅も気になっていました。

本家の otoyo/notion-blog を見ると、Tableの列幅がとても自然なんです。

内容に対して少し余白があるくらいで、無駄に広くない。

こちらの実装では、セルに min-width: 120px が入っていたせいで、短い文字だけの列でも幅が広く見えていました。

なので、固定の最小幅はやめて、内容に合わせて幅が決まるようにしました。

必要な分だけ余白が乗る、くらいの自然な見え方になったと思います。

Notionのグラフ表示にも挑戦した

次に取り組んだのが、データベースのチャートビューです。

Notionでは、データベースを表だけでなく、縦棒グラフ、横棒グラフ、線グラフ、ドーナツグラフ、数値グラフとして表示できます。

せっかくNotionで作ったグラフなら、Blog側でもそれに近い雰囲気で出したい。

そう思って、かなり手探りで実装していきました。

Notion APIからチャート設定を読む

最初は、 child_database があることだけ分かれば、こちら側でそれっぽく描画するしかないのかなと思っていました。

でも確認してみると、Notion APIのViews APIから、チャートビューの configuration を取得できました。

そこには、たとえばこんな情報が入っています。

  • チャートの種類
  • X軸・Y軸に使うプロパティ
  • 色テーマ
  • 高さ
  • データラベルの表示
  • グリッド線
  • 軸ラベル
  • 凡例の位置
  • ソート順
  • ドーナツグラフのデータラベル
  • 数値グラフの集計方法

これをBlog側で読み取って、できるだけNotionの見た目に寄せて描画するようにしました。

対応したグラフ

今回対応したのは、この5種類です。

  • 縦棒グラフ
  • 横棒グラフ
  • 線グラフ
  • ドーナツグラフ
  • 数値グラフ

描画は、外部のグラフライブラリを入れるのではなく、まずはSVGで実装しています。

Notion側はHighchartsを使っているようですが、そのまま同じものを使うのではなく、 tsukurun-Lab のBlogに合う軽さと見た目を優先しました。

細かい見た目も少しずつ合わせた

グラフは、表示できるようになってからが長かったです。

最初に表示されたものは、Notionとはだいぶ違う見た目でした。

そこから少しずつ、設定を読み取りながら直していきました。

たとえば、こんなところです。

  • 棒グラフの色テーマ
  • 値に応じた色の濃淡
  • ソート順
  • グラフの高さ
  • グリッド線の有無
  • 軸ラベルの表示位置
  • 凡例の表示
  • 線グラフのなめらかさ
  • ドーナツグラフの中心値
  • ドーナツグラフのデータラベル
  • ドーナツグラフのキャプション
  • 数値グラフの集計ラベル

特に、軸ラベルとグリッド線は何度も見直しました。

縦棒グラフでは、縦線はバーの上ではなく、バーとバーの間に来てほしい。

横棒グラフでは、Notion側の設定名と画面上の見え方が少しややこしい。

そういう細かいズレを、ひとつずつ調整していきました。

取得できない情報もあった

一方で、Notion APIから取れない情報もありました。

たとえば、ドーナツグラフの「値ラベルを表示」のON/OFFは、今回確認したAPIレスポンスには明示的には返ってきていませんでした。

なのでBlog側では、もし show_value_label のような値が返ってきた場合だけ、中心の値の下にラベルを出すようにしています。

返ってこない場合は、値だけを中央に表示します。

このあたりは、Notion APIの仕様に左右されるところです。

完全再現というより、「取れる情報はできるだけ反映し、取れないところは自然に見える形へ寄せる」方針にしました。

Homeのテンプレートの並びも整えた

最後に、Homeに出しているテンプレートの順番も見直しました。

公開しているものが少しずつ増えてきたので、見てほしい流れに合わせて並べ替えています。

今はこの順番です。

  1. Spark Brain OS Pro
  2. Spark Brain OS Lite
  3. Color Palette
  4. Font Chooser
  5. Themes / Goal Management
  6. Themes / Goal Management Lite

あわせて、「いま公開しているテンプレート。」の説明文も少し直しました。

販売ページへの導線というより、今の tsukurun-Lab の入口として、選びやすさを大切にしたいなと思っています。

動かして確認したこと

今回の変更のあと、いつものコマンドを確認しました。

npm run lint
npm run build

どちらも無事に通っています。

Notionの supported-blocks ページでも、テーブル結合、Link to page、各種グラフ、追従目次の表示を確認しました。

もちろん、見た目についてはまだ微調整したくなるところもあります。

でも、「Notionで書いたものが、Blogでもちゃんと読める形で出てくる」状態には、だいぶ近づいてきました。

ここまで進めてみて

今回の作業で、Blog詳細ページは「読む場所」としてだいぶ整ってきました。

右側の目次で今いる場所が分かるようになって、目次が長いときも内部スクロールで追いかけてくれるようになりました。

H4、PDF、Tab、Link to page、テーブル結合、そしてデータベースのグラフ表示にも対応できました。

もちろん、まだ完成ではありません。

Notion APIの仕様に左右されるところもありますし、これからもこまめに直していくことになりそうです。

それでも、「何も表示されない場所」を少しずつ減らして、読める形・気づける形にしていけたのは、自分の中では大きな一歩でした。

ここからは、テンプレートの使い方記事も書いていきたいです。

それと並行して、サイト全体の構成も、もう少し見直していきたいと思っています。

一気に完成形へ持っていくというより、使いながら、書きながら、気になるところを少しずつ。

tsukurun-Lab らしく、無理なく育てていけたらいいなと思います。