<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maciek Fitzner</title>
    <description>The latest articles on DEV Community by Maciek Fitzner (@mackfitz).</description>
    <link>https://dev.to/mackfitz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F994626%2Fa1d048a6-0efd-4616-9d2d-e3495ff50a29.png</url>
      <title>DEV Community: Maciek Fitzner</title>
      <link>https://dev.to/mackfitz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mackfitz"/>
    <language>en</language>
    <item>
      <title>Approximating a sphere using CSS - and pumpkins!</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Tue, 21 Nov 2023 12:55:53 +0000</pubDate>
      <link>https://dev.to/mackfitz/approximating-a-sphere-using-css-and-pumpkins-1dle</link>
      <guid>https://dev.to/mackfitz/approximating-a-sphere-using-css-and-pumpkins-1dle</guid>
      <description>&lt;p&gt;Before I get to the main topic, please allow me to paint you a more detailed picture of how I got into 3D CSS:&lt;/p&gt;

&lt;p&gt;I have been obsessed with 3D graphics all my life. An 80's kid, I'm not sure which I saw first: &lt;a href="https://www.youtube.com/watch?v=EtyHX7z8fi8&amp;amp;t=8s" rel="noopener noreferrer"&gt;TRON&lt;/a&gt; or Dire Strait's &lt;a href="https://www.youtube.com/watch?v=wTP2RUD_cL0" rel="noopener noreferrer"&gt;Money For Nothing&lt;/a&gt; video, all I know is I was hooked, instantly. I watched the CGI revolution - Jurassic Park and Terminator 2 - with wide eyes and jaw on the floor. I witnessed games boldly fully embracing the third dimension - with the likes of Virtua Fighter and Quake.&lt;/p&gt;

&lt;p&gt;Then, of course, is the 3D graphic software - which I didn't devote myself quite as much as I would've liked. Obsessed as I was with CGI as a teen, I didn't pursue it when I got online in the early '00s and suddenly had so many amazing, intuitive tools to pick from. It was only at work that I got into CAD - and while it did allow for some fancy experiments with surfaces, it was mostly for technical applications.&lt;/p&gt;

&lt;p&gt;Case in point: to create a sphere you had to draw a semi-circle and spin it around a selected axis:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftulb3s87uztzfkjawivg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftulb3s87uztzfkjawivg.png" alt="A simple demonstration of how a sphere is created in CAD by rotating a semicircle around an axis" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In contrast, artistic 3D modeling software like Sculptris would allow me to sculpt lifelike statues with tools that felt like actual fingertips:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l6r4hytvcqmadgdvoa1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l6r4hytvcqmadgdvoa1.gif" alt="A GIF of a 3D model of an orc made using artistic 3D software Sculptris" width="600" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But just as I was preparing for a deeper dive into 3D art, making my first steps in Blender, I found out that you could do 3D in CSS. Yes, that weird, fascinating and often deeply frustrating thing that you have to use to style your web pages has an intricate system of creating art in three dimensions.&lt;/p&gt;

&lt;p&gt;A system that works nothing like Blender - and puts CAD to shame with the number of steps to create even the most basic solid primitive.&lt;/p&gt;

&lt;p&gt;Investigating the topic, I stumbled upon this amazing &lt;a href="https://keithclark.co.uk/articles/creating-3d-worlds-with-html-and-css/" rel="noopener noreferrer"&gt;blog article by Keith Clark&lt;/a&gt;. The man created a 3D FPS in (mostly) plain CSS. Ten years ago! His post details the intricacies of creating cubes and cylinders - to create the staples of boomer shooters: crates and barrels:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxl0ugh10ecaigaubpbge.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxl0ugh10ecaigaubpbge.jpg" alt="A demonstration of 3D crates (cubes) and barrels and tires (cylinders) from Keith Clark's article linked below" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(image taken from the blog post, original image URL &lt;a href="https://keithclark.co.uk/articles/creating-3d-worlds-with-html-and-css/cssfps-objects.jpg" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;All these were created out of plain div elements - the basic building blocks of HTML. You don't get a dedicated "cube" element that you could style. All you have at your disposal is a basic rectangular container - and you assemble 3D shapes by placing a series of appropriately sized containers in 3D space. You have to rotate and push each and every one of them around until they start to resemble the thing.&lt;/p&gt;

&lt;p&gt;This got me so fascinated that I forgot all about Sculptris and Blender. The cylinders especially. They weren't perfectly smooth - but close enough.&lt;/p&gt;

&lt;p&gt;If that's not good enough for you, look at this circle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwi6t6nghnee5hxt80gi7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwi6t6nghnee5hxt80gi7.png" alt="A polygon of 64 sides that almost looks like a perfect circle if you don't look too closely" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's not a circle. It's a polygon with 64 sides. It merely approximates a circle.&lt;/p&gt;

&lt;p&gt;I started exploring what more I could do from there, given that I took up webdev in 2018, and some things that Keith talked about as the bright future - such as blend modes - were already a fact.&lt;/p&gt;

&lt;p&gt;Soon enough, I started thinking: what if I could create more complex shapes - like cones?&lt;/p&gt;

&lt;p&gt;Or spheres!&lt;/p&gt;

&lt;p&gt;...out of rectangles...&lt;/p&gt;

&lt;p&gt;The most obvious idea seemed to be something like the globe from geography class - with a series of parallel horizontal circles, each narrower the farther it is from the equator, and vertical circles all the same size, converging on the poles. Something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24521hra340deqwl18rv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24521hra340deqwl18rv.png" alt="A geometric imitation of a globe, with meridians running from the north to the south pole, and circles of latitude" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...except less round or smooth. We're only approximating the sphere - with the crudest shapes possible, imitating the most basic vector graphics. Forget curves and splines. What we're creating will have to look more like this if it's going to run smoothly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyafs11xhp7lvt95285wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyafs11xhp7lvt95285wg.png" alt="A simplified, more angular imitation of a globe, with meridians running from the north to the south pole, and circles of latitude " width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See, HTML and CSS - as powerful as they are on their own these days - aren't exactly Unreal Engine. Whereas the latter can move around millions of polygons, shade and texture them and never break a sweat, your browser might make your CPU sweat with just 100+ objects. And our sphere exceeds that limit. It's based on a 16-gon, which means it consists of 16 slices (like those in an orange), each itself comprised of 8 faces. Our solid, therefore, is made of 128 trapezoid faces - formed by the intersecting meridians and circles of latitude - which, again, we'll have to cut one by one out from rectangular containers, then rotate and push into position using CSS. And math! Lots and lots of math - which is as hard for the CPU as it is for that squishy thing under your skull.&lt;/p&gt;

&lt;p&gt;Terrifying and fascinating!&lt;/p&gt;

&lt;p&gt;So there I was - and in order to get anywhere from there, some 20 years after high-shool I had to relearn trigonometry. Sines, cosines and the lot. I'll spare you the exact details of figuring out the formulas for what I needed and transplanting the basics of classic geometry onto the constraints of CSS syntax - and PHP, while we're at it - because that's a whole other rabbit hole. Eventually, I was cranking out sphere after sphere. Originally, I would use SVG - which I got really comfortable with - but would eventually transition to clip-path.&lt;/p&gt;

&lt;p&gt;For the kicks, I even made one out of borders:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/oNZMXeZ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;...and (much later) out of conic gradients:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/YzJdKgm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This paradigm - a series of trapezoids starting out as triangles at one pole, going via the equator and converging again on the other pole - was neat and intuitive enough, even if I had some slip-ups along the way and needed to rethink the structure a bunch of times. But, as much as I liked it, I had a major issue with it:&lt;/p&gt;

&lt;p&gt;The lines converging at the poles meant that the facets that they formed got ever smaller while contributing less and less to the geometry.&lt;/p&gt;

&lt;p&gt;This forced me to rethink my approach to the basic shape used for the facet - the trapezoid. I thought of vector graphics - where the basic unit is the triangular vertex, and a trapezoid is just two conjoined vertices combined:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlx9t9awowvp5khy8pls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlx9t9awowvp5khy8pls.png" alt="An illustration of how in vector-based graphics a trapezoid is formed out of two conjoined triangles" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It would have made little sense to split a trapezoid into two triangles to create a geometry like above. But what if you rotated every other circle of latitude by half a step - 11.25 degrees ( 0.5 * 360degrees / 16 ) - so that, instead of straight lines running from the poles outward, the meridian lines would have to zigzag to connect the tips, thus forming neat triangles:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhu0a13hoc7wy3su1pn7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhu0a13hoc7wy3su1pn7.png" alt="A different approach to forming a sphere: from triangles rather than trapezoids, formed by meridians running from the poles outward in zigzags rather than in straight lines" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This seemed not just a step up from the previous paradigm. It was just perfect for the 3D Jack O' Lantern that I wanted to make for Kevin Powell's 3D CSS challenge: with sharp teeth and triangle-shaped cutout eyes. What's more, it let me lower the facet count, given how much was "carved away":&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/dyzjOQm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In reality, though, what CPU usage I saved there, I now had to transfer into calculating the bright orange facets of where the cutouts pierced the virtual flesh of the CSS pumpkin. In keeping with the Halloween spirit, this was a nightmare to figure out and coordinate, as was the faux texturing, done with gradients, which ran in straight lines like it would in my primary sphere, not in zigzags, like my facets did.&lt;/p&gt;

&lt;p&gt;Also, my facets weren't actual vector vertices, they were just imitating those, just as the trapezoids were. Splitting the trapezoids in two would've made sense in a vector-based system. Here, I just doubled the facet count.&lt;/p&gt;

&lt;p&gt;So can you blame me for going back to my original paradigm for my subsequent Hallooween's Jack O' Lantern?&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/BaPNyLw?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This made the texturing way easier, and so I focused on that. Even so, I didn't quite achieve what I'd set out to do: I had to settle for linear gradients instead of conic gradients. Promised myself I'd keep trying and improve - next year!&lt;/p&gt;

&lt;p&gt;And so I did!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6bzjmbesafh6woslubg8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6bzjmbesafh6woslubg8.png" alt="A different approach to forming a sphere than the basic geoid, where the polygon forming the vertical profile is titled by 11.22deg, thus resulting in a different, more efficient geometry" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You're probably scratching your heads right now. Isn't this the same basic globe-like sphere, with meridians and circles of latitude? Yes - and no. The bases for both these spheres - the old one and the new one - look the same:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfa87uxm57lrhp5flu1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfa87uxm57lrhp5flu1k.png" alt="The 16-gons forming the basic vertical profile of my spheres; almost identical, except one is titled by 11.25deg" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Except they aren't the same. Notice that the right 16-gon is rotated by half a step - 11.25 degrees - similarly to what we did with every other circle of latitude in the second paradigm. So what you get both at the poles and the equator aren't sharp tips, it's flat surfaces. This results in the faces at the equator being perfect rectangles (left), not trapezoids (right):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sbrxzc4rypi2y3hvios.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sbrxzc4rypi2y3hvios.png" alt="Two sphere paradigms side by side. Note how the right one is all trapezoids - and triangles near the poles - and the other  is all squares around the equator" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, the tips at the poles are no longer spiky, 16-gon-based pyramids. These triangles form a perfectly flat 16-gon. Which means we don't have to use 16 triangle-shaped containers to form a 16gon, we can just as well use a single, 16-gon-shaped div (one for each pole). This saves us a lot of computational power. We can even go as far as just rounding out our square container into a basic circle with border-radius: 50%&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcbfy4g4dzle17ablrb6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcbfy4g4dzle17ablrb6.png" alt="In the current setup the secment closest to the poles is flat, so we don't need to make it out of 16 triangles, a 16gon or even a circle is just fine" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How does that make it a better material for a Jack O' Lantern?&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/JjxEeKa?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It doesn't, really. Again, the savings weren't as great as I'd have expected. The flat pole gets skipped altogether, as the Halloween pumpkin is usually gutted through the top of the head.&lt;/p&gt;

&lt;p&gt;And so, the quest for the optimal approximation of the sphere continues - and Jack O' Lantern might not be the ideal test subject.&lt;/p&gt;

</description>
      <category>3d</category>
      <category>css</category>
      <category>geometry</category>
      <category>sphere</category>
    </item>
    <item>
      <title>Pure CSS low-poly: Aquatic Life</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Wed, 04 Oct 2023 11:00:00 +0000</pubDate>
      <link>https://dev.to/mackfitz/pure-css-low-poly-aquatic-life-ii5</link>
      <guid>https://dev.to/mackfitz/pure-css-low-poly-aquatic-life-ii5</guid>
      <description>&lt;p&gt;I am obsessed with 3D shapes: cubes, spheres, pyramids, prisms, dodecahedrons - and their various mutations. Discovering and exploring the 3D capabilities of pure CSS has been my passion for a couple of years now. The biggest part of it is learning and figuring out the placement of divs and cutting them into shape - using proper trigonometry to calculate the lengths and angles. Most of the time. Usually towards the end of a project, my brain melts and I settle for just eyeballing that last unwieldy triangle's position. But I also love to experiment with colors, filters, blend modes and animation - especially of aquatic animals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here's a hyperventilating fish
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/LYMJVEr?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In its basic form, it's a cube - which morphs into a cube's level-1 subdivision. Basically what that means is breaking each edge of a cube in two and pushing it outward so as to try to approximate a sphere. It's not very close - but you can kind of see it. Even with simpler shapes. Case in point:&lt;/p&gt;

&lt;h2&gt;
  
  
  An octopus
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/vYvdjRN?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;its head is a level-1 subdivision of an octagon. Think two pyramids conjoined at the base - with each edge broken at the midpoint and pushed away from the center.&lt;/p&gt;

&lt;p&gt;Sadly, you can't go much lower. Subdividing the only simpler regular polyhedron - the tetrahedron (the three-sided pyramid) yields a rather ghastly lump that in no way resembles a sphere.&lt;/p&gt;

&lt;p&gt;But with some splicing, poking and twisting you can get something that evolution wouldn't shy away from making, deep down at the bottom of the sea where no one can see and judge it:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/eYbRxwj?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It wasn't a wasted effort, either. Toying around with this shape further actually lead me to accidentally arrive at the cube level-1 subdivision (shape of the pufferfish)&lt;/p&gt;

&lt;h2&gt;
  
  
  Speaking of fish - and happy accidents...
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/gOZLKYW?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This one started with me trying to create a proper sphere using rhomboids - instead of trapezoids/triangles. At some point, I realized I'd have to use triangles for a certain effect. But instead of scraping the whole thing, I thought it'd be fun to make the squares (precursors to the aforementioned rhomboids, which I would create by using the skewX/skewY transforms) into fish. All I needed was two conic-gradients stacked on top of one another, so I could apply background-blend-mode: difference. And mix-blend-mode: difference, for good measure, so that they're see-through. Plus the eyes and tails.&lt;/p&gt;

&lt;p&gt;This was also a call-back to an older demo of entranced submarine critters.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/vYRKppV?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The starfish are laid out on an invisible scaffolding of a &lt;a href="https://codepen.io/MackFitz/pen/rNZZyPe" rel="noopener noreferrer"&gt;dodecahedron&lt;/a&gt; - a regular polyhedron with twelve pentagonal that's as magical as it is eldritch. I keep coming back to it, it's a gift that keeps on giving - both in terms of the visuals as well as the math. Just this week, whatever I try, I stumble upon Fibonacci's golden ratio all the time everywhere.&lt;/p&gt;

&lt;p&gt;The dodecahedron is actually what started my artistic exploration of submarine biology of--&lt;/p&gt;

&lt;h2&gt;
  
  
  What lurks in the dark...
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/LYOXKxY?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is another fascinating mutation of the basic polyhedra: a stellation. Here's a simple presentation of the idea in 2D:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr1x5pfmakw51yl9b6kd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr1x5pfmakw51yl9b6kd.png" alt="A pentagram inside a pentagon as a 2D example of a stellation" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pentagram is a star formed from a pentagon. You can create one by joining every second tip of the pentagon - OR by breaking each edge at the midpoint and pushing the newly formed corners away from the center (or, if you're feeling defiant, towards it; still works).&lt;/p&gt;

&lt;p&gt;The spooky, moving, three-dimensional pentagram was made by pushing the center - not the tips - of the pentagonal faces up, away from the center. This forms pentagonal pyramids on each of the dodecahedron's faces.&lt;/p&gt;

&lt;p&gt;3D is my biggest passion, my obsession, my mission, my oxygen - but I'll occasionally try something two-dimensional.&lt;/p&gt;

&lt;p&gt;With very subtle 3D - because I'm me and I can't help myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  More fish, anyone?
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/NWBMzEq?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This was my first attempt at trigonometric calcs in CSS - back when only Safari and Firefox had it. It's nothing mind-blowing, but the back/belly and the tail were shaped using the sine curve.&lt;/p&gt;

&lt;p&gt;Trigs were a much more substantial part of the jellyfish/starfish. The whole thing is basically a sine curve that follows a circular/polar coordinate system rather than a linear one.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/QWZpyMa?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;There's more to come. Geometry is an ocean of possibilities - and as a casual, self-taught explorer I'm finding something new and fun almost daily. Very often I'm manually piecing together formulas that have been established and well-documented for centuries, or millennia - but that is also part of the fun.&lt;/p&gt;

</description>
      <category>3d</category>
      <category>css</category>
      <category>geometry</category>
      <category>animation</category>
    </item>
    <item>
      <title>Laying out dots on a dice using display:flex &amp; pseudo-classes</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Thu, 17 Aug 2023 11:35:05 +0000</pubDate>
      <link>https://dev.to/mackfitz/laying-out-dots-on-a-dice-using-displayflex-pseudo-classes-5444</link>
      <guid>https://dev.to/mackfitz/laying-out-dots-on-a-dice-using-displayflex-pseudo-classes-5444</guid>
      <description>&lt;p&gt;I love experimenting with cubes - and to each time somehow embelish the six faces of this beautiful but simple form. I experiment with colors, gradients, filters, mix-blend-modes, patterns, etc. This week I tried making a dice:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/XWorQaB?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In the process, getting the right number of pips to appear on each face was actually the most fun of it all. I used a grid template of three columns and three rows, and hid the dots that I didn't need.&lt;/p&gt;

&lt;p&gt;But it was rather quick and easy. So I thought:&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's do that using display: flex!
&lt;/h2&gt;

&lt;p&gt;For this tutorial we're going to go 2D - so you can see all the faces at all times. Instead of making a full-blown cube, let's lay out the six faces in two rows of three.&lt;/p&gt;

&lt;p&gt;Also, whereas in my demo the 9 dots were nested directly in the face - and the display: grid handled arranging them into a 3x3, for display: flex we'll need to first divide the face into 3 rows, and place 3 dots in each.&lt;/p&gt;

&lt;p&gt;Making use of Emmet - supported by VScode and Codepen - you can quickly spawn the .dice of 6 .faces, each containing 3 .rows of 3 .dots, just by typing in:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;.dice&amp;gt;.face*6&amp;gt;.row*3&amp;gt;.dot*3&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice we're not naming the faces. In my original pen I gave each of them an explicit id naming the number of dots on it. But as you'll see, we don't really need to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  height: 100vh;
  margin: 0;
  background-color: #000; /* hex code for black */
  font-size: .8vmin; /* it's not for text, it's for using the font-size unit of 'em' for sizing containers */
  place-items: center; /* centers the content vertically and horizontally*/
}

body, .dice {
  display: grid; /* on the body it's for centering items, on the others it's for setting up grid rows and columns */
}

.dice {
  grid-template-columns: repeat(3, 1fr); /* three even columns */
  grid-template-rows: repeat(2, 1fr); /* two even rows */
}

.face, .row {
  display: flex; /* only these will require flex for our purposes */
}

.face {
  flex-direction: column; /* arranging the sets of 3 dots into a stack of 3 rows*/
  background-color: hsl(calc(60deg*var(--step)) 100% 50%);
  grid-template-rows: repeat(3, 1fr); /* three even rows */
  grid-template-columns: repeat(3, 1fr); /* three even columns */
  width: 30em;
  aspect-ratio: 1; /* height equals width */
  margin: 30px;
  position: relative; /* this makes .face the point of reference for its children */

}

.row {
  position: relative; /* this makes .face the point of reference for its children */
  aspect-ratio: 3; /* the width is 100% of the .dice's width, the height is a third of that */
}

.face:nth-child(1) { --step: 0; }
.face:nth-child(2) { --step: 1; }
.face:nth-child(3) { --step: 2; }
.face:nth-child(4) { --step: 3; }
.face:nth-child(5) { --step: 4; }
.face:nth-child(6) { --step: 5; }

.dot {
  aspect-ratio: 1; /* the height is 100% of the .row's height (which is a third of the .face's height); this makes .dot a square that is a third of the .face's length and width */
  background-image: radial-gradient(#fff 25%, #fff0 0); /* creating a small dot in the middle of the square using gradients (but there are other ways to do this) */
  mix-blend-mode: difference;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ought to produce this horrendously vibrant set of six faces - nine dots on each, arranged in a 3x3 grid.&lt;/p&gt;

&lt;p&gt;You should be seeing something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjp5dcgwv46vx2xnhialn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjp5dcgwv46vx2xnhialn.png" alt="A set of 6 dice faces, 9 pips each, laid out in 2 rows of 3 faces" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you can try this demo and code along using that:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/abPoxyQ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We'll target each of these individually and use selectors and pseudo-classes to try and display the correct number of dots.&lt;/p&gt;

&lt;p&gt;Also, to hide the pips we don't need, we'll be using visibility: hidden - not display: none. See, the latter would wipe the targeted elements from the grid and break the intended layout. The former just hides the element, but leaves it there, as if transparent, but still holding its position, occupying the space.&lt;/p&gt;

&lt;p&gt;Let's start with&lt;/p&gt;

&lt;h2&gt;
  
  
  One
&lt;/h2&gt;

&lt;p&gt;In the demo I went for the simplest, quickest way to target the .dot at the center of the 3x3 grid of the .face:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#one .dot:not(:nth-child(5)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does is explicitly target the fifth .dot (out of nine) - or, rather, everything but that fifth dot. By default, all dots are visible. We need our central fifth dot to stay that way - and hide everything else.&lt;/p&gt;

&lt;p&gt;In our new setup, using display: flex rather than grid, we need to do things differently - but the goal stays the same: target the dot at the center. So we first need to hit the .row that's in the middle (ergo, the second one) and then the .dot at its center (again, the second one).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) .row:nth-child(2) .dot:nth-child(2) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does the trick. Kinda: we hit the central dot of the central row... and removed it.&lt;/p&gt;

&lt;p&gt;To the rescue comes the :not pseudo-class - which will help us specify what we don't want.&lt;/p&gt;

&lt;p&gt;But it's tricky - and depending on the way you intertwine it with other pseudo-classes, you'll get different results.&lt;/p&gt;

&lt;p&gt;Targetting the second .row like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) .row:not(:nth-child(2)) .dot:nth-child(2) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;erases only the central dots on rows 1 and 3:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fun64iu8f8vjenbs8u0s8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fun64iu8f8vjenbs8u0s8.png" alt="A failed attempt at getting the right number of pips on a dice face, instead of 1 displaying H" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Targetting both the second .row and the second .dot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) .row:not(:nth-child(2)) .dot:not(:nth-child(2)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;isn't it, either:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fucxhd2yfrikhz1j9da.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fucxhd2yfrikhz1j9da.png" alt="A failed attempt at getting the right number of pips on a dice face, removing only the 1s on the corners" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It deleted dots 1 and 3 on row 1 and 3:&lt;/p&gt;

&lt;p&gt;How about just the second dot, then?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) .row:nth-child(2) .dot:not(:nth-child(2)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nope.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6jf33ybrdvetnwou13qq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6jf33ybrdvetnwou13qq.png" alt="A failed attempt at getting the right number of pips on a dice face, hiding only outermost pips on row 2" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Predictably, it hid dot 1 and 3 on row 2.&lt;/p&gt;

&lt;p&gt;No, we clearly need both - and we need to tie them together somehow.&lt;/p&gt;

&lt;p&gt;Sadly, attempts at concatenating them like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) .row:not(:nth-child(2)).dot:not(:nth-child(2)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;yield nothing.&lt;/p&gt;

&lt;p&gt;Luckily, there's another way we can combine the pseudo-classes. Let's rewrite the bit of code that most closely resembles what we want: the one targetting the 2nd .row and 2nd .dot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) :not(.row:nth-child(2)) :not(.dot:nth-child(2)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrapping the :not pseudo-class around the whole selector rather than its :nth-child(2) pseudo-class is possible - and produces the same result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fucxhd2yfrikhz1j9da.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fucxhd2yfrikhz1j9da.png" alt="A failed attempt at getting the right number of pips on a dice face, removing only the 1s on the corners" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And just when you're starting to ask what we're even doing here, you try concatenating:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) :not(.row:nth-child(2)):not(.dot:nth-child(2)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and a mircacle happens:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqe18rniwzkaz5hds7hs5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqe18rniwzkaz5hds7hs5.png" alt="A successful attempt at getting only 1 pip to display on a dice face" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't ask me how or why. It just does, OK?&lt;/p&gt;

&lt;p&gt;What's even crazier, though, is this could be achieved faster - thanks to how the setup was structured. We're trying to hit the second .dot in the second .row. Both second elements - and while we've been targetting each of them specifically, by class, we can generalize a bit, and also avoid repeating ourselves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) :not(div:nth-child(2)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does the trick just as well. As does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) div:not(:nth-child(2)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes you have to go to extreme ways to arrive at simple, elegant solutions like this.&lt;/p&gt;

&lt;p&gt;Also, I got another while trying to work on the .face with two pips:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) div:nth-child(2n+1) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hides odd divs - in our case: the first and last row, and first and last dot on row 2.&lt;/p&gt;

&lt;p&gt;Speaking of which:&lt;/p&gt;

&lt;h2&gt;
  
  
  Two
&lt;/h2&gt;

&lt;p&gt;For this one, we need to target one pip in the top left corner - and another in the opposite corner: bottom right. The simplest way to do this would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(2) .row:nth-child(1) .dot:nth-child(1),
.face:nth-child(2) .row:nth-child(3) .dot:nth-child(3){
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which explicitly targets the first .dot in the first .row and the third .dot in the third .row - resulting in this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxvmkf68t8b2fac3ji6xs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxvmkf68t8b2fac3ji6xs.png" alt="A semi-successful attempt at targetting only the first and last pip on a dice face, we want to keep these and hide the others" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we adress the elephant in the room - the fact that we want the reverse of this - let's try something else. Our code is kinda long and repetitive. Can we do something to shorten it? Like, target the element type rather than class - so we don't have to repeat :nth-child() for both?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(2) div:nth-child(1),
.face:nth-child(2) div:nth-child(3){
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nope. That's also 1:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femdpd7rkmr09h6iu10md.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femdpd7rkmr09h6iu10md.png" alt="This shows one pip on a dice face but we wanted 2" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and also a longer version of my failed attempt at 2 from before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) div:nth-child(2n+1) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, so how about we replace 1 and 3 with 2n+1 so we only need to state the whole combination once?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(2) .row:nth-child(2n+1) .dot:nth-child(2n+1){
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No, we did this before, too:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixbtlm1vmskiynyo7vjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixbtlm1vmskiynyo7vjt.png" alt="A failed attempt at getting the right number of pips on a dice face, removing only the 1s on the corners" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This, however, should be helpful when we get to 4.&lt;/p&gt;

&lt;p&gt;OK, so let's try :not on all classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(2) :not(.row:nth-child(1)) :not(.dot:nth-child(1)),
.face:nth-child(2) :not(.row:nth-child(3)) :not(.dot:nth-child(3)){
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yup, that works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bc5b9y15g80qukxebar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bc5b9y15g80qukxebar.png" alt="A successful attempt at displaying 2 pips of a dice face - but it's flipped horizontally" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also appears that wrapping :not around the class works just as well as wrapping it around just the :nth-child pseudoclass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(2) .row:not(:nth-child(1)) .dot:not(:nth-child(1)),
.face:nth-child(2) .row:not(:nth-child(3)) .dot:not(:nth-child(3)){
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unintended effect - in either version - is that whereas originally the top left and bottom right pips were hidden, applying :not flipped it around, and now the top right and bottom left are the only ones visible. We can fix that by modifying which nth-child of the .dots is targeted, switching 1 to 3 and conversely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(2) .row:not(:nth-child(1)) .dot:not(:nth-child(3)),
.face:nth-child(2) .row:not(:nth-child(3)) .dot:not(:nth-child(1)){
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31lktgmdehpup7dz7lok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31lktgmdehpup7dz7lok.png" alt="A successful attempt at displaying 2 pips of a dice face" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three
&lt;/h2&gt;

&lt;p&gt;This one is really disappointing. You'd think there's a quick way to target the same :nth-children on .row and .dot - first dot on first row, second dot on second row, and third dot on third row - like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(3) .row:nth-child(n) .dot:nth-child(n)
 {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that's not how n works. The n's don't seem to coordinate: the n on .row:nth-child seems in no way related to the n on .dot:nth-child - and what happens is all rows and all dots are targeted, and we get this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnseefuqr3mehayjjrfj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnseefuqr3mehayjjrfj7.png" alt="A failed attempt at displaying 3 pips on a dice face - wrong arguments make all pips disappear" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, to hit only the right ones, we need to name the nth-children explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(3) .row:nth-child(1) .dot:nth-child(1),
.face:nth-child(3) .row:nth-child(2) .dot:nth-child(2),
.face:nth-child(3) .row:nth-child(3) .dot:nth-child(3) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibnvjc3ixjrdftm2pnbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibnvjc3ixjrdftm2pnbh.png" alt="A successful but needlessly verbose method of targeting the three pips on a dice face. Also, these we want to keep, not hide" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, it's fortunately quite easy to reverse which dots are hidden - by :not-ing only the .dots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(3) .row:nth-child(1) :not(.dot:nth-child(1)),
.face:nth-child(3) .row:nth-child(2) :not(.dot:nth-child(2)),
.face:nth-child(3) .row:nth-child(3) :not(.dot:nth-child(3)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku3d57ixs7nxheqmps3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku3d57ixs7nxheqmps3m.png" alt="A successful but needlessly verbose method of targeting the three pips on a dice face" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also take some comfort in the fact that at least you can eliminate some repetition using the :is pseudo-class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(3) :is(
  .row:nth-child(1) :not(.dot:nth-child(1)),
  .row:nth-child(2) :not(.dot:nth-child(2)),
  .row:nth-child(3) :not(.dot:nth-child(3))
)
{
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does is state the selector that doesn't change just once at the beginning, followed by :is() - and within the parentheses put all the bits that have some variation to them, separated by commas).&lt;/p&gt;

&lt;p&gt;And yes, I tried that for two pips, too - but that didn't work. :is and :not seemed not to like being either the direct descendant or the parent of the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four
&lt;/h2&gt;

&lt;p&gt;We already got halfway there - with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(2) .row:nth-child(2n+1) .dot:nth-child(2n+1){
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixbtlm1vmskiynyo7vjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixbtlm1vmskiynyo7vjt.png" alt="A failed attempt at getting the right number of pips on a dice face, removing only the 1s on the corners" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and before that, with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) :not(.row:nth-child(2)) :not(.dot:nth-child(2)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fucxhd2yfrikhz1j9da.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fucxhd2yfrikhz1j9da.png" alt="A failed attempt at getting the right number of pips on a dice face, removing only the 1s on the corners" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These two seem to be doing the reverse of what we want in two different ways - either by targetting :nth-child(2n+1) or :not(:nth-child(2)) - ergo, an odd number, or not 2. So we need to target the opposite of either of those.&lt;/p&gt;

&lt;p&gt;The funny thing that happens when you un-:not the second variant (the red face with cyan pips) like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(4) .row:nth-child(2) .dot:nth-child(2) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you get this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx0owuk940so8johdc9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx0owuk940so8johdc9u.png" alt="A failed attempt at getting the right number of pips on a dice face, removing only the dot in the middle" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same when you replace :nth-child(2n+1) with 2 - or 2n (same thing, no more even numbers to target). Applying :not to variant 1 (yellow face, blue pips):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(4) .row:not(:nth-child(2n+1)) .dot:not(:nth-child(2n+1)) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;results in the same.&lt;/p&gt;

&lt;p&gt;So what do we do to hit the second dot on rows 1 and 3, and the whole second row?&lt;/p&gt;

&lt;p&gt;Hit the element, not the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(4) div:nth-child(2n) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We did something similar for 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(1) div:nth-child(2n+1) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There, we needed to clear the odd rows and odd dots on the even row. Here we wanted the inverse: clear the even row and even dots on odd rows - and that's what exactly the code did:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4phiq9bkwax4ifw8y85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4phiq9bkwax4ifw8y85.png" alt="A successful attempt at displaying 4 pips on a dice face, the 1s on the corners" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Five
&lt;/h2&gt;

&lt;p&gt;With five it won't be as smooth - but not as bad as we had it with 3. Basically, we need to hit the second dot on rows 1 and 3, and dots 1 and 3 on row 2. But I'm being unnecessarily blunt and explicit here. We can also generalize: 1 and 3 are odd numbers and we can reference them as such, no problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(5) .row:nth-child(2n+1) .dot:nth-child(2n),
.face:nth-child(5) .row:nth-child(2n) .dot:nth-child(2n+1){
  visibility: hidden
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, this clears even dots on odd rows and odd dots on the even row:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcd90hn02gx6owhgqmp68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcd90hn02gx6owhgqmp68.png" alt="A successful attempt at displaying 5 pips on a dice face, the 1s on the corners and the 1 in the middle" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also shorten our code by :is-ifying the redundant repetition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(5) :is(
  .row:nth-child(2n+1) .dot:nth-child(2n),
  .row:nth-child(2n) .dot:nth-child(2n+1)
) {
  visibility: hidden
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Six
&lt;/h1&gt;

&lt;p&gt;The shorten and sweetest of them all: target just the second dot on each row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.face:nth-child(6) .dot:nth-child(2) {
  visibility: hidden;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No unpleasant surprises there:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9at1tb6pr4wa06h4of9m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9at1tb6pr4wa06h4of9m.png" alt="A successful attempt at displaying 6 pips on a dice face, the 1s in the leftmost and rightmost column" width="292" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And so we have a full set of 6 faces with the correct number of dots - using display: flex. I think I had more fun with this than I did with display: grid that I used in the demo. That let me get straight to the point in a single line. But I love to tinker with and combine pseudo-elements, elements and selectors.&lt;/p&gt;

&lt;p&gt;I also do like dice. I'm not quite done with D6 (that is, the six-faced, cuboid dice) - and there are plenty more: D10, D12 and D20.&lt;/p&gt;

&lt;p&gt;Let the good times roll!&lt;/p&gt;

</description>
      <category>pseudoclasses</category>
      <category>selectors</category>
      <category>dice</category>
    </item>
    <item>
      <title>Particles &amp; spiral patterns in CSS: part IV - the logarithmic spiral</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Wed, 16 Aug 2023 10:20:06 +0000</pubDate>
      <link>https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-iv-g4b</link>
      <guid>https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-iv-g4b</guid>
      <description>&lt;p&gt;The Archimedean - that we talked about in &lt;a href="https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-iii-3ljj"&gt;part 3&lt;/a&gt; - isn't the only type of concentric spiral. It's the OG, having been described by Archimedes ca. 225 BC. The next big thing didn't come for almost 2000 years. But in reality, it had always been there, everywhere around, and long before Archimedes himself walked the Earth. The logarithmic spiral:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdxdayagdmrf1218cn4o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdxdayagdmrf1218cn4o.png" alt="A logarithmic spirals of small orbs made with HTML and SCSS" width="627" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This shape - which is actually pretty common in nature: from sea shells and ram horns to sunflower pods - differs from the Archimedean in one crucial way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4rv5aqfej2f3uxxwa49.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4rv5aqfej2f3uxxwa49.png" alt="A comparison of logarithmic and Archimedean spirals of small orbs made with HTML and SCSS" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Archimedean variant, as the particles go in loops toward the center, the distance shrinks by a steady increment, resulting in the space between the loops being even throughout. In the logarithmic spiral, each particle is a set fraction of the one directly preceding it. For instance: the second particle is 99% of the first. The third is 99% of the second - and 98.01% of the first. Or 99% squared. The fourth is 97.0299% of the first, or 99% cubed. And so on.&lt;/p&gt;

&lt;p&gt;In CSS we can achieve a similar effect with nesting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;div&amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;div&amp;gt;
                &amp;lt;div&amp;gt;
                  &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;


div {
  position: relative; /* to make sure the parent, not the body, will be the point of reference for the child */
  width: 50%; /* half of the width of the parent */
  aspect-ratio: 1; /* height equals width */
  border: 2px solid;
  display: grid; /* to center the squares inside their containers/parents */
  place-items: center;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces a series of squares inside a square inside a square:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggrf6wpsb7g69sub95gd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggrf6wpsb7g69sub95gd.png" alt="A series of nested squares created with HTML and CSS where each square is half the width and height of its parent" width="705" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each square has a width and height of 50% of its parent - but the shrinkage is so dramatic that we can only distinguish 5, maybe 6 squares (out of a total of 10), anything beyond that blends in with the rest. At the same time, unlike in the Archimedean, even if you go up to 1000 in this HTML matryoshka, the width will never be zero. It'll be a half of half of half of half, getting ever closer to zero - but ever slower, and ultimately never reaching it.&lt;/p&gt;

&lt;p&gt;Now, we could create our logarithmic spiral that way - but that'd require too much fiddling with HTML: either manually recursively nesting 300 divs, or getting that done with a pre-processor like Pug, which isn't very straightforward, either.&lt;/p&gt;

&lt;p&gt;Instead, let's do it with SCSS - reusing the code from the Archimedean:&lt;/p&gt;

&lt;p&gt;For the HTML type:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;.logarithmic&amp;gt;.arm*300&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and press either Enter or Tab for the Emmet extension to instantly spawn 300 copies of &lt;/p&gt;

&lt;p&gt;For the styling, we'll only have to marginally modify the SCSS of the Archimedean spiral:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$particles: 300; /* an SCSS variable defining the number of particles across the whole serpentine; not the same as a CSS variable */

body {
  margin: 0;
  background-color: #000; /* hex code for the color black */
  height: 100vh; /* so that the body takes up the entire viewport's height */
  display: grid; /* we'll want to center the content */
  place-items: center; /* centers the contents of the div horizontally and vertically */
  overflow: hidden;
  font-size: .8vmin; /* setting font size to be .8 of 1% of the shorter viewport dimension (height on desktop, width on mobile */
}

.logarithmic { /* rather than .archimedean just to match with the HTML setup above */
  position: relative; /* thus the container will be the point of reference for its child, the .foreArm */
  display: flex; /* for centering the content */
  height: 100em; /* using font size as a unit for object size - making it 80% of the shorter vieport dimension */
  aspect-ratio: 1; /* the width is the same as the height */ 
  justify-content: center; /* we'll want the .arm to be placed at the center of the horizontal axis */
}

.arm { /* the single joint depositing the dots */
  --particles: #{$particles}; /* a proper CSS variable that takes its name and value after its SCSS counterpart */
  --loops: 5;
  position: absolute; /* the container floats freely, independent of any other objects */
  bottom: 50em; /* the bottom end of the .arm is anchored at the center of the viewport */
  height: calc(50em*(1 - var(--turnStep)/var(--particles))); /* each new instance of the .arm will be incrementally shortened by 1/300 of the original height */
  width: 5em; /* that's for defining the width of the dot */
  transform-origin: bottom; /* its pivot point is located at its bottom end */
  rotate: calc(360deg/var(--particles)*var(--turnStep)*var(--loops));
  /* it performs 5 loops of 360 degrees over 300 particles - ergo, each loop is 60 particles, each rotated incrementally by 6deg - but this formula will let you tweak that number */
}

.arm::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  aspect-ratio: 1;
  --increment: calc(360deg/var(--particles)*var(--loops));
  --color: calc(var(--turnStep)*var(--increment));
  background-image: radial-gradient(at 33% 33%, white, hsl(var(--color) 100% 50%) 50%);
  border-radius: 50%;
  scale: calc(1 - var(--turnStep)/var(--particles));
  box-shadow: inset -1em -1em 1em #000b;
  border-right: 1px solid var(--color);
  rotate: calc(-360deg/var(--particles)*var(--turnStep)*var(--loops));
}

@for $i from 1 through $particles { /* in our case that's 300*/
  .arm:nth-child(#{$i}) {
    --turnStep: #{$i - 1};
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, about $particles add:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/use"&gt;@use&lt;/a&gt; "sass:math";&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This will let us use mathematical functions. We're going to need powers - as you might remember from the example of squaring and cubing 99%. We're going to add a second variable right below --turnstep in our for-loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@for $i from 1 through $particles { /* in our case that's 300*/
  .arm:nth-child(#{$i}) {
    --turnStep: #{$i - 1};
    --shrinkage: #{ math.pow(.99, $i - 1) }; /* .99 will be reaised to the power that is the current loops $i-1 */
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this means .99 will be raised to the power of 0 (which equals 1, so no shrinkage occurs), then to the power of 1, which gives us .99, our default shrinkage factor, then to the powers of 2, 3, 4, all the way to 300.&lt;/p&gt;

&lt;p&gt;We'll then apply this variable to change the paradigm of progressive shortening of the .arm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm { /* the single joint depositing the dots */
  --particles: #{$particles};
  --loops: 5;
  position: absolute;
  bottom: 50em;
  height: calc(50em*var(--shrinkage)); /* the shrinkage factor is applied to the arm's default length is 50% of the container */
  width: 5em;
  transform-origin: bottom;
  rotate: calc(360deg/var(--particles)*var(--turnStep)*var(--loops));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should give us something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcb1le84flu5osp9hgom7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcb1le84flu5osp9hgom7.png" alt="A logarithmic spirals of small, non-scaling orbs made with HTML and SCSS" width="627" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It gives you a nice idea of what we're going for - but the shrinkage of the orbs themselves is still off, so let's take care of that. We need to modify the scale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  aspect-ratio: 1;
  --increment: calc(360deg/var(--particles)*var(--loops));
  --color: calc(var(--turnStep)*var(--increment));
  background-image: radial-gradient(at 33% 33%, white, hsl(var(--color) 100% 50%) 50%);
  border-radius: 50%;

  scale: var(--shrinkage); /* and that's it, no calcs needed */

  box-shadow: inset -1em -1em 1em #000b;
  border-right: 1px solid var(--color);
  rotate: calc(-360deg/var(--particles)*var(--turnStep)*var(--loops));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our scale needs to start at 1 and shrink from there - and .99 to the power of zero is exactly that. The orbs shrink properly now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdxdayagdmrf1218cn4o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdxdayagdmrf1218cn4o.png" alt="A logarithmic spirals of small, gradually scaling orbs made with HTML and SCSS" width="627" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But you can play with it some more:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/MWZgmJR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;For example, fiddle with the shrinkage factor on that last line of SCSS. It is at .99 - which might seem like barely anything at first. But remember how halving the width/height shrank the squares into mere dots ten steps in. Here we have 300 steps. If we changed it to .98 - again, a seemingly insignificant change - we'd get this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkxm3jtmtqi387ee3wt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkxm3jtmtqi387ee3wt6.png" alt="Image description" width="627" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We don't even see much at all beyond the second loop (out of five)&lt;/p&gt;

&lt;p&gt;Conversely, a shrinkage factor or .995 results in this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qoguhdhjk6tt82e07aa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qoguhdhjk6tt82e07aa.png" alt="Image description" width="627" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 5 loops are clearly visible, but the last orb isn't even close to the center. You'd have to add more orbs to cover more ground.&lt;/p&gt;

&lt;p&gt;So, depending on the number of loops and the shrinkage factor of your choosing, you'll have to try and decide what looks good.&lt;/p&gt;

&lt;p&gt;There are more types of concentric spirals. Archimedes isn't the only one that had one named after him. Fermat, Fibonacci and Cornu had theirs - all very interesting on their own. There's also the hyperbolic spiral. But these are too much math for this series, which focuses more on the fun of CSS. So, upcoming parts will focus on other aspects of styling: extending the serpentine and spiral patterns into the third dimension and animating them.&lt;/p&gt;

</description>
      <category>css</category>
      <category>spiral</category>
      <category>cssart</category>
      <category>generativeart</category>
    </item>
    <item>
      <title>Particles &amp; spiral patterns in CSS: part III - the Archimedean spiral</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Thu, 10 Aug 2023 10:30:23 +0000</pubDate>
      <link>https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-iii-3ljj</link>
      <guid>https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-iii-3ljj</guid>
      <description>&lt;p&gt;If you thought my obsession with particles and curves waned over the past two weeks since the last post, then I'm here to disappoint you. In &lt;a href="https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-i-2p9n"&gt;Part 1&lt;/a&gt; and &lt;a href="https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-ii-4bn8"&gt;Part 2&lt;/a&gt; I covered linear and circular serpentine patterns. But for me where it's really at is spirals:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/MackFitz/pen/qBQKvEM" rel="noopener noreferrer"&gt;https://codepen.io/MackFitz/pen/qBQKvEM&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OK, not the best example, since it puts too much emphasis on my beloved trigonometry - so let's take away the irrelevant third dimension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd614azjkp8y8ru1hgoa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd614azjkp8y8ru1hgoa.png" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a spiral. An Archimedean spiral, to be exact. One in which the dots are deposited by an arm pivoting around the center of the screen - and steadily getting shorter as it goes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftui1ygpo9n7hw3vqopoj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftui1ygpo9n7hw3vqopoj.gif" width="600" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This produces a spiral for a number of loops until it ends right in the center.&lt;/p&gt;

&lt;p&gt;Our HTML setup is going to be as basic as it gets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="archimedean"&amp;gt; &amp;lt;!-- the container --&amp;gt;
  &amp;lt;div class="arm"&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- the depositior of the dots --&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To recreate the spiral above, we're only going to need 300 of .arm (5 loops, each consisting of 60 dots). If your code editor supports Emmet, all you'll need to type is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;.archimedean&amp;gt;.arm*300&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and press either Enter or Tab for it to instantly spawn 300 copies of &lt;/p&gt;

&lt;p&gt;Our CSS/SCSS is going to be pretty similar to what we used for our circular serpentine patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$particles: 300; /* an SCSS variable defining the number of particles across the whole serpentine; not the same as a CSS variable */

body {
  margin: 0;
  background-color: #000; /* hex code for the color black */
  height: 100vh; /* so that the body takes up the entire viewport's height */
  display: grid; /* we'll want to center the content */
  place-items: center; /* centers the contents of the div horizontally and vertically */
  overflow: hidden;
  font-size: .8vmin; /* setting font size to be .8 of 1% of the shorter viewport dimention (height on desktop, width on mobile */
}

.archimedean {
  position: relative; /* thus the container will be the point of reference for its child, the .foreArm */
  display: flex; /* for centering the content */
  height: 100em; /* using font size as a unit for object size - making it 80% of the shorter vieport dimension */
  aspect-ratio: 1; /* the width is the same as the height */ 
  justify-content: center; /* we'll want the .arm to be placed at the center of the horizontal axis */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arm depositing the dots is going to be even simpler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm { /* the single joint depositing the dots */
  --particles: #{$particles}; /* a proper CSS variable that takes its name and value after its SCSS counterpart */
  --loops: 5;
  position: absolute; /* the container floats freely, independent of any other objects */
  bottom: 50em; /* the bottom end of the .arm is anchored at the center of the viewport */
  height: 50em; /* since there's got to be room for the .foreArm, we don't want it to be be 50em - but can't be less than 25em */
  width: 5em; /* that's for defining the width of the dot */
  transform-origin: bottom; /* its pivot point is located at its bottom end */
}

.arm::before { /* the dot */
  content: '';
  position: absolute;
  inset-inline: 0; /* defines the width of the dot - at 100% of the .arm's width */
  aspect-ratio: 1; /* the height matches the width */
  background-color: red; /* placeholder color */
  border-radius: 50%; /* it's a dot, so it needs to be perfectly round */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you should be seeing a single red dot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqxf1701d7y1s7nymjju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqxf1701d7y1s7nymjju.png" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the dots are there, but they're stacked on top of one another - by virtue of having the position: absolute, which allows them to ignore the presence of other divs - so they appear as one, at the top center of their container. They have also not been rotated yet - so let's do that. Before we spin them, we need to define the increment of rotation for each of the 300 dots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@for $i from 1 through $particles { /* in our case that's 300*/
  .arm:nth-child(#{$i}) {
    --turnStep: #{$i};
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, at the bottom of .arm's code we can add the instruction for rotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm {
  --particles: #{$particles};
  --loops: 5;
  position: absolute;
  bottom: 50em;
  height: 50em;
  width: 5em;
  transform-origin: bottom;
  rotate: calc(360deg/var(--particles)*var(--turnStep)*var(--loops));
  /* it performs 5 loops of 360 degrees over 300 particles - ergo, each loop is 60 particles, each rotated incrementally by 6deg - but this formula will let you tweak that number */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should result in this neat circle of tightly packed red dots:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz2y1c2hih1awf3fbzkh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz2y1c2hih1awf3fbzkh.png" width="769" height="773"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But that's not what we wanted. We wanted a spiral of five loops, ending in the middle.&lt;/p&gt;

&lt;p&gt;For the record - all 300 dots are there (as we've already established). It's just that the five loops are on top of each other (just as the dots appeared as one before we rotated them). For us to see them we need to steadily shorten the arm.&lt;/p&gt;

&lt;p&gt;We'll need to tweak the height:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm {
  --particles: #{$particles};
  --loops: 5;
  position: absolute;
  bottom: 50em;
  height: calc(50em*(1 - var(--turnStep)/var(--particles))); /* each new instance of the .arm will be incrementally shortened by 1/300 of the original height */
  width: 5em;
  transform-origin: bottom;
  rotate: calc(360deg/var(--particles)*var(--turnStep)*var(--loops));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much more like it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3ndvdhiehsadzoa8ubw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3ndvdhiehsadzoa8ubw.png" width="709" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But still not quite: the dots get progressively more cramped as the loops go, so much so that towards the end they look like a single thick line.&lt;/p&gt;

&lt;p&gt;There isn't much we do about the spacing of the dots. The math for that would be too complex for this post. This would be much easier to do in SVG - where the spacing is handled automatically by just two parameters in the "stroke-dashArray" attribute, which would give us this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfgv0sxdq0j0i7kl6cet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfgv0sxdq0j0i7kl6cet.png" width="800" height="838"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But we can't do that. Plus, that's not 300 dots, it's simply however many identical dots it could squeeze in along the curve. Instead, what we could do in CSS is make use of our --turnStep variable. Just as we defined the rotation increment and the progressive shortening of the .arm, so now we can shrink the dots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  aspect-ratio: 1;
  background-color: red;
  border-radius: 50%;
  scale: calc(1 - var(--turnStep)/var(--particles));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should finally give us a basic approximation of what we came here for: a proper Archimedean spiral of dots shrinking as the spiral progresses toward the center:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpv7qvjnfy0o0bo4uzbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpv7qvjnfy0o0bo4uzbf.png" width="697" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's add some 3D to the scene by styling the dots into proper glossy orbs.&lt;/p&gt;

&lt;p&gt;First, the light: a white spotlight, slightly off-center:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  aspect-ratio: 1;
  --color: red; /* the value is a placeholder */
  background-image: radial-gradient(at 33% 33%, white, var(--color) 50%); /* notice that we switched from background-color to background-image for this one */
  border-radius: 50%;
  scale: calc(1 - var(--turnStep)/var(--particles));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, how about some shading on the opposite end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  aspect-ratio: 1;
  --color: red;
  background-image: radial-gradient(at 33% 33%, white, var(--color) 50%);
  border-radius: 50%;
  scale: calc(1 - var(--turnStep)/var(--particles));
  box-shadow: inset -1em -1em 1em #000b; /* the shadow is inside rather than outside the dot, it's offset by 1em vertically and horizontally, and blurred */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and some subtle backlight for contrast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  aspect-ratio: 1;
  --color: red;
  background-image: radial-gradient(at 33% 33%, white, var(--color) 50%);
  border-radius: 50%;
  scale: calc(1 - var(--turnStep)/var(--particles));
  box-shadow: inset -1em -1em 1em #000b;
  border-right: 1px solid var(--color);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2873mz6ikduptqp8tjb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2873mz6ikduptqp8tjb.png" width="723" height="755"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This looks nice - but a little off. Notice how the shading is inconsistent? For the orbs at the top of the spiral, it seems to be coming from top left, and for those at the bottom - from bottom right. That's because the orbs rotate with the arm, and with them so does the shading. So we need to counteract that - by rotating the orbs by the same degree, but in the opposite direction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  aspect-ratio: 1;
  --color: red;
  background-image: radial-gradient(at 33% 33%, white, var(--color) 50%);
  border-radius: 50%;
  scale: calc(1 - var(--turnStep)/var(--particles));
  box-shadow: inset -1em -1em 1em #000b;
  border-right: 1px solid var(--color);
  rotate: calc(-360deg/var(--particles)*var(--turnStep)*var(--loops));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj21wf56zsm51u4v81c37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj21wf56zsm51u4v81c37.png" width="732" height="761"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now all we need to do is add some color to it - by replacing the placeholder with something that uses the rotation increment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.arm::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  aspect-ratio: 1;
  --increment: calc(360deg/var(--particles)*var(--loops));
  --color: calc(var(--turnStep)*var(--increment));
  background-image: radial-gradient(at 33% 33%, white, hsl(var(--color) 100% 50%) 50%);
  border-radius: 50%;
  scale: calc(1 - var(--turnStep)/var(--particles));
  box-shadow: inset -1em -1em 1em #000b;
  border-right: 1px solid var(--color);
  rotate: calc(-360deg/var(--particles)*var(--turnStep)*var(--loops));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so, after many twists and turns, here we are at last:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqohxsyc33f4vre5ovh7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqohxsyc33f4vre5ovh7.png" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But again, this is just the beginning of the fun. For instance, if this isn't trippy enough on its own, try to spin it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.archimedean {
  position: relative; 
  display: flex; 
  height: 100em; 
  aspect-ratio: 1; 
  justify-content: center;
  animation: spin 6s linear infinite;
}

@keyframes spin {
  100% {
    rotate: 360deg;
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, remember how the dots looked like a single thick line when smushed together the closer they were to the middle. You could extend that effect to the whole spiral by spawning more of the dots. A thousand or two maybe? Since at such quantities the intricacies of the shading would be lost, you could try different things instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/MackFitz/pen/qBQGRGE?editors=0100" rel="noopener noreferrer"&gt;https://codepen.io/MackFitz/pen/qBQGRGE?editors=0100&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And lots of other things - so see you next time!&lt;/p&gt;

</description>
      <category>css</category>
      <category>spiral</category>
      <category>cssart</category>
      <category>generativeart</category>
    </item>
    <item>
      <title>Particles &amp; spiral patterns in CSS: part II</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Mon, 24 Jul 2023 04:58:17 +0000</pubDate>
      <link>https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-ii-4bn8</link>
      <guid>https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-ii-4bn8</guid>
      <description>&lt;p&gt;The week of Codepen's particle challenge is up - but I'm far from done obsessing over particles and serpentines. In Friday's &lt;a href="https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-i-2p9n"&gt;Part I&lt;/a&gt; I merely scratched the surface, showing you my first demo for this week's challenge:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/RwqyKVa?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;and based on that, showed you how to draw the most basic 2D serpentine pattern:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/XWyYvwm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To reiterate: the dots are deposited in equal intervals by a line spinning around its center point and also moving in a straight line - like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnclsf3oamobrkexwu0y.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnclsf3oamobrkexwu0y.gif" alt="A demonstration of how a spiral is drawn by an arom spinning around its center and moving from left to right, depositiing dots as it goes" width="600" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For my second demo I decided to push it a little further in terms of complexity:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/VwVxOgR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;and then tweaked it a bit:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/qBQKqPo?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It was a headache to coordinate - and now might be a headache to wrap your head around ;)&lt;/p&gt;

&lt;p&gt;Let me break down what's happening here - which is only a slight modification of my original linear serpentine. This time, however, instead of traveling along a straight line from one end to another, the pivot point is stationary. It is at the center of the viewport. Also, the line that spins around like the hand of a clock isn't the one drawing. It is merely a join for another line, and it is that one that deposits the dots as it spins while being spun itself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0uxalrb2oxluc1252g7a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0uxalrb2oxluc1252g7a.gif" alt="A demonstration of how an arm draws a circular, spirograph-like spiral by rotating around the center and flailing another, shorter joint even faster" width="600" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As complex as that sounds, the HTML setup for it is almost as simple as for the linear serpentine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="spiroGraph"&amp;gt; &amp;lt;!-- the container for our serpentine --&amp;gt;
  &amp;lt;div class="arm"&amp;gt; &amp;lt;!-- the spinning joint anchored at the center of the viewport --&amp;gt;
    &amp;lt;div class="foreArm"&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- the joing that spins while also being spun --&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll need to spawn a large number of the .arms - and like last time, the easiest way to do that is using Emmet - which is built into VS Code and Codepen. Instead of typying up the entire HTML markup above manually, just type:&lt;/p&gt;

&lt;p&gt;.spiroGraph&amp;gt;.arm*180&amp;gt;.foreArm&lt;/p&gt;

&lt;p&gt;and press Enter (VS Code) or Tab (Codepen) - and witness 180 .arms, each with a .foreArm, spawned instantly.&lt;/p&gt;

&lt;p&gt;That does it for HTML - so let's go over to our style sheets. As before, let's use SCSS - which will allow us to write most of our code just as we would in plain CSS, but will let us use for-loops for quickly styling each of our 180 .arms and .foreArms, and also declare variables.&lt;/p&gt;

&lt;p&gt;First, let's set up the scene:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$particles: 360; /* an SCSS variable defining the number of particles across the whole serpentine; not the same as a CSS variable */

body {
  margin: 0;
  background-color: #000; /* hex code for the color black */
  height: 100vh; /* so that the body takes up the entire viewport's height */
  display: grid; /* we'll want to center the content */
  align-content: center;
  overflow: hidden;
  font-size: .8vmin; /* setting font size to be .8 of 1% of the shorter viewport dimention (height on desktop, width on mobile */
}

.spiroGraph {
  position: relative; /* thus the container will be the point of reference for its child, the .foreArm */
  display: flex;
  height: 100em; /* using font size as a unit for object size - making it 80% of the shorter vieport dimension */
  justify-content: center; /* we'll want the .arm to be placed at the center of the horizontal axis */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for all the moving bits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
.arm {
  --particles: #{$particles}; /* a proper CSS variable that takes its name and value after its SCSS counterpart */
  position: absolute; /* the container floats freely, independent of any other objects */
  bottom: 50em; /* the bottom end of the .arm is anchored at the center of the viewport */
  height: 30em; /* since there's got to be room for the .foreArm, we don't want it to be be 50em - but can't be less than 25em */
  transform-origin: bottom; /* its pivot point is located at its bottom end */
  rotate: calc(360deg/var(--particles)*var(--turnStep)); /* it does a full 360 over 360 particles - ergo each step is 1deg - but this formula will let you tweak that number */
}

.foreArm {
  --loops: 9; /* we'll want our serpentine to have 9 loops */
  position: absolute;
  bottom: 100%; /* its bottom will be where normally its top would be; thus it'll be anchored at the tip of the .arm */
  height: 20em; /* 50em (half of the viewport's height) minus the height of the .arm */
  /* also note we didn't declare the width for either - which is why we do this: */
  display: flex;
  justify-content: center; /* the .foreArm's child WILL have width, and we need to cente it */
  transform-origin: bottom; /* its pivot point is located at its bottom end */
  rotate: calc(360deg/var(--particles)*var(--loops)*var(--turnStep)); /* it has to make 9 loops of full 360 rotations, ergo it has to spin 9 times faster than the foreArm: 1deg x 9 = 9deg */
}

.foreArm::before {
  content: ''; /* no text */
  position: absolute;
  width: 2em;
  aspect-ratio: 1; /* the height matches the width */
  border-radius: 50%; /* our particle will be round */
  background-color: red /* for now */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we haven't set up the variable of --turnStep for each .arm:nth-child, no rotation was applied - so what you're probably seeing right now is a single red dot in the middle at the top of the container;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu7tvw9aqjydx2gjy1f4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu7tvw9aqjydx2gjy1f4.png" alt="A single red dot - indicating that the setup for the spiral works, but the variable for rotation step was not applied" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's fix that - and quickly loop over the variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@for $i from 1 through 360 {
  .arm:nth-child(#{$i}) {
    --turnStep: #{$i};
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that's more like it. Are you seeing a spiral of 360 red dots?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku0y11pd3klqr75d9l0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku0y11pd3klqr75d9l0m.png" alt="A spiral of red dots made with HTML and CSS" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can just as easily add some more color to it - by applying the same degree-based formula that we did to rotation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.foreArm::before {
  content: ''; /* no text */
  position: absolute;
  width: 2em;
  aspect-ratio: 1; /* the height matches the width */
  border-radius: 50%; /* our particle will be round */
  background-color: hsl(calc(360deg/var(--particles)*var(--turnStep)) 100% 50%); /* the color will cycle through the full spectrum of colors of the rainbow */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should make our serpentine look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahbtcaw2k3lej94t9pyg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahbtcaw2k3lej94t9pyg.png" alt="A spiral of rainbow-colored dots, made with HTML and CSS" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And thus we get a proper spirograph-like serpentine. But again, this is really basic, and there's a whole lot more you can do with it. You can make more - or fewer - particles as you like (as long as you make sure your HTML and your SCSS have the same number of objects). Or experiment with dot sizes - or arm lengths and centers of rotation.&lt;/p&gt;

&lt;p&gt;To make things easier, here's a live demo on Codepen that you can tinker with. For instance, see the madness that happens when you just reverse the direction of rotation on the .foreArm - like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rotate: calc(-360deg/var(--particles)*var(--loops)*var(--turnStep)); /* notice the minus before 360deg - that makes it assume the negative value, and this reverse the direction of rotation */

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/eYQjwEG?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Have fun - and come back next time for another crazy loop done in (almost) pure HTML and CSS.&lt;/p&gt;

</description>
      <category>css</category>
      <category>spiral</category>
      <category>cssart</category>
      <category>generativeart</category>
    </item>
    <item>
      <title>Particles &amp; spiral patterns in CSS: part I</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Fri, 21 Jul 2023 12:40:14 +0000</pubDate>
      <link>https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-i-2p9n</link>
      <guid>https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-i-2p9n</guid>
      <description>&lt;p&gt;This week &lt;a href="http://codepen.io/" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt;, known for its fun weekly coding &lt;a href="https://codepen.io/challenges" rel="noopener noreferrer"&gt;challenges&lt;/a&gt;, rolled out one that made me drop everything and code, code, code. This week it's all about particles - i.e. large amounts of small, identical/similar objects, such as droplets of water, sparks, marbles or dust - usually in the context of them being subjected to forces such as gravity, wind, etc. But I decided to try something else that fascinates me.&lt;/p&gt;

&lt;h1&gt;
  
  
  Spirals!
&lt;/h1&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/qBQKqPo?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Serpentines, squiggles and all sorts of non-linear, twisted paths. Something that CSS isn't really known for. It's the domain of SVG, with its fascinating system of curves. Which you indeed &lt;em&gt;can&lt;/em&gt; bring over to CSS via clip-path: path() - but it isn't animatable (last time I checked, at least).&lt;/p&gt;

&lt;p&gt;Either way, there are ways to squeeze that flowy aesthetic out of pure CSS. Let it be your &lt;a href="https://en.wikipedia.org/wiki/Spirograph" rel="noopener noreferrer"&gt;Spirograph&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Over the next few days I'll show you how to make some of my favorite simple, two-dimensional serpentine shapes.&lt;/p&gt;

&lt;p&gt;Today let's start with the most basic of them:&lt;/p&gt;

&lt;h2&gt;
  
  
  Linear squiggle
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/RwqyKVa?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;All the pens I made for the challenge are in 3D - but the basic idea behind them is simple, and the only difference is the axis of rotation. In 2D it'd look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkp50xiv1zbmagma20s7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkp50xiv1zbmagma20s7m.png" alt="A spiral of rainbow-colored little orbs" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's happening here is we have a row of 180 tall, narrow div with a candy/marble at the top. Each of them is incrementally rotated by a multiple of 12degrees depending on its order in the sequence: the first one - by 12deg (1*12deg), the second - by 24deg (2*12deg), the third - 36deg (3*12deg), etc.&lt;/p&gt;

&lt;p&gt;The HTML setup is as simple as it gets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="chain"&amp;gt;
  &amp;lt;div class="segment"&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- we'll need 180 of those --&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using a code editor with Emmet, all you need to do to spawn as many divs as you want is type:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;.segment*180&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and press either Enter (VS Code) or Tab (Codepen) and that is it. You could use Pug - a preprocessor I love for its indentation-based syntax, and have used for the demo above - but truly, Emmet is the most straightforward way to do this.&lt;/p&gt;

&lt;p&gt;With that out of the way, we can now supply the style. We'll be using SCSS - which is a midpoint between Sass and plain CSS, with the fun functionalities of the former but with classic CSS syntax that doesn't rely on indentation.&lt;/p&gt;

&lt;p&gt;First the containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  margin: 0;
  background-color: #000; /* hex code for the color black */
  height: 100vh; /* so that the body takes up the entire viewport's height */
  display: grid; /* we'll want to center the content */
  align-content: center;
  overflow: hidden;
}

.chain {
  display: flex;
  flex-direction: row; /* non-essential, but will come in handy if/when declaring @media mobile views */
  position: absolute; /* the container floats freely, independent of any other objects */
  inset: 25vh 0; /* it takes up 50% of container height, at its center, and 100% of its width*/
  justify-content: space-between; /* we'll want to space out the anchors */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for the main actors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.segment { /* that's our anchor */
  --particles: 180; /* a CSS variable defining the number of particles */
  height: 100%; /* notice we don't really need the width */
  position: relative; /* we need this so that our subordinate pseudo-element takes this as its point of reference in terms of measurements */
  rotate: calc(var(--turnStep)*12deg); /* the variable for rotating the .segment based on its order in the sequence will be defined in a for-loop later on */
  display: flex; /* for centering the content in the X axis, as done in the following line */
  justify-content: center;
}

.segment::before { /* that's the actual single particle */
  content: ''; /* no text */
  position: absolute; /* makes it look at its direct parent as a point of reference */
  width: 5vmin; /* 5% of the smaller viewport dimenstion; just a stylistic choice, make it more or less as you like */
  aspect-ratio: 1; /* makes the height match the width 1:1*/  
  border-radius: 50%; /* turns the div into a circle */
  background-color: red; /* placeholder - for now */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point you should be seeing the whole chain as a single, thick horizontal line:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31h917ugfhau1ssddpdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31h917ugfhau1ssddpdc.png" alt="Image description" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The red dots form a single straight line because the rotation has not been applied. That's because we haven't set up the variable specifying the ordinal number of each dot in the sequence.&lt;/p&gt;

&lt;p&gt;We could do this manually in CSS - but declaring each :nth-child and its variable would take ages. Luckily, SCSS lets us run a single loop that will do it for us in split-second.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@for $i from 1 through 30 {
  .segment:nth-child(30n+#{$i}) {
    --turnStep: #{$i};
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assumes that we want our serpentine of 180 particles to have 6 loops - hence the 30 (180/6). The particles' number will be incremented from 1 until 30, and then back from 1 for as long as the serpentine lasts. With that, each subsequent segment now knows to incrementally by multiplying its --turnstep by 12deg. It makes a full 360 loop every 30 segments:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2idfddrtn2lviyk89mtd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2idfddrtn2lviyk89mtd.png" alt="A spiral of red dots made with HTML and CSS" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It'd also be nice if we could make the serpentine itself a little more colorful. Luckily, we can - using the same value that we applied to rotation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;calc(var(--turnStep)*12deg)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can declare colors using an angle. For this we need to utilize the hsl() functional notation. HSL is an acronym for Hue, Saturation, Lightness - the three parameters it uses to describe colors. Hue is the one we are most interested in, since that's the one that takes in angles - as if picking the colors from rainbow spectrum of red, yellow, green, cyan, blue, fuchsia and back to red:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7q79rggcvz4foicndii1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7q79rggcvz4foicndii1.png" alt="A rainbow spiral made with HTML and CSS" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Their values are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;red: 0deg&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;yellow: 60deg&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;green/lime: 120deg&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;cyan: 180deg&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;blue: 240deg&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fuchsia: 300deg&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;red: 360deg&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one might seem redundant. But what this means is that the colors, as it were, run in circles: the hue beyond 359deg is still a perfectly valid color: whatever its value is - say, 420 - the code subtracts 360 (or its multiple) from it. 420 is the same as 60 - ergo, yellow. Similarly, -60deg is fuchsia.&lt;/p&gt;

&lt;p&gt;Knowing that, we now can declare our color using hsl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.segment::before { /* that's the actual single particle */
  content: ''; /* no text */
  position: absolute; /* makes it look at its direct parent as a point of reference */
  width: 5vmin; /* 5% of the smaller viewport dimenstion; just a stylistic choice, make it more or less as you like */
  aspect-ratio: 1; /* makes the height match the width 1:1*/  
  border-radius: 50%; /* turns the div into a circle */
  background-color: hsl(calc(var(--turnStep)*12deg) 100% 50%); /* the colors run through all colors of the rainbow within a single loop */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should produce a rainbow-tinted serpentine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhchgckdf3v3209i6drw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhchgckdf3v3209i6drw.png" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the most basic, simple serpentine. The fun thing about SCSS is that it lets you easily modify the spiral. Let's say you'd like to have more particles - or more (or fewer) loops. Or for the colors to go through the hues of a rainbow across the whole serpentine, not a single loop.&lt;/p&gt;

&lt;p&gt;Of course, that'd be too much for a single blog post - so instead have this demo to play with:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/XWyYvwm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can play around with the number of particles and loops. Make sure that the number of $particles in SCSS matches the respective property in Pug (the HTML preprocessor).&lt;/p&gt;

</description>
      <category>scss</category>
      <category>pug</category>
      <category>spiralparticles</category>
    </item>
    <item>
      <title>The weird ways you can create triangles with CSS</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Tue, 11 Apr 2023 10:24:18 +0000</pubDate>
      <link>https://dev.to/mackfitz/the-geodesic-sphere-1oi1</link>
      <guid>https://dev.to/mackfitz/the-geodesic-sphere-1oi1</guid>
      <description>&lt;p&gt;In my never-ending quest to code crazy 3D geometries I've long been fantasizing and theorizing about geodesic spheres - approximations of an orb made exclusively out of triangles connecting at weird, incomprehensible angles.&lt;/p&gt;

&lt;p&gt;Now the nightmare finally came into being:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/bGxZzGg?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Careful touching it, it's a real CPU hog - and some browsers might choke on it, taking a good few seconds to load and for the Play button to notice it's been pressed. In my experience, on Firefox it runs fine, but Chrome and Edge are struggling, at least in the beginning. My guess is it's the CSS trigonometry. Chrome was usually the faster one when I did my trigs with PHP or SCSS and the browser got the pre-baked values.&lt;/p&gt;

&lt;p&gt;For this reason, I probably better not bother talking about the calculations going on here. They fried my brain to a crisp - and I'm used to this stuff. You don't need to go down this rabbit hole - unless you really, &lt;em&gt;really&lt;/em&gt; want to.&lt;/p&gt;

&lt;p&gt;But there's a simpler, more bite-sized lesson here:&lt;/p&gt;

&lt;h1&gt;
  
  
  Weird ways to create triangles with CSS
&lt;/h1&gt;

&lt;p&gt;If you'd like your standard rectangular container to be a different polygon, these days your first choice would be the clip-path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;div {
    clip-path: polygon(0 100%, 50% 0, 100% 100%);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which will produce a triangle by slicing the square, starting from bottom left, to top center, then bottom right:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/zYJQqKa?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It's a quick, elegant solution that was designed for this specific purpose - unlike the old workaround involving tinkering with borders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.triangle {
  width: 0;
  aspect-ratio: 1;
  border: 50em solid; /* percentages won't work, you need concrete units: px, em, ch, etc. */
  border-color: red yellow blue green;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which creates a triangle by setting really thick borders on your container, then the colors on three of them to transparent:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/XWxrWZx?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;br&gt;
But these aren't my today's subject. I needed to mention them to give you some historical background - and I will talk about them in the context of other methods - but If you'd like to read more on them, here's a &lt;a href="https://alvaromontoro.com/blog/67970/drawing-a-triangle-with-css" rel="noopener noreferrer"&gt;comprehensive article&lt;/a&gt; from our fellow blogger &lt;a href="https://dev.to/alvaromontoro"&gt;Alvaro Montoro&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What I *would* like to talk about today is how CSS gives you many, many ways to create non-standard shapes. These methods will be closer in nature to the border-based method - i.e. not designed specifically to cut out shapes, just exhibiting certain properties that can be used to form them.&lt;/p&gt;

&lt;p&gt;For instance, take a look at the cube:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/JjBzyqr?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It's a set of six square walls, each perpendicular to its direct neighbors - but perspective distorts those angles. The farther something is from the viewer, the smaller it appears. The walls on the left, right, top and bottom appear to be trapezoids - because one edge is closer to us than the other.&lt;/p&gt;

&lt;p&gt;If the walls were taller and extended further into the distance away from us - at some point, their farther edge would shrink so much it'd almost look like a single point:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/mdzdEzy?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The same would happen if, instead, you shortened the perspective:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/wvYvWjW?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In both cases, however, it'd have to go to extreme lengths for the lines to converge. In the case of stretching the walls, even if they went past the specified distance of the perspective, they still have a long way to go.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/NWOWbNe?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Similarly, shortening the perspective would bring you ever closer to the point of convergence, but never &lt;em&gt;quite&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/WNaNoRm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Unless, maybe, you pulled it all the way down to zero...? This could never work, right?&lt;/p&gt;

&lt;p&gt;And yet, it does - and perspective: 0 is something very different from perspective: none:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/LYgYxNB?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In terms of the visual result, they are the opposite. With perspective: none the top, bottom, left and right walls are invisible to us, because they are perpendicular to the front face. With perspective: zero the vanishing point acts like a black hole: it sucks everything in with incredible force that defies all logic. The height of the wall and the angles no longer matter, everything is stretched to its limits and physics seem to no longer apply.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/LYgVvda?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;So, whether you want to or not, you get four beautiful, perfectly spiky triangles - or at least approximating perfection very, very closely in the case of very slight inclinations (why you would even use them will be discussed below).&lt;/p&gt;

&lt;p&gt;But by far the most awesome and useful property of perspective-based triangles is the gradients.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6a8n072wgdm1vrvkhq8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6a8n072wgdm1vrvkhq8a.png" alt="A linear CSS gradient applied to a 3D cube's walls imitating a conic gradient" width="800" height="799"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="cube"&amp;gt;
  &amp;lt;div class="wall"&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class="wall"&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class="wall"&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class="wall"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;


.cube {
  position: relative;
  width: 100em;
  aspect-ratio: 1;
  perspective: 0;
}

.wall {
  position: absolute;
  left: 50em;
  height: 100em;
  aspect-ratio: 1;
  transform-origin: 0 50%;
  transform: rotate(calc(90deg*(var(--nth)))) translateX(50em) rotateY(90deg);
  background-image: linear-gradient(hsl(calc(var(--nth)*90deg) 100% 50%), hsl(calc((var(--nth) + 1)*90deg) 100% 50%));
  transition: all .3s ease-in-out;
}

.wall:nth-child(1) {
  --nth: 1;
}

.wall:nth-child(2) {
  --nth: 2;
}

.wall:nth-child(3) {
  --nth: 3;
}

.wall:nth-child(4) {
  --nth: 4;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you get looks just like a regular conic-gradient - and in fact it is what I'd use before we had that (the time when we didn't isn't that ancient - and we STILL don't have conics in SVG). But is there a reason to still use perspective-based gradients today? They look the same - even when the conic is spinning and the linear only pretending to, and in fact just sliding its gradient along a line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* replacing the background-image definition */

.wall {
    background-size: 100% 400%;
    background-position-y: calc(var(--nth)*-100%);
    animation: slide 18s linear infinite;
}

@keyframes slide {
  100% {
    background-position-y: calc(var(--nth)*-100% - 400%);
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/yLRNrRp?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;But there are things conic gradients can't do - and that is animation. If you try to apply animation to them, they'll skip between the keyframes in single steps - whether it's about color transitions or change of angles. Meanwhile, linear-gradients - while still constrained as far as colors are concerned - do offer some freedom in defining background-size and background-position, with smooth transitions between keyframes:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/OJBVGeW?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can also stretch and bend the triangles easily - by changing the perspective-origin (you might need to double-click)&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/rNqVgxo?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Why, then, if perspective-based triangles are so great, have I only used them on only three vertices in the pen with the geodesic sphere - which has hundreds of them? Didn't use them in the sphere, itself, either - just the play button.&lt;/p&gt;

&lt;p&gt;Performance.&lt;/p&gt;

&lt;p&gt;Your browser and your CPU/GPU don't like hundreds of objects flying around. I've known that for years - and forced my way, anyway. Still, perspective requires two containers: the one that sets the perspective, and another than is subjected to it. That doubles the polygon count - and I haven't even begun! They generally don't like too many gradients, either. Animations, transforms, filters and blend modes - all of which I love to pieces - are hard on them, too. On top of that, now we have trigonometry in CSS, which forces browsers to crunch the numbers in real time. If you're reading this on Chrome, chances are you had to wait 10 seconds for the geodesic sphere to even load.&lt;/p&gt;

&lt;p&gt;Because of that - and also little glitches and idiosyncracies - I needed to curb my ambitions. I can't have &lt;em&gt;everything&lt;/em&gt; animating - and since I can't, there was no point using the more resource-heavy solution. It's still my favorite, but needs to be used sparingly. It's like VHS vs Betamax - or DVD vs Blu-Ray. The superior technology loses because it's more expensive - here in terms of processing power.&lt;/p&gt;

&lt;p&gt;There is also one area where gradients outshine both clip-path and perspective - and that is in nesting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Clip-path will cut off anything non within its bounds - and that includes any nested content. It also flattens perspective/overrides 3D transforms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Perspective distorts nested content - whether you want it or not.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are ways to work around this, but it'd require extra containers - and I usually don't want that.&lt;/p&gt;

&lt;p&gt;So, how does one make a triangle analogous to what we did above with just conic-gradients? Let's take the most basic container with an aspect-ratio of 1 (that is, the height is equal to the width):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="triangle"&amp;gt;&amp;lt;/div&amp;gt;


body {
    background: #000;
}

.triangle {
    width: 40%; /* roughtly half of the viewport's width - using this insead of my usual em for simplicity */
    aspect-ratio: 1;
    border: 3px #fff dashed;
    margin: 5em;
    background-image: conic-gradient(red,blue);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0miok7lbb4tim14mv70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0miok7lbb4tim14mv70.png" alt="A conic CSS gradient starting from 0 degrees with red, progressing clockwise through yellow, green and ending in blue" width="791" height="790"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the gradient starts and ends in the upper half, for simplicity let's place its origin at 50% 100% (center, bottom).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.triangle {
    background-image: conic-gradient(at 50% 100%, red, yellow, green, blue);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukq059uokfyu8qydnefo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukq059uokfyu8qydnefo.png" alt="A conic CSS gradient starting from 0 degrees with red, progressing clockwise through yellow, green and ending in blue, with its center offset to bottom center" width="791" height="790"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we need the gradient to start at the top left corner with the color red, and progress through yellow, green and blue, finishing at the right corner. You &lt;em&gt;could&lt;/em&gt; do this manually, through trial and error getting the angles just right. Luckily, you don't have to! Trigonometry can do that for you. All you need is to serve it two pieces of information:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The height of your container - equal to the width, ergo: 40% (disregard the unit, we just need a number).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Half&lt;/em&gt; of the width: 40/2 = 20.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With that we can now calculate the angle of the line that goes from our gradient's origin (50% 100%) to either of the container's corners.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.triangle {
    --angle: atan(calc(20/40));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What that does is first calculate the ratio of half of the width to the height of the container. That's 0.5. Then it finds what angle's tangent that would be. Since it won't tell it to you after it's done, let me: it's 26.5650512 degrees.&lt;/p&gt;

&lt;p&gt;Now we need to use that data. I split the whole property into separate lines so I can comment on each piece of information served.&lt;/p&gt;

&lt;p&gt;Let's check if that's correct by tilting the start point by 26.5650512 degrees towards the left corner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.triangle {
    --angle: atan(calc(20/40));
    background-image: conic-gradient(
        from calc(-1*var(--angle)) /* the starting angle: -26.5650512deg */
        at 50% 100%, /* gradient's center=point: bottom center */
        red, yellow, green, blue);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kg6ebsexczev0nsn6co.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kg6ebsexczev0nsn6co.png" alt="A conic CSS gradient starting from -26.5650512 degrees with red, progressing 360degrees clockwise through yellow, green and ending in blue" width="791" height="790"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a reason why it needed to be rotated by negative 26.5650512 degrees. As far as I know, the conic-gradient's direction can't be changed; it progresses clockwise only.&lt;/p&gt;

&lt;p&gt;Which lets us know what to do next: make the gradient extend only to the top right corner instead of doing the whole 360. That corner is at 26.5650512 degrees - which means it's twice that from where we currently are: at -26.5650512deg.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.triangle {
    --angle: atan(calc(20/40));
    background-image: conic-gradient(
        from calc(-1*var(--angle)) /* the starting angle: -26.5650512deg */
        at 50% 100%, /* gradient's center=point: bottom center */
        red, yellow, green, blue calc(var(--angle)*2)); /* the gradient goes from red to blue over the span of 53.1301024 degrees only */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwztvvzdnx7t5ria96yv4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwztvvzdnx7t5ria96yv4.png" alt="A conic CSS gradient starting from -26.5650512 degrees with red, progressing 53.1301024 degrees clockwise through yellow, green and ending in blue, but continuing with blue for the remainder of the turn" width="791" height="790"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only problem is we didn't tell our gradient to shut off after 53.1301024 degrees. It still wants to do a full circle, spreading the last color declared. That means that if we want no color after the 53.1301024deg mark, we need to declare it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.triangle {
    --angle: atan(calc(20/40));
    background-image: conic-gradient(
        from calc(-1*var(--angle)) /* the starting angle: -26.5650512deg */
        at 50% 100%, /* gradient's center=point: bottom center */
        red, yellow, green, blue calc(var(--angle)*2),
        transparent 0 ); /* the gradient goes from red to blue over the span of 53.1301024 degrees only */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Transparency does the trick - no color after 53.1301024 degrees:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4xxxryaufvdyibqfz7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4xxxryaufvdyibqfz7q.png" alt="A conic CSS gradient starting from -26.5650512 degrees with red, progressing 360degrees clockwise through yellow, green and ending in blue" width="774" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But the real trick is the zero following the color specification. If we were to be super strict with our syntax, we'd do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.triangle {
    --angle: atan(calc(20/40));
    background-image: conic-gradient(
        from calc(-1*var(--angle))
        at 50% 100%,
        red, yellow, green, blue calc(var(--angle)*2),
        transparent calc(var(--angle)*2) ); /* instead of transparent 0 */

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so that the transparency starts at the same point the blue ends (we have to do that, by the way, because if we don't specify the endpoint, CSS will assume the transparency starts at 100% - and you can probably guess what happens). But it can be 0, too - since the gradient progresses clockwise only, it cannot turn back, so if the onset of transparency is a smaller value than the end of blue, the smaller one will be ignored, and the blue's end will act as the onset of transparency.&lt;/p&gt;

&lt;p&gt;Here's the whole code for creating a triangle using conic-gradient, with comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.triangle {
    width: 40%; /* roughtly half of the viewport's width - using this insead of my usual em for simplicity */
    aspect-ratio: 1;
    margin: 5em;
    --angle: atan(calc(20/40)); /* calculating the angle between either of the top corners and the center of the bottom by calculating the reverse tangent the base and the height */
    background-image: conic-gradient(
      from calc(-1*var(--angle)) /* the angle needs to be negative since the vertical line is the angle of 0, and here we're going counter-clockwise*/
      at 50% 100%, /* center, bottom*/
      red, yellow, green,blue calc(var(--angle)*2), /* the gradient ends with blue after making an arc of 53.1301024 (from 26.5650512 to get to the vertical line, and then, mirroring that, another 26.5650512 to reach the top right angle*/
      transparent 0); /* the transparency needs to start right where the blue ends, so you could also serve it the same calc - but if you're lazy, you can give it any number that is smaller than the calculated value, because it can't go back to it, and will continue onwards from where it ended */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's, of course, way more you can do going off from this basic setup - like having your triangle's tip point in different directions: up, down, left or right:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/OJBVYBQ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;and many more tweaks and more subtle and precise calibrations.&lt;/p&gt;

&lt;p&gt;But that's a story for another time.&lt;/p&gt;

</description>
      <category>css</category>
      <category>gradients</category>
      <category>perspective</category>
      <category>3d</category>
    </item>
    <item>
      <title>What's your bread and butter?</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Mon, 10 Apr 2023 21:04:19 +0000</pubDate>
      <link>https://dev.to/mackfitz/whats-your-bread-and-butter-5dfl</link>
      <guid>https://dev.to/mackfitz/whats-your-bread-and-butter-5dfl</guid>
      <description>&lt;p&gt;Despite the idiom used, I'm not asking about your job. The very opposite of it, actually. Is there something you do in your spare time that has nothing to do with coding, and yet satisfies the same drive and passion.&lt;br&gt;
For me that's my kitchen experiments:&lt;/p&gt;

&lt;p&gt;Bread:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;plain wheat, crazy shapes 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8ztlyjxho40iy9k1vp7.jpg" alt="A loaf of plain wheat bread composed of small balls of dough" width="800" height="450"&gt;
&lt;/li&gt;
&lt;li&gt;chickpea/lentil bread (that's crazy enough in itself)
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frd519ermjdb7i57sta9g.jpg" alt="A loaf of chickpea and lentil gluten-free bread" width="800" height="450"&gt;
That's what started my foodie passion. I needed to learn to make my own gluten-free bread because those cost an arm and a leg back in the day. I developed my own buckwheat bread recipe. And while I can't eat gluten, I love the texture, the stretchiness and the maleable nature - so I'm happy to make wheat and rye breads for my family.
A couple years later I needed to make another switch, to low-oxalate diet - and ditch my buckwheat bread entirely. Enter chickpea and lentils.
While researching low-oxalate options I read somewhere that while walnuts are a no-no, the oil is fine. Not a super fan of walnuts, and never had walnut oil before - but I had to try.
Now I'm absolutely obsessed with it - and while I can't eat walnuts, my family love walnut cakes, and so I'm more than happy to make them:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfcnuvi8dk3qpgiyxglo.png" alt="Walnut cake and a bottle of walnut oil, bot of my own making" width="800" height="439"&gt;
Those are just the tip of the iceberg of what I do in the kitchen. Yogurt, sourdough bread, flax gel, flax butter, custard... Any simple, basic dish that I can try, tweak, re-interpret - sometimes just for the sake of trying rather than any practical use. Which mirrors my aprroach to coding. Webdev is not my actual job, just a hobby - and rather than proper websites or apps I mostly experiment with 3D shapes coded in pure CSS. It's not useful or practical - but it's just so much FUN:
&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/jOxzKmL?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's &lt;em&gt;your&lt;/em&gt; chickpea bread and flax butter?&lt;/p&gt;

</description>
      <category>hobby</category>
      <category>extracurricular</category>
      <category>experiment</category>
    </item>
    <item>
      <title>Happy holidays!</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Sat, 08 Apr 2023 13:21:59 +0000</pubDate>
      <link>https://dev.to/mackfitz/happy-holidays-3ok1</link>
      <guid>https://dev.to/mackfitz/happy-holidays-3ok1</guid>
      <description>&lt;p&gt;This is my homage to the tradition of painting eggs - an art I'm still fascinated as ever but have not actively practiced for years. So today, I coded one, instead:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/VwEYrpB?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Pardon the mid-90's internet level of stylelessness. It's obviously rushed, done partly late last night, but mostly this morning among the noise and the fury of spring cleaning and Easter preparations. My wife's stare right now could kill ;) I myself still have two batches of wallnuts to press oil out of, and then bake at least two pastries out of the resulting walnut meal to gift to our family.&lt;/p&gt;

&lt;p&gt;I might also try dyeing eggs with onion peels or aronia juice.&lt;/p&gt;

&lt;p&gt;And add more patterns to the CSS egg in the coming days. So many ideas?&lt;/p&gt;

&lt;p&gt;Anyway, merry Easter, everyone! What are your favorite holiday traditions and foods?&lt;/p&gt;

</description>
      <category>css</category>
      <category>3d</category>
      <category>easter</category>
      <category>sphere</category>
    </item>
    <item>
      <title>CSS geometric art: the Great Dodecahedron</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Mon, 20 Mar 2023 10:51:14 +0000</pubDate>
      <link>https://dev.to/mackfitz/css-geometric-art-the-great-dodecahedron-3kjc</link>
      <guid>https://dev.to/mackfitz/css-geometric-art-the-great-dodecahedron-3kjc</guid>
      <description>&lt;p&gt;Riding on the wave of joy following the release of Chrome 111 - and so, finally decent support of trigonometric functions in CSS calcs across all major browsers - I coded another insane geometry in CSS - and gave it faux metallic shading, for good measure:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/JjaBpVw?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is what is called the Great Dodecahedron. It's an expansion of the basic dodecahedron:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/rNZZyPe?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This basic dodecahedron has twelve five-sided faces. The Great Dodecahedron builds upon each of these faces an elevated starfish pattern - geometricallly resulting in a total of 180 faces (12 pentagons subdivided into 5 elevated triangular faces, which are then subdivided into 3 - ergo: 12*5*3 = 180).&lt;/p&gt;

&lt;p&gt;This, however, not exactly true. I simplified the geometry a bit: on top of the twelve pentagonal faces I placed five obtuse triangles, suspended between the center of the pentagon and every second tip of the pentagon:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84d7up12jjcjoumql6zi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84d7up12jjcjoumql6zi.gif" alt="An animation of five obtuse triangles being drawn from the center of the pentagon to one tip, then another two edges away, and back to the center, all creating a star pattern" width="600" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The intersecting obtuse triangles created the ilusion that there are in fact ten triangles forming the star's arms (the red and yellow ones), and then the surface not covered by them also seemed like separate triangles (the blue ones).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwcu1o4rglpbx9jyq2g1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwcu1o4rglpbx9jyq2g1x.png" alt="A star pattern on top of a pentagon, formed by five intersecting obtuse triangles anchored at the center of the pentagon and rotated iteratively" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All I needed Then I needed to place each of these obtuse triangles slightly above the pentagonal face, and incline it so that the long edge touches the face - so that the star looks like it's elevated above the pentagon's surface, as if pulled up by the center.&lt;/p&gt;

&lt;p&gt;There is some beautiful / fascinating / nightmarish geometry and logic behind it, but that's a story for another time.&lt;/p&gt;

&lt;p&gt;For now, take a look at what an eldritch abomination I've created on the basis of the pentagon when I first I first heard the call of the Dodecahedron:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/LYOXKxY?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;br&gt;
The deep CSSeas hide some hypnotic beauty, too:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/vYRKppV?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And there is much, much more than can be built upon the mighty dodecahedron. Oh, so much work - and so few tentacles...&lt;/p&gt;

</description>
      <category>css</category>
      <category>3d</category>
      <category>trigonometry</category>
      <category>dodecahedron</category>
    </item>
    <item>
      <title>CSS 3D art: a 3D translucent rainbow-colored donut</title>
      <dc:creator>Maciek Fitzner</dc:creator>
      <pubDate>Wed, 15 Mar 2023 07:19:28 +0000</pubDate>
      <link>https://dev.to/mackfitz/css-3d-art-a-3d-translucent-rainbow-colored-donut-9gf</link>
      <guid>https://dev.to/mackfitz/css-3d-art-a-3d-translucent-rainbow-colored-donut-9gf</guid>
      <description>&lt;p&gt;3D geometries were always THE thing that I loved CSS for - regardless of its performance and practicality of it. But to really make the best of it, I needed trigonometry - for which I originally used PHP and then switched to Sass/SCSS (a CSS preprocessor).&lt;/p&gt;

&lt;p&gt;But now it's here - in CSS calcs!&lt;/p&gt;

&lt;p&gt;sin(), cos(), tan(), asin(), acos() and atan() have been under consideration for the longest time. I remember them being old news when I first looked into it in 2019, and then no news for a couple of years. Thought they'd never happen.&lt;/p&gt;

&lt;p&gt;Then one day last year, Safari quietly rolled out the whole lot - and then also sqrt(), pow() and hypot(). Last December Firefox picked up. But it seemed like that was it. The usual outliers, who usually lagged behind, did something weird - and the cool kids couldn't be bothered.&lt;/p&gt;

&lt;p&gt;But finally, Chrome 111 was released and it too supports trigonometry in calcs.&lt;/p&gt;

&lt;p&gt;So I went to town with it! Here's a &lt;a href="https://codepen.io/MackFitz/full/BaOdeOB" rel="noopener noreferrer"&gt;live demo on Codepen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/MackFitz/embed/BaOdeOB?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can have some fun with it, too!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;you click on the numbers on the left and the fractions on the right (the currently selected option is the one glowing white). The input labels might be slow/stubborn, so you might need to click again for them to react.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can modify the shape of the ring and the cross-section - the numbers denote how many sides the basic polygonal shape has. By default it's at 16, approximating a circle - and 32 is even closer. But you can go lower: you've got an octagon, a hexagon, a pentagon, a square and a triangle&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can change the girth, too: it's 1/4 by default - meaning the girth is a quarter of the ring's diameter. 1/10 will look like a bracelet. 1/3 is like a real beefy donut. 1/2 gives you a torus with no visible gap inside, and at 2/3 the ring intersects itself. And in case you were wondering, 1/1 would simply be a sphere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Depending on your device, the performance might be slow at higher segment numbers. On mine it runs smoothly up to 16 segments, and starts stuttering at 32.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance depends on the browsers, too. The demo seems to run faster on Firefox than Chrome.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it should run on mobile, too - but I haven't actually checked. I still only use my 13-year-old Nokia 1616.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The math behind it all is fascinating ;) I'm doing a series of 3D CSS tutorials, but it's going slow, and I haven't even gone over the cube entirely - and haven't even touched on trigonometry yet - so getting all the way to the torus might take a while.&lt;/p&gt;

&lt;p&gt;Speaking of which, here are the tutorials:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/mackfitz/css-3d-the-cube-part-1-393j"&gt;CSS 3D: The Cube - Part I&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/mackfitz/css-3d-the-cube-part-2-5132"&gt;CSS 3D: The Cube - Part II&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
