概要
PostCard でカード全体をクリックすると投稿詳細に遷移し、アバター部分だけクリックするとユーザーページに遷移させたかった。HTML ではリンク(<a>)のネストは無効なので、CSS の ::after 疑似要素を使った「Stretched Link パターン」で解決した。
どういうこと?
例えば上みたいな
- 1投稿をクリックしたらその投稿の詳細画面にいける
- 右下のアバターアイコンをクリックしたら、そのUserのprofile画面に飛べる
でこれをやろうとして、この投稿(Card)を全部 Link で囲んで、
アバターのアイコンをLinkで囲むってことをやろうとしたが、
そもそも a タグは ネストできないらしいのでどうしましょということが起きた。
最初は以下のように 投稿の画像は専用のLinkでavatarは別のLinkでやっていた。
ただCardにホバーしたら 大きくなるcss組み込んでて、「全部クリックできる」UXを
醸し出していたから気持ち悪さが残っていた。
<Card>
<Link>
<div>
{/* 投稿の画像の部分 */}
</div>
</Link>
<CardContent>
{/* tagの部分 */}
<Link>
{/* avatarの部分 */}
</Link>
</CardContent>
</Card>
結論
| やりたいこと | 方法 |
|---|---|
| カード全体を 1 つのリンクにする |
::after + absolute + inset: 0 でリンク領域を親要素全体に広げる |
| 一部だけ別のリンクにする | そのリンクに relative + z-index を付けて ::after の上に浮かせる |
::after 疑似要素とは
CSS の ::after は、要素の内部の末尾に自動で挿入される擬似的な子要素。HTML の DOM には現れないが、CSS でサイズや位置を持たせることができる。
.link::after {
content: ""; /* これがないと生成されない */
position: absolute;
inset: 0; /* top/right/bottom/left 全て 0 → 親要素いっぱいに広がる */
}
content: "" で空の要素が生成され、absolute + inset: 0 で直近の position: relative な祖先要素を基準に全体を覆う。見た目は透明だが、クリック可能な領域として機能する。
仕組み
要素の構造
Card (relative) ← ::after の基準
├── Link (after:absolute after:inset-0)
│ ├── ::after ← Card 全体を覆う透明なクリック領域
│ └── 画像
└── CardContent
├── タグ ← ::after の下 → クリックすると Link が反応
└── アバター Link (z-10) ← ::after の上 → クリックするとこちらが反応
z-index による重なり制御
z-index: 10 → アバター Link(ユーザーページへ)
z-index: 0 → ::after 疑似要素(投稿詳細へ)
::after はカード全体を覆っているが、アバターは z-10 でその上にいるため、アバターの領域だけはアバターのリンクが優先される。
クリック時の挙動
| クリック場所 | 何が受け取るか | 遷移先 |
|---|---|---|
| 画像 |
::after(= 画像の Link) |
投稿詳細 |
| タグ |
::after(= 画像の Link) |
投稿詳細 |
| アバター | アバターの Link(z-10) | ユーザーページ |
実装(Tailwind CSS)
<Card className="relative">
{/* カード全体のリンク: ::after で Card 全体にクリック領域を広げる */}
<Link href="/posts/1" className="after:absolute after:inset-0">
<Image ... />
</Link>
<CardContent>
<div>タグ</div>
{/* アバターだけ別リンク: z-10 で ::after の上に浮かせる */}
<Link href="/users/1" className="relative z-10">
<Image ... />
</Link>
</CardContent>
</Card>
Tailwind の after: プレフィックスは ::after 疑似要素にスタイルを適用する。content: "" は Tailwind が自動で付与するため明示不要。
When using these variants, Tailwind will automatically add content: '' by default so you don't have to specify it unless you want a different value:
注意点
- Card に
relativeがないと、::afterのabsoluteが意図しない祖先を基準にしてしまう -
<a>のネストは HTML 仕様で無効なので、このパターンはネストを避けつつ同等の UX を実現する手段として有効
終わりに
前作った時に、Linkを2つくらいに分けて作った記憶があり、スマートじゃないなと感じてたのでよかった。わーい。他の方法あれば教えてください。SNS系とか投稿系のサービスはよく使うテクニックだと思う。
参考
- Bootstrap の Stretched Link: https://getbootstrap.com/docs/5.3/helpers/stretched-link/
- tailwindcss で ::afterつけるとcontentが自動でつく : https://tailwindcss.com/docs/hover-focus-and-other-states#before-and-after
