{"id":6567,"date":"2025-08-06T10:08:24","date_gmt":"2025-08-06T15:08:24","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=6567"},"modified":"2025-08-12T10:48:52","modified_gmt":"2025-08-12T15:48:52","slug":"count-auto-fill-columns","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/count-auto-fill-columns\/","title":{"rendered":"Get the number of auto-fit\/auto-fill columns in CSS"},"content":{"rendered":"\n<p><a href=\"https:\/\/gist.github.com\/thebabydino\/645471b8aa19fb1164e512962b6c4f0f\/raw\/d4ad52fef89cfaa2207882f7a6078a9779a2de34\/number-of-auto-fit-columns.md\"><\/a>Ever wanted to get the number of <code>auto-fit<\/code>\/<code>auto-fill<\/code> columns in a grid? For example, because you want to highlight just the items in the first or last row or column? Do something special just for even or for odd rows or columns (e.g. zebra striping)? Or for any one specific row or column? Create responsive non-rectangular grids? And all of this with zero breakpoints?<\/p>\n\n\n\n<p>This is all doable with pure CSS by using container query units, CSS variables, and CSS mathematical functions! Of course, it also involves navigating browser bugs and support gaps. But at the end of the day, it is possible to do it cross-browser!<\/p>\n\n\n\n<p>Let&#8217;s see how!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-basic-idea\">The Basic Idea<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"setup\">Setup<\/h3>\n\n\n\n<p>We start with a <code>.grid<\/code> with a lot of items, let&#8217;s say <code>100<\/code>. I normally prefer to generate them in a loop using a preprocessor to avoid clutter in the HTML and to make it easy to change their number, but it&#8217;s also possible to do so using Emmet. For the demos illustrating the concept here, we&#8217;re using <a href=\"https:\/\/pugjs.org\/api\/getting-started.html\">Pug<\/a>, and also numbering our items via their text content:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">.grid<br>  - for(let i = 0; i &lt; 100; i++)<br>    .item #{i + 1}<\/pre>\n\n\n\n<p>Our <code>.grid<\/code> has <code>auto-fit<\/code> columns:<\/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\">.grid<\/span> {\n  <span class=\"hljs-attribute\">--u<\/span>: <span class=\"hljs-number\">7em<\/span>;\n\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(auto-fit, var(--u));\n  <span class=\"hljs-attribute\">container-type<\/span>: inline-size\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>This means our <code>.grid<\/code> has as many columns of unit width <code>u<\/code> as can fit within its own <code>content-box<\/code> width. This width is flexible and is given by the page layout, we don&#8217;t know it. However, its children (the <code>.item<\/code> elements) can know it as <code>100cqw<\/code> in container query units. To have these container units available for the <code>.grid<\/code> element&#8217;s children (and pseudos), we&#8217;ve made the <code>.grid<\/code> an inline container.<\/p>\n\n\n\n<p>This <em>should<\/em> work just fine. And it does, in both Chrome and Firefox. However, if we try it out in Safari, we see our <code>.grid<\/code> is collapsed into a point. Unfortunately, in Safari, <code>auto-fit<\/code> grids break if they are also containers. (Note: this <a href=\"https:\/\/bugs.webkit.org\/show_bug.cgi?id=282326\">Safari bug is actually fixed<\/a>, it&#8217;s just waiting to make its way to a stable release.)<\/p>\n\n\n\n<p>We have two options in this case.<\/p>\n\n\n\n<p>The first would be to replace <code>auto-fit<\/code> with <code>auto-fill<\/code>. When we have as many items as we do in this case, we can use either of them, the difference between them is only noticeable <a href=\"https:\/\/codepen.io\/thebabydino\/full\/gOayvpb\">when we don&#8217;t even have enough items to fill one row<\/a>.<\/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\"><span class=\"hljs-selector-class\">.grid<\/span> {\n  <span class=\"hljs-attribute\">--u<\/span>: <span class=\"hljs-number\">7em<\/span>;\n\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(auto-fill, var(--u));\n  <span class=\"hljs-attribute\">container-type<\/span>: inline-size\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>The second would be to put the <code>.grid<\/code> inside a wrapper <code>.wrap<\/code> element and move the <code>container<\/code> property on the wrapper.<\/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\">.wrap<\/span> { <span class=\"hljs-attribute\">container-type<\/span>: inline-size }\n\n<span class=\"hljs-selector-class\">.grid<\/span> {\n  <span class=\"hljs-attribute\">--u<\/span>: <span class=\"hljs-number\">7em<\/span>;\n\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(auto-fit, var(--u))\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>We&#8217;re going for the first option here.<\/p>\n\n\n\n<p>Now we&#8217;re getting to the interesting part!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"getting-the-number-of-columns\">Getting the number of columns<\/h3>\n\n\n\n<p>In theory, we could get the number <code>n<\/code> of columns on the <code>.item<\/code> children of the <code>.grid<\/code> via division, whose result we round down (if the container width of <code>100cqw<\/code> is 2.23 times the unit width <code>u<\/code> of a column, then we round down this ratio to get the number of columns we can fit, which is 2 in this case):<\/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-tag\">--n<\/span>: <span class=\"hljs-selector-tag\">round<\/span>(<span class=\"hljs-selector-tag\">down<\/span>, 100<span class=\"hljs-selector-tag\">cqw<\/span>\/<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--u<\/span>))<\/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>In practice, while this <em>should<\/em> work, it only works in Safari (since <a href=\"https:\/\/webkit.org\/blog\/15860\/release-notes-for-safari-technology-preview-203\/\">Sept 2024<\/a>) and in Chrome (since <a href=\"https:\/\/issues.chromium.org\/issues\/40768696\">June 2025<\/a>), where we can test it out by displaying it using <a href=\"https:\/\/stackoverflow.com\/a\/40179718\/1397351\">the counter hack<\/a>:<\/p>\n\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\">.grid<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">--n<\/span>: <span class=\"hljs-built_in\">round<\/span>(down, <span class=\"hljs-number\">100<\/span>cqw\/var(--u));\n\n  <span class=\"hljs-attribute\">counter-reset<\/span>: n <span class=\"hljs-built_in\">var<\/span>(--n);\n  <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-built_in\">counter<\/span>(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>We&#8217;ve wrapped this inside a <code>@supports<\/code> block so we have a message that lets us know about this failing in non-supporting browsers (basically Firefox), where we see the following:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458856822-01b5ed8e-2dc6-42d4-86e4-a0bf73c184fe.png?resize=1024%2C512&#038;ssl=1\" alt=\"base_idea_fallback\" class=\"wp-image-6570\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458856822-01b5ed8e-2dc6-42d4-86e4-a0bf73c184fe.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458856822-01b5ed8e-2dc6-42d4-86e4-a0bf73c184fe.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458856822-01b5ed8e-2dc6-42d4-86e4-a0bf73c184fe.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458856822-01b5ed8e-2dc6-42d4-86e4-a0bf73c184fe.png?w=1080&amp;ssl=1 1080w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">the result in non-supporting browsers: no number of columns can be computed<\/figcaption><\/figure>\n\n\n\n<p>In Safari and Chrome, things look like in the recording below:<\/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='250' src='https:\/\/videopress.com\/embed\/fYq3ydvn?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>We can see we have a problem when we have one column and it overflows the parent: the ratio between the parent <code>.grid<\/code> width of <code>100cqw<\/code> and the column unit width <code>u<\/code> drops below <code>1<\/code>, so we can fit one item <code>0<\/code> times inside the <code>content-box<\/code> width of the <code>.grid<\/code>. And this is reflected in the <code>n<\/code> value, even though, in practice, we cannot have a grid with less than one column. However, the fix for this is simple: use a <code>max()<\/code> function to make sure <code>n<\/code> is always at least <code>1<\/code>.<\/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-selector-tag\">--n<\/span>: <span class=\"hljs-selector-tag\">max<\/span>(1, <span class=\"hljs-selector-tag\">round<\/span>(<span class=\"hljs-selector-tag\">down<\/span>, 100<span class=\"hljs-selector-tag\">cqw<\/span>\/<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--u<\/span>)))<\/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>Whenever the division result drops below <code>1<\/code>, the result of the <code>max()<\/code> function isn&#8217;t the <code>round()<\/code> value anymore, but <code>1<\/code> instead.<\/p>\n\n\n\n<p>You can see it in action in demo below, but keep in mind it can only compute the number of columns in supporting browsers (Safari\/Chrome):<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_zxGyRZX\/5c1e69c59a2360f74886b2774e7079c9\" src=\"\/\/codepen.io\/anon\/embed\/zxGyRZX\/5c1e69c59a2360f74886b2774e7079c9?height=450&amp;theme-id=1&amp;slug-hash=zxGyRZX\/5c1e69c59a2360f74886b2774e7079c9&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed zxGyRZX\/5c1e69c59a2360f74886b2774e7079c9\" title=\"CodePen Embed zxGyRZX\/5c1e69c59a2360f74886b2774e7079c9\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Great, but what Firefox? The <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1827404\">Firefox bug<\/a> looks like it&#8217;s dormant, so we cannot get the ratio between two length values there.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"extending-support\">Extending support<\/h3>\n\n\n\n<p>However, we have <a href=\"https:\/\/dev.to\/janeori\/css-type-casting-to-numeric-tanatan2-scalars-582j\">a clever hack<\/a> to solve the problem!<\/p>\n\n\n\n<p>The idea behind is the following: the tangent of an acute angle in a right triangle is the ratio between the length of the cathetus opposing the angle and the length of the cathetus adjacent to it. So basically, the tangent is a ratio between two length values and <em>such a ratio is precisely what we need<\/em>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"527\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458914425-90bc6a99-75b9-48c9-ab0d-3e7b48358589.png?resize=1024%2C527&#038;ssl=1\" alt=\"A diagram illustrating basic trigonometry, featuring the tangent function, labeled 'tan(a) = opposing \/ adjacent', with definitions for 'opposing cathetus' and 'adjacent cathetus', and an angle 'a' represented in a right triangle.\" class=\"wp-image-6573\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458914425-90bc6a99-75b9-48c9-ab0d-3e7b48358589.png?resize=1024%2C527&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458914425-90bc6a99-75b9-48c9-ab0d-3e7b48358589.png?resize=300%2C154&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458914425-90bc6a99-75b9-48c9-ab0d-3e7b48358589.png?resize=768%2C395&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458914425-90bc6a99-75b9-48c9-ab0d-3e7b48358589.png?w=1080&amp;ssl=1 1080w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\"><em>basic trigonometry recap<\/em><\/figcaption><\/figure>\n\n\n\n<p>Now you may be wondering what right triangle and what angle do we even have here. Well, we can imagine building a triangle where a cathetus has the same length as the <code>.grid<\/code> parent&#8217;s <code>content-box<\/code> width (<code>100cqw<\/code> on the <code>.item<\/code> elements, which we&#8217;ll call <code>w<\/code>) and the other has the same length as the column unit width (<code>u<\/code>).<\/p>\n\n\n\n<p>The tangent of the angle opposing the cathetus of length <code>w<\/code> is the ratio between <code>w<\/code> and <code>u<\/code>. Okay, but what is this angle?<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458929259-01f2a7cf-dd36-4c3a-9230-d0f0e1dcd6ba.png?resize=1024%2C512&#038;ssl=1\" alt=\"A mathematical diagram illustrating the relationship between the tangent function, the angle 'a', and the lengths 'w' and 'u' in a right triangle.\" class=\"wp-image-6574\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458929259-01f2a7cf-dd36-4c3a-9230-d0f0e1dcd6ba.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458929259-01f2a7cf-dd36-4c3a-9230-d0f0e1dcd6ba.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458929259-01f2a7cf-dd36-4c3a-9230-d0f0e1dcd6ba.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458929259-01f2a7cf-dd36-4c3a-9230-d0f0e1dcd6ba.png?w=1080&amp;ssl=1 1080w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">using trigonometric functions to get around browser support gaps<\/figcaption><\/figure>\n\n\n\n<p>We can get this angle using the <code>atan2()<\/code> function, which takes two arguments, the length of the opposing cathetus <code>w<\/code> and the length of the adjacent cathetus <code>u<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">--a<\/span>: <span class=\"hljs-selector-tag\">atan2<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--w<\/span>), <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--u<\/span>))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><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>Having the angle <code>a<\/code> and knowing that the ratio <code>f<\/code> between <code>w<\/code> and <code>u<\/code> is the tangent of this angle, we can write:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">--f<\/span>: <span class=\"hljs-selector-tag\">tan<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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>Or, replacing the angle in the formula:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">--f<\/span>: <span class=\"hljs-selector-tag\">tan<\/span>(<span class=\"hljs-selector-tag\">atan2<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--w<\/span>), <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--u<\/span>)))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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>In general, know that a length ratio like <code>w\/u<\/code> can always be computed as <code>tan(atan2(w, u))<\/code>.<\/p>\n\n\n\n<p>Rounding down this ratio <code>f<\/code> gives us the number of columns of unit width <code>u<\/code> that fit within the <code>.grid<\/code> parent&#8217;s <code>content-box<\/code> width <code>w<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">--n<\/span>: <span class=\"hljs-selector-tag\">round<\/span>(<span class=\"hljs-selector-tag\">down<\/span>, <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--f<\/span>))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><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 we can write it all as follows, introducing also the correction that the number of columns needs to be at least <code>1<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">--f<\/span>: <span class=\"hljs-selector-tag\">tan<\/span>(<span class=\"hljs-selector-tag\">atan2<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--w<\/span>), <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--u<\/span>)));\n<span class=\"hljs-selector-tag\">--n<\/span>: <span class=\"hljs-selector-tag\">max<\/span>(1, <span class=\"hljs-selector-tag\">round<\/span>(<span class=\"hljs-selector-tag\">down<\/span>, <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--f<\/span>)))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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>That&#8217;s it, that&#8217;s the formula for <code>--n<\/code> in the case when we don&#8217;t have support for getting the ratio of two length values! There is one catch, though: both <code>--w<\/code> and <code>--u<\/code> have to be registered as lengths in order for <code>atan2()<\/code> <a href=\"https:\/\/bsky.app\/profile\/anatudor.bsky.social\/post\/3kxmt5qlbbi2e\">to work properly<\/a>!<\/p>\n\n\n\n<p>Putting it all together, the relevant code for our demo looks as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.grid<\/span> {\n  <span class=\"hljs-attribute\">--u<\/span>: <span class=\"hljs-number\">7em<\/span>;\n\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(auto-fill, var(--u));\n  <span class=\"hljs-attribute\">container-type<\/span>: inline-size\n}\n\n<span class=\"hljs-selector-class\">.grid<\/span><span class=\"hljs-selector-pseudo\">::before<\/span>, <span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--w<\/span>: <span class=\"hljs-number\">100<\/span>cqw;\n  <span class=\"hljs-attribute\">--f<\/span>: <span class=\"hljs-built_in\">var<\/span>(--w)\/<span class=\"hljs-built_in\">var<\/span>(--u);\n  <span class=\"hljs-attribute\">--n<\/span>: <span class=\"hljs-built_in\">max<\/span>(<span class=\"hljs-number\">1<\/span>, round(down, var(--f)));\n}\n\n<span class=\"hljs-keyword\">@supports<\/span> <span class=\"hljs-keyword\">not<\/span> (<span class=\"hljs-attribute\">scale:<\/span> calc(<span class=\"hljs-number\">100<\/span>cqh\/<span class=\"hljs-number\">3<\/span>lh)) {\n  <span class=\"hljs-keyword\">@property<\/span> --w {\n    <span class=\"hljs-selector-tag\">syntax<\/span>: '&lt;<span class=\"hljs-selector-tag\">length-percentage<\/span>&gt;';\n    <span class=\"hljs-selector-tag\">initial-value<\/span>: 0<span class=\"hljs-selector-tag\">px<\/span>;\n    <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>\n  }\n\n  <span class=\"hljs-keyword\">@property<\/span> --u {\n    <span class=\"hljs-selector-tag\">syntax<\/span>: '&lt;<span class=\"hljs-selector-tag\">length-percentage<\/span>&gt;';\n    <span class=\"hljs-selector-tag\">initial-value<\/span>: 0<span class=\"hljs-selector-tag\">px<\/span>;\n    <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>\n  }\n\t\n  <span class=\"hljs-selector-class\">.grid<\/span><span class=\"hljs-selector-pseudo\">::before<\/span>, <span class=\"hljs-selector-class\">.item<\/span> { <span class=\"hljs-attribute\">--f<\/span>: <span class=\"hljs-built_in\">tan<\/span>(atan2(var(--w), <span class=\"hljs-built_in\">var<\/span>(--u))) }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><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>Note that the <code>.grid<\/code> pseudo is only needed to display the <code>--n<\/code> value (using the counter hack) for us to see in the demo without having to register it and then look for it in DevTools (which is the tactic I most commonly use to check the computed value of a CSS variable).<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_MYwZzrY\/f81318b4731413045ea4dae83844ab72\" src=\"\/\/codepen.io\/anon\/embed\/MYwZzrY\/f81318b4731413045ea4dae83844ab72?height=450&amp;theme-id=1&amp;slug-hash=MYwZzrY\/f81318b4731413045ea4dae83844ab72&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed MYwZzrY\/f81318b4731413045ea4dae83844ab72\" title=\"CodePen Embed MYwZzrY\/f81318b4731413045ea4dae83844ab72\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Almost there, but not exactly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"fixing-tiny-issues\">Fixing tiny issues<\/h3>\n\n\n\n<p>If you&#8217;ve played with resizing the demo above, you may have noticed something is off in Firefox at times. At certain points when the <code>.grid<\/code> element&#8217;s <code>content-box<\/code> width <code>w<\/code> is a multiple of the unit column width <code>u<\/code>, for example, when <code>w<\/code> computes to <code>1008px<\/code> and the unit column with <code>u<\/code> of <code>112px<\/code> fits inside it exactly <code>9<\/code> times, Firefox somehow computes the number of columns as being smaller (<code>8<\/code> instead of <code>9<\/code>, in this example).<\/p>\n\n\n\n<p>My first guess was this is probably due to some rounding errors in getting the angle via <code>atan2()<\/code> and then going back from an angle to a ratio using <code>tan()<\/code>. Indeed, if we register <code>--f<\/code> so we can see its value in DevTools, it&#8217;s displayed as <code>8.99999<\/code> in this case, even though <code>1008px\/112px<\/code> is exactly <code>9<\/code>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"689\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458947888-cb10b290-d1e2-4ea6-b918-64732facca70.png?resize=1024%2C689&#038;ssl=1\" alt=\"Screenshot of browser developer tools showing a grid layout with items numbered from 1 to 18. The grid has a header indicating the number of auto-fill columns, which is 8. The inspector highlights CSS variables related to grid sizing.\" class=\"wp-image-6578\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458947888-cb10b290-d1e2-4ea6-b918-64732facca70.png?resize=1024%2C689&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458947888-cb10b290-d1e2-4ea6-b918-64732facca70.png?resize=300%2C202&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458947888-cb10b290-d1e2-4ea6-b918-64732facca70.png?resize=768%2C517&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/458947888-cb10b290-d1e2-4ea6-b918-64732facca70.png?w=1082&amp;ssl=1 1082w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">rounding error caught by Firefox DevTools<\/figcaption><\/figure>\n\n\n\n<p>So this means rounding down <code>f<\/code> results in the number of columns <code>n<\/code> being computed as <code>8<\/code>, even though it&#8217;s actually <code>9<\/code>. Hmm, in this case, it might be better to round <code>f<\/code> to a tiny precision of <code>.00001<\/code> <em>before<\/em> rounding it down to get the number of columns <code>n<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">--f<\/span>: <span class=\"hljs-selector-tag\">round<\/span>(<span class=\"hljs-selector-tag\">tan<\/span>(<span class=\"hljs-selector-tag\">atan2<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--w<\/span>), <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--u<\/span>))), <span class=\"hljs-selector-class\">.00001<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><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>This seems to get the job done.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ZYGVwLM\/16914110ac5371e885607d30dc37a5a9\" src=\"\/\/codepen.io\/anon\/embed\/ZYGVwLM\/16914110ac5371e885607d30dc37a5a9?height=450&amp;theme-id=1&amp;slug-hash=ZYGVwLM\/16914110ac5371e885607d30dc37a5a9&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ZYGVwLM\/16914110ac5371e885607d30dc37a5a9\" title=\"CodePen Embed ZYGVwLM\/16914110ac5371e885607d30dc37a5a9\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Still, I&#8217;m a bit worried this still might fail in certain scenarios, even though I&#8217;ve kept resizing obsessively in Firefox and haven&#8217;t encountered any problems after rounding <code>f<\/code>.<\/p>\n\n\n\n<p>So let&#8217;s make sure we&#8217;re on the safe side and place the <code>.grid<\/code> in a wrapper <code>.wrap<\/code>, make this wrapper the <code>container<\/code>, compute the number of columns <code>n<\/code> on the <code>.grid<\/code> and use it to set the <code>grid-template-columns<\/code>. This way, the essential CSS becomes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.wrap<\/span> {\n  <span class=\"hljs-attribute\">container-size<\/span>: inline-type;\n}\n\n<span class=\"hljs-selector-class\">.grid<\/span> {\n  <span class=\"hljs-attribute\">--w<\/span>: <span class=\"hljs-number\">100<\/span>cqw;\n  <span class=\"hljs-attribute\">--u<\/span>: <span class=\"hljs-number\">7em<\/span>;\n  <span class=\"hljs-attribute\">--f<\/span>: <span class=\"hljs-built_in\">var<\/span>(--w) \/ <span class=\"hljs-built_in\">var<\/span>(--u);\n  <span class=\"hljs-attribute\">--n<\/span>: <span class=\"hljs-built_in\">max<\/span>(<span class=\"hljs-number\">1<\/span>, round(down, var(--f)));\n\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(var(--n), <span class=\"hljs-built_in\">var<\/span>(--u));\n  <span class=\"hljs-attribute\">justify-content<\/span>: center;\n}\n\n<span class=\"hljs-keyword\">@supports<\/span> <span class=\"hljs-keyword\">not<\/span> (<span class=\"hljs-attribute\">scale:<\/span> calc(<span class=\"hljs-number\">100<\/span>cqh \/ <span class=\"hljs-number\">3<\/span>lh)) {\n  <span class=\"hljs-keyword\">@property<\/span> --w {\n    <span class=\"hljs-selector-tag\">syntax<\/span>: \"&lt;<span class=\"hljs-selector-tag\">length-percentage<\/span>&gt;\";\n    <span class=\"hljs-selector-tag\">initial-value<\/span>: 0<span class=\"hljs-selector-tag\">px<\/span>;\n    <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  }\n\n  <span class=\"hljs-keyword\">@property<\/span> --u {\n    <span class=\"hljs-selector-tag\">syntax<\/span>: \"&lt;<span class=\"hljs-selector-tag\">length-percentage<\/span>&gt;\";\n    <span class=\"hljs-selector-tag\">initial-value<\/span>: 0<span class=\"hljs-selector-tag\">px<\/span>;\n    <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  }\n\n  <span class=\"hljs-selector-class\">.grid<\/span> {\n    <span class=\"hljs-attribute\">--f<\/span>: <span class=\"hljs-built_in\">round<\/span>(tan(atan2(var(--w), <span class=\"hljs-built_in\">var<\/span>(--u))), <span class=\"hljs-number\">0.00001<\/span>);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><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>Note that we may also use <code>1fr<\/code> instead of <code>var(--u)<\/code> for the <code>grid-template-columns<\/code> property if we want the <code>.item<\/code> elements to stretch.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_PwqXVBK\/fcbfc38fb1f80b2cdb7f617d20c25a49\" src=\"\/\/codepen.io\/anon\/embed\/PwqXVBK\/fcbfc38fb1f80b2cdb7f617d20c25a49?height=450&amp;theme-id=1&amp;slug-hash=PwqXVBK\/fcbfc38fb1f80b2cdb7f617d20c25a49&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed PwqXVBK\/fcbfc38fb1f80b2cdb7f617d20c25a49\" title=\"CodePen Embed PwqXVBK\/fcbfc38fb1f80b2cdb7f617d20c25a49\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"mind-the-gap\">Mind the <code>gap<\/code><\/h2>\n\n\n\n<p>Nice, but oftentimes we also want to have a gap in between our rows and columns, so let&#8217;s see how the number of columns can be computed in that case.<\/p>\n\n\n\n<p>Whenever we have <code>n<\/code> columns, we have <code>n - 1<\/code> gaps in between them.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_LEVqOpZ\" src=\"\/\/codepen.io\/anon\/embed\/LEVqOpZ?height=700&amp;theme-id=1&amp;slug-hash=LEVqOpZ&amp;default-tab=result\" height=\"700\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed LEVqOpZ\" title=\"CodePen Embed LEVqOpZ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>This means that <code>n<\/code> times the unit column width plus <code>(n - 1)<\/code> times the gap space adds up to the container&#8217;s <code>content-box<\/code> width:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">n\u00b7u + (n - 1)\u00b7s = w<\/pre>\n\n\n\n<p>If we add <code>s<\/code> on both sides in the equation above, we get:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">n\u00b7u + (n - 1)\u00b7s + s = w + s \u21d2 <br>n\u00b7u + n\u00b7s - s + s = w + s \u21d2 <br>n\u00b7u + n\u00b7s = w + s \u21d2 <br>n\u00b7(u + s) = w + s \u21d2<br>n = (w + s)\/(u + s)<\/pre>\n\n\n\n<p>Putting this into CSS, our ratio looks as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\">(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--w<\/span>) + <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--s<\/span>))\/(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--u<\/span>) + <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--s<\/span>))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><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>Note that in our case, it&#8217;s the fraction <code>f<\/code> that we compute this way before we round it to get the number of items <code>n<\/code> and ensure <code>n<\/code> is always at least <code>1<\/code>.<\/p>\n\n\n\n<p>Also note that the CSS variables we need to register for the no <code>calc()<\/code> length division fallback are the numerator and denominator of this fraction. So our essential CSS becomes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.wrap<\/span> {\n  <span class=\"hljs-attribute\">container-size<\/span>: inline-type;\n}\n\n<span class=\"hljs-selector-class\">.grid<\/span> {\n  <span class=\"hljs-attribute\">--w<\/span>: <span class=\"hljs-number\">100<\/span>cqw;\n  <span class=\"hljs-attribute\">--u<\/span>: <span class=\"hljs-number\">7em<\/span>;\n  <span class=\"hljs-attribute\">--s<\/span>: <span class=\"hljs-number\">3vmin<\/span>;\n  <span class=\"hljs-attribute\">--p<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--w) + <span class=\"hljs-built_in\">var<\/span>(--s)); <span class=\"hljs-comment\">\/* numerator *\/<\/span>\n  <span class=\"hljs-attribute\">--q<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--u) + <span class=\"hljs-built_in\">var<\/span>(--s)); <span class=\"hljs-comment\">\/* denominator *\/<\/span>\n  <span class=\"hljs-attribute\">--f<\/span>: <span class=\"hljs-built_in\">var<\/span>(--p) \/ <span class=\"hljs-built_in\">var<\/span>(--q);\n  <span class=\"hljs-attribute\">--n<\/span>: <span class=\"hljs-built_in\">max<\/span>(<span class=\"hljs-number\">1<\/span>, round(down, var(--f)));\n\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-gap<\/span>: <span class=\"hljs-built_in\">var<\/span>(--s);\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(var(--n), <span class=\"hljs-number\">1<\/span>fr);\n}\n\n<span class=\"hljs-keyword\">@supports<\/span> <span class=\"hljs-keyword\">not<\/span> (<span class=\"hljs-attribute\">scale:<\/span> calc(<span class=\"hljs-number\">100<\/span>cqh \/ <span class=\"hljs-number\">3<\/span>lh)) {\n  <span class=\"hljs-keyword\">@property<\/span> --p {\n    <span class=\"hljs-comment\">\/* numerator *\/<\/span>\n    <span class=\"hljs-selector-tag\">syntax<\/span>: \"&lt;<span class=\"hljs-selector-tag\">length-percentage<\/span>&gt;\";\n    <span class=\"hljs-selector-tag\">initial-value<\/span>: 0<span class=\"hljs-selector-tag\">px<\/span>;\n    <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  }\n\n  <span class=\"hljs-keyword\">@property<\/span> --q {\n    <span class=\"hljs-comment\">\/* denominator *\/<\/span>\n    <span class=\"hljs-selector-tag\">syntax<\/span>: \"&lt;<span class=\"hljs-selector-tag\">length-percentage<\/span>&gt;\";\n    <span class=\"hljs-selector-tag\">initial-value<\/span>: 0<span class=\"hljs-selector-tag\">px<\/span>;\n    <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  }\n\n  <span class=\"hljs-selector-class\">.grid<\/span> {\n    <span class=\"hljs-attribute\">--f<\/span>: <span class=\"hljs-built_in\">round<\/span>(tan(atan2(var(--p), <span class=\"hljs-built_in\">var<\/span>(--q))), <span class=\"hljs-number\">0.00001<\/span>);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_QwbYWrX\/40277e2147a053fd101bd8f541eb2449\" src=\"\/\/codepen.io\/anon\/embed\/QwbYWrX\/40277e2147a053fd101bd8f541eb2449?height=450&amp;theme-id=1&amp;slug-hash=QwbYWrX\/40277e2147a053fd101bd8f541eb2449&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed QwbYWrX\/40277e2147a053fd101bd8f541eb2449\" title=\"CodePen Embed QwbYWrX\/40277e2147a053fd101bd8f541eb2449\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"lets-go-wild\">Let&#8217;s Go Wild!<\/h2>\n\n\n\n<p>And let&#8217;s see where we can use this!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"highlighting-items-on-a-certain-column\">Highlighting items on a certain column<\/h3>\n\n\n\n<p>In order to do something like this, we use the item indices. Once <code>sibling-index()<\/code> is supported cross-browser, we&#8217;ll be able to do this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> { <span class=\"hljs-attribute\">--i<\/span>: <span class=\"hljs-built_in\">calc<\/span>(sibling-index() - <span class=\"hljs-number\">1<\/span>) }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><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>Note that we need to subtract <code>1<\/code> because <code>sibling-index()<\/code> is <code>1<\/code>-based and we need our index <code>i<\/code> to be <code>0<\/code>-based for modulo and division purposes.<\/p>\n\n\n\n<p>Until then, we add these indices in <code>style<\/code> attributes when generating the HTML:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">.grid<br>  - for(let i = 0; i &lt; 100; i++)<br>    .item(style=`--i: ${i}`) #{i + 1}<\/pre>\n\n\n\n<p>Let&#8217;s say we want to highlight the items on the first column. We get the number of columns <code>n<\/code> just like before. An item is on the first column if <code>i%n<\/code> (which gives us the <code>0<\/code>-based index of the column an item of index <code>i<\/code> is on) is <code>0<\/code>. Now given I used the word <em>if<\/em> there, you might be thinking about <a href=\"https:\/\/css-tricks.com\/lightly-poking-at-the-css-if-function-in-chrome-137\/\">the new CSS <code>if()<\/code> function<\/a>. However, we have a way better supported method here.<\/p>\n\n\n\n<p>If the column index <code>i%n<\/code> is <code>0<\/code>, then <code>min(1, i%n)<\/code> is <code>0<\/code>. If the column index <code>i%n<\/code> isn&#8217;t <code>0<\/code>, then <code>min(1, i%n)<\/code> is <code>1<\/code>. So we can do the following:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, mod(var(--i), <span class=\"hljs-built_in\">var<\/span>(--n))); <span class=\"hljs-comment\">\/* 1 if NOT on the first column *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay)); <span class=\"hljs-comment\">\/* 1 if on the first column! *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><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 then we can use <code>--yay<\/code> to highlight the items on the first column by <a href=\"https:\/\/css-tricks.com\/dry-switching-with-css-variables-the-difference-of-one-declaration\/\">styling them differently<\/a>, for example by giving them a different <code>background<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, mod(var(--i), <span class=\"hljs-built_in\">var<\/span>(--n))); <span class=\"hljs-comment\">\/* 1 if NOT on the first column *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay)); <span class=\"hljs-comment\">\/* 1 if on the first column! *\/<\/span>\n\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">color-mix<\/span>(in srgb, #fcbf49 calc(var(--yay)*<span class=\"hljs-number\">100%<\/span>), <span class=\"hljs-number\">#dedede<\/span>)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><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>You can see it in action in the live demo below:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_gbpqaXK\/fd5dd716b42b1a0c5179399982cd2116\" src=\"\/\/codepen.io\/anon\/embed\/gbpqaXK\/fd5dd716b42b1a0c5179399982cd2116?height=450&amp;theme-id=1&amp;slug-hash=gbpqaXK\/fd5dd716b42b1a0c5179399982cd2116&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed gbpqaXK\/fd5dd716b42b1a0c5179399982cd2116\" title=\"CodePen Embed gbpqaXK\/fd5dd716b42b1a0c5179399982cd2116\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Now let&#8217;s say we want to highlight the items on the last column. In this case, the column index <code>i%n<\/code> is <code>n - 1<\/code>, which means that their difference is <code>0<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">(n - 1) - (i%n) = 0<\/pre>\n\n\n\n<p>Using this, we can do something very similar to what we did before, as the minimum between <code>1<\/code> and this difference is <code>0<\/code> for items on the last column and <code>1<\/code> for those that aren&#8217;t on the last column:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-comment\">\/* 1 if NOT on the last column *\/<\/span>\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, (var(--n) - <span class=\"hljs-number\">1<\/span>) - <span class=\"hljs-built_in\">mod<\/span>(var(--i), <span class=\"hljs-built_in\">var<\/span>(--n))));\n  <span class=\"hljs-comment\">\/* 1 if on the last column! *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><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>For example, if <code>n<\/code> is <code>7<\/code>, then the column index <code>i%n<\/code> can be <code>0<\/code>, <code>1<\/code>, &#8230; <code>6<\/code> and <code>n - 1<\/code> is <code>6<\/code>. If our item of index <code>i<\/code> is on the last column, then its column index <code>i%n = i%7 = 6<\/code>, so the difference between <code>n - 1 = 7 - 1 = 6<\/code> and <code>i%n = i%7 = 6<\/code> is <code>0<\/code>. If our item of index i isn\u2019t on the last column, then its column index <code>i%n = i%7 &lt; 6<\/code>, so the difference between <code>n - 1 = 6<\/code> and <code>i%n&nbsp;&lt; 6<\/code> is <code>1<\/code> or bigger. Taking the minimum between <code>1<\/code> and this difference ensures we always get either <code>0<\/code> or <code>1<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_NPqoxWK\/b1e8cfbc5b9719664a3dcb45106a247d\" src=\"\/\/codepen.io\/anon\/embed\/NPqoxWK\/b1e8cfbc5b9719664a3dcb45106a247d?height=450&amp;theme-id=1&amp;slug-hash=NPqoxWK\/b1e8cfbc5b9719664a3dcb45106a247d&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed NPqoxWK\/b1e8cfbc5b9719664a3dcb45106a247d\" title=\"CodePen Embed NPqoxWK\/b1e8cfbc5b9719664a3dcb45106a247d\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>In general, if we want to highlight a column of index <code>k<\/code> (<code>0<\/code>-based, but we can just subtract <code>1<\/code> in the formula below if it&#8217;s given <code>1<\/code>-based), we need to compute the difference between it and <code>i%n<\/code> (the column index of an item of index <code>i<\/code>), then use the absolute value of this difference inside the <code>min()<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--dif<\/span>: <span class=\"hljs-built_in\">var<\/span>(--k) - <span class=\"hljs-built_in\">mod<\/span>(var(--i), <span class=\"hljs-built_in\">var<\/span>(--n));\n  <span class=\"hljs-attribute\">--abs<\/span>: <span class=\"hljs-built_in\">abs<\/span>(var(--dif));\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, var(--abs)); <span class=\"hljs-comment\">\/* 1 if NOT on column k *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay)); <span class=\"hljs-comment\">\/* 1 if on column k! *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><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>The difference and its absolute value are <code>0<\/code> when the item of index <code>i<\/code> is on column <code>k<\/code> and different (bigger in the case of absolute value) when it isn&#8217;t.<\/p>\n\n\n\n<p>We need the absolute value here because, while the difference between <code>n - 1<\/code> and <code>i%7<\/code> is always <code>0<\/code> or bigger, that is not the case for the difference between any random <code>k<\/code> and <code>i%n<\/code>. For example, if <code>n<\/code> is <code>7<\/code> and <code>k<\/code> is <code>2<\/code>, the <code>k - i%n<\/code> difference is negative when <code>k<\/code> is smaller than <code>i%n<\/code>, for example when <code>i%n<\/code> is <code>5<\/code>. And we need the difference that goes into the <code>min()<\/code> to be <code>0<\/code> or bigger in order for the <code>min()<\/code> to always give us either <code>0<\/code> or <code>1<\/code>.<\/p>\n\n\n\n<p>All modern stable browsers support <code>abs()<\/code>, but for the best possible browser support, we can still test for support and use <a href=\"https:\/\/css-tricks.com\/using-absolute-value-sign-rounding-and-modulo-in-css-today\/\">the fallback<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@supports<\/span> <span class=\"hljs-keyword\">not<\/span> (<span class=\"hljs-attribute\">scale:<\/span> abs(-<span class=\"hljs-number\">2<\/span>)) {\n  <span class=\"hljs-selector-class\">.item<\/span> { <span class=\"hljs-attribute\">--abs<\/span>: <span class=\"hljs-built_in\">max<\/span>(var(--dif), -<span class=\"hljs-number\">1<\/span>*(<span class=\"hljs-built_in\">var<\/span>(--dif))) }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><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>Also, note that if the selected column index <code>k<\/code> is equal to <code>n<\/code> or bigger, no items get selected.<\/p>\n\n\n\n<p>In the interactive demo below, clicking an item selects all items on the same column:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_raVPYqx\/b8d722add96b8e4551a79f9a8f43e131\" src=\"\/\/codepen.io\/anon\/embed\/raVPYqx\/b8d722add96b8e4551a79f9a8f43e131?height=450&amp;theme-id=1&amp;slug-hash=raVPYqx\/b8d722add96b8e4551a79f9a8f43e131&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed raVPYqx\/b8d722add96b8e4551a79f9a8f43e131\" title=\"CodePen Embed raVPYqx\/b8d722add96b8e4551a79f9a8f43e131\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>It does this by setting <code>--k<\/code> (in the <code>style<\/code> attribute of the <code>.grid<\/code>) to the index of that column.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"558\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/460213331-95aec4e4-3664-4112-97ea-750361218e3f.png?resize=1024%2C558&#038;ssl=1\" alt=\"A code snippet from a web developer's browser console showcasing CSS rules for items in a grid layout, including custom properties for styling.\" class=\"wp-image-6579\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/460213331-95aec4e4-3664-4112-97ea-750361218e3f.png?resize=1024%2C558&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/460213331-95aec4e4-3664-4112-97ea-750361218e3f.png?resize=300%2C164&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/460213331-95aec4e4-3664-4112-97ea-750361218e3f.png?resize=768%2C419&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/460213331-95aec4e4-3664-4112-97ea-750361218e3f.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">Chrome DevTools screenshot showing <code>--k<\/code> being set on the <code>.grid<\/code> parent and used in computations on <code>.item<\/code> children<\/figcaption><\/figure>\n\n\n\n<p>We can also highlight items on either odd or even columns:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-comment\">\/* 1 if on an even column, 0 otherwise *\/<\/span>\n  <span class=\"hljs-attribute\">--even<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, mod(mod(var(--i), <span class=\"hljs-built_in\">var<\/span>(--n)), <span class=\"hljs-number\">2<\/span>));\n  <span class=\"hljs-comment\">\/* 1 if on an odd colum, 0 otherwise *\/<\/span>\n  <span class=\"hljs-attribute\">--odd<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--even));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_OPVqzKJ\/326683ec2e06323a5858b0959570ecb9\" src=\"\/\/codepen.io\/anon\/embed\/OPVqzKJ\/326683ec2e06323a5858b0959570ecb9?height=450&amp;theme-id=1&amp;slug-hash=OPVqzKJ\/326683ec2e06323a5858b0959570ecb9&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed OPVqzKJ\/326683ec2e06323a5858b0959570ecb9\" title=\"CodePen Embed OPVqzKJ\/326683ec2e06323a5858b0959570ecb9\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>This is a particular case of highlighting every <code>k<\/code>-th column starting from column <code>j<\/code> (again, <code>j<\/code> is a <code>0<\/code>-based index and smaller than <code>k<\/code>):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--dif<\/span>: <span class=\"hljs-built_in\">var<\/span>(--j) - <span class=\"hljs-built_in\">mod<\/span>(mod(var(--i), <span class=\"hljs-built_in\">var<\/span>(--n)), <span class=\"hljs-built_in\">var<\/span>(--k));\n  <span class=\"hljs-attribute\">--abs<\/span>: <span class=\"hljs-built_in\">abs<\/span>(var(--dif));\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, var(--abs));\n  <span class=\"hljs-comment\">\/* 1 if on one of every kth col starting from col of index j *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_Jodzpeq\/bfe8bc4b76af31eb95d005e7a9a6ee3d\" src=\"\/\/codepen.io\/anon\/embed\/Jodzpeq\/bfe8bc4b76af31eb95d005e7a9a6ee3d?height=450&amp;theme-id=1&amp;slug-hash=Jodzpeq\/bfe8bc4b76af31eb95d005e7a9a6ee3d&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed Jodzpeq\/bfe8bc4b76af31eb95d005e7a9a6ee3d\" title=\"CodePen Embed Jodzpeq\/bfe8bc4b76af31eb95d005e7a9a6ee3d\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"highlighting-items-on-a-certain-row\">Highlighting items on a certain row<\/h3>\n\n\n\n<p>If we want to highlight the items on the first row, this means their index <code>i<\/code> must be smaller than the number of columns <code>n<\/code>. This means the difference <code>n - i<\/code> must be bigger than <code>0<\/code> for items on the first row. If we clamp it to the <code>[0, 1]<\/code> interval, we get a value that&#8217;s <code>0<\/code> on every row but the first and <code>1<\/code> on the first row.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">clamp<\/span>(<span class=\"hljs-number\">0<\/span>, var(--n) - <span class=\"hljs-built_in\">var<\/span>(--i), <span class=\"hljs-number\">1<\/span>)  <span class=\"hljs-comment\">\/* 1 if on the first row! *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_pvJGpzJ\/2a7f5f3c6eff481b5baef4775ad59550\" src=\"\/\/codepen.io\/anon\/embed\/pvJGpzJ\/2a7f5f3c6eff481b5baef4775ad59550?height=450&amp;theme-id=1&amp;slug-hash=pvJGpzJ\/2a7f5f3c6eff481b5baef4775ad59550&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed pvJGpzJ\/2a7f5f3c6eff481b5baef4775ad59550\" title=\"CodePen Embed pvJGpzJ\/2a7f5f3c6eff481b5baef4775ad59550\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>There is more than one way to skin a cat however, so another approach would be to get the row index, which is the result of <code>i\/n<\/code> rounded down. If this is <code>0<\/code>, the item of index <code>i<\/code> is on the first row. If it&#8217;s bigger than <code>0<\/code>, it isn&#8217;t. This makes the minimum between <code>1<\/code> and <code>i\/n<\/code> rounded down be <code>0<\/code> when the item of index <code>i<\/code> is on the first row and <code>1<\/code> when it isn&#8217;t.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, round(down, var(--i)\/<span class=\"hljs-built_in\">var<\/span>(--n))); <span class=\"hljs-comment\">\/* 1 if NOT on the first row *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay)); <span class=\"hljs-comment\">\/* 1 if on the first row! *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><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>This second approach can be modified to allow for highlighting the items on any row of index <code>k<\/code> as the difference between <code>k<\/code> and <code>i\/n<\/code> rounded down (the row index) is <code>0<\/code> if the item of index <code>i<\/code> is on the row of index <code>k<\/code> and non-zero otherwise:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--dif<\/span>: <span class=\"hljs-built_in\">var<\/span>(--k) - <span class=\"hljs-built_in\">round<\/span>(down, var(--i)\/<span class=\"hljs-built_in\">var<\/span>(--n));\n  <span class=\"hljs-attribute\">--abs<\/span>: <span class=\"hljs-built_in\">abs<\/span>(var(--dif));\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, var(--abs)); <span class=\"hljs-comment\">\/* 1 if NOT on row of index k *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay)); <span class=\"hljs-comment\">\/* 1 if on row of index k! *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_raVRpVe\/bee2c34bb6c24ec45ebeb923c4478996\" src=\"\/\/codepen.io\/anon\/embed\/raVRpVe\/bee2c34bb6c24ec45ebeb923c4478996?height=450&amp;theme-id=1&amp;slug-hash=raVRpVe\/bee2c34bb6c24ec45ebeb923c4478996&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed raVRpVe\/bee2c34bb6c24ec45ebeb923c4478996\" title=\"CodePen Embed raVRpVe\/bee2c34bb6c24ec45ebeb923c4478996\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Highlighting the items on any row includes the last one. For this, we need to know the total number <code>t<\/code> of items on our grid. This means <code>(t - 1)<\/code> is the index of the last grid item, and we can get the index of the row it&#8217;s on (that is, the index of the final row) by rounding down <code>(t - 1)\/n<\/code>. Then we substitute <code>k<\/code> in the previous formula with the index of the final row we&#8217;ve just obtained this way.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-comment\">\/* 1 if NOT on last row *\/<\/span>\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, round(down, (var(--t) - <span class=\"hljs-number\">1<\/span>)\/<span class=\"hljs-built_in\">var<\/span>(--n)) - <span class=\"hljs-built_in\">round<\/span>(down, var(--i)\/<span class=\"hljs-built_in\">var<\/span>(--n)));\n  <span class=\"hljs-comment\">\/* 1 if on last row! *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><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>There are two things to note here.<\/p>\n\n\n\n<p>One, we don&#8217;t need the absolute value here anymore, as the last row index is always going to be bigger or equal to any other row index.<\/p>\n\n\n\n<p>Two, we&#8217;re currently passing the total number of items <code>t<\/code> to the CSS as a custom property when generating the HTML:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">- let t = 24; \/\/- total number of items on the grid<br><br>.wrap<br>  .grid(style=`--t: ${t}`)<br>    - for(let i = 0; i &lt; t; i++)<br>      .item(style=`--i: ${i}`) #{i + 1}<\/pre>\n\n\n\n<p>But once <code>sibling-count()<\/code> is supported cross-browser, we won&#8217;t need to do this anymore and we&#8217;ll be able to write:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> { <span class=\"hljs-attribute\">--t<\/span>: <span class=\"hljs-built_in\">sibling-count<\/span>() }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_jEPJGNV\/24f22ee7a71f5a4475e66be0148ee6ad\" src=\"\/\/codepen.io\/anon\/embed\/jEPJGNV\/24f22ee7a71f5a4475e66be0148ee6ad?height=450&amp;theme-id=1&amp;slug-hash=jEPJGNV\/24f22ee7a71f5a4475e66be0148ee6ad&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed jEPJGNV\/24f22ee7a71f5a4475e66be0148ee6ad\" title=\"CodePen Embed jEPJGNV\/24f22ee7a71f5a4475e66be0148ee6ad\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Just like before, we can highlight items on odd or even rows.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-comment\">\/* 1 if on an even row *\/<\/span>\n  <span class=\"hljs-attribute\">--even<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, mod(round(down, var(--i)\/<span class=\"hljs-built_in\">var<\/span>(--n)), <span class=\"hljs-number\">2<\/span>));\n  <span class=\"hljs-comment\">\/* 1 if on an odd row *\/<\/span>\n  <span class=\"hljs-attribute\">--odd<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--even));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_qEdvpzy\/a0866b3458d366d97fc1f2716dfeae72\" src=\"\/\/codepen.io\/anon\/embed\/qEdvpzy\/a0866b3458d366d97fc1f2716dfeae72?height=450&amp;theme-id=1&amp;slug-hash=qEdvpzy\/a0866b3458d366d97fc1f2716dfeae72&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed qEdvpzy\/a0866b3458d366d97fc1f2716dfeae72\" title=\"CodePen Embed qEdvpzy\/a0866b3458d366d97fc1f2716dfeae72\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>And the odd\/ even scenario is a particular case of highlighting items on every <code>k<\/code>-th row, starting from row of index <code>j<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.item<\/span> {\n  <span class=\"hljs-attribute\">--dif<\/span>: <span class=\"hljs-built_in\">var<\/span>(--j) - <span class=\"hljs-built_in\">mod<\/span>(round(down, var(--i)\/<span class=\"hljs-built_in\">var<\/span>(--n)), <span class=\"hljs-built_in\">var<\/span>(--k));\n  <span class=\"hljs-attribute\">--abs<\/span>: <span class=\"hljs-built_in\">abs<\/span>(var(--dif));\n  <span class=\"hljs-attribute\">--nay<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">1<\/span>, var(--abs));\n  <span class=\"hljs-comment\">\/* 1 if on one of every kth row starting from row of index j *\/<\/span>\n  <span class=\"hljs-attribute\">--yay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1<\/span> - var(--nay));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_jEPJJWx\/c5bfecacba3dc0d096ba9b510a4e98e0\" src=\"\/\/codepen.io\/anon\/embed\/jEPJJWx\/c5bfecacba3dc0d096ba9b510a4e98e0?height=450&amp;theme-id=1&amp;slug-hash=jEPJJWx\/c5bfecacba3dc0d096ba9b510a4e98e0&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed jEPJJWx\/c5bfecacba3dc0d096ba9b510a4e98e0\" title=\"CodePen Embed jEPJJWx\/c5bfecacba3dc0d096ba9b510a4e98e0\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"taking-it-further\">Taking it Further<\/h2>\n\n\n\n<p>Another thing this technique can be used for is creating responsive grids of non-rectangular shapes with no breakpoints. An example would be the hexagon grid below. We aren&#8217;t going into the details of it here, but know it was done using this technique plus a few more computations to get the right hexagon alignment.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_QwWQqeR\" src=\"\/\/codepen.io\/anon\/embed\/QwWQqeR?height=550&amp;theme-id=1&amp;slug-hash=QwWQqeR&amp;default-tab=result\" height=\"550\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed QwWQqeR\" title=\"CodePen Embed QwWQqeR\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n","protected":false},"excerpt":{"rendered":"<p>The whole point of auto-fit and auto-fill is that you aren&#8217;t saying how many columns to use. But if you knew how many the browser chose, you can make nice design decisions.<\/p>\n","protected":false},"author":32,"featured_media":6614,"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":""},"categories":[1],"tags":[7,24,123,53],"class_list":["post-6567","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-design","tag-grid","tag-layout"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/07\/auto-fill-columns-and-more-in-CSS-v3.jpg?fit=1140%2C676&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/6567","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\/32"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=6567"}],"version-history":[{"count":18,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/6567\/revisions"}],"predecessor-version":[{"id":6732,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/6567\/revisions\/6732"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/6614"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=6567"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=6567"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=6567"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}