リンクカードを実装するなら代替リンクも残さないか?
2025-11-05
リンクカードを実装した。
この行は変換されます。
https://github.com/ras0q
この行は変換されません。
[@ras0q](https://github.com/ras0q)
が、以下のように変換される。
<p>この行は変換されます。</p>
<p class="sr-only"><strong>ras0q - Overview</strong></p>
<p class="sr-only">🦖🦖🦖💨. ras0q has 142 repositories available. Follow their code on GitHub.</p>
<p><a href="https://github.com/ras0q" target="_blank" rel="noopener">https://github.com/ras0q</a></p>
<figure class="linkcard bg-neutral-200 dark:bg-neutral-700 bg-opacity-50!">
<img src="https://avatars.githubusercontent.com/u/66677201?v=4?s=400" alt="ras0q - Overviewのサムネイル" loading="lazy">
<figcaption>
<a href="https://github.com/ras0q" target="_blank" rel="noopener">ras0q - Overview</a>
<p>🦖🦖🦖💨. ras0q has 142 repositories available. Follow their code on GitHub.</p>
</figcaption>
</figure>
<p>この行は変換されません。</p>
<p><a href="https://github.com/ras0q" class="decoration-underline" target="_blank" rel="noopener">@ras0q</a></p>
設定しているCSS
figure.linkcard {
position: relative;
display: flex;
flex-wrap: wrap;
margin: 1.25rem 0;
& > img {
width: 100%;
height: auto;
margin: 0;
object-fit: contain;
aspect-ratio: 2 / 1;
}
& > figcaption {
padding: 0.5rem 1rem;
& > a::after {
position: absolute;
inset: 0;
content: "";
}
& > p {
margin: 0;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
}
}
}
この変換が上手くいっていれば、直接リンクを貼ったときだけリンクカードが表示されているはず。↓
この行は変換されます。
ras0q - Overview
🦖🦖🦖💨. ras0q has 141 repositories available. Follow their code on GitHub.
🦖🦖🦖💨. ras0q has 141 repositories available. Follow their code on GitHub.
この行は変換されません。
ここからは実装の話。アクセシビリティを意識したつもりだが、多分間違っているので指摘してほしい。
変換はRemarkプラグインとして実装した。HTMLに変換済みの各ページを愚直にVisitして該当するリンクをリンクカードに変換している。
blog/_config.ts at 9edd5ef85613aa6b8d7fd3814d48c5e86dfbdf72 · ras0q/blog
Contribute to ras0q/blog development by creating an account on GitHub.
https://github.com/ras0q/blog/blob/9edd5ef85613aa6b8d7fd3814d48c5e86dfbdf72/_config.ts#L36-L128
Contribute to ras0q/blog development by creating an account on GitHub.
Remark
Use Remark to render the markdown content.
https://lume.land/plugins/remark/

Use Remark to render the markdown content.
ちなみに <https://github.com/ras0q> のように <> で囲んでも同じく変換される。Remarkでは <> で囲んでもそうでなくても構造に違いがないため、これらを区別できなかった。
実装方針を調べると <a> タグの中に ブロック要素を入れて大きなカードリンクとする方法と、ブロック要素の中に入れた <a> タグのクリック範囲をCSSで広げる方法の2つがあった。後者はBootstrapでStretched linkとして採用されている方法で、今回はこちらを使うことにした。Stretched linkを使うとカード全体がリンクとなるため、カード内のテキストを選択できなくなることには注意。
Stretched link
Make any HTML element or Bootstrap component clickable by “stretching” a nested link via CSS.
https://getbootstrap.com/docs/5.3/helpers/stretched-link/

Make any HTML element or Bootstrap component clickable by “stretching” a nested link via CSS.
リンクカードは <figure> と <figcaption> を使って実装した。これはリンクカードが本文の補足情報であることを示すため。
筆者はリーダー (アプリによってリーディングモードやリーダービューやイマーシブリーダーとも呼ぶ) で記事を読むことが多いのだが、世のWebページに実装されているリンクカードはことごとくこのリーダーからは見えなくなってしまう。これが大変不便で、せめて自分だけは...とZennにリンクカードと共に生リンクを貼る抵抗をしている。
参考:
READMEを実行するGo製タスクランナー「xc」のススメ
https://zenn.dev/trap/articles/af32614c07214d

リーダーによって抽出のアルゴリズムが異なるため、リンクカードが消えるのを許容し、リンクカードとは別に生のリンクを描画することにした。また、.sr-only クラスで記事のタイトルと説明文を隠し、リーダーを使う際にはリンクカードの代わりにテキスト情報が出るようにした。