リンクの入れ子は subgrid が最適解かもしれない
はじめに
リンクの入れ子とは何かというと、以下のようなデザインです。
カード全体がリンクでクリッカブルになっていて、中のタグやカテゴリーもそれぞれがリンクになっています。ニュースやブログの投稿などでよく見るデザインだと思います。
しかし、以下のようにマークアップすることはできません。
<a href="https://example.com/posts/hello-world/">
<h2>Hello, World!</h2>
<p>...</p>
<a href="https://example.com/tag/hello/">#hello</a>
<a href="https://example.com/tag/world/">#world</a>
</a>
HTML のルール的に <a>
の入れ子はダメだからです。
Subgrid を使った方法
Subgrid がまだない時代からいろいろな方法が編み出されてきましたがここでは取り上げません。ではさっそく subgrid を使った方法を解説します。
HTML マークアップ
HTML は以下のようにマークアップします。至って普通です。可能なかぎりデザインに寄せます。
<div class="card">
<a class="link" href="https://example.com/posts/hello-world/">
<h2>Hello, World!</h2>
<p>...</p>
</a>
<p class="tags">
<a href="https://example.com/tag/hello/">#hello</a>
<a href="https://example.com/tag/world/">#world</a>
</p>
</div>
Grid を使って全体のリンクを拡張
.card {
display: grid;
}
.link {
grid-row: 1 / 3;
grid-column: 1;
}
.tags {
grid-row: 2 / 3;
grid-column: 1;
}
これだけで全体のリンクがタグまで拡張されるようになります。しかし、grid-row: 2 / 3;
は .link
.tags
のどちらも使っているので、当然被ります。.link
に padding-bottom
などを指定すればそれを回避できますが、.tags
の高さの可変には対応できません。
Subgrid の出番
.card {
display: grid;
}
.link {
grid-row: 1 / 3;
grid-column: 1;
+ display: grid;
+ grid-template-rows: subgrid;
}
.tags {
grid-row: 2 / 3;
grid-column: 1;
}
.link
に subgrid
を指定して、親の .card
のグリッドを使うようにします。
もし .link
内に要素が 1 つしかない場合は、これでほぼ OK ですが、今回は 2 つあるので全体のグリッド設定を 3 行にします (もしくは .link
内の要素を 1 つの要素にまとめても OK) 。
.card {
display: grid;
}
.link {
- grid-row: 1 / 3;
+ grid-row: 1 / 4;
grid-column: 1;
display: grid;
grid-template-rows: subgrid;
}
.tags {
- grid-row: 2 / 3;
+ grid-row: 3 / 4;
grid-column: 1;
}
クリッカブルエリアの調整
もう上記で基本的な部分はできましたが、.tags
全体が .link
の上に被っているので、.tags
の中のリンクではない部分も、下の .link
がクリックできないようになっています。
.tags
のポインターイベントを無効にして中のリンクだけを有効にします。
.card {
display: grid;
}
.link {
grid-row: 1 / 4;
grid-column: 1;
display: grid;
grid-template-rows: subgrid;
}
.tags {
grid-row: 3 / 4;
grid-column: 1;
+ pointer-events: none;
+
+ a {
+ pointer-events: auto;
+ }
}
リンクのパディングとカードの横並び
.link
にパディングが必要なく、かつ .card
が縦に並ぶ場合は以上で十分ですが、現状だと下のパディングは思ったとおりにつきません。また、カードを横並びにすると、中の要素が上下均等に配置されます。逆に .card
に align-content: start;
を指定すると、カード全体の高さが揃わなくなります。
以下のようにするとこの 2 つの問題は解消されます。
.card {
display: grid;
+ grid-template-rows: auto auto auto 1fr;
}
.link {
- grid-row: 1 / 4;
+ grid-row: 1 / 5;
grid-column: 1;
display: grid;
grid-template-rows: subgrid;
}
.tags {
grid-row: 3 / 4;
grid-column: 1;
pointer-events: none;
a {
pointer-events: auto;
}
}
詳しくは解説しませんが、使われない 4 行目が fr
であることで全体の大きさに対して拡大し、一方 auto
は中身に合わせたサイズになります。
ここで 2 つ注意が必要です。
-
.card
のグリッドに対してrow-gap
を指定すると、3 行目と使われていない 4 行目の間にもギャップが出ます。要素間はマージンを使うか、.link
のpadding-bottom
で調整してください。 -
.link
の左右パディングは.tags
には適用されないので、同じ値の左右パディングかマージンを.tags
にも指定してください。
おわりに
HTML のマークアップは特殊のことをしておらず、CSS のほうも最低限の .tags
のポインターイベント無効しかしていないので、tab
キーによるフォーカスの移動や読み上げもほぼ問題ないと思います。さらに、この方法のメリットとして、alt
(Win) / option
(Mac) キーを押しながらのリンク内テキストの選択もほぼそのまま行えます。以下動画があります。
CodePen には入れ子リンクがタイトルと説明文の間にある場合のサンプルもありますのでよければご参考ください。グリッドの行の位置を調整するだけですが。
KITERETZ inc. (株式会社キテレツ) ではこれからめちゃくちゃわくわくするようなプロジェクトがたくさん予定されていますので、ご興味がある方はぜひお声がけください!特にデザイナーとディレクターを募集しています!
Discussion