Exploring the CSS random() Function
CSS has always leaned on JavaScript when randomness was needed — scattered positions, varied animation delays, unpredictable color shifts. That dependency may soon shrink. The CSS random() function, part of the CSS Values and Units Module Level 5 specification, lets stylesheets generate random numeric values directly, no scripting required.
It’s experimental. Browser support is limited. But it’s worth understanding now.
Key Takeaways
- The CSS
random()function generates random numeric values natively in stylesheets, removing the need for JavaScript in many visual randomness scenarios. - The syntax accepts a minimum, maximum, and optional step value — all arguments must share the same unit type.
- A caching system controls whether random values are shared across matching elements, per element, or across specific properties, giving you fine-grained control over randomness distribution.
- Browser support is currently limited, with stable support now available in Safari 26.2. Always provide fallbacks using
@supports.
What the CSS random() Function Actually Does
The core idea is straightforward: you define a minimum value, a maximum value, and optionally a step increment. CSS picks a number from that range and applies it.
.card {
top: random(5rem, 20rem); /* any value between 5rem and 20rem */
rotate: random(0deg, 360deg); /* random rotation */
animation-delay: random(0s, 3s); /* staggered animation timing */
}
That’s it. No Math.random(), no inline styles, no JavaScript loops generating hundreds of :nth-child() rules.
One important constraint: all arguments must share the same unit type. You can’t mix rem with px or % with em. Pick a unit and stay consistent.
The Syntax
random(<caching-options>?, <min>, <max>, [by <step>]?)
The current draft specification defines the function more formally, but this simplified form is the easiest way to understand it in practice.
The by parameter controls stepping. Without it, you may get decimal values like 13.47px. With it, you constrain the output to a predictable sequence:
/* Possible values: 10px, 20px, 30px, 40px, 50px */
padding: random(10px, 50px, by 10px);
Note: the maximum value isn’t always reachable when using steps. random(100px, 200px, by 30px) can only produce 100px, 130px, 160px, or 190px — never 200px.
Controlling How Randomness Is Shared
This is where the CSS random() function gets genuinely interesting. By default, each random() instance in a stylesheet resolves to a single cached value shared by all elements using that style.
To give each element its own unique value, use the per-element keyword or a dashed ident:
/* Each element gets its own random value */
.item {
top: random(per-element, 2rem, 15rem);
}
/* Both properties share the same value within one element */
.box {
width: random(--size, 100px, 200px);
height: random(--size, 100px, 200px); /* matches width */
}
/* All matched elements share the same named value globally */
.badge {
width: random(--element-shared, 50px, 150px);
}
The per-element keyword is a built-in caching option that tells the browser to resolve a distinct random value for each element that matches the selector. A dashed ident like --size ties multiple random() calls together so they resolve to the same value within a given element — useful when you want a square with a randomized but consistent width and height. A dashed ident like --element-shared can also act as a named cache key across matching elements.
This caching system is deliberate and well-designed — but it’s also where confusion tends to creep in. Understanding it early saves debugging time later.
Discover how at OpenReplay.com.
CSS random() vs. SCSS random()
If you’ve used random() in Sass, the behavior differs in a few key ways:
| Feature | CSS random() | SCSS random() |
|---|---|---|
| When it runs | Page load | Compile time |
| Min/max range | Both defined | Max only (starts from 1) |
| Step support | Yes (by) | No |
| Refreshes on reload | Yes | No |
CSS random() generates a new value on every page load. SCSS locks the value in at build time. They serve different purposes.
Browser Support
As of early 2026, random() in CSS has shipped in Safari 26.2. Broader cross-browser support is not yet guaranteed, so you should still treat it as an experimental feature and use @supports for progressive enhancement with a sensible fallback:
.element {
top: 10rem; /* fallback */
}
@supports (top: random(1rem, 5rem)) {
.element {
top: random(5rem, 20rem);
}
}
The CSS Working Group adopted the function in 2022, and the specification continues to evolve. Open issues remain, and the final syntax could still shift before broad implementation.
Conclusion
The CSS random() function won’t replace JavaScript for logic-driven randomness or anything requiring cryptographic unpredictability. But for purely visual variation — scattered layouts, organic animation timing, generative backgrounds — it’s a clean, declarative solution that belongs in the stylesheet.
Try it in Safari 26.2, keep fallbacks in place, and watch the spec progress. The gap between experimental and broadly available is closing.
FAQs
Not reliably across the board. As of early 2026, Safari 26.2 ships the feature, but broader cross-browser support is still limited. You can experiment with it now, but any production use should include a solid fallback value and an @supports check to avoid broken layouts in unsupported browsers.
The function will be invalid and the declaration will be ignored by the browser. All arguments passed to random(), including min, max, and the optional step value, must use the same unit type. You cannot combine rem with px or percentage with em. Choose one unit and use it consistently across all parameters.
By default, a random() instance resolves to a cached value shared by all elements using that style. To get a unique value per element, use the per-element keyword. To share a value across specific properties, use a dashed ident like --size. Understanding caching is essential to getting the visual variation you expect.
Yes. Unlike SCSS random(), which locks in a value at compile time and bakes it into the output CSS, the native CSS random() function resolves a fresh value each time the page loads. This makes it suitable for creating visual variety that changes between visits without any JavaScript involvement.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.