隣接する要素のボーダーが重なって太くならないようにbox-shadowでボーダーを表現する
カレンダーのJSライブラリを月単位で表示することを想像してみてください。横軸は週で最大7つ、縦軸は曜日で最小で4、最大で6です。
そしてすべての項目は1pxのボーダーを指定する必要があるとします。
そのライブラリがtableタグで出力されていたらborder-collapse: collapse;
を反映した状態でボーダーを指定すればいいだけです。もしtableタグが使われていなかったとしてもパターンは固定ですからtable
プロパティを使用したり、:nth-of-type
プロパティで調整することも難しくありません。
では、次のようなUIを実装するとしたらどうしますか?モバイルでは2カラムだが、タブレットで3カラム、デスクトップで4カラムになるとします。
カラム数が変わるためtableタグやtableプロパティは使えず、border-collapse: collapse;
も同様に使えません。
ブレイクポイントごとに:nth-of-type
プロパティでボーダーを調整する方法などが考えられますが、指定がややめんどうになるかもしれません。
完全な代替にはならないのですが、ちょっと便利な方法があります。
borderの代わりにbox-shadowで指定する
指定方法は次のとおりです。
.item {
box-shadow:
1px 1px #000,
inset 1px 1px #000;
}
ここで少しbox-shadow
の構文をおさらいします。
/* <inset> | <offset-x> | <offset-y> | <color> */
box-shadow: inset 1px 1px #000;
box-shadow - CSS: カスケーディングスタイルシート | MDN
inset
の有無に関わらず<offset-x>
と<offset-y>
は同じ挙動です。
-
<offset-x>
:水平方向(右)にシャドウが1pxズレる -
<offset-y>
:垂直方向(下)にシャドウが1pxズレる
そしてinset
を省略した場合は要素の外側にシャドウが付きますが、inset
を指定すると要素の内側にシャドウが付きます。
たとえば次のように指定すると、
.item {
border: 1px solid #000;
box-shadow:
4px 4px rgb(0 0 255 / 50%), // 青色
inset 4px 4px rgb(255 0 0 / 50%); // 赤色
このように表示されます。
外側と内側のシャドウが組み合わさることでボーダーのように見えるということです。
問題点
便利なこの方法ですが、実は問題点が2つあります。
右上と左下に1pxずつスキマができている
box-shadow
の仕様上、border
と同じ位置に配置することができません。
次のGIFアニメでは黒色をborder
、青と赤色をbox-shadow
で指定していますが、border
とbox-shadow
の位置が合っておらず、右上と左下にボーダーがないことがわかります(1pxのスキマなので拡大しないとわからないですが)。
つまり、完全にborder
の代わりにはなっていないけれど、簡単な指定でほとんど違和感が出ないくらいの再現ができる方法ということです。
いちばん最初に載せた画像も実は右上と左下にボーダーが1pxずつ足りていませんでした。
ボーダーを2pxにするとスキマも2pxになるので少し目立っていますね。
擬似要素で表示して位置をズラせばborder
と同じ配置にはできますが、スキマを埋めることはできません。つまり2px以上の場合に採用することは難しくなります。
角丸にすると線がキタナクなってしまう
たとえば四隅を角丸にしたデザインの場合、角丸部分の線が少し太く表示されてしまいます。
次の画像は左上だけ角丸にしてみました(拡大しています)。
まとめ
-
border
の代わりにbox-shadow
を使うとボーダーの重なりを考慮せずに実装ができる - ただし2px以上や角丸の場合はアラが目立ってしまう
完璧な方法ではありませんが、問題点だけクリアできれば便利な場面もあるCSSの記述方法でした。
Discussion