CSSで、背景に水玉やストライプの模様を作る方法

​​CSS​HTML,Playwright

背景のストライプや水玉の模様は、画像を用意しなくても、CSS のグラデーションだけで描けます。ストライプは repeating-linear-gradient() で色の帯をくり返し、水玉は radial-gradient() で描いた1つの丸を background-size の大きさで並べます。斜めの縞をタイルで描くときに縞がつながらない問題、水玉を互い違いに並べる方法、模様のふちがギザギザになるときの直し方もあわせて書きます。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS のページを Playwright で開き、画面を撮って、色が変わる位置と、画像に使われている色の数を記録しました。Firefox と Safari、スマートフォンの実機は試していません。

repeating-linear-gradient() で、斜めのストライプを描く

斜めのストライプの CSS は次のとおりです。

css
/* 斜めのストライプ:水色10px、白10pxをくり返す */
.stripe {
  background-color: #fff;
  background-image: repeating-linear-gradient(
    45deg,
    #00c8e8 0 10px,
    #fff 10px 20px
  );
}

模様を出したい要素に、このクラスを付けます。

html
<div class="stripe"></div>
上下2段の比較。上の「斜め(45deg)」では、水色と白の帯が、左上から右下へ斜めに走っている。下の「横(to bottom)」では、水色と白の帯が横向きに上から交互に並んでいる
repeating-linear-gradient() で描いた斜めと横のストライプ

repeating-linear-gradient() は、書いた色の並びを、グラデーションの向きにくり返し描きます。#00c8e8 0 10px のように1つの色に位置を2つ書くと、0から10pxまでがその色になります。次の白も同じ10pxの位置から始まるので、色はぼかされずに切り替わり、10pxずつの帯になります。

45deg は、色が変わっていく向き(グラデーション線の向き)です。帯はその向きと直角に伸びるので、左上から右下へ斜めに走ります。上の画像の下の段のように、45deg を to bottom にすると横のストライプになります。左端の列を調べると、水色は y が 0〜9、20〜29、40〜49… の位置にあり、10pxずつ交互に並んでいました。

斜めのストライプで注意したいのは、帯の幅です。10px は帯と直角の向きに測った幅で、横の1行で見ると、もっと広くなります。幅240pxの要素で上端の1行を調べたところ、左端で切れた帯を除くと、水色は x が 22〜35、50〜63、78〜92… の位置にあり、水色と白がそれぞれ14pxほどの幅で、28〜29pxごとに並んでいました。

斜めの縞をタイルで描くときは、4つに分ける

repeating-linear-gradient() を使わずに、linear-gradient() で描いたタイルを background-size で並べる書き方もあります。このとき、タイルの中で色を1回切り替えるだけでは、縞になりません。

css
/* 40px四方のタイルに斜めの縞を描く(色の切り替えが1か所だけ) */
.tile-two {
  background-color: #fff;
  background-image: linear-gradient(45deg, #00c8e8 50%, #fff 50%);
  background-size: 40px 40px;
}
上下2段の比較。どちらにも40px四方のタイルの区切りを灰色の破線で重ねている。上の「色の切り替えが1か所(50%)」では、タイルごとに左下が水色、右上が白の三角形になり、のこぎりの歯のような模様になっている。下の「4つに分ける(25% 50% 75%)」では、斜めの帯がタイルの区切りをまたいでつながっている
色の切り替えが1か所だと三角形が並び、4つに分けると縞がつながる

上の段のように、どのタイルも左下が水色、右上が白の三角形になり、のこぎりの歯のような模様になりました。上端の行(y が 0)は白だけで、タイルの下端に近い行(y が 39)は、x が 0〜38、40〜78… と、タイルごとに途切れた水色でした。斜めの境目がタイルの角から角へ1本あるだけなので、となりのタイルの境目とつながりません。

縞をつなげるには、タイルの中を 25%、50%、75% で4つに分け、水色と白を2回ずつ置きます。

css
/* 4つに分けると、となりのタイルと縞がつながる */
.tile-four {
  background-color: #fff;
  background-image: linear-gradient(
    45deg,
    #00c8e8 25%, #fff 25% 50%,
    #00c8e8 50% 75%, #fff 75%
  );
  background-size: 40px 40px;
}

タイルの区切りをはさんだ2つの行を比べると、違いが分かります。.tile-four では、タイルの下端の行(y が 39)の水色は x が 0〜18、39〜58…、1つ下のタイルの上端の行(y が 40)は 0〜19、40〜59… で、1pxずれただけでした。縞は区切りをまたいで、そのまま斜めにつながります。.tile-two では、y が 39 の行は 0〜38、40〜78… とほぼ水色なのに、y が 40 の行には水色がまったくなく、区切りで模様が途切れていました。

radial-gradient() で、水玉を並べる

水玉は、radial-gradient() で1つの丸を描き、background-size で決めたタイルを縦横にくり返して並べます。

css
/* 24pxごとに、半径5pxの水玉を並べる */
.dots {
  background-color: #fff;
  background-image: radial-gradient(circle, #00c8e8 5px, transparent 5px);
  background-size: 24px 24px;
}
上下2段の比較。上の「縦横にそろえる」では、水色の丸が縦横にまっすぐ並んでいる。下の「互い違いにする(2枚目を 12px ずらす)」では、1段ごとに丸が半分ずつずれて並んでいる
縦横にそろえた水玉(上)と、互い違いの水玉(下)

radial-gradient() は、何も指定しないとタイルの真ん中から外へ色が変わります。中心から5pxまでを水色、5pxから先を透明にしたので、半径5pxの丸になります。丸の外は透明なので、background-color の白が見えます。

試したところ、水玉の中心を通る行(y が 12)で、水色は x が 7〜16、31〜40、55〜64… の位置にあり、幅10pxの丸が24pxごとに並んでいました。縦の列でも同じ位置でした。丸の大きさは半径で、並ぶ間隔は background-size で変えます。

2枚重ねて、水玉を互い違いにする

水玉を1段ごとに半分ずらすときは、同じ radial-gradient() を2枚重ね、2枚目の位置を background-position でタイルの半分(12px)ずらします。

css
/* 2枚目を半分ずらして、互い違いに並べる */
.dots-staggered {
  background-color: #fff;
  background-image:
    radial-gradient(circle, #00c8e8 5px, transparent 5px),
    radial-gradient(circle, #00c8e8 5px, transparent 5px);
  background-size: 24px 24px;
  background-position: 0 0, 12px 12px;
}

background-size と background-position は、カンマで区切った順に、background-image の1枚ずつに対応します。background-size の値が1つだけのときは、複数の背景の使い方を説明した MDN(英語版)のページにあるとおり、値がくり返して使われ、2枚とも同じ大きさになります。

1枚目の水玉は、これまでと同じく、中心が 12、36、60… の位置にあります。2枚目は12pxずれるので、中心が 0、24、48… の位置に来ます。要素の上端の行(y が 0)を調べると、水色は x が 0〜4、19〜28、43〜52… の位置にありました。x が 0〜4 は、要素の左上の角で切れた2枚目の丸です。上の画像の下の段のとおり、2枚目の水玉は、要素の上端と左端で半分に切れて見えます。

タイルが長方形のとき、circle を書かないと水玉が楕円になる

radial-gradient() の最初の circle を省くと、形は ellipse(楕円)として扱われます。24×24px の正方形のタイルでは、circle を省いても、水玉の中心を通る行と列はどちらも 7〜16 の10pxで、丸のままでしたが、タイルが長方形だと、水玉がつぶれました。

上下2段の比較。どちらもタイルの大きさは 32×16px。上の「circle を書かない」では、水玉が横に長い楕円につぶれている。下の「circle を書く」では、水玉が丸いまま並んでいる
タイルが長方形のとき、circle を書かないと楕円になる(上)

タイルを 32×16px にして、水玉の中心を通る行と列を調べました。circle を書かないと、横は x が 11〜20 の10pxなのに、縦は y が 6〜9 の4pxしかありませんでした。circle を書くと、縦も y が 3〜12 の10pxになりました。MDN(英語版)の radial-gradient() の説明によると、形を省略したとき、大きさに長さを1つだけ書いた場合は circle、そうでなければ ellipse(楕円)になります。水玉を描くときは、circle を書いておくと、タイルの形が変わっても丸のままでした。

境目に1pxの幅を持たせて、ふちのギザギザを消す

ここまでの CSS は、水色と透明(または白)を同じ位置で切り替えています。検証環境の Chromium 153 では、この切り替えの境目がぼかされず、水玉や斜めの縞のふちが階段のようにギザギザになりました。

上下2段の比較。水玉の模様の左上を4倍に拡大している。上の「境目が 5px の1か所」では、丸のふちが階段状のギザギザになっている。下の「境目を 4.5px から 5.5px に広げる」では、ふちに中間の薄い水色が入り、丸がなめらかに見える
境目に1pxの幅を持たせると、丸のふちがなめらかになる(下)

境目を1pxの幅に広げると、その間だけ色が少しずつ変わり、ふちがなめらかになりました。

css
/* 境目に1pxの幅を持たせて、ふちのギザギザをなめらかにする */
.dots-smooth {
  background-color: #fff;
  background-image: radial-gradient(circle, #00c8e8 4.5px, transparent 5.5px);
  background-size: 24px 24px;
}

.stripe-smooth {
  background-color: #fff;
  background-image: repeating-linear-gradient(
    45deg,
    #00c8e8 0 9.5px,
    #fff 10.5px 19.5px,
    #00c8e8 20px
  );
}
上下2段の比較。斜めのストライプの左上を4倍に拡大している。上の「境目が 10px の1か所」では、帯のふちが階段状のギザギザになっている。下の「境目を 9.5px から 10.5px に広げる」では、ふちに中間の薄い水色が入り、帯の線がなめらかに見える
斜めのストライプでも、境目を広げるとふちがなめらかになる(下)

画面を撮った画像に使われている色の数を数えて比べました。

書き方 等倍で撮った画像 2倍(deviceScaleFactor: 2)で撮った画像
水玉、境目が 5px の1か所 2色 2色
水玉、境目が 4.5px から 5.5px 13色 32色
ストライプ、境目が 10px の1か所 2色 2色
ストライプ、境目が 9.5px から 10.5px 48色 117色

境目が1か所の書き方では、どちらの倍率でも、白と #00c8e8 の2色しかありませんでした。ふちの画素は、水色か白のどちらかになります。境目を広げると、ふちに rgb(115, 225, 242) のような中間の色が入りました。

ストライプでは、くり返しの最後に #00c8e8 20px を置いています。白から次の水色へ移る境目にも、19.5pxから20pxまでの幅を持たせるためです。これを書かずに #fff 10.5px 20px で終えると、くり返しの継ぎ目で白から水色へぼかさずに切り替わりました。y が 30 の行の x が 0〜59 を調べると、中間の色の画素は水色から白へ移る x が 9 と 37 の2か所だけで、白から水色へ移る側にはありませんでした。最後の色を置いた .stripe-smooth では、x が 9、23、37、51 の4か所、つまり帯の両側にありました。

境目を広げても、丸の大きさは変わりませんでした。水玉の中心を通る行では、境目が1か所のときと同じく、x が 7〜16 の幅10pxに色が付いていて、両端の画素だけが rgb(7, 202, 233) のような、ほぼ水色の中間の色でした。

対応しているブラウザ

MDN の互換性データ(@mdn/browser-compat-data 8.1.8)で、この記事で使っている機能を調べました。

機能 Chrome Firefox Safari
linear-gradient() 26 16 7
repeating-linear-gradient() 26 16 7
radial-gradient() 26 16 7
1つの色に位置を2つ書く(#00c8e8 0 10px) 71 64 12.1
背景画像を複数重ねる 1 3.6 1.3
background-size 3 4 5

1つの色に位置を2つ書く書き方だけは、ほかより新しい機能です。MDN の linear-gradient() の説明にあるとおり、1つの色に位置を2つ書くのは、同じ色を2つの位置で書くのと同じ意味です。古いブラウザにも合わせたいときは、#00c8e8 0, #00c8e8 10px, #fff 10px, #fff 20px のように、色を2回ずつ書きます。検証環境の Chromium で、.stripe の background-image の計算値を見ると、ブラウザの側でもこの形に書き直されていました。

​Posted by 86