Skip to main content

Bridging the Web’s Randomness Gap: How a New Polyfill Brings CSS random() to All Browsers

In his book on moral philosophy, How to Be Perfect, Mike Schur—creator of the hit television series The Good Place—devotes a chapter titled “The Luck of the Draw” to the myth of meritocracy. Schur explores how individuals consistently underestimate the profound role that pure chance plays in shaping their lives. That philosophical observation mirrors a growing trend across modern software development: the embrace of non-determinism, controlled chaos, and probabilistic design in digital interfaces.

From subtle interface flux to generative user interfaces—such as Google’s planned deployment of generative UI in search results—web designers and engineers are increasingly viewing web pages not as static documents, but as dynamic environments. Much like the ancient Heraclitean adage that one cannot step into the same river twice, contemporary interfaces are experimenting with subtle variations each time a user lands on a page.

However, implementing presentational randomness has traditionally required heavy reliance on JavaScript, introducing unnecessary complexity to what should be purely aesthetic decisions. The recent introduction of native randomness in Cascading Style Sheets (CSS) marks a major shift in how browsers handle unpredictable design elements, even as browser fragmentation creates new hurdles for developers.


Real-World Use Cases for Randomness

The demand for controlled randomness on the web is far from theoretical. In commercial web development and consulting, project brief requirements frequently call for stochastic visuals to create delight, engagement, or delight-driven interaction.

A common example is the celebratory particle burst—the classic confetti effect triggered when a user completes an action, such as executing a randomized draw software configuration. While conceptually simple, achieving brand-compliant randomness in enterprise settings often presents unexpected technical friction. Development teams frequently start with existing JavaScript libraries, only to discover that client branding guidelines require precise control over particle sizing, color palettes, trajectory speeds, and element distribution.

When off-the-shelf plugins fail to accommodate hyper-specific design requirements, engineering teams are often forced to write custom JavaScript implementations. This dynamic highlights an ongoing structural tension in user experience design: the delicate balance between presentational unpredictability and precise engineering control.

This tension raises a fundamental question for modern web architecture: why should web developers rely on imperative JavaScript execution merely to manage presentational randomness that belongs squarely in the styling layer?


The Push for Native Randomness in CSS

The evolution of web standards has long been guided by the philosophy of "paving the cowpaths"—the practice of identifying common design patterns implemented through complex script workarounds and standardizing them into declarative HTML and CSS specifications.

The W3C Technical Architecture Group’s Rule of Least Power further supports this approach, recommending that web features be implemented using the least powerful language capable of expressing them. Because presentation is the domain of CSS, delegating randomized layout parameters to declarative style sheets aligns directly with established architectural best practices.

In late 2025, Apple’s Safari became the first major browser to ship support for the official CSS random() specification as part of its Safari release. The feature was introduced with a clear objective: allowing developers to handle ubiquitous UI patterns directly through native HTML and CSS, drastically reducing reliance on external JavaScript frameworks and build-step dependencies.

Following Safari’s release, developers across the web community showcased what native CSS randomness makes possible. Web developer Schalk Neethling demonstrated how declarative CSS random() functions provide fine-grained control over particle systems like confetti. Frontend author Alvaro Montoro argued that CSS is inherently the most suitable layer for presentational variance, emphasizing that moving presentational logic out of JavaScript improves performance and architectural cleanliness.

Despite these advancements, widespread adoption faces a significant cross-browser compatibility bottleneck. Months after Safari introduced native CSS random() support, alternative browser engines have yet to ship the feature to production. While issue trackers for both Google Chrome (Chromium) and Mozilla Firefox demonstrate active development, neither engine has committed to a definitive timeline for stable cross-browser baseline support, leaving developers unable to rely on native CSS randomness outside the Apple ecosystem.


Polyfilling CSS random() across Engines

This stark disparity in engine support creates a rare inversion of historic web development norms. Developers have grown accustomed to experimenting with cutting-edge CSS features first in Chromium-based browsers, often leaving WebKit and Safari trailing behind. With CSS random(), the reverse is true: the feature functions natively within WebKit, while users on Windows, Android, and Linux desktop environments remain locked out.

Furthermore, because Safari updates are tied to macOS and iOS system updates, even users within the Apple ecosystem may lack support if they remain on older operating system releases.

To bridge this engine gap, frontend developer Lee Meyer created css-random-polyfill, an open-source library designed to bring functional CSS random() capabilities to non-supported browsers today.

Polyfilling CSS features is notoriously fraught with performance and architectural risks. Unlike JavaScript polyfills—which can easily extend global prototypes—CSS polyfills often require fetching external stylesheets, parsing non-standard syntax at runtime, or injecting modified DOM structures. However, because the CSS random() specification uses valid CSS functional notation, it is possible to implement a polyfill strategy that avoids destructive stylesheet re-parsing.

By leveraging custom CSS properties prefixed with --random, developers can store randomized variables in syntactically valid CSS that all modern browser parsers accept without throwing syntax errors.


Practical Applications and Demos

To test the viability of cross-browser CSS randomness, several prominent WebKit visual demos were adapted using the css-random-polyfill library, demonstrating how declarative styling can execute complex, unpredictable visual layouts across diverse browser engines.

Random Starfield

The original WebKit demonstration of CSS random() featured a twinkling starfield: a dark background populated by hundreds of randomly scattered star elements fading in and out at unpredictable intervals. Larger four-pointed stars tilt at uniformly shared, randomly chosen angles, while each star casts a soft, randomly hued drop-shadow.

To adapt this WebKit-only implementation for universal browser support, the HTML markup includes a marker class—randomized—allowing the lightweight script to identify elements requiring evaluation:

<!-- Script processes custom CSS random properties on load -->
<script src="https://unpkg.com/css-random-polyfill@latest/dist/css-random-polyfill.js"></script>

<!-- Star elements marked for runtime evaluation -->
<div class="randomized star"></div>
<div class="randomized star"></div>
<div class="randomized star fourpointed"></div>
<div class="randomized star fourpointed"></div>

The underlying CSS uses intermediate --random prefixed custom properties to define values for size, position, color hues, animation speeds, and stagger delays:

.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) infinite;

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

This pattern demonstrates key aspects of the CSS Values and Units Module Level 5 specification, including the optional third parameter for step intervals (1px), ensuring that calculated values resolve cleanly to whole-number increments within a defined range. Furthermore, using the element-shared base value ensures that related elements—such as four-pointed star rotations—share calculated random parameters across instances:

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

Importantly, code written using this custom property convention remains fully forward-compatible. When a browser gains native support for CSS random(), the polyfill script bypasses execution completely, handing full rendering control over to the underlying browser engine.

Random Colored Grid Cells

A second implementation explores multi-variable mathematical evaluation inside complex CSS layout modules, such as CSS Grid. In a grid layout featuring thousands of distinct child elements, inline mathematical calls can randomly assign grid area constraints across dynamic rows and columns:

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

This structural test proves that custom CSS variables can successfully resolve multiple embedded random() function calls within a single shorthand layout property, resolving dynamic grid parameters dynamically at runtime.

Interactive Wheel of Fortune

A demonstration originally authored by WebKit engineer Tim Nguyen illustrates how native CSS randomness handles mixed measurement units and typed arithmetic. In an interactive spinning wheel element, the final rotation target mixes turns and angular degrees within the same function call:

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


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

Under the W3C specification, mixing unit types such as turn and deg is explicitly valid provided both parameters resolve to the same fundamental dimension type—in this case, angle measurements. CSS typed arithmetic processes these mixed units seamlessly, allowing developers to define flexible, multi-unit constraints.

Random Squares and Custom Value Sharing

Frontend developer Chris Coyier introduced a simplified baseline test featuring randomly positioned squares with variable colors. Expanding on that concept, the implementation can be modified to test random value sharing across distinct CSS properties using custom identifiers:

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

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

By referencing a named identifier (--side), the engine guarantees that both the height and width properties evaluate to identical values, preserving perfectly square aspect ratios while still maintaining variable dimensions across different DOM instances.


Simulating random-item() with Custom CSS Functions

While numerical randomization solves many spatial and sizing requirements, web designers frequently need to choose randomly from discrete lists of non-numeric values, such as specific brand color swatches or font families.

The editor’s draft of the CSS Values and Units Module Level 5 outlines a proposed random-item() function specifically designed for non-numeric lists:

/* Proposed W3C syntax for discrete value lists */
color: random-item(element-shared, red, blue, green);

While random-item() remains largely unimplemented in stable browsers, developers experimenting in Chromium can combine emergent CSS specifications—specifically CSS Custom Functions (@function) and inline conditional logic (if())—to simulate list-based selection logic entirely within CSS.

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

To support this lookup mechanism, a generic custom CSS function can map numeric selection indices directly to variable argument lists:

@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 JavaScript hacks or color-specific workarounds, this functional approach leverages upcoming native CSS constructs, allowing developers to execute arbitrary data-type selections without leaving the stylesheet layer.


Architectural Mechanics of the Polyfill

The underlying execution of css-random-polyfill illustrates how modern tools can extend CSS features without introducing invasive stylesheet re-parsing.

Instead of writing a custom CSS engine from scratch, the polyfill builds upon low-level evaluation utilities from the open-source CSSTools ecosystem—specifically @csstools/css-calc. Originally developed for build-time PostCSS processing, these parsing utilities were adapted to run client-side within browser runtime environments.

When an unsupported browser loads the page, the polyfill script executes a clear sequence of operations:

First, the library checks engine feature support using native feature queries via CSS.supports("width", "random(0px, 100px)"). If the browser natively understands the random() syntax, execution halts immediately, permitting the engine to render the styles natively.

If feature support is missing, the polyfill temporarily hides elements marked with the .randomized target class to prevent Flash of Unstyled Content (FOUC). It then generates unique document-level and element-level identifiers using crypto.randomUUID() to maintain consistent keying semantics.

Next, the script queries the DOM for target elements and reads their raw inline custom properties through the browser’s standard getComputedStyle() API. Custom property strings beginning with --random are extracted and passed directly into the @csstools/css-calc resolution engine.

Finally, the evaluated string outputs—with resolved numerical ranges, step values, and unit identifiers—are injected back onto the target element’s inline style object via element.style.setProperty(), instantly revealing the fully styled, randomized elements to the layout engine.

By restricting evaluation strictly to standard CSS custom properties, the polyfill establishes a clean extension pattern for emerging CSS capabilities. Developers gain immediate access to next-generation layout utilities today, while maintaining a clear upgrade path toward native browser performance as baseline engine support expands across the industry.

Lina Hope

Author at DesignEnt.

Flash News