CSSで、文字に蛍光ペンのような線を引く方法
文字に蛍光ペンのような線を引くには、文字を <span> などで囲み、上の60%を透明、下の40%を黄色にしたグラデーションを背景に置きます。インライン要素の背景なので、文が次の行に折り返しても、線は行ごとに続きます。背景を印刷しない設定のまま Chromium で PDF にすると背景の線は消え、Windows のハイコントラストモードのような強制カラーモードでも消えるので、その直し方もあわせて書きます。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS のページを Playwright で開き、getComputedStyle() の値と、画面を撮って黄色の画素がある範囲を記録しました。印刷は page.pdf() で作った PDF を画像にして調べ、強制カラーモードは Playwright のエミュレーションで試しています。Firefox と Safari、スマートフォンの実機、実際の Windows のハイコントラストモード、紙への印刷は試していません。
背景のグラデーションで線を引く
CSS は次の3行です。
/* 文字の下40%に、蛍光ペンのような線を引く */
.marker {
background: linear-gradient(transparent 60%, #fff27a 60%);
}
線を引きたい部分に、このクラスを付けます。
<p>猫の手を借りたいときは、<span class="marker">まず作業を小さく分けてから、順番に片付けていく</span>のが近道です。</p>

linear-gradient() は、方向を書かないと上から下へ色が変わります。transparent 60% と #fff27a 60% のように、2つの色を同じ位置に置くと、色はぼかされずにそこで切り替わります。上の60%は透明で文字の後ろの背景が見え、下の40%だけが黄色になります。
文字の大きさが20pxの段落で調べると、<span> の行の箱は高さ29pxで、黄色の画素は、箱の上端から17px下がった位置から12px分ありました。箱の高さの40%(11.6px)に当たる帯です。2行に分かれた <span> は、getClientRects() で行ごとに2つの箱になり、どちらの箱にも同じ位置に同じ太さの帯が描かれていました。
線の太さは、行の高さ(line-height)では変わりませんでした。line-height を1.8から3にしても、getClientRects() の箱の高さは29pxのままで、帯も12pxでした。背景が描かれる箱の高さが、行の高さでは変わらなかったためです。文字を30pxにすると、箱は44px、帯は18pxになり、文字の大きさに合わせて太くなりました。
mark 要素に付けるときは、文字の色も確かめる
検索した語や、引用の中で注目してほしい部分のように、文の中で目立たせる意味があるときは <mark> 要素を使えます。ただの見た目の飾りなら <span> で十分です。
何も指定しない <mark> は、文字の箱の全体が黄色(rgb(255, 255, 0))で塗られ、文字の色は黒(rgb(0, 0, 0))でした。ブラウザが最初から持っているスタイルです。<mark class="marker"> のようにクラスを付けると、background の一括指定で背景色が透明に戻り、下の40%だけの線になりました。文字の色は黒のままでした。color: inherit も足すと、文字の色はまわりの段落と同じ rgb(31, 42, 48) になりました。
MDN の説明によると、多くのスクリーンリーダーは、既定の設定では <mark> があることを読み上げません。線が引かれていることを知らないと意味が通じない文には、線だけに頼らない書き方が必要です。
折り返した行の端にも余白を付ける
線を文字の両端より少し長くしたいときは、左右に padding を付けます。ただ、padding だけでは、行をまたいだときに余白が付くのは、線の始まりと終わりだけです。行ごとの端にも余白を付けるには、box-decoration-break を clone にします。
/* 線の両端を少し伸ばし、折り返した行の端にも余白を付ける */
.marker-padded {
padding: 0 0.3em;
background: linear-gradient(transparent 60%, #fff27a 60%);
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
}

3行に分かれた線の、行ごとの箱の幅を比べました。初期値の slice では106px、340px、26pxで、余白は1行目の左と3行目の右にしか付きませんでした。clone では112px、352px、32pxで、どの行も左右に6px(0.3em)ずつ余白が付きました。
記事の最後に載せた互換性データによると、Safari は -webkit- を付けた書き方にしか対応していません。上の CSS で、-webkit- 付きの行も書いているのはそのためです。
太い下線で引くときは、skip-ink を切らないと英字の線が消える
背景ではなく、太い下線を文字の上まで持ち上げて、蛍光ペンの線の代わりにする書き方もあります。
/* 太い下線を文字の上に持ち上げて、線の代わりにする */
.marker-underline {
text-decoration-line: underline;
text-decoration-thickness: 0.4em;
text-decoration-color: #fff27a;
text-underline-offset: -0.3em;
text-decoration-skip-ink: none;
}
text-decoration-thickness で下線を太くし、text-underline-offset をマイナスにして、下線を文字の側へ上げます。文字の大きさが20pxのとき、下線の太さは8px、位置は-6pxになり、帯は、行の箱の上端から17px下がった位置から8px分に描かれました。画面では、文字が線の上に描かれ、線に隠れずに読めました。
この書き方では、最後の text-decoration-skip-ink: none が欠かせません。text-decoration-skip-ink は、下線が文字に触れたり横切ったりするところで、下線を途切れさせるかどうかを決めるプロパティです。初期値の auto のままだと、太い下線が文字と大きく重なるので、線が欠けます。

「Typography gjpq と日本語」に線を引いて比べると、auto のときは、英字の部分に黄色の画素が1つもありませんでした。線があったのは、続く「と日本語」の部分だけです。none にすると、英字の部分にも線が続きました。
太い下線で引く書き方は、次の節のとおり、背景を印刷しない設定でも消えません。ただし、強制カラーモードでは、文字に太い黒い線が重なって読みにくくなります。
背景を印刷しない設定では、背景の線が消える
Playwright の page.pdf() で PDF を作りました。背景のグラフィックを印刷するかどうかを決める printBackground は、初期値の false のままです。その PDF を画像にして黄色の画素を数えると、背景で引いた線は0個で、線は消えていました。printBackground: true にすると542個ありました。
print-color-adjust の説明には、初期値の economy では、印刷するときにブラウザが背景画像を取り除くことがあると書かれています。グラデーションも背景画像の1つです。線を印刷でも残したいときは、exact を指定します。
/* 印刷でも背景の線を出す */
.marker {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
この CSS を足すと、printBackground が false のままでも、黄色の画素は542個になり、線が印刷されました。太い下線で引く書き方は、何も足さなくても320個の黄色の画素があり、線が残りました。下線は背景画像ではないので、取り除かれませんでした。
強制カラーモードでは、Mark の色で塗り直す
強制カラーモードは、Windows のハイコントラストモードのように、ページの色を限られた色に置き換えて表示するモードです。forced-colors の説明によると、このモードでは、URL を使わない background-image は none になり、text-decoration-color などの色はブラウザが決めた色になります。
Playwright で強制カラーモードを有効にして開くと、背景で引いた線は background-image が none になり、黄色の画素は0個でした。線は何も残りません。太い下線で引いた線は、黄色ではなく文字と同じ黒になり、文字の上に太い黒い線が重なって、取り消し線のように見えました。

強制カラーモードのときだけ、システムカラーの Mark と MarkText で塗り直すと、線の代わりに文字の箱が塗られます。Mark は、<mark> のように目立たせた文字の背景に使う色です。
/* 強制カラーモードでは、背景の画像と下線の色が消されるので、Mark の色で塗り直す */
@media (forced-colors: active) {
.marker,
.marker-padded,
.marker-underline {
background-color: Mark;
color: MarkText;
text-decoration: none;
}
}
この CSS を足すと、強制カラーモードで背景色が rgb(255, 255, 0) になり、どの書き方でも文字の箱の全体が黄色で塗られました。下線は消え、取り消し線のような黒い線もなくなりました。強制カラーモードでないときは、線の位置も太さも、この CSS を足す前と同じでした。何も指定しない <mark> は、強制カラーモードでも初めから黄色で塗られていました。
対応しているブラウザ
MDN の互換性データ(@mdn/browser-compat-data 8.1.8)で、この記事で使っている機能を調べました。
| 機能 | Chrome | Firefox | Safari |
|---|---|---|---|
linear-gradient() |
26 | 16 | 7 |
box-decoration-break |
130(-webkit- 付きは22から一部) |
32 | -webkit- 付きのみ、7から一部 |
text-decoration-thickness |
89 | 70 | 12.1 |
text-underline-offset |
87 | 70 | 12.1 |
text-decoration-skip-ink |
64 | 70 | 15.4 |
print-color-adjust |
136(-webkit- 付きは17から) |
97 | 15.4(-webkit- 付きは6から) |
forced-colors |
89 | 89 | 16 |
Mark の色 |
102 | 109 | 対応なし |
box-decoration-break の「一部」は、インライン要素にしか使えないという意味です。この記事の使い方はインライン要素なので、影響はありません。
太い下線の書き方で、text-decoration の一括指定に太さまで入れる書き方(text-decoration: underline 0.4em #fff27a)は、Safari では 26.2 からの対応でした。上の CSS で、text-decoration-line などを1つずつ書いているのはそのためです。





