suzuuuuu09.com

suzuuuuu09.com

#

Astro

#

React

#

PandaCSS

#

TypeScript

#

GSAP

#

Cloudflare Pages

AstroとReactを使ったポートフォリオサイト兼ブログサイトを構築した。UXを意識したデザインやアニメーション作成を意識した。遊び心を感じられるようなイースターエッグを仕込んだ。

##はじめに

こんにちは。​はじめまして。suzu (@suzuuuuu09) です。​私は​情報系の​大学 1 年で、​個人的に​ Web フロントエンド技術を​使った​ Web 開発を​おこなっています。​夏休みの​機会を​利用して、​触った​ことがなかった​技術を​使って​新しい​ものを​作ろうと​思いました。
そこで​今回、​Astro と​ React を​使った​ポートフォリオサイト兼ブログサイトを​制作しましたので、​紹介します。

##画像・動画

suzuuuuu09.com-1758351963075.webp

##技術スタック

###フレームワーク

####Astro

静的サイト生成に​適していて、​扱いやすい​ Astro を​採用しました。
パフォーマンスに​優れており、​非常に​高速な​ページの​読み込みが​デフォルトで​行えるようになっています。​また、​後述する​ Content Collections も​ Astro を​採用した​理由の​ひとつです。

####React

UI フレームワークに​ React を​使用しました。

####GSAP

アニメーションを​行う​ために、GSAP を​採用しました。​ Motion(旧Framer Motion)↗️ の​採用も​考えましたが、​React でしか​動かせないのが​ネックだったので​諦めました。

###スタイリング・デザイン

####Panda CSS

スタイリングには​ Panda CSS を​採用しました。
Chakra UI 開発チームに​よって​作られた​ サーバーファースト時代における CSS-in-JS の課題を解決することを目的とした新しい CSS-in-JS エンジン です。​(公式ページより​)
Tailwind CSS や​ UnoCSS のような​ユーティリティクラスを​活用した​ CSS フレームワークと​違い、​高い​保守性が​あります。​(余談ですが、​当初この​サイトは​ UnoCSS で​作っていましたが、​保守性が​低さから​サイトを​一度​作り直しています)

####フォント

フォントは​本文に​ IBM Plex Sans JP 、​コードブロックに​ Moralerspace を​使用しました。

####絵文字

絵文字は​ Twitter(現 X) が​開発したTwemojiを​採用しています。
絵文字を​入力すると​自動で​ SVG 画像に​変換してくれます。
😄⭐☔←こんな​感じ

####アイコン

アイコンはastro-iconifyを​使っています。
astro-iconify を​使う​ことで​ mdi や​ devicon などの​アイコンや​ロゴを​簡単に​扱うことができます。​ もともとは​ Astro Icon↗️ を​使っていましたが、​アイコンを​使う​ために​別途で​パッケージを​インストールする​必要が​あり​煩わしかった​ため、​Astro Icon は​採用しませんでした。

###コンテンツ管理

####Content Collections

Astro を​採用した​もう​ひとつの​理由です。
Content Collections は​ Astro の​プロジェクトフォルダ内に​ src/content ディレクトリを​作る​ことで​ディレクトリ内に​ある​ Markdown ファイルを​簡単に​管理する​ことができます。​また、​title や​ slug などの​データを​型安全に​処理できます。

###ホスティング

####Cloudflare Pages

ホスティングには​ Cloudflare Pages を​使用しました。
知り合いから​「Cloudflare Pages が​楽で​使いやすい」と​聞いたので​試しに​導入してみました。

###OGP 画像の​生成

####Satori & Resvg

Satori を​使って​ HTML と​ CSS から​ SVG を​生成します。​生成した​ SVG を​ Resvg を​用いて​ PNG 画像に​変換しています。

####BudouX

BudouX は​タイトルの​文を​分かち​書きする​ために​使いました。

###アナリティクス

####Google Analytics

アナリティクスは​ Google Analytics を​使用しています。​特に​こだわりが​なかったので​無難な​ものを​選びました。

###コードブロック

####Expressive Code

コードブロックに​ Expressive Code を​使用しました。​Astro との​相性も​良く、​簡単に​リッチな​コードブロックを​扱えるのが​特徴です。

##こだわりポイント

###ヘッダーナビゲーション

navigation.webp

GSAPを​使った​ヘッダーの​ホバーアニメーションです。
触っていて​楽しさや​爽快感を​感じられるような​アニメーションを​作りました。​また、​この​アニメーションを​作る​ことで、​カーソルの​位置が​ナビゲーションの​どの​位置に​あるのか​分かりやすくなりました。

###見出しの​「#」​レベル

先日 Twitter を​見ていた​ところ、​このような​ツイートを​見かけました。​

なる​ほど…​確かに​読みやすい…て​ことで​当サイトにも​実装してみました。

suzuuuuu09.com-1762359073619.webp

このような​感じに​なりました。​見出しの​レベルが​分かりやすくなりました。
実装方​法に​ついて​説明します。
rehype-slug↗️ と​ rehype-autolink-headings↗️ を​インストールします。​簡単に​説明すると、​rehype-slug は​見出しに​固有 id を​持たせる​プラグイン。​rehype-autolink-headings は​見出しに​リンクを​追加する​プラグインです。

astro.config.mjs
import { defineConfig } from "astro/config";
import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
export default defineConfig({
markdown: {
rehypePlugins: [
rehypeSlug,
[
rehypeAutolinkHeadings,
{
behavior: "prepend",
content: /** @param {any} node */ (node) => {
// headingのlevelに応じて#の数を変える
// h1 から h6 まで対応
const level = Number.parseInt(node.tagName.substring(1), 7);
const hashes = "#".repeat(level);
return {
type: "element",
tagName: "span",
properties: {
className: ["heading-anchor"],
},
children: [{ type: "text", value: hashes }],
};
},
group: {
type: "element",
tagName: "div",
properties: {
className: ["heading-wrapper"],
},
children: [],
},
},
],
],
},
});

これで​見出しの​レベルに​応じて​「#」の​数を​変える​ことが​出来ます。

##リンク

###デプロイ先

##参考記事・リンク

Content の​管理方​法を​参考に​しました。

記事の​読了時間の​計算方​法に​ついての​書き方を​参考に​しました。

リンクカードを​作成する​際の​ favicon 情報を​取得する​方​法を​参考に​しました。

OGP 画像作成時の​確認手段と​して​使いました。

OGP 画像の​タイトルを​分かち​書きする​方​法を​参考に​しました。

Discussion