【初心者】Next.jsでホームページをつくろう⑦〜アイコンやボタンの実装〜
はじめに
こんにちは!「選挙×テクノロジー」で民主主義をアップデートすべく、個人開発をしながら勉強中です!今回はNext.jsでホームページを作成していくシリーズの第7回です。
前回はshadcn/uiでダークモードを実装しました。今回はアイコンやボタンを設置していきましょう。
前回の記事はコチラ
対象読者
- 初学者として同じ目線で学びたい方
- 初学者の私にアドバイスしてくださる聖人
もくじ
- 顔アイコンとSNSアイコン(入れ子)
- 名前と概要テキスト
- SNSボタン
1.顔アイコンとSNSアイコン(入れ子)
まずはサイトの顔となるプロフィールアイコンとSNSアイコンです。
実装後は以下のような感じです。スマホとPCで以下のようにレスポンシブとなります。


「2つのブロック」で捉える
このプロフィール部分は、大きく分けると「A:顔写真」と「B:情報エリア」の2つしかありません。
【親:section】(全体を包む箱)
│
├─【子A:ProfileAvatar】(顔写真)
│
└─【子B:div】(情報の袋)
│
├─ 名前とか自己紹介
└─ SNSボタンたち
スマホとPCでの動き(図解)
スマホ↓
┌──────────────────┐
│ 【親:section】 │
│ │
│ ┌────────────┐ │
│ │ A:顔写真 │ │
│ └────────────┘ │
│ ↓ │
│ ┌────────────┐ │
│ │ B:情報 │ │
│ └────────────┘ │
│ │
└──────────────────┘
PC↓
┌──────────────────┐
│ 【親:section】 │
│ │
│ ┌─────┐ ┌─────┐ │
│ │A:顔写真 │ │B:情報│ │
│ └─────┘ └─────┘ │
│ │
└──────────────────┘
【親】大きな枠(section)
<section className="flex flex-col items-start gap-8 md:flex-row md:items-center md:gap-10">
...
</section>
Tailwind CSSは「モバイルファースト」といって、何もついていないクラスはスマホに適用され、md: がついているクラスは画面が広がった時(タブレット・PC)に上書きされます。
分解して見てみましょう。
1. 共通のスイッチ
flex:
「Flexboxを使うよ!」という宣言です。これを書かないと、並び替えの命令が効きません。
2. スマホの時の命令(基本)
まずはスマホで見た時のスタイルです。
flex-col (縦並び):
要素を上から下へ、縦積みにします。「顔写真」の下に「名前」が来ます。
items-start (左寄せ):
要素を左端に揃えます。
gap-8 (隙間):
写真と名前の間に、少し狭め(32px)の隙間を空けます。
3.PCの時の命令(md:がついているやつ)
画面幅が 768px 以上(iPadやPCなど)になると、md: がついたクラスが発動して、スマホの命令を上書きします。
md:flex-row (横並び):
ここが一番の変化です!縦積みだった要素を、横一列に並べ替えます。
md:items-center (上下中央揃え):
横並びになった時、顔写真と名前の「高さの中心」を揃えます。これがないと、名前が上に寄ってしまって格好悪くなります。
md:gap-10 (広い隙間):
画面が広いので、隙間も少し広め(40px)に調整して、ゆったり見せます。
<ProfileAvatar
src="/icon-yoshiki.jpg"
alt="渥美 嘉樹のプロフィール画像"
initial="YA"
className="self-start"
imageClassName="animate-spin [animation-duration:12s]"
/>
【A子】顔写真
プロフィール画像です。今回は shadcn/ui の Avatar コンポーネントをベースに作成した、自作の <ProfileAvatar> コンポーネントを使っています。
<ProfileAvatar:
これはHTMLの標準タグではなく、自分で作ったカスタムコンポーネントの呼び出しです。
頭文字が大文字になっているからわかりますね。
通常、shadcn/uiでアイコンを表示するには <Avatar><AvatarImage /><AvatarFallback /></Avatar> と3つもタグを書く必要がありますが、それをひとまとめにして、「画像のパスと名前だけ渡せばいい便利セット」にしてあるわけです。
src="/icon-yoshiki.jpg":
画像の「場所」を指定しています。
Next.jsのルールでは、/ から始まるパスは public フォルダの中身 を指します。つまり、プロジェクトの public フォルダ直下に icon-yoshiki.jpg というファイルを置いているということです。
alt="渥美 嘉樹のプロフィール画像":
これは 「アクセシビリティ(誰にでも使いやすく)」 のための記述です。
もし画像が読み込めなかった時や、目の不自由な方が使うスクリーンリーダー(読み上げソフト)がここを通った時に、「ここは渥美嘉樹の画像ですよ」と言葉で伝えてくれます。
initial="YA":
画像の読み込みに失敗した時や、画像が表示されるまでの「一瞬の間」に表示される文字です。
「Yoshiki Atsumi」のイニシャルを取って「YA」としています。これがあることで、画像が出なくても誰のアイコンかなんとなく伝わります。
className="self-start":
これは レイアウト崩れを防ぐための重要なTailwind CSS です。
self-start: 「自分自身を上端に揃える」という意味です。
もしこれを書かないと、隣の自己紹介文が長くなった時に、アイコンが変に縦に伸びたり、真ん中に浮いたりしてしまいます。どんなに文章が長くなっても、アイコンは常に「左上」にピシッと固定されます。
imageClassName="animate-spin [animation-duration:12s]":
ここが今回の 「遊び心」 ポイントです!
imageClassName: 外側の枠ではなく、中の「画像そのもの」にスタイルを適用するための設定です。※このimageClassNameは<ProfileAvatar> コンポーネントで定義しています。
animate-spin: くるくると回転させるアニメーションです。
[animation-duration:12s]: 通常の回転だと速すぎて目が回ってしまうので、「1周するのに12秒かける」 という指定をして、ゆっくり優雅に回るようにカスタマイズしています。
2.名前と概要テキスト
次に、以下のテキストとSNSアイコンの部分を実装します。

<div className="flex flex-1 flex-col items-start gap-5 text-left">
まずはテキストとSNSアイコンが入る箱を作ります。
flex-1: 親要素(section)の中で、余ったスペースをいっぱいに使うように指示しています。これにより、顔写真の横のスペースを綺麗に埋めることができます。
flex-col: 中の要素(テキスト群とSNSアイコン群)を縦並びにしています。
gap-5: テキスト部分とSNSアイコンの間に、均等な余白(20px)を作ってくれます。
<div className="space-y-2">
<h1 className="text-2xl font-semibold text-foreground md:text-3xl">
渥美 嘉樹(あつみ よしき)
</h1>
<p className="text-sm text-muted-foreground md:text-base">
政治×テクノロジーの仕事をしてます。現在、Next.jsの勉強中。
</p>
</div>
次ちテキストを実装します。
- 魔法の余白調整 space-y-2
一番外側の <div className="space-y-2"> に注目してください。
通常、見出し(名前)と段落(自己紹介)の間に隙間を作りたいとき、「見出しの下に margin-bottom をつける」といった指定をしがちです。
しかし Tailwind CSSの space-y-2 を親要素に指定すると、「中に入っている要素同士の縦の隙間を、自動で均等(今回は8px)に空けてくれる」 という魔法のような働きをします。要素が増えても勝手に間隔を調整してくれるので、コードがとてもスッキリします。
2. ダークモード対応の文字色(shadcn/uiの恩恵)
文字色には、特定のカラーコード(黒やグレーなど)ではなく、shadcn/ui特有のクラスを指定しています。
text-foreground(名前部分):
メインの文字色です。ライトモードでは「ほぼ黒」、ダークモードでは「ほぼ白」に自動で切り替わります。
text-muted-foreground(自己紹介部分):
少し控えめな(Mutedな)文字色です。自己紹介文をグレーがかった色にすることで、名前(メイン)との視覚的な優先順位(メリハリ)をつけています。もちろんこれもダークモードに対応しています。
3. スマホとPCで文字サイズを変える(レスポンシブ対応)
名前の h1 タグには text-2xl md:text-3xl、自己紹介の p タグには text-sm md:text-base と指定しています。
名前:基本は 2xl(少し大きめ)、PC画面(md:)では 3xl(さらに大きく)。
自己紹介:基本は sm(小さめ)、PC画面(md:)では base(標準サイズ)。
画面サイズに合わせて文字の大きさが変わるため、スマホで見ても文字が大きすぎて圧迫感が出たり、逆にPCで見たときに小さすぎて貧相になったりするのを防いでいます。font-semibold は文字を少し太字(半太字)にして、名前をくっきり見せるためのクラスです。
なお、以下からフォントサイズを確認できます。
3.SNSボタン
<div className="flex flex-wrap gap-3">
<Button asChild size="icon" variant="ghost">
<Link
href="https://github.com/yoshiki-atsumi"
target="_blank"
rel="noopener noreferrer"
aria-label="GitHub"
>
<FaGithub className="size-5" />
</Link>
</Button>
<Button asChild size="icon" variant="ghost">
<Link
href="https://x.com/atsumi_yoshiki"
target="_blank"
rel="noopener noreferrer"
aria-label="X (旧Twitter)"
>
<FaXTwitter className="size-5" />
</Link>
</Button>
<Button asChild size="icon" variant="ghost">
<Link
href="https://www.facebook.com/atsumi.yoshiki"
target="_blank"
rel="noopener noreferrer"
aria-label="Facebook"
>
<FaFacebook className="size-5" />
</Link>
</Button>
</div>
3つのSNSアイコン(GitHub、X、Facebook)が並んでいる部分です。ただアイコンを置くだけでなく、押しやすさや安全性に配慮した設計になっています。
1. スマホでもはみ出さない flex-wrap
アイコンを囲む親要素には <div className="flex flex-wrap gap-3"> を指定しています。
flex-wrap: もし画面が極端に狭くなって横幅が足りなくなった時、アイコンが画面外にはみ出すのを防ぎ、自動で「下の行に折り返して」くれます。
gap-3: アイコン同士の隙間を均等に(12px)空けています。
2. shadcn/ui の魔法 asChild(超重要!)
ここが今回のハイライトです!アイコンをリンクにする際、shadcn/uiの <Button> コンポーネントを使っています。
<Button asChild size="icon" variant="ghost">
<Link href="...">
HTMLのルールとして、「<button> タグの中に <a> タグ(リンク)を入れてはいけない」 という決まりがあります。
しかし、shadcn/ui の Button に asChild を付けると、「ボタンの見た目(ホバー時のエフェクトなど)だけを、中身の <Link> にそのまま合体させる」 ことができます!
variant="ghost": 背景が透明で、マウスを乗せるとフワッと背景色が変わる「ゴースト」スタイル。
size="icon": ボタンを綺麗な正方形(アイコン用)に整えてくれます。
これにより、ルール違反をせずに、綺麗で押しやすいリンクボタンが完成します。
<Button asChild size="icon" variant="ghost">
<Link href="https://github.com/yoshiki-atsumi" target="_blank" rel="noopener noreferrer" aria-label="GitHub">
<FaGithub className="size-5" />
</Link>
</Button>
target="_blank"
リンクをクリックしたときに、今のページを移動せずに「新しいタブ」で開くための指定です。外部サイトへ誘導するときによく使います。
rel="noopener noreferrer"
target="_blank" を使うときの必須の相棒です。これをつけないと、移動先のサイトから元のサイト(自分のサイト)のURLを勝手に書き換えられてしまう「タブナビング(Tabnabbing)」というセキュリティ上の弱点が生まれてしまいます。悪意のあるサイトから身を守るための大切なお守りです。
aria-label="GitHub"
画面が見えない方などが使う「音声読み上げソフト」のための設定です。アイコン画像だけだと、ソフトは「何のボタンか」を読み上げられません。ここで「これはGitHubへのリンクですよ」と裏側で教えてあげることで、誰にでも優しいサイトになります(アクセシビリティの向上)。
<FaGithub />
<FaGithub /> は、HTMLの <img> タグで画像ファイル(.pngなど)を読み込んでいるわけではありません。Next.js(React)で定番の react-icons というライブラリ(アイコンの詰め合わせパック)から呼び出したコンポーネントです。
Fa は「Font Awesome(フォントオーサム)」という世界中で使われている有名なアイコンブランドの略です。
画像ではなく「ベクターデータ(SVG)」として描画されるため、どれだけ拡大しても画質が荒くならず、文字色と同じようにCSSで簡単に色を変えることができます。
className="size-5"
アイコン(<FaGithub>)の大きさを指定しています。Tailwind CSSの size-5 は、「縦幅(height)と横幅(width)を同時に20pxにする」という便利な省略の書き方です。
Discussion