{"id":7764,"date":"2025-11-18T15:23:44","date_gmt":"2025-11-18T20:23:44","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=7764"},"modified":"2025-11-18T15:23:46","modified_gmt":"2025-11-18T20:23:46","slug":"more-css-random-learning-through-experiments","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/more-css-random-learning-through-experiments\/","title":{"rendered":"More CSS random() Learning Through Experiments"},"content":{"rendered":"\n<p>The <code>random()<\/code> function in CSS <a href=\"https:\/\/webkit.org\/blog\/17285\/rolling-the-dice-with-css-random\/\">is well-specced<\/a> and <em>just so <a href=\"https:\/\/frontendmasters.com\/blog\/very-early-playing-with-random-in-css\/\">damn fun<\/a><\/em>. I had some, ahem, random ideas lately I figured I&#8217;d write up.<\/p>\n\n\n\n<p class=\"learn-more\">As I write, you can <em>only<\/em> see <code>random()<\/code> work in <a href=\"https:\/\/developer.apple.com\/safari\/technology-preview\/\">Safari Technical Preview<\/a>. I&#8217;ve mostly used videos to show the visual output, as well as linked up the demos in case you have STP. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Rotating Star Field<\/h2>\n\n\n\n<p>I was playing this game <a href=\"https:\/\/www.devolverdigital.com\/games\/ball-x-pit\">BALL x PIT<\/a> which makes use of this rotating background star field motif. See the video, snipped from one of the games promo videos. <\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player wp-block-jetpack-videopress--has-max-width\" style=\"max-width: 511px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='281' src='https:\/\/videopress.com\/embed\/SQHN1re9?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption>I like how the star field is random, but rotates around the center, and in rings where the direction reverses. <\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>My idea for attempting to reproduce it was to make a big stack of <code>&lt;div&gt;<\/code> containers where the top center of them are all in the exact center of the screen. Then apply:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>A <code>random()<\/code> height<\/li>\n\n\n\n<li>A <code>random()<\/code> rotation<\/li>\n<\/ol>\n\n\n\n<p>Then if I put the &#8220;star&#8221; at the <em>end<\/em> (bottom center) of each <code>&lt;div&gt;<\/code>, I&#8217;ll have a random star field where I can later rotate the container around the center of the screen to get the look I was after.<\/p>\n\n\n\n<p>Making a ton of divs is easy in Pug:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">- let n = 0;<br>- let numberOfStars = 1000;<br>while n &lt; numberOfStars<br>  - ++n<br>  div.starContainer<br>    div.star<\/pre>\n\n\n\n<p>Then the setup CSS is:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.starContainer<\/span> {  \n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-number\">50%<\/span>;\n  <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">50%<\/span>;\n   \n  <span class=\"hljs-attribute\">rotate<\/span>: <span class=\"hljs-built_in\">random<\/span>(<span class=\"hljs-number\">0deg<\/span>, <span class=\"hljs-number\">360deg<\/span>);\n  <span class=\"hljs-attribute\">transform-origin<\/span>: top center;\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">4px<\/span>;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span>dvh * var(--c));\n\n  &amp;:nth-child(-n+500) {\n    <span class=\"hljs-comment\">\/* Inside Stars *\/<\/span>\n    <span class=\"hljs-attribute\">--rand<\/span>: <span class=\"hljs-built_in\">random<\/span>(--distAwayFromCenter, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">35<\/span>);\n  }\n\n  &amp;<span class=\"hljs-selector-pseudo\">:nth-child(n+501)<\/span> {\n    <span class=\"hljs-comment\">\/* Outside Stars *\/<\/span>\n    <span class=\"hljs-attribute\">--rand<\/span>: <span class=\"hljs-built_in\">random<\/span>(--distAwayFromCenter2, <span class=\"hljs-number\">35<\/span>, <span class=\"hljs-number\">70<\/span>);\n  }\n\n}\n\n<span class=\"hljs-selector-class\">.star<\/span> {\n  <span class=\"hljs-attribute\">place-self<\/span>: end;\n  <span class=\"hljs-attribute\">background<\/span>: red;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span>dvh * var(--rand));\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-built_in\">random<\/span>(<span class=\"hljs-number\">2px<\/span>, <span class=\"hljs-number\">6px<\/span>);\n  <span class=\"hljs-attribute\">aspect-ratio<\/span>: <span class=\"hljs-number\">1<\/span>;\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">50%<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>If I chuck a low-opacity white border on each container so you can see how it works, we&#8217;ve got a star field going! <\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"952\" height=\"998\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.04.53-AM.png?resize=952%2C998&#038;ssl=1\" alt=\"\" class=\"wp-image-7783\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.04.53-AM.png?w=952&amp;ssl=1 952w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.04.53-AM.png?resize=286%2C300&amp;ssl=1 286w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.04.53-AM.png?resize=768%2C805&amp;ssl=1 768w\" sizes=\"auto, (max-width: 952px) 100vw, 952px\" \/><figcaption class=\"wp-element-caption\">with border on container<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"992\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.05.49-AM.png?resize=1024%2C992&#038;ssl=1\" alt=\"\" class=\"wp-image-7784\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.05.49-AM.png?resize=1024%2C992&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.05.49-AM.png?resize=300%2C291&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.05.49-AM.png?resize=768%2C744&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.05.49-AM.png?w=1082&amp;ssl=1 1082w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">border removed<\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>Then if we apply some animated rotation to those containers like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\">...\n<span class=\"hljs-selector-tag\">transform-origin<\/span>: <span class=\"hljs-selector-tag\">top<\/span> <span class=\"hljs-selector-tag\">center<\/span>;\n<span class=\"hljs-selector-tag\">animation<\/span>: <span class=\"hljs-selector-tag\">r<\/span> 20<span class=\"hljs-selector-tag\">s<\/span> <span class=\"hljs-selector-tag\">infinite<\/span> <span class=\"hljs-selector-tag\">linear<\/span>;\n\n&amp;<span class=\"hljs-selector-pseudo\">:nth-child(-n+500)<\/span> {\n  ...\n  <span class=\"hljs-attribute\">--rotation<\/span>: <span class=\"hljs-number\">360deg<\/span>;\n}\n\n&amp;<span class=\"hljs-selector-pseudo\">:nth-child(n+501)<\/span> {\n  ...\n  <span class=\"hljs-attribute\">--rotation<\/span>: -<span class=\"hljs-number\">360deg<\/span>;\n}\n\n<span class=\"hljs-keyword\">@keyframes<\/span> r {\n  100% {\n    <span class=\"hljs-attribute\">rotate<\/span>: <span class=\"hljs-built_in\">var<\/span>(--rotation);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We get the inside stars rotating one way and the outside stars going the other way:<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='415' src='https:\/\/videopress.com\/embed\/ApTvTZUz?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><a href=\"https:\/\/codepen.io\/editor\/chriscoyier\/pen\/019a795f-93a6-71a4-bc05-76795c97c161\">Demo<\/a><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>I don&#8217;t think I got it nearly as cool as the BALL x PIT design, but perhaps the foundation is there. <\/p>\n\n\n\n<p>I found this particular setup really fun to play with, as flipping on and off what CSS you apply to the stars and the containers can yield some really beautiful randomized stuff. <\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='399' src='https:\/\/videopress.com\/embed\/INYGJIIE?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>Imagine what you could do playing with colors, shadows, size transitions, etc! <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Parallax Stars<\/h2>\n\n\n\n<p>While I had the star field thing on my mind, it occurred to me to attach them to a scroll-driven animation rather than just a timed one. I figured if I selected a <em>random<\/em> selection of 1\/3 of them into three groups, I could animate them at different speeds and get a parallax thing going on. <\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='443' src='https:\/\/videopress.com\/embed\/w90zZrDu?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><a href=\"https:\/\/codepen.io\/editor\/chriscoyier\/pen\/019a82f5-7fde-72f5-90ab-d23632dd9ca6\">Demo<\/a><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>This one is maybe easier conceptually as we just make a bunch of star <code>&lt;div&gt;<\/code>s (I won&#8217;t paste the code as it&#8217;s largely the same as the Pug example above, just no containers) then place their top and left values randomly. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.star<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-built_in\">random<\/span>(<span class=\"hljs-number\">2px<\/span>, <span class=\"hljs-number\">5px<\/span>);\n  <span class=\"hljs-attribute\">aspect-ratio<\/span>: <span class=\"hljs-number\">1<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: white;\n  <span class=\"hljs-attribute\">position<\/span>: fixed;\n  <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-built_in\">calc<\/span>(random(<span class=\"hljs-number\">0<\/span>dvh, <span class=\"hljs-number\">150<\/span>dvh) - <span class=\"hljs-number\">25<\/span>dvh);\n  <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-built_in\">random<\/span>(<span class=\"hljs-number\">0<\/span>dvh, <span class=\"hljs-number\">100<\/span>dvw);\n\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0.5<\/span>;\n  &amp;:nth-child(-n + 800) {\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0.7<\/span>;\n  }\n  &amp;<span class=\"hljs-selector-pseudo\">:nth-child(-n<\/span> + 400) {\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0.6<\/span>;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then attach the stars to a scroll-driven animation off the root. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.star<\/span> {\n  ...\n\n  <span class=\"hljs-attribute\">animation<\/span>: move-y;\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: <span class=\"hljs-built_in\">scroll<\/span>(root);\n  <span class=\"hljs-attribute\">animation-composition<\/span>: accumulate;\n  <span class=\"hljs-attribute\">--move-distance<\/span>: <span class=\"hljs-number\">100px<\/span>;\n\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0.5<\/span>;\n  &amp;:nth-child(-n + 800) {\n    <span class=\"hljs-attribute\">--move-distance<\/span>: <span class=\"hljs-number\">300px<\/span>;\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0.7<\/span>;\n  }\n  &amp;<span class=\"hljs-selector-pseudo\">:nth-child(-n<\/span> + 400) {\n    <span class=\"hljs-attribute\">--move-distance<\/span>: <span class=\"hljs-number\">200px<\/span>;\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0.6<\/span>;\n  }\n}\n\n<span class=\"hljs-keyword\">@keyframes<\/span> move-y {\n  100% {\n    <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-built_in\">var<\/span>(--move-distance);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>So each group of stars either moves their <code>top<\/code> position 100px, 200px or 300px over the course of scrolling the page. <\/p>\n\n\n\n<p>The real trick here is the <code>animation-composition: accumulate;<\/code> which is saying not to animate the <code>top<\/code> position to the new value but to take the position they already have and &#8220;accumulate&#8221; the new value it was given. Leading me to think:<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-rich is-provider-bluesky-social wp-block-embed-bluesky-social\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"bluesky-embed\" data-bluesky-uri=\"at:\/\/did:plc:xhhcrzsilpamjmz4dvrpt7df\/app.bsky.feed.post\/3m5m3oad3mk2t\" data-bluesky-cid=\"bafyreibqvzybv5yceesvck5zsbxljijtsvyr5idklpn2f57knlnsxuajpy\"><p lang=\"en\">I think `animation-composition: accumulate` is gonna see more action with `random()`, as it&#39;s like &#34;take what you already got as a value and augment it rather than replace it&#34;.Here&#39;s a parallax thing where randomly-fixed-positioned stars are moved different amounts (with a scroll-driven animation)<\/p>&mdash; <a href=\"https:\/\/bsky.app\/profile\/did:plc:xhhcrzsilpamjmz4dvrpt7df?ref_src=embed\">Chris Coyier (@chriscoyier.net)<\/a> <a href=\"https:\/\/bsky.app\/profile\/did:plc:xhhcrzsilpamjmz4dvrpt7df\/post\/3m5m3oad3mk2t?ref_src=embed\">2025-11-14T16:22:46.035Z<\/a><\/blockquote><script async src=\"https:\/\/embed.bsky.app\/static\/embed.js\" charset=\"utf-8\"><\/script>\n<\/div><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Horizontal Rules of Gridded Dots<\/h2>\n\n\n\n<p>Intrigued by combining <code>random()<\/code> and different animation controlling things, I had the thought to toss <code>steps()<\/code> into the mix. Like what if a scroll-driven animation wasn&#8217;t smooth along with the scrolling, it kinda stuttered the movement of things only on a few &#8220;frames&#8221;. I considered trying to <code>round()<\/code> values at first, which is maybe still a possibility somehow, but landed on <code>steps()<\/code> instead.<\/p>\n\n\n\n<p>The idea here is a &#8220;random&#8221; grid of dots that then &#8220;step&#8221; into alignment as the page scrolls. Hopefully creating a satisfying sense of alignment when it gets there, half way through the page. <\/p>\n\n\n\n<p>Again Pug is useful for creating a bunch of repetitive elements<sup data-fn=\"f4e82bd9-57d5-40da-a1bd-aa426327e848\" class=\"fn\"><a href=\"#f4e82bd9-57d5-40da-a1bd-aa426327e848\" id=\"f4e82bd9-57d5-40da-a1bd-aa426327e848-link\">1<\/a><\/sup> (but could be JSX or whatever other templating language):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">- var numberOfCells = 100;<br>- var n = 0;<br><br>.hr(role=\"separator\")<br>  - n = 0;<br>  while n &lt; numberOfCells<br>    - ++n;<br>    .cell<\/pre>\n\n\n\n<p>We can make that <code>&lt;div class=\"hr\" role=\"seperator\"&gt;<\/code> a flex parent and then randomize some top positions of the cells to look like:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"898\" height=\"516\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.40.21-AM.png?resize=898%2C516&#038;ssl=1\" alt=\"\" class=\"wp-image-7789\" style=\"width:332px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.40.21-AM.png?w=898&amp;ssl=1 898w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.40.21-AM.png?resize=300%2C172&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-18-at-10.40.21-AM.png?resize=768%2C441&amp;ssl=1 768w\" sizes=\"auto, (max-width: 898px) 100vw, 898px\" \/><\/figure>\n<\/div>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.hr<\/span> {\n  <span class=\"hljs-attribute\">view-timeline-name<\/span>: --hr-timeline;\n  <span class=\"hljs-attribute\">view-timeline-axis<\/span>: block;\n\n  <span class=\"hljs-attribute\">display<\/span>: flex;\n  <span class=\"hljs-attribute\">gap<\/span>: <span class=\"hljs-number\">1px<\/span>;\n\n  &gt; .cell {\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">4px<\/span>;\n    <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">4px<\/span>;\n    <span class=\"hljs-attribute\">flex-shrink<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">background<\/span>: black;\n\n    <span class=\"hljs-attribute\">position<\/span>: relative;\n    <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-built_in\">calc<\/span>(random(<span class=\"hljs-number\">0px<\/span>, <span class=\"hljs-number\">60px<\/span>));\n\n    <span class=\"hljs-attribute\">animation-name<\/span>: center;\n    <span class=\"hljs-attribute\">animation-timeline<\/span>: --hr-timeline;\n    <span class=\"hljs-attribute\">animation-timing-function<\/span>: <span class=\"hljs-built_in\">steps<\/span>(<span class=\"hljs-number\">5<\/span>);\n    <span class=\"hljs-attribute\">animation-range<\/span>: entry <span class=\"hljs-number\">50%<\/span> contain <span class=\"hljs-number\">50%<\/span>;\n    <span class=\"hljs-attribute\">animation-fill-mode<\/span>: both;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Rather than using a <em>scroll<\/em> scroll-driven animation (lol) we&#8217;ll name a <code>view-timeline<\/code> meaning that each one of our separators triggers the animation based on it&#8217;s page visibility. Here, it starts when it&#8217;s at least half-visible on the bottom of the screen, and finished when it&#8217;s exactly half-way across the screen. <\/p>\n\n\n\n<p>I&#8217;ll scoot those top positions to a shared value this time, and wait until the last &#8220;frame&#8221; to change colors:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@keyframes<\/span> center {\n  99% {\n    <span class=\"hljs-attribute\">background<\/span>: black;\n  }\n  100% {\n    <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">30px<\/span>;\n    <span class=\"hljs-attribute\">background<\/span>: greenyellow;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And we get:<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player aligncenter wp-block-jetpack-videopress--has-max-width\" style=\"max-width: 347px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='695' src='https:\/\/videopress.com\/embed\/suEPOgKX?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><a href=\"https:\/\/codepen.io\/editor\/chriscoyier\/pen\/019a8385-66ea-748e-a104-d1e2ae011ed4\">Demo<\/a><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>Just playing around here. I think <code>random()<\/code> is an awfully nice addition to CSS, adding a bit of texture to the dynamic web, as it were. <\/p>\n\n\n<ol class=\"wp-block-footnotes\"><li id=\"f4e82bd9-57d5-40da-a1bd-aa426327e848\">Styling grid cells would be a sweet improvement to CSS in this case! Here where we&#8217;re creating hundreds or thousands of divs just to be styleable chunks on a grid, that&#8217;s a lot of extra DOM weight that is really just content-free decoration. <a href=\"#f4e82bd9-57d5-40da-a1bd-aa426327e848-link\" aria-label=\"Jump to footnote reference 1\">\u21a9\ufe0e<\/a><\/li><\/ol>\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>There is quite a bit of interesting design possibility with `random()` coming to CSS. It pairs nicely with animation, particularly animation-composition for agumenting those generated values.<\/p>\n","protected":false},"author":1,"featured_media":7799,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"[{\"content\":\"Styling grid cells would be a sweet improvement to CSS in this case! Here where we're creating hundreds or thousands of divs just to be styleable chunks on a grid, that's a lot of extra DOM weight that is really just content-free decoration.\",\"id\":\"f4e82bd9-57d5-40da-a1bd-aa426327e848\"}]"},"categories":[1],"tags":[100,7,388,57],"class_list":["post-7764","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-animation","tag-css","tag-random","tag-scroll-driven-animations"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/random.jpg?fit=2000%2C1144&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7764","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=7764"}],"version-history":[{"count":15,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7764\/revisions"}],"predecessor-version":[{"id":7801,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7764\/revisions\/7801"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/7799"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=7764"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=7764"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=7764"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}