@ryoppippi

vim-jp ラジオ オフィシャルサイト制作の舞台裏!

22 Jul 2024 ・ 19 min read


0.png

https://vim-jp-radio.com/

Note

この​記事はVim 駅伝の​ 7/22 の​記事です。 また、​この​記事は​ デザインを​担当した 輪ごむ さんとの​共同執筆記事です。

はじめに

2024年7月8日月曜12時、​ポッドキャストラジオ番組​「エンジニアの​楽園vim-jpラジオ」が​AuDee​(TOKYO FM)​公式番組と​して​配信開始されました。

https://blog.tomoya.dev/posts/vim-jp-radio/

https://github.com/vim-jp-radio/LP

本記事では、​この​ vim-jp ラジオの​ Landing Page(LP) 制作の​舞台​裏を​紹介します! デザインや​技術的な​ポイントなど、​様々な​視点から​制作の​ポイントを​紹介します。

デザインに​ついて​ (by 輪ごむ)

コンセプト

ロゴから​以下のような​イメージを​膨らませて​デザインを​考えました。

  • バーみたい
  • わいわいとした​雰囲気
  • ネオン調

これらの​イメージを​頭の​片隅に​置いて​大量の​ web サイトや​ LP を​大量に​インプットして​なんとか​ひねり出したのが​今回完成した​ LP に​なります。 デザインの​レビューを​してくださった​ tomoya さん​ありがとう​ございます!!

絵を​描く​ことは​ありますが、​初めての​ web デザインだったので、​大変でしたが​とっても​楽しい​経験が​できました。

お気に​入りポイント

背景アニメーションが​お気に​入りです。 夜の​街で​光が​揺れているような​雰囲気が​出てお洒落かもと​思って​作ってみました。 当初は​衝突する​たびに​少しずつ玉の​スピードが​早くなってしまう​不具合が​ありましたが、​ryoppippiさんが​直してくれました。​感謝!!!

技術的な​ポイント (by ryoppippi)

この​ LP の​制作の​ために​採用した​技術的な​ポイントを​紹介します。 Zenn なので​技術的な​話も​少し​深めに​書いていきます。

今回 LP の​制作に​あたり、​特に​表示速度には​こだわりました。

  • LP である以上、​モバイルからの​アクセスが​ある​程度​多いと​予想される​ため
  • LP は​一般的に​表示速度が​遅くなりがちであるが、​それに​対して​挑戦したかった​ため

結果と​して、​ PageSpeed Insights では​満足の​いく​スコアを​得る​ことができました。

4.png PageSpeed Insights の​スコア

それでは、​技術的な​ポイントを​見ていきましょう!

Svelte5 / SvelteKit2

フレームワーク (FW) には​ Svelte5 および​ SvelteKit2 を​採用しました。

https://kit.svelte.jp/ https://svelte-5-preview.vercel.app/docs/introduction

チームメンバーが​ Svelte に​興味を​持っていた​ことが​採用の​決め手と​なりました。

今回の​ビルドは​ SSG (Static Site Generation) を​採用し、​SSR (Server Side Rendering) 等の​機能は​使用していません。 その​ため、​後述のように、​ Cloudflare Pages では​実質無料で​運用する​ことができます。

https://kit.svelte.jp/docs/adapter-static

Svelte5

Svelte5 は​現時点では​まだ​ RC 版ですが​十分​実用的でした。 今回は​ runesや​ snippets など​ Svelte5 で​導入された​新機能を​積極的に​使用しましたが、​非常に​書き心地が​良かったです。

runes は​コンポーネントの​引数の​受け渡しや​ Reactivity の​実装など、​様々な​場面で​使用しました。​型安全性が​向上し、​冗長な​記述も​減り、​非常に​快適に​開発を​進める​ことができました。

snippetsは​Svelteファイル内に​繰り返し​使う​コードを​まとめて​置く​ことができる​機能です。 Svelte4 までは​同じような​実装を​使い回すには​別ファイルに​コンポーネントと​して​切り出す必要が​ありましたが、​Svelte5では​ファイル内に​まとめて​記述できるようになりました。 その​ため、​例えば​レスポンシブデザインなどで​同じような​コードを​繰り返し書く​場合に、​より​簡潔に​実装する​ことができました。

https://github.com/vim-jp-radio/LP/blob/b97bb019c15299ce3b2fbb2c9f4a7855bfbf7aae/src/routes/Platforms.svelte#L7-L43

enhanced-img

画像の​最適化には、​ enhanced-img を​使用しました。 enhanced-img は、​スクリーンサイズに​応じて​適切な​画像が​選択されるように​複数サイズの​画像を​自動生成したり、png などの​画像を​ webp や​ avif に​変換する​機能を​提供します。 こちらも​ preview 版であり、​1ヶ月前の​ breaking change の​影響で​ import 文で​type error が​発生するなどの​不具合は​ある​ものの、​機能の​面では​概ね問題なく​使用する​ことができました。 自動で​画像の​最適化を​行ってくれるのは​非常に​便利で、​パフォーマンス向上に​大きく​寄与しました。

favicon/metadata の​自動生成

LP の​サイトは​ favicon や​ metadata の​設定が​重要です。 ある​程度は​手作業で​問題ありませんが、​様々な​サイズの​ favicon を​用意するなど​一部​手作業では​面倒な​部分も​あります。 そこで、​favicon や​ metadata の​生成を​自動化する​ために​ favicons と​いう​ライブラリを​使用しました。

https://github.com/itgalaxy/favicons

この​ライブラリは、​設定に​応じて​ metadata や​ 様々な​サイズの​ favicon を​生成してくれます。 今回は​ favicons を​使用して​ビルド時に​ favicon や​ metadata を​生成する​ための​ vite plugin を​独自に​作成、​使用しました。

https://github.com/vim-jp-radio/LP/blob/b97bb019c15299ce3b2fbb2c9f4a7855bfbf7aae/vite.config.ts#L29-L46

この​ plugin では​ favicon や​ metadata を​読み込むための​ head タグの​コードも​生成されます。​今回は​ src/hooks.server.ts を​使用して、​生成された​コードを​ head タグに​埋め込みました。

https://github.com/vim-jp-radio/LP/blob/b97bb019c15299ce3b2fbb2c9f4a7855bfbf7aae/src/handles/meta.ts

BudouX

BudouXは、​日本語や​中国語のような、​単語間に​スペースを​入れない​言語の​テキストを​自然に​改行する​ための​ライブラリです。 これを​使う​ことで、​いわゆる​ 「あなたと​JAVA 」​現象と​呼ばれる​不自然な​改行を​防ぐことができます。

https://developers-jp.googleblog.com/2023/09/budoux-adobe.html

上の​ブログでも​紹介されているように、​BudouX は​非常に​便利な​ツールです。 しかし、​そのまま​BudouX を​実装に​含めると​約7KBの​bundle cost および​ runtime cost が​発生します。

この​ runtime costを​避ける​ため、​この​LPの​制作に​合わせて​新たなSvelte Preprocessorを​作成しました。 これに​より、​runtime cost を​抑えつつ BudouX の​機能を​活用できます。

https://github.com/ryoppippi/svelte-preprocess-budoux

使い方は​簡単です:

  1. まず、svelte.config.js に​ preprocessor を​追加します。
  2. 次に、​分かち​書きが​必要な​要素に​ data-budoux 属性を​追加します。
  3. ビルドを​実行すると、​preprocessor が​ BudouX を​使って​分かち​書きを​行い、​その​結果を​ Svelte の​ markup 部分に​直接埋め込みます。

https://github.com/vim-jp-radio/LP/blob/b97bb019c15299ce3b2fbb2c9f4a7855bfbf7aae/src/routes/Platforms.svelte#L8-L10

1.gif ウィンドウの​幅が​変わっても​単語が​途中で​途切れる​ことなく​適切に​改行されている

この​方​法に​より、​runtime の​ overhead を​増やす​ことなく、​テキストを​適切に​改行できるようになりました。 添付の​画像から​わかるように、​ウィンドウの​幅が​変わっても、​テキストが​自然に​改行されています。 いい​感じですね!

URL Validation on build time

この​プロジェクトでは、​URL が​正しい​ものであるかを​ビルド時に​検証しています。

SvelteKit には​ building や​ dev と​いった​変数が​あり、​これらが​ true の​時に​のみ​実行される​コードを​書く​ことができます。 今回は、ensureURL と​いう​関数を​作成し、​dev 環境および​ ビルド時のみ​ validation logic を​実行するようにしました。 これらの​ validation logic は​本番環境では​ tree-shaking の​対象と​なり、​bundle size に​影響を​与えません。

https://github.com/vim-jp-radio/LP/blob/2f9c28b4a4de4cd7be8db61a78ae04fd5bd08a03/src/lib/utils/url.ts

SvelteKit-tweet

2.png

LP の​ ​「リスナーの​声」の​セクションには、​リスナーの​皆さんからの​投稿 (旧ツイート) を​埋め込んでいます。 この​投稿の​埋め込みには​ SvelteKit-tweet を​採用しました。

https://github.com/fayez-nazzal/sveltekit-tweet

この​ライブラリは​ react-tweet の​ Svelte 版であり、​ ビルド時に​投稿の​内容を​取得して​カードを​生成します。

追記: ​その後、​こちらの​ライブラリを​forkして、​sveletweetと​いう​ライブラリを​使用する​ことにしました。

https://github.com/ryoppippi/sveltweet

これに​より、​bundle cost を​最小限にし、​ runtime cost を​ゼロにしながらも、​見た目は​公式の​ものと​変わらない​投稿カードを​埋め込むことができます。 実際に​ devTools を​起動して​ JavaScript を​無効に​してみてください。​投稿カードが​問題なく​表示される​ことが​確認できるはずです。

Background Animation

LP を​開いてみると、​背景に​ゆっくりと​動く​アニメーションが​見えます。 こちらは​ Canvas を​使用して​実装しています(輪​ごむさんが​実装)。

https://github.com/vim-jp-radio/LP/blob/2f9c28b4a4de4cd7be8db61a78ae04fd5bd08a03/src/lib/Backgroud/circle.ts

また、​A11y への​配慮と​して、prefers-reduced-motion を​確認し、​アニメーションの​有無を​切り替えています。 この​ media query は​通常 CSS で​使用されますが、​今回は​ Animation の​実装に​ JS を​使用している​ため、​Svelte5 の​新機能である​ runes を​使用して​ reactive に​ media query の​変化を​監視しています。

https://github.com/vim-jp-radio/LP/blob/76c1826cab5745541f772ad56281c39d9b2c937e/src/lib/utils/runes.svelte.ts

UnoCSS

CSS の​管理には​ UnoCSS を​採用しました。

https://unocss.dev/

最初は​ TailwindCSS を​採用しようと​思っていましたが、​実は​ UnoCSS も​良いと​いう​話を​聞き、​試しに​採用してみました。

使い​始めは​戸惑いも​ありましたが、​振り返ってみると​採用して​良かったと​思います。

個人的 (ryoppippi的) に​良いなと​思ったのは、​classの​内部ではなく、​属性に​ style を​書く​ことができる​ところです。 TailwindCSS では​全ての​ style を​ class に​書くので、​style が​増えると​ class が​複雑に​なりがちです。​また、​1行に​複数の​ class を​書く​ことが​多いため、​コードの​見通しが​悪くなりがちです。

UnoCSS では、​html タグの​属性と​して​ style を​書く​ことができます。 また​同じ​種類の​ style は​まとめて​記述する​ことも​できる​ため、​コードの​見通しが​良くなります。

https://github.com/vim-jp-radio/LP/blob/b97bb019c15299ce3b2fbb2c9f4a7855bfbf7aae/src/routes/Header.svelte#L6-L11 https://github.com/vim-jp-radio/LP/blob/2f9c28b4a4de4cd7be8db61a78ae04fd5bd08a03/src/routes/Personality.svelte#L25-L28

rule や​ shortcut も​便利でした。 rule は​生の​ CSS を、​shortcut は​複数の​ class を、​ それぞれ​一つの​塊と​して​定義する​ことができます。 繰り返し登場するような​ style を​簡潔に​書く​時には​ rule や​ shortcut が​便利でした。

https://github.com/vim-jp-radio/LP/blob/2f9c28b4a4de4cd7be8db61a78ae04fd5bd08a03/uno.config.ts#L35-L42

個人的に​一番役に​立ったのは、​UnoCSS Inspector です。 3.jpeg UnoCSS Inspector https://unocss.dev/tools/inspector

UnoCSS Inspector を​用いると、​ページごと、​コンポーネントごとに、​どの​ style が​適用されているかを​簡単に​確認する​ことができます。 この​ツールの​おかげで​不要な​ style を​削除する​ことができ、​CSS の​ bundle size を​削減する​ことができました。 また、​style が​うまく​適用されない​時の​ debug にも​大いに​役立ちました。

個人的には​ UnoCSS は​ TailwindCSS の​上位互換のように​思えました。 今後も​積極的に​採用していきたいと​思います。

Note

今回、属性に​直接styleを​書く​記法 を​採用しましたが、​1つだけ​注意点が​あります。

以下は​ uno.config.ts の​一部です。

// uno.config.ts

export default defineConfig({'\{'}
//...
  presets: [
    // ...
    presetAttributify({'\{'} prefix: 'uno-', prefixedOnly: true {'\}'}),
    // ...
  ],

// ...
){'\}'}

重要なのは、​ prefixedOnly: true 、​そして​ prefix: 'uno-' の​設定です。 これに​より、uno- で​始まる​属性のみが​ style と​して​処理されるようになります。

この​設定を​行わないと、​以下のような​コードが​書けてしまいます。

<div
	bg-red-500
	mb-4
/>;

この​コードでも​大抵の​場合は​問題ありませんし、​公式の​ Document でも​採用されている​記法です。 しかし、​将来的に​この​記法が​問題に​なる​可能性が​あります。 例えば、mb のような​属性が​規格と​して​追加された​場合、​既存の​コードが​意図しない​挙動を​する​可能性が​あります。 その​ため、​書き手に​ uno- の​ prefix を​付けるように​強制する​ことで、​将来的な​問題を​回避する​ことができます^参考: https://dev.to/owlnai/writing-future-proof-unocss-5b68

Cloudflare Pages

デプロイ環境は、​Cloudflare Pages を​採用しました。

  • 基本無料
  • 速い
  • Web Analytics も​いい​感じに​使える
  • CD 環境も​ある

と​いい​こと​づくめでした。

前述のように、​本プロジェクトでは​ビルド手法と​して​ SSG を​採用している​ため、​Cloudflare Workers を​一切​使用していません。 その​ため、​基本的に​無料で​運用する​ことができます。

Redirect

今回の​プロジェクトでは​特定の​ Path に​対して​ redirect を​設定し、​短縮 URL のように​使用しています。 例えば​ https://vim-jp-radio.com/apple に​アクセスすると、​Apple Podcast の​ページに​ redirect されます。

実は、​Cloudflare Pages では​ _redirects ファイルを​一緒に​ deploy する​ことで、​Cloudflare Workers を​使用する​ことなく​ redirect を​設定する​ことができます。

https://developers.cloudflare.com/pages/configuration/redirects/

しかし、​この​ _redirects ファイルの​内容を​手動で​管理するのは​面倒です。 また、​開発環境(vite) は​標準では​ _redirects ファイルを​認識しないため、​手元で​ redirect を​確認する​ことができません。

そこで、vite-plugin-cloudflare-redirect を​作成しました。 https://github.com/ryoppippi/vite-plugin-cloudflare-redirect

この​ plugin は​以下の​機能を​提供します。

  • vite.config.ts に​記述した​設定から​ _redirects ファイルを​生成する
  • 開発時や​プレビュー時の​ vite server で​動作する​ middleware を​提供し、​手元でも​ redirect を​確認する​ことができる

これに​より​ _redirects ファイルの​管理を​簡略化し、​local で​ redirect を​確認できるようになりました。

https://github.com/vim-jp-radio/LP/blob/b97bb019c15299ce3b2fbb2c9f4a7855bfbf7aae/vite.config.ts#L49-L60

Easter Eggs 🐰🥚

実は​この​ LP には​イースターエッグが​仕込まれています。 ソースコードを​読み解くのも​よし、​ブラウザの​ devTools で​謎解きを​するのも​よし! ぜ​ひ探してみてください​!!

もし​見つけられたら​ X や​ Bluesky で​ ハッシュタグ #vimjpradio を​つけて​投稿してみてください。

おわりに

vim-jp ラジオの​ LP を​制作しました。

我々自身も​ vim-jp ラジオを​リスナーと​しても​とても​楽しみに​しています。 とても​面白い​番組なので、​ぜひ聞いてみてください。

P.S. vim-jp に​ついて

vim-jp ラジオで​ vim-jp に​興味を​持った方は、​ぜひ vim-jp に​参加してみてください。

https://vim-jp.org/

またいく​つか​ vim-jp 入門?​なる​記事が​あるので、​ぜひ​読んで​みてください。 https://blog.tomoya.dev/posts/vim-jp-is-a-paradise-for-engineers/ https://wagomu.me/blog/2024-06-26-vim-ekiden/

comment on bluesky / twitter
CC BY-NC-SA 4.0 2022-PRESENT © ryoppippi