cssのmod()を使って方眼紙風の背景を作る

こんにちは、ナカムラです。
今回は方眼紙風の背景を作ります。
ただ格子状に線を引くだけなら難しくないですが、「要素の幅や高さが変わっても背景がぶつ切りにならないように作りたい」となると少々工夫が必要になります。
そこで、CSSのmod()を使ってみたいと思います。

mod()とは?

CSSの値関数の一つです。
リファレンスからの引用ですが、下記の計算をしてくれる関数です。

ある数値を別の数で割ったときのモジュラス(除数と同じ符号を持つ)を計算します。 developer.mozilla.org

マス目が途切れないようにするには、背景を設定する要素のサイズを、「マス目のサイズの倍数+線幅」にしたいです。
「マス目のサイズの倍数」にするには、マス目のサイズで割ったあまりの数を背景を設定する要素の幅と高さから引くことで調整できます。

※余剰を求める関数はもう一つありますが、こちらは用途が異なるので使いません。
詳しくは下記の記事で解説されていました。 qiita.com

DEMO

では、早速DEMOをみていきましょう。
背景は要素のサイズを調整してしまうので、コンテンツに影響しないように擬似要素のbeforeにしています。

DEMOでは要素のサイズを+10px、-10pxするボタンを用意しましたので、クリックしてサイズを変更してみてください。
割り切れる数の時は要素ピッタリに、端数が出るときには余白が生まれますが、マス目が途中で途切れることなく表示されると思います。
※Chromeの場合、端の線が若干掠れる場合があります。1px未満の数値も表示してしまうため、中央配置だと微妙なずれが起きることがあります。(デフォルトで整数に寄せてほしい)

See the Pen css mod()を使って方眼紙風の背景を作る by Nakamura (@takayo-nakamura) on CodePen.

(1)親要素のサイズを取得する

ここはjavaScriptで取得します。
100%を割れば良いかと思いましたが、単位の混在ができないのと、線幅が1pxなのでキッチリ計算できるように単位はpxで実装します。
Chromeだと1px未満の数字も表現されてしまいますが、
offsetWidth・offsetHeightで取得した時点で整数になります。

取得した幅と高さをルート変数としてHTMLに挿入することでCSSで使えるようになります。

window.addEventListener('load', () => {
  addBodySize()
})

function addBodySize () {
  // 要素の幅と高さを取得する
  const targetElement = document.querySelector('.wrapper')
  const elementWidth = targetElement.offsetWidth + 'px'
  const elementHeight = targetElement.offsetHeight + 'px'

  // 取得した数値をルート変数として挿入する
  const html = document.documentElement
  html.style.setProperty('--element-width', elementWidth)
  html.style.setProperty('--element-height', elementHeight)
}

(2)背景用の要素の幅と高さを調整する

擬似要素を中央配置にし、背景を設定します。
背景は上と左に線が入っている、1マス分のSVGをリピートしています。
1マス分のサイズは30px × 30pxです。

  &::before {
    position: absolute;
    top: 50%;
    left: 50%;
    translate: -50% -50%;
    width: calc(var(--element-width) - mod(var(--element-width), 30px) + 1px);
    height: calc(var(--element-height) - mod(var(--element-height), 30px) + 1px);
    content: '';
    background: url('data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2030%2030%22%3E%20%3Cpath%20class%3D%22cls-1%22%20fill%3D%22%23ddd%22%20d%3D%22M30%2C0H0v30h1V1h29V0Z%22%2F%3E%3C%2Fsvg%3E') repeat 0 0 / 30px 30px;
  }

今回のポイントとなる幅と高さの値だけみてみましょう。

    width: calc(var(--element-width) - mod(var(--element-width), 30px) + 1px);
    height: calc(var(--element-height) - mod(var(--element-height), 30px) + 1px);

calc()関数も使用して、「要素の幅(または高さ) - 要素の幅を30pxで割ったあまり + 線幅」という計算式にしています。

要素の幅:var(--element-width)
要素の幅を30pxで割ったあまり:mod(var(--element-width), 30px)
線幅:1px

まとめ

計算部分もjavaScriptでできるんじゃないの?と言われてしまうとそうなのですが、スタイルのことはできるだけCSSでやりたいと思っています。
親要素のサイズの取得もCSSでできたりしないかなど、知らないだけでやり方は色々あるかと思いますが、mod()を使ってみたかったので今回はこれで良しとします。

要素内の文字量が可変する動的な箇所などにも、繰り返しの背景をキレイに収めたいんだというデザイナーは多いかと思います。
CSSがだいぶ進化しているので、javaScriptに疎くてもできることが増えてきました。
もちろん、シンプルな構造の方がソースコードも少なく、処理も軽くできて良いと思いますが、 これはwebだぞ?と言う前に、実装方法の模索は常に心がけたいです。