CSSで、文字に縁取りを付ける方法
CSS で文字に縁取りを付けるには、-webkit-text-stroke に線の太さと色を指定します。ただ、この線は文字の輪郭の外側だけでなく内側にもかかるので、太くするほど文字の中の色が見えなくなります。paint-order: stroke fill を足すと、線が文字の後ろに描かれ、文字の形はそのままで外側にだけ縁が出ます。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。フォントは Noto Sans CJK JP を使いました。HTML と CSS のページを Playwright で開いて getComputedStyle() の値を調べ、画面を撮って、文字の中の白い画素と縁取りの濃い色の画素を数えました。Firefox と Safari、スマートフォンの実機、ほかのフォントでの見え方は試していません。
-webkit-text-stroke で縁取りを付ける
CSS は次のとおりです。color が文字の中の色、-webkit-text-stroke が縁取りの太さと色です。
/* 文字に縁取りを付ける */
.stroke {
color: #ffffff;
-webkit-text-stroke: 4px #1f2a30;
}
-webkit-text-stroke は、-webkit-text-stroke-width(線の太さ)と -webkit-text-stroke-color(線の色)をまとめて書く一括指定です。名前に -webkit- が付いていますが、記事の最後の表のとおり、Chrome、Firefox、Safari のどれでも使えます。
線を太くすると、文字の中が線で埋まる
水色の背景に、太字(700)で64pxの白い「猫の手」を置き、線の太さを変えて比べました。

文字の中の白い画素を数えると、縁取りなしで4,513個でした。2pxにすると3,164個(約70%)、4pxで1,813個(約40%)に減り、8pxではわずか8個で、白い部分はほぼ消えました。
一方で、線は文字の外にも広がりました。縁取りなしのときは、文字の上端が段落の箱の上端から17px下にありましたが、8pxの線を付けると12pxの位置まで上に広がりました。左側では、段落の箱の左端より6px外にはみ出しました。線は輪郭を中心に、内側と外側の両方に描かれているということです。
細い字ではもっと早く白い部分がなくなります。字の太さを300にすると、縁取りなしで2,648個あった白い画素が、4pxの線を付けただけで43個になりました。
paint-order: stroke fill で、線を文字の後ろに回す
paint-order は、文字の塗りと輪郭の線を、どちらから先に描くかを決めるプロパティです。初期値の normal では塗りが先で、その上に線が描かれます。stroke fill にすると線を先に描き、その上に塗りを重ねるので、輪郭の内側にかかった線は塗りに隠れます。
/* 縁取りを文字の後ろに描き、文字の形を細らせない */
.stroke-outside {
color: #ffffff;
-webkit-text-stroke: 8px #1f2a30;
paint-order: stroke fill;
}

8pxの線に paint-order: stroke fill を足すと、白い画素は4,517個で、縁取りなしのとき(4,513個)とほぼ同じでした。字の太さが300でも、白い画素は2,658個で、縁取りなしの2,648個とほぼ同じです。
外側への広がりは、paint-order を足す前と同じでした。縁取りなしと比べて、上に5px(17pxから12pxの位置へ)、左に7px(箱の1px内側から6px外へ)広がりました。輪郭の内側に入る分は塗りに隠れるので、外側に見える縁は、指定した8pxよりも細くなります。縁を太く見せたいときは、そのぶん -webkit-text-stroke の値を大きくします。
線だけで文字を描く(中を塗らない)
文字の中を塗らずに、輪郭の線だけにするときは、color を transparent にします。
/* 中を塗らずに、縁取りの線だけで文字を描く */
.outline-only {
color: transparent;
-webkit-text-stroke: 2px #067a98;
}

-webkit-text-fill-color は、文字の中を塗る色を決めるプロパティで、指定しないときは color の値が使われます。検証では -webkit-text-fill-color を指定していないので、計算値は color と同じ rgba(0, 0, 0, 0) になり、文字の中には背景の色だけが見えました。
この形では、paint-order: stroke fill を付けても見た目は変わりませんでした。線の濃い水色の画素は、付けても付けなくても2,423個です。上に重ねる塗りが透明なので、輪郭の内側にかかった線も隠れません。
text-shadow で代用すると、斜めの線がギザギザになる
-webkit-text-stroke を使わずに、text-shadow の影を上下左右と斜めの8方向にずらして重ね、縁取りに見せる書き方もあります。
/* text-shadow を8方向にずらして重ね、縁取りの代わりにする */
.shadow-outline {
color: #ffffff;
text-shadow:
2px 0 0 #1f2a30, -2px 0 0 #1f2a30,
0 2px 0 #1f2a30, 0 -2px 0 #1f2a30,
2px 2px 0 #1f2a30, -2px -2px 0 #1f2a30,
2px -2px 0 #1f2a30, -2px 2px 0 #1f2a30;
}
影は文字の後ろに置かれるので、文字は細りません。白い画素は4,517個で、縁取りなしのときとほぼ同じでした。
ずらす量を4pxにすると、-webkit-text-stroke との違いがはっきり出ました。影は文字の形をそのまま8方向に動かしたものなので、斜めや曲がった線のところで、影と影のすき間が段差になって見えます。

太い縁取りを付けたいときは、-webkit-text-stroke と paint-order を組み合わせるほうが、きれいに仕上がります。text-shadow を使うときは、影の色を必ず書きます。互換性データの注記によると、Safari では色を書かない影は透明になります。
対応しているブラウザ
MDN の互換性データ(@mdn/browser-compat-data 8.1.8)で、この記事で使っている機能を調べました。
| 機能 | Chrome | Firefox | Safari |
|---|---|---|---|
-webkit-text-stroke |
4 | 49 | 3 |
-webkit-text-fill-color |
1 | 49 | 3 |
paint-order(HTML の文字の縁取り) |
123 | 60 | 11 |
text-shadow |
2 | 3.5 | 1.1 |
paint-order は、Chrome 122 以前と Safari 10.1 以前では、HTML の文字の縁取りには効きません(互換性データの注記より)。その場合は、paint-order を指定していないときと同じ描き方になります。





