タグアーカイブ CSS関数

CSS random()関数を全ブラウザで動かす。polyfillの仕組みと活用法

CSS random()関数を全ブラウザで動かす。polyfillの仕組みと活用法

CSSのrandom()関数がSafariで先行実装されてから約半年。ChromeやFirefoxではまだ使えないが、そのギャップを埋めるpolyfillがnpmで公開された。この記事ではrandom()の基本構文からpolyfillの仕組みまでを解説する。

random()は要素のサイズや色、配置などをランダムに決めるCSS関数だ。これまでJavaScriptで行っていた演出を宣言的に書けるため、紙吹雪や星野のようなビジュアルエフェクトを純粋なCSSで実現できる。

ただし現時点ではSafariでしかネイティブ動作しない。そこで役立つのが、CSS-Tricksの著者が開発したcss-random-polyfillだ。この記事ではその使い方と内部動作をデモ付きで紹介する。

CSSのrandom()関数とは?Safariが先行対応した新機能

CSSのrandom()関数とは?Safariが先行対応した新機能

CSS Values and Units Module Level 5のエディタードラフトで定義されているrandom()関数は、calc()min()と同じように、プロパティの値の一部として利用できる。乱数を生成して、要素のサイズや色、角度、アニメーションの遅延時間などをランダムに設定できるのが特徴だ。

2025年後半、Safari 26.2で最初にサポートされた。WebKitチームは「一般的なユースケースをHTMLとCSSだけで解決できるようにする」という方針を掲げており、JavaScriptやサードパーティフレームワークへの依存を減らす狙いがある。

random()の基本構文

random()関数は最小値と最大値を指定する。オプションで第3引数にステップ間隔を指定できる。たとえば以下のコードは、1pxから7pxの間で1px刻みのランダムな値を生成する。

--random-star-size: random(1px, 7px, 1px);
random() 関数の引数
第1引数(最小値)
ランダム値の下限を指定する
1px
第2引数(最大値)
ランダム値の上限を指定する
7px
第3引数(ステップ間隔)
値の刻み幅を指定する。省略すると連続値になる
1px

このように、random()calc()と同じく値の一部として使える。ステップ間隔を指定すると、指定した範囲内の離散的な値(この例では1px、2px、3px…7px)だけが選ばれる。

キャッシュとキーの概念

random()には値のキャッシュを制御する仕組みがある。element-sharedfixedなどのベース値を指定すると、同じ値を複数の要素やプロパティで共有できる。たとえば、以下のコードでは4つの尖った星すべてに同じ乱数を適用する。

.star.fourpointed {
  --random-rotation: random(element-shared, -45deg, 45deg);
  rotate: var(--random-rotation);
}

element-sharedを指定すると、同じ要素に適用されるrandom()呼び出しが同じ結果を返す。これにより、複数の要素で共通のランダム値を保つことができる。

他ブラウザで使うためのpolyfillが登場

他ブラウザで使うためのpolyfillが登場

Safariが先行したrandom()だが、ChromeとFirefoxでも開発の兆しはあるものの、正式リリースの時期は未定だ。Chromeには関連する課題が報告されており、Firefoxもバグジラで進捗が追跡されている。ただし、フラグ付きでさえ使える状態にはなっていない。

この状況を受けて、CSS-Tricksの著者がcss-random-polyfillというnpmパッケージを公開した。このpolyfillを使うと、Safari以外のブラウザでもrandom()関数を利用できる。polyfillはネイティブサポートを検出し、未対応ブラウザでのみ動作する設計だ。

polyfillの使い方

polyfillを利用するには、HTMLのhead内にスクリプトを読み込み、ランダムにしたい要素にrandomizedクラスを追加する。CSSでは--randomで始まるカスタムプロパティにrandom()関数の値を格納する。


<script src="https://unpkg.com/css-random-polyfill@latest/dist/css-random-polyfill.js"></script>


<div class="randomized star"></div>
<div class="randomized star fourpointed"></div>
/* CSS */
.star {
  --random-star-size: random(1px, 7px, 1px);
  width: var(--random-star-size);
  aspect-ratio: 1/1;
  /* その他のプロパティ */
}
polyfill の処理フロー
STEP 1 ブラウザがネイティブ対応しているか判定する
STEP 2 未対応なら randomized クラスを一時的に非表示にする
STEP 3 --random で始まるプロパティを探して乱数を計算する
STEP 4 インラインスタイルに値を設定して表示を戻す

polyfillの処理はシンプルで、ネイティブサポートがある場合は何もせず、未対応の場合のみJavaScriptで乱数を計算してインラインスタイルに反映する。randomizedクラスを付けた要素だけが対象になるため、ページ全体のパフォーマンスへの影響は最小限に抑えられる。

実践デモで見るrandom()の活用例

実践デモで見るrandom()の活用例

polyfillの動作を確認するために、いくつかのデモが用意されている。ここでは代表的な例を紹介する。

ランダムな正方形デモ

最もシンプルな例として、ランダムな色とサイズの正方形を3つ配置するデモがある。CSSではrandom()を使ってカスタムプロパティを定義し、それを幅や高さ、背景色に反映する。

/* ランダムなサイズを共有する例 */
--random-height: random(--side, 40px, 100px);
--random-width: random(--side, 40px, 100px);

width: var(--random-height);
height: var(--random-width);
ランダム正方形の生成
固定値(Before)
すべて同じサイズと固定色
ランダム値(After)
サイズと色がランダムに変化。カスタムキーで幅と高さが同じ値になる

--random-height--random-widthにカスタムキー--sideを指定することで、幅と高さが同じ乱数を共有する。これにより正方形の形状を保ったままサイズだけがランダムに変化する。

星野デモと運命の車輪

より視覚的な例として、ランダムに散らばる星野デモや、回転角度がランダムになる運命の車輪デモも公開されている。星野デモでは200個の星がランダムな位置に配置され、それぞれ異なるタイミングで明滅する。

運命の車輪デモでは、回転角度をrandom(2turn, 10turn, 20deg)のように指定している。最小値と最大値はturn単位、ステップはdeg単位という異なる単位の組み合わせも可能だ。これはcalc()と同じく、同じデータ型に解決できる単位同士であれば混在できるためだ。

@keyframes spin {
  from {
    rotate: 0deg;
  }
  to {
    rotate: var(--random-rotation);
  }
}

#wheel {
  --random-rotation: random(2turn, 10turn, 20deg);
}

このコードでは、車輪が2回転から10回転の間でランダムな角度だけ回転する。ステップ間隔が20度なので、結果は20度刻みの値になる。random()を使うことで、ユーザーがページを開くたびに異なる結果になる。

polyfillの内部動作を理解する

polyfillの内部動作を理解する

このpolyfillは独自のCSSパーサーを実装しておらず、既存のオープンソースライブラリを利用している。具体的には、PostCSSプラグインとして知られる@csstools/css-calcの内部実装を活用している。このライブラリはCSSのcalc()関数を計算するために作られたもので、依存関係がなく、random()の最新仕様にも対応済みだ。

カスタムプロパティの許容性を活用

polyfillの鍵となるのは、CSSカスタムプロパティの値が「非常に許容度が高い」という仕様だ。ブラウザがrandom()関数を理解できなくても、カスタムプロパティの値としては文字列として保持される。たとえば以下のような式は、未対応ブラウザでもエラーにならず、JavaScriptから読み出せる。

/* ブラウザが解釈できなくてもエラーにならない */
--random-grid-area: random(1, var(--rows), 1) / random(1, var(--columns), 1);

ブラウザはvar()参照を解決してから値を文字列として保持するため、polyfillはgetComputedStyle()で値を読み取り、乱数を計算してインラインスタイルとして書き戻す。この手法は、スタイルシートを書き換えたり再取得したりする必要がないため、従来のCSS polyfillにありがちな副作用を回避できる。

処理の流れ

polyfillの動作は以下のJavaScriptコードに要約されている。ネイティブサポートを判定し、未対応の場合のみ--randomプレフィックスの付いたプロパティを収集して乱数を計算する。

import { calc } from "@csstools/css-calc";
const calcFn = calc;

if (!CSS.supports("width", "random(0px, 100px)")) {
  // 要素を一時的に非表示にしてフリッカーを防ぐ
  // --random プレフィックスのプロパティを収集
  // @csstools/css-calc で乱数を計算
  // インラインスタイルに反映
}

注意点として、random()のキャッシュ制御に関わる部分がある。仕様ではelement-sharedfixedなどのベース値を指定できるが、未指定の場合にライブラリが均等な分布で乱数を生成しないことがある。この問題を回避するため、polyfillでは乱数値を明示的に注入するパッチを適用している。

random-item()を模倣する実験と今後の展望

random-item()を模倣する実験と今後の展望

CSS Values and Units Module Level 5には、random()と似たrandom-item()という関数が定義されている。これは与えられたリストからランダムに1つの値を選ぶ関数だ。しかし、現時点ではどのブラウザも実装していない。

Chromiumベースのブラウザでは、CSSカスタム関数とインライン条件分岐を組み合わせることで、random-item()に近い機能を実現できる。たとえば、以下のコードはリストからランダムに1つの色を選ぶ。

--random-index: random(element-shared, 1, 5, 1);
--random-color: --item(var(--random-index), aqua, purple, pink, grey, green);

これを実現するカスタム関数--itemは、インデックス番号に応じて対応する引数を返す。CSSカスタム関数は可変長引数をサポートしないため、事前に想定される最大数だけ引数を定義しておく必要がある。

@function --item(--index,
  --arg-1: ,
  --arg-2: ,
  --arg-3: ,
  --arg-4: ,
  --arg-5: ) {
  result: if(
    style(--index: 1): var(--arg-1);
    style(--index: 2): var(--arg-2);
    style(--index: 3): var(--arg-3);
    style(--index: 4): var(--arg-4);
    else: var(--arg-5);
  );
}

この手法は、CSS-Tricksの著者が「ハックではなく、意図された標準機能の使い方」と述べている。かつてTemani Afif氏が行った色リストの実装はハックに近いものだったが、カスタム関数を使えば任意のデータ型で動作する汎用的な解決になる。ただし、CSSカスタム関数もまだすべてのブラウザで利用できるわけではないため、現時点ではChromium限定の実験的な機能だ。

この記事のポイント

  • CSS random()関数はSafariが先行対応。ChromeとFirefoxは対応時期が未定
  • polyfillを使えば他ブラウザでもrandom()を利用できる
  • 使い方は--randomプレフィックスのカスタムプロパティとrandomizedクラスを設定するだけ
  • polyfillはカスタムプロパティの許容性を利用し、@csstools/css-calcで乱数を計算する
  • Chromiumではカスタム関数と組み合わせてrandom-item()を模倣可能