border-widthにpx値を小数で指定した場合、要素の境界線の幅はどうなる？

CSSで`border-width`プロパティの値に0.3pxとか1.7pxみたいに小数を指定した場合、表示される要素の境界線の線幅にどれくらい違いが現れるのか以前から気になってたので、サンプルのコードを書いて各ブラウザーで確認してみました。

サンプルのコードはこちら： https://codepen.io/kaz_hashimoto/pen/VwyVqzN

細い境界線を１本引いただけでは差がわかりにくいため、複数本並べて表示してみます。Chromeで表示するとこうなりました。(GIFアニメーションです。Firefoxではクリックして再生）
![border-width.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/2e539476-9ec0-7a55-204a-48e7370977ef.gif)
ピンク色と白のストライプの１本１本が境界線です。境界線の幅は0.1pxから2pxまで0.1px刻みに、そして並べる本数は1本から10本まで変化させています。
セルの右側の数式は、boxの幅100pxを**内法**と境界線の**ストライプの幅**の和に分解したものです。数式に表示したpx値はCSSプロパティの[使用値(used value)](https://developer.mozilla.org/ja/docs/Web/CSS/used_value)[^1]です。
[^1]:JavaScriptの`getBoundingClientRect()`と`getComputedStyle()`を使って取得。

![fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/0d59abee-2f81-de13-d8ae-3afd74134011.png)
境界線のストライプを作るHTMLのコードは、`border-right`を設定したdiv.boxを境界線の本数分ネストすることで実現しています。
![dom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/991c74fb-f0ac-5b20-c307-29f49def88fd.png)

## ブラウザーによる表示の違いを比較
他のブラウザーではどんな線幅で表示されるでしょうか？ 結果はご覧のとおり。

**デスクトップ版ブラウザー**
![border-width_1920.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/fbec0299-35a4-757f-8d90-8101cd662260.png)
**スマホ版ブラウザー**
![border-width_sp_1920.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/ef829ff2-abc1-dd30-c088-b138f9b2e534.png)
画面に表現できる線幅の細かさや段階は、ブラウザーによる違いだけでなくディスプレイのデバイスピクセル比(DPR, device pixel ratio)も関係してそうです。デスクトップ版の左側２つは同じディスプレイ(DPR=2)で表示したものですが、ChromeとOperaの場合、**見かけの線幅**も0.1px刻みで変化する（最小0.5px）のに対して、FirefoxとSafariでは0.5px刻みに丸めた線幅になりました。

今回テストした環境は下表のとおりです。

| DPR | OS | ブラウザー | 実行環境 |
|:-:|:--|:--|:--|
| 1 | Windows 10 | Edge 100, IE11 | [BrowserStack](https://www.browserstack.com/) |
| 2 | macOS 11.6.5  | Chrome 100, Firefox 99, Safari 15.4, Opera 85 | 実機 |
| 3 | iOS 15.2 | Safari 15.2 | iOS Simulator|
| 3.5 | Android 10 | Chrome 100 | 実機 |

## 半端なpxの境界線はどのように描画されているか
0.7pxなど半端な線幅が指定された境界線を、Chromeは画面上のピクセルでどのように表現しているのでしょうか？
そこで、サンプルの画面をChrome DevToolsの"capture full size screenshot"コマンド[^2]を使って保存します。Photoshopでその画像を開いて、境界線のストライプ部分をピクセルが確認できるくらいまで拡大してみました。
[^2]:DevTools画面でShitf+⌘+P (macOS)キーを押し、コマンドを入力

![device-pixel_1280.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/18c7e705-e26a-938d-0346-a51dce2e2a7a.png)
使用したディスプレイはDPR=2なので、1 css pxは画面上で2x2のタイル（シアン色で示した矩形）に相当します。見てのとおり、

- `border-width`の指定値が0.6pxや0.7pxの場合も、border１本に対して塗りつぶされるタイルは１列の幅 = 1 device px (dpx) = 0.5 css pxである。
-  一方、`border-width`の使用値はそれぞれ0.59375pxと0.695312pxで算出されている。
-  境界線のストライプが太く見えたのは、所々タイル１列の幅の隙間が挿入されていたため。その隙間からは背景が見えてしまう。

このように、**計算上の幅**（使用値）と**塗りつぶしたタイルの幅の合計**とのズレは隙間を挿入することにより補正していたようです。

では、境界線を１本だけ引いた場合に隙間は生じるでしょうか？下図は指定幅0.5px〜0.9pxで引いた境界線です。拡大するとタイル１列分だけが塗りつぶされています。
![fig5_border_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/b617f36e-eeb3-02bc-8236-6549f003f0c5.png)
次にdiv.boxのパディング領域の背景を青色で塗りつぶしてみます。パディング領域は階層の最も内側のdiv.innerが占めているので、その`background-color`を`blue`に設定します。
![fig6_border_1_bg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/3edf16bb-0e25-d903-84b6-54d5f796dd91.png)
指定幅0.6pxや0.9pxなどの場合、境界線とパディングの縁との間に隙間ができてその下の背景が見えてしまっているのがわかります。

隙間が生じるか否かはboxの寸法にも依存するようです。下図は指定幅0.7pxの境界線を1本から10本まで順に増やしていった時の様子です。内側のboxの幅が狭くなるにつれて、境界線とパディングの縁との間に隙間が現れたり消えたりしています。(GIFアニメーションです）
![bgblue-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/c14b06e2-1e35-38de-0c5a-f2119745549c.gif)

## 実際の画像で検証
要素の境界線とパディングの縁との間に生じる隙間について、実際の画像を使って検証してみましょう。

サンプル2: https://codepen.io/kaz_hashimoto/pen/vYpMrRw

サンプル2は、グリッドレイアウトに同じ画像６個を同じ寸法で配置します。各グリッドセルのdiv.boxには幅0.8pxの境界線を設定し、背景を赤色で塗りつぶしてあります。画像と境界線との間に隙間がなければ、赤い背景色は画像に隠れて見えないはずです。下図はChromeで表示した画面です。背景色がチラチラ見えているのがわかります。これが隙間です。
![border-sample1_75p.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/a1bc5630-74a9-6b17-8026-9aa489b1990d.jpeg)
※素材画像: Photo by [Evangeline Sarney](https://unsplash.com/@createandmakeup) on [unsplash](https://unsplash.com/photos/EyrjiJAwLjQ)

境界線の色を白にすると隙間の箇所がはっきりします。
![border-sample2_75p.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/2a262bab-d8fd-1c71-b354-d28d788c77ae.jpeg)
boxの上下左右の境界線のどこに隙間が生じるかは、boxの位置や寸法にも依存するようです。ウインドウ幅を変えてグリッドの列数やセルの幅を変化させた時にも隙間が現れる辺が切り替わるのを確認できます。
注）Nextボタンをクリックして画面を再描画させる必要があります[^3]
[^3]: 画像データがChromeのキャッシュから読み込まれた時は隙間の背景色が塗られないようです。

表示サイズが大きい画像の場合、隙間ができると境界線に接する画像の端がぼやけたり筋のように見えることがあります。特にコーナーの部分では隙間が目立ちやすいです。

サンプル3: https://codepen.io/kaz_hashimoto/pen/BaJEbmZ
![large-sample_x2_80p.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/1c862684-93ad-2f74-7d5e-9053fc3e8314.jpeg)
※素材画像: Photo by [Content Pixie](https://unsplash.com/@contentpixie) on [unsplash](https://unsplash.com/photos/WdJ4WnLxyDs)



関連記事： [1pxのborder付けた画像をChromeでズームした時に現れる隙間を消す方法](https://qiita.com/kazhashimoto/items/a64a1f7d346bfa728ec2)