CSSのみで外部リンクにアイコンをつける
要件
- 外部リンクにのみアイコンをつける
- 訪問済みのリンクは色を変える
- とにかく軽量 → Font Awesomeなどのwebフォント、JavaScript等は使わずにhtml&CSSのみで実装
表示するアイコン
Google Material SymbolsのOpen In Newというアイコンを使いました。人力圧縮でオリジナルの300Bから190Bまで圧縮されています。
<svg viewBox="0 0 99 99" xmlns="http://www.w3.org/2000/svg"><path d="M10,99C5,99 0,94 0,89V10C0,5 5,0 10,0H50V10H10V89H89V50H99V89c0 5-5 10-10 10ZM37,70l-7-7L80,10H60V0H99V39H89V19Z"/></svg>
外部リンクに一致するCSSセレクタ
このブログはrehype-external-linksというパッケージで外部リンクにtarget="_blank"
を設定しています。大体のサイトで同じだと思うので、これを属性セレクタで一致させます。正確にはtarget="_blank"
は新規タブで開くことを意味しているのでサイト内リンクの可能性もあるのですが、アイコンの名前もOpen In Newですし、まぁいいでしょう。
a[target="_blank"] {
/* ここにスタイル */
}
アイコン画像をつける
CSSセレクタは完成したのでアイコンを付けましょう。これは::after
擬似要素一択ですね、content
に<svg>
要素を入れて.....
😭😭😭
どうやらcontent
プロパティにhtml要素を入れることはできないようです。
https://developer.mozilla.org/ja/docs/Web/CSS/content
しかしurl()
で画像を入れることはできるようなのでdataURLでsvgを入れると
成功しました✨
ですが実はこれ、2つ目の要件「訪問済みのリンクは色を変える」を達成できません。(詳細は後述)
そのため、試行錯誤をした結果background-color
を設定してmask-image
で切り取ることにしました。
a[target="_blank"]::after {
background-color: blue;
mask-image: url('data:image/svg+xml;utf-8,<svg viewBox="0 0 99 99" xmlns="http://www.w3.org/2000/svg"></svg>');
}
訪問済みのリンクで色を変える
当初、CSSで訪問済みの要素に一致させるにはそう:visited
!ということで、こんな感じのCSSにしたのですが、全て未訪問として表示されてしまいました。
a::after {
content: url('data:image/svg+xml;utf-8,<svg fill="未訪問のときの色"></svg>');
}
a:visited::after {
content: url('data:image/svg+xml;utf-8,<svg fill="訪問済みのときの色"></svg>');
/* 効かない */
}
MDNによると、プライバシー保護のため:visited
擬似クラスで適用されたスタイルには制限があり、利用可能なプロパティは一部の色に関する物のみのようです。
https://developer.mozilla.org/ja/docs/Web/CSS/:visited#プライバシー上の制約
color
プロパティは使えるのでFont Awesomeなどのアイコンフォントなら:visited
で色を変えることができますが、要件の通りwebフォントはナシなので却下。
また、fill
プロパティも使えますがbackground-image
などで設定されたsvgには適用できないのでこれも却下。
そのため先述のbackground-color
で背景色を設定してmask-image
で切り取るという方針になったのです。
a[target="_blank"]::after {
content: '';
mask-image: url('data:image/svg+xml;utf-8,<svg viewBox="0 0 99 99" xmlns="http://www.w3.org/2000/svg"></svg>');
background-color: blue;
}
a[target="_blank"]:visited::after {
background-color: purple
}
実装
これで必要なものは揃ったので微調整して完成です✨
a {
color: blue;
}
a:visited {
color: purple;
}
a[target="_blank"]::after {
content: '';
display: inline-block;
width: .5em;
height: .5em;
margin-inline: 2px;
background-color: currentColor;
mask-image: url('data:image/svg+xml;utf-8,<svg viewBox="0 0 99 99" xmlns="http://www.w3.org/2000/svg"><path d="M10,99C5,99 0,94 0,89V10C0,5 5,0 10,0H50V10H10V89H89V50H99V89c0 5-5 10-10 10ZM37,70l-7-7L80,10H60V0H99V39H89V19Z"/></svg>');
vertical-align: super; /* テキストよりちょっと上に配置 */
}
currentColor!?
突然出てきたcurrentColor
ですが、これはcolor
プロパティの値を表すキーワード値で、要するに文字と一緒の色にするよってことです。
「テキストとアイコンで別々に色を設定」ではなく「テキストで色を設定し、アイコンはcurrentColor
で追従」とすることでちょっとだけシンプルに記述できます。
このcurrentColor、borderやsvgのfillなどに便利なのでぜひ覚えておいてください。