CSSのborderで描いた三角形を配置した時に生じる隙間を消す方法

CSSのborderを組み合わせて描いた三角形を、下図のように長方形の左辺にくっつけて配置してできた図形をレスポンシブ対応にしたい。つまり、三角形と長方形の相対的な位置関係を維持したまま、ウインドウ幅に応じて図形のサイズを変化させたい。そんなCSSを書いたところ、ブラウザに依存した現象にハマりました。その解決策のメモです。
![fig01_w200.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/6a7ef291-3bc8-477e-6559-eca630f0761f.png)
**動作環境**
macOS 10.14 (Mojave), Retinaディスプレイ(Radeon Pro 575), Firefox 75, Safari 13.1

## ダメな例
まずは失敗例から。本記事では話を簡単にするため、長方形のサイズは固定とし、三角形のみサイズを可変にしたもので説明します。三角形の高さを6vwとしましょう。

```html:html
<div class="box">
  <div class="triangle p-left"></div>
</div>
```

```css:css
.box {
  width: 150px;
  height: 100px;
  background-color: #000;
  position: relative;
  margin-left: 200px;
  margin-bottom: 20px;
  color: #fff;
  padding: 16px 16px 0 20px;
}
.triangle {
  width: 0;
  height: 0;
  border-right: 6vw solid #000;
  border-top: 3vw solid transparent;
  border-bottom: 3vw solid transparent;
}
.p-left {
  position: absolute;
  top: 20px;
  left: -6vw;
}
```

右側borderの幅6vw、上下のborderの幅3vwという組み合わせで作った三角形`div.triangle`を、`div.box`に対して**三角形の高さ分、左側に**`left: -6vw`の絶対位置指定により配置しました。

これをFirefoxで表示し、ブラウザのウインドウ幅をゆっくり変えていってみましょう。すると、下図の**A**のように、三角形と長方形との接合部分にチラチラと隙間が空いてしまうのがわかります。
![vid01_trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/a6457767-9992-a5d1-c660-d64bd432657b.gif)
隙間の部分を拡大すると（下図）、そこには1デバイスピクセル分の空きが生じていました（使用したモニタはデバイスピクセル比2）。
![fig02_details.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/a661bc5c-5a5f-add4-73d9-2b61663ad544.png)
先ほどのアニメーションに示した残りの３つの図形**B**〜**D**も、`div.box`の左側に子要素を`left`プロパティの絶対位置指定で配置したものですが、隙間が生じないものもあります。それらの違いについて見ていきましょう。

### 図形B： 隙間あり
子要素はwidth 0の長方形で、右側borderを幅6vwにして塗りつぶしのように見せた図形です（**A**の三角形と同様の手法）。子要素は`left: -6vw`で配置。**A**の場合と同じく、接合部に隙間ができました。

```html:html
<div class="box">
  <div class="rect p-left"></div>
</div>

```

```css:css
.rect {
  width: 0;
  height: 50px;
  border-right: 6vw solid #000;
}
```

### 図形C： 隙間なし
子要素はwidth 6vwの塗りつぶした長方形です。子要素は`left: -6vw`で配置。こちらは隙間ができません。

```html:html
<div class="box">
  <div class="rect2 p-left"></div>
</div>
```

```css:css
.rect2 {
  width: 6vw;
  height: 50px;
  background-color: #000;	
}
```

### 図形D： 隙間なし
子要素は固定幅60px, 30pxのborderを組み合わせて描いた三角形です。子要素は固定px値`left: -60px`で配置。これも隙間は生じません。

```html:html
<div class="box">
  <div class="triangle-px p-left-px"></div>
</div>
```

```css:css
.triangle-px {
  width: 0;
  height: 0;
  border-right: 60px solid #000;
  border-top: 30px solid transparent;
  border-bottom: 30px solid transparent;
}
.p-left-px {
  position: absolute;
  top: 20px;
  left: -60px;
}
```

### 結果のまとめ
以上の結果をまとめると下表のとおりです。

| 図形 | width指定 | border幅指定 | left指定 | 隙間 |
|:-:|:-:|:-:|:-:|:-:|
| A | 0 | vw | vw | あり |
| B | 0 | vw  | vw | あり |
| C | vw | (none)|  vw | なし |
| D | 0 | px | px | なし |

要素の形状に関係なく、border使って塗りつぶした場合はborder幅とleftの長さを両方ともvw単位で指定すると相性が悪く、ブラウザによっては半端なピクセル分の隙間ができてしまうようです。下記のブラウザで確認したところ、本記事の執筆時点（2020年5月）ではFirefoxとSafariでこの現象が発生してました。

| ブラウザ | Engine | 現象の発生 |
|:-:|:-:|:-:|
| Firefox 75  | Gecko | する |
| Safari 13.1 | WebKit | する |
| Chrome 81 | Blink | しない  |
| Opera 68 | Blink | しない  | 

## 解決策
子要素の位置を`left`で指定するのは諦めて、`right`を使ってみましょう。子要素の右側を`div.box`の左辺にぴったり付けるには、たった１行`right: 100%`に変更するだけ。

```css:css
.p-left {
	position: absolute;
	top: 20px;
/*	left: -6vw;*/
	right: 100%;
}
```

先ほどのアニメーションの図形**A**〜**D**で、子要素をすべて`right: 100%`に変更したのが下図です。これでFirefoxやSafariでも隙間が出なくなりました。
![vid02_fix_trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/34101663-b83d-c91c-466a-487003224bb8.gif)

`right`プロパティは、**要素の右辺と包含ブロックの右辺との間の距離**を表し、値にパーセントを指定した場合は**包含ブロックの幅に対するpercentage**になるので[^1]、100%を指定すれば、`div.box`の幅が変動しても子要素の右側がぴったりくっつくというわけですね。

[^1]: https://developer.mozilla.org/ja/docs/Web/CSS/right