Skip to main content

Bringing Controlled Chaos to the Web: Inside the Push to Polyfill Native CSS Randomness Across Browsers

In his book How to Be Perfect, Michael Schur—creator of the philosophical sitcom The Good Place—includes a chapter titled "The Luck of the Draw." In it, he examines how the myth of meritocracy prompts individuals to systematically underestimate the role that sheer chance plays in their achievements. When observing how unpredictable forces govern the real world, there is a distinct resonance in how modern web design has increasingly begun to reflect life by embracing controlled chaos.

While extreme manifestations of non-determinism, such as generative user interfaces (GenUI) in search engines, have sparked debate among developers and users regarding unpredictable user experiences, the appetite for subtle dynamism remains strong. A web page that presents subtle variations upon every visit mirrors the ancient philosophical observation of Heraclitus: one cannot step into the same river twice.

As designers and developers explore non-deterministic patterns, a central challenge emerges: how to balance unpredictability with control. Traditionally, creating probabilistic visual features required heavy JavaScript interventions. However, the ongoing evolution of declarative CSS seeks to bring native randomness directly into the styling layer, sparking new tools and architectural workarounds while major browser engines catch up.

Real-world use cases for randomness

The need for controlled randomness frequently emerges in enterprise software design. In modern client projects, particularly short-term greenfield applications, UI requirements often reflect broader industry trends. A common example includes gamified reward interactions, such as launching a burst of confetti following a randomized draw feature.

Even a seemingly straightforward visual element like confetti exposes the underlying tension between design control and visual chaos. Corporate design systems often mandate that randomized elements align strictly with brand guidelines, including specific particle colors, physics ranges, and distribution logic. Standard open-source JavaScript plugins often prove insufficiently customizable for these requirements, forcing engineering teams to construct bespoke implementations.

This dynamic highlights a long-standing desire among web developers: the ability to handle presentational randomness natively within CSS without relying on script-heavy solutions.

Historically, the standard-setting process for CSS has focused on standardizing recurring web design patterns into declarative specifications—a philosophy often referred to within the World Wide Web Consortium (W3C) as "paving the cowpaths." Following this approach, Apple’s Safari became the first web browser to ship native support for the CSS random() function as part of its late-2025 Safari release, aligned with WebKit updates aimed at eliminating unnecessary JavaScript dependencies for standard UI behaviors.

The introduction of native random() in Safari triggered widespread experimentation across the web development community. Developer Schalk Neethling demonstrated how declarative CSS randomness enables granular control over complex particle animations like confetti. Similarly, web developer Alvaro Montoro emphasized that native CSS randomness aligns with the web’s Rule of Least Power—the principle that web features should be implemented using the least powerful language capable of expressing them correctly.

However, cross-browser availability remains fractured. Months after Safari’s rollout, native support for CSS random() has yet to land in production builds of Chromium or Firefox. While active tracker tickets exist in both Chrome’s issue database and Mozilla’s Bugzilla, neither browser vendor has guaranteed a concrete timeline for release, even behind experimental flag toggles.

Chrome issue tracker: https://issues.chromium.org/issues/413385732
Firefox Bugzilla tracker: https://bugzilla.mozilla.org/show_bug.cgi?id=1836588

This delay presents a practical hurdle for developers working across non-Apple operating systems. Furthermore, the specification for CSS random()—governed under the CSS Values and Units Module Level 5 draft—remains in an early exploration phase. The spec includes intricate mechanics for caching, keying semantics, base value definitions, and step intervals, meaning breaking spec changes remain possible before final standardization.

Attempting to bridge this gap via client-side scripting is traditionally considered risky, as polyfilling CSS syntax often introduces severe performance overhead or requires parsing raw stylesheets manually. Despite these challenges, a new lightweight utility—css-random-polyfill—has been created to deliver cross-browser compatibility today.

Let’s polyfill CSS random()

The exclusive presence of CSS random() in Safari represents an unusual reversal of typical web platform rollout trends, where modern features frequently land in Chromium-based engines first. Because Safari browser updates are intrinsically tied to macOS and iOS system updates, many Apple users remain on older versions that lack native support for the random() specification.

To address these limitations, the css-random-polyfill package was developed as an open-source solution published on npm. The library enables developers to author CSS randomness using valid CSS custom property syntax, automatically polyfilling the feature in unsupported browsers like Chrome and Firefox while allowing native WebKit engines to execute the specification directly.

Much of the polyfill’s baseline functionality has been benchmarked against visual test suites developed originally by the WebKit team to demonstrate CSS random() capabilities.

Demo: Random starfield

One primary visual demonstration converted for cross-browser support is a dynamic starfield background featuring randomly distributed, twinkling stars. Larger four-pointed stars tilt at uniform, randomly calculated angles, while all star elements display subtle variations in animation speed, entrance delays, and glow effects rendered via oklch color spaces.

To run the starfield across all major browsers, the HTML markup includes a script reference to the polyfill alongside a designated marker class (randomized) on elements targeted for evaluation:

<!-- The script processes usages of CSS random on page load -->
<script src="https://unpkg.com/css-random-polyfill@latest/dist/css-random-polyfill.js"></script>

<!-- Star elements tagged with the "randomized" class for polyfill targeting -->
<div class="randomized star"></div>
<div class="randomized star"></div>
<div class="randomized star fourpointed"></div>
<div class="randomized star fourpointed"></div>
<div class="randomized star fourpointed"></div>

On the styling side, because JavaScript engines cannot natively parse unknown CSS function signatures directly inside standard property declarations without throwing syntax errors, the polyfill relies on intermediate CSS custom properties prefixed with --random:

.star 
  --random-star-size: random(1px, 7px, 1px);
  background-color: white;
  border-radius: 50%;
  aspect-ratio: 1/1;
  width: var(--random-star-size);
  position: fixed;

  --random-top: random(0%, 100%);
  --random-left: random(0%, 100%);
  top: var(--random-top);
  left: var(--random-left);

  --random-hue: random(0, 360);
  filter: drop-shadow(0px 0px calc(var(--random-star-size) * 0.7) oklch(0.7 0.2 var(--random-hue)))
    drop-shadow(0px 0px calc(var(--random-star-size) * 3) white);
  mix-blend-mode: hard-light;

  --random-speed: random(2s, 5s);
  animation: fade-in var(--random-speed);
  animation-iteration-count: infinite;

  --random-delay: random(2s, 5s);
  animation-delay: var(--random-delay);
  animation-direction: normal;

This implementation highlights several variations supported by the CSS draft specification. For instance, passing an optional third argument defines a step interval, ensuring the calculation resolves exclusively to whole-number increments within the defined range:

--random-star-size: random(1px, 7px, 1px);

Similarly, the element-shared keyword allows distinct elements to inherit matching randomized parameters, such as applying a uniform rotation angle across all four-pointed stars:

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

While native CSS random() allows expressions to be passed directly inline within standard CSS declarations (such as width: random(1px, 7px)), routing values through custom properties ensures that stylesheets remain valid CSS syntax across all engines. When running in environments with native support—such as updated Safari builds—the polyfill bypasses processing entirely, passing execution directly to the browser’s engine.

Demo: Random Colored Grid Cells

A secondary layout pattern tested using the polyfill involves a 100×100 CSS grid structure populated by dynamically colored and positioned cells. Web developer Chris Coyier noted that declarative random values make complex grid experiments significantly easier to express directly in markup and CSS without external scripting.

The polyfill handles complex syntax permutations, including nested variable references and multiple inline calls within a single custom property declaration. For instance, developers can configure shorthand properties like grid-area using randomized row and column coordinates:

.rectangle 
  --random-grid-area: random(1, var(--rows), 1) / random(1, var(--columns), 1);
  grid-area: var(--random-grid-area);

Demo: Wheel of fortune

A third implementation scenario, originally constructed by Safari engineer Tim Nguyen, demonstrates probabilistic mechanics applied to interactive components, such as a spinning prize wheel.

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


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

This demo highlights CSS typed arithmetic semantics defined in the W3C spec. The parameters passed to random() mix distinct rotational units—turn and deg—in a single declaration:

random(2turn, 10turn, 20deg)

Because both turn and deg resolve to the underlying data type of <angle>, the math remains fully valid under CSS spec rules, functioning identically to mixed-unit additions inside standard calc() functions.

To ensure stability across browsers, the polyfill evaluates computed styles applied during the initial page load. Dynamic modifications to runtime computed styles via state changes (such as checkbox hacks or dynamic keyframe alterations) represent an area for future polyfill expansion using APIs like MutationObserver.

Demo: Random squares

To evaluate key-based value sharing, a simplified layout featuring three colored squares demonstrates how custom keys allow multiple CSS properties to share a single generated value.

--random-height: random(--side, 40px, 100px);
--random-width: random(--side, 40px, 100px);

width: var(--random-height);
height: var(--random-width);

By assigning a custom identifier (--side) as the first argument, the polyfill ensures that the random value generated for --random-height is reused for --random-width, keeping each square perfectly symmetrical without hardcoding static values.

Chromium-only bonus demo: Simulate random-item using a custom CSS function

While CSS random() focuses on generating values within continuous numeric ranges, web developers frequently require mechanisms to select an item from a discrete list of arbitrary values, such as choosing from a specific palette of brand colors.

The CSS Values and Units Module Level 5 draft addresses this with a proposed random-item() function:

/* Spec proposal for selecting from arbitrary lists */
background-color: random-item(element-shared, red, blue, green);

Outside of experimental testing builds in Safari Technology Preview, random-item() lacks native engine support. However, in Chromium-based browsers, developers can approximate this functionality today by combining experimental support for CSS Custom Functions (@function) with inline CSS conditional logic (if()).

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

A reusable --item custom function accepts an --index parameter followed by a series of optional arguments defaulted to empty values:

@function --item(--index,
  --arg-1: ,
  --arg-2: ,
  --arg-3: ,
  --arg-4: ,
  --arg-5: ,
  --arg-6: ,
  --arg-7: ,
  --arg-8: ,
  --arg-9: ,
  --arg-10: ) 

  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);
    style(--index: 5): var(--arg-5);
    style(--index: 6): var(--arg-6);
    style(--index: 7): var(--arg-7);
    style(--index: 8): var(--arg-8);
    style(--index: 9): var(--arg-9);
    else: var(--arg-10);
  );

Unlike historical workarounds—such as using complex CSS color math to index through array-like structures—this modular custom function approach operates natively on any valid CSS data type, including lengths, colors, strings, or asset paths.

How the polyfill works

Rather than building a CSS math parser from scratch, css-random-polyfill leverages existing open-source infrastructure from the CSSTools ecosystem. Specifically, it builds upon @csstools/css-calc, a dependency-free engine originally written to parse Level 4 and Level 5 CSS value specifications for PostCSS build tools.

The core client-side polyfill script executes through a structured workflow:

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

if (!CSS.supports("width", "random(0px, 100px)")) 
  const styleTag = document.createElement("style");
  styleTag.textContent = ".randomized  display: none; ";
  document.head.appendChild(styleTag);
  const elementIDs = new WeakMap();
  const documentID = crypto.randomUUID();

  document.querySelectorAll(".randomized").forEach((element) => 
    const styles = getComputedStyle(element);
    [...styles]
      .filter((property) => property.startsWith("--random"))
      .forEach((propertyName) => 
        const css = styles.getPropertyValue(propertyName);
        const value = resolveRandom(css, 
          element,
          propertyName,
          documentID,
          elementIDs,
          calcFn,
          crypto,
        );
        element.style.setProperty(propertyName, value);
      );
  );
  if (styleTag.parentNode) 
    styleTag.parentNode.removeChild(styleTag);
  


function resolveRandom(css,  element, propertyName, documentID, elementIDs, calcFn, crypto ) scoped)b

The execution flow operates through the following sequence:

  1. Feature Detection: The polyfill tests native platform support via CSS.supports("width", "random(0px, 100px)"). If the browser returns true, script execution terminates immediately.
  2. Layout Concealment: If native support is absent, a temporary <style> block applies display: none to all .randomized target elements to prevent Flash of Unstyled Content (FOUC) while calculations occur.
  3. DOM Inspection: The script queries all elements marked with .randomized and reads their current computed styles via window.getComputedStyle().
  4. Property Extraction: The polyfill filters all declared properties, isolating custom properties that begin with the --random prefix. Because CSS custom properties accept arbitrary valid token sequences, non-supporting browsers store the unparsed random(...) expression string safely in the computed style map.
  5. Expression Parsing & Caching: The script normalizes the expression and passes it to @csstools/css-calc. Unique identifiers generated via crypto.randomUUID() and mapped through a WeakMap ensure that random seed values adhere to the specification’s caching key logic across elements, properties, and document sessions.
  6. Property Injection: Calculated numerical values (with units attached) are written back onto the element’s inline style object via element.style.setProperty().
  7. DOM Unveil: The temporary hide-style block is detached from <head>, rendering the completed, randomized presentation to the screen.

Leveraging CSS custom properties as carrier vehicles avoids the primary historical drawbacks of polyfilling CSS. Because expressions remain valid syntax within CSS custom properties, the script eliminates the need to fetch external .css files over HTTP, run heavy full-stylesheet AST parsers, or rewrite stylesheet rules dynamically in the DOM.

Random parting thoughts

The realization of client-side CSS randomness highlights the collaborative nature of the open-source web platform. By coupling build-time specification tools with lightweight DOM inspection techniques, developers can experiment with emerging CSS Values and Units specifications long before cross-browser baseline status is formally reached.

As browser vendors continue working toward full implementation of declarative randomness, polyfill utilities offer a pragmatic bridge. They allow engineering teams to deliver dynamic, non-deterministic design features today using standards-compliant CSS patterns that seamlessly transition to native hardware acceleration as browser support expands.

Asro

Author at DesignEnt.

Flash News