<?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: Johnny Fekete</title>
    <description>The latest articles on DEV Community by Johnny Fekete (@johnnyfekete).</description>
    <link>https://dev.to/johnnyfekete</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%2F383746%2F311c9519-de5b-4f20-9054-6502a5a05719.jpg</url>
      <title>DEV Community: Johnny Fekete</title>
      <link>https://dev.to/johnnyfekete</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnnyfekete"/>
    <language>en</language>
    <item>
      <title>I Thought Creating My Browser Extension Course Would Take a Weekend. It Took 4 Months</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Thu, 08 Jan 2026 10:03:54 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/i-thought-creating-my-browser-extension-course-would-take-a-weekend-it-took-4-months-2nn6</link>
      <guid>https://dev.to/johnnyfekete/i-thought-creating-my-browser-extension-course-would-take-a-weekend-it-took-4-months-2nn6</guid>
      <description>&lt;p&gt;I thought creating a &lt;em&gt;“short video course”&lt;/em&gt; would take a weekend.&lt;br&gt;
It took 4 months.&lt;/p&gt;

&lt;p&gt;Here's what happened, what I learned, and why I'm actually glad it spiraled out of control.&lt;/p&gt;

&lt;h2&gt;
  
  
  How This Started
&lt;/h2&gt;

&lt;p&gt;Last November, I spoke at React Summit US about building browser extensions with React (&lt;a href="https://gitnation.com/contents/building-browser-extensions-with-react-that-dont-break-browsers" rel="noopener noreferrer"&gt;watch it here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;As I was preparing to the talk, I found that there’s not a lot of education material available, let alone a full course for total beginners.&lt;/p&gt;

&lt;p&gt;So I decided to create one, as a side project. "I'll spend a weekend recording a few quick videos," I thought. "Cover the basics, publish them, done."&lt;/p&gt;

&lt;p&gt;That weekend project turned into 14 comprehensive lessons and 4 months of work 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  The Learning Curve I Didn’t Expect
&lt;/h2&gt;

&lt;p&gt;Creating a conference talk is one thing. Creating a full course? Completely different beast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I thought would be hard:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recording videos, talking in front of the camera&lt;/li&gt;
&lt;li&gt;Explaining technical concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What was actually hard:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ok, recording was indeed challenging, but once you have the nice backdrop and some basic lights, it’s fine (until your neighbor starts vacuum cleaning in the middle of your shot)&lt;/li&gt;
&lt;li&gt;Structuring 14 lessons so they build on each other logically&lt;/li&gt;
&lt;li&gt;Writing scripts that work for video (turns out speaking at a conference ≠ teaching on camera)&lt;/li&gt;
&lt;li&gt;Editing (my DaVinci Resolve skills definitely improved 😅)&lt;/li&gt;
&lt;li&gt;Deciding what to include vs. what to cut&lt;/li&gt;
&lt;li&gt;Testing everything to make sure it actually works for someone following along&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;The course teaches browser extension development by building a real note-taking extension called TabNotes.&lt;/p&gt;

&lt;p&gt;An actual, production-ready extension that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syncs data across devices&lt;/li&gt;
&lt;li&gt;Works on both Chrome and Firefox&lt;/li&gt;
&lt;li&gt;Uses modern architecture with the WXT framework&lt;/li&gt;
&lt;li&gt;Gets published to both extension stores&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It covers everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manifest V3 configuration&lt;/li&gt;
&lt;li&gt;Popup interfaces and background scripts&lt;/li&gt;
&lt;li&gt;Storage APIs and cross-device syncing&lt;/li&gt;
&lt;li&gt;Content scripts and messaging&lt;/li&gt;
&lt;li&gt;Cross-browser compatibility&lt;/li&gt;
&lt;li&gt;Publishing to both stores&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The course is pretty technical, but I tried to explain concepts as clearly as possible. It's for anyone who wants to build their first browser extension and understand the gotchas (and there are many).&lt;/p&gt;

&lt;h2&gt;
  
  
  What Worked
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Starting with an outline.&lt;/strong&gt; I mapped out all 14 lessons before recording anything. This structure kept the course coherent and ensured each lesson built logically on the previous one. I didn’t need a pre-written script, because my outline was super detailed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building the extension first.&lt;/strong&gt; Before recording a single video, I built the complete TabNotes extension. This meant I knew exactly what I was teaching and could confidently walk through each step without surprises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batching tasks.&lt;/strong&gt; I grouped similar work together - wrote all scripts, then recorded all videos, then edited everything. This flow kept me in the right mindset for each type of work and saved weeks of context-switching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The mirror trick.&lt;/strong&gt; I recorded with my iPhone but put a mirror in front of me so I could see myself. That visual feedback helped me catch awkward moments and maintain better energy on camera. Simple but effective.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accept "good enough."&lt;/strong&gt; As developers, we love perfection. But I spent way too long tweaking things that students probably won't even notice. Ship it, get feedback, iterate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invest in better audio equipment.&lt;/strong&gt; I used my iPhone's built-in microphone. It works, but audio quality matters more than I realized. Next time, I'd get a proper external mic from day one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record in shorter sessions.&lt;/strong&gt; I tried to power through entire lessons in one go. Breaking recordings into smaller chunks would have kept my energy more consistent and made editing easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Surprised Me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Teaching forces you to understand things deeply.&lt;/strong&gt; I thought I knew extension development well. But explaining it to someone else exposed gaps in my own understanding. I had to dig deeper into the Chrome and Firefox docs multiple times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;People want context, not just code.&lt;/strong&gt; Early lessons were just "here's how to do X." Better lessons explained &lt;em&gt;why&lt;/em&gt; you'd do X and when you shouldn't. That context matters more than I realized.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistakes are part of the process.&lt;/strong&gt; When I got stuck and made some errors, instead of cutting them out, I embraced them. It’s normal to make mistakes, and showing the debugging process is also a valuable lesson.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Video editing is time-consuming.&lt;/strong&gt; So. Much. Editing. Removing "ums," cutting, adjusting audio levels, adding captions... I have new respect for YouTubers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I'm Sharing This
&lt;/h2&gt;

&lt;p&gt;Video content is super easy to consume, but quite hard to create.&lt;/p&gt;

&lt;p&gt;If you've thought about creating educational content - a course, tutorial series - here's my advice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It will take longer than you think.&lt;/strong&gt; 3x your time estimate. Then add 50% more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You'll learn as much as your students.&lt;/strong&gt; Teaching forces clarity. You can't hand-wave over concepts you don't fully understand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start before you feel ready.&lt;/strong&gt; I kept thinking "I should learn more first" or "I should make it perfect." But you learn by doing, and perfect is the enemy of done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make it free first.&lt;/strong&gt; I made this course completely free (no signup, no paywall). It removes the pressure to be "worth the price" and lets you focus on making it genuinely helpful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;After 4 months, the course is live: &lt;a href="https://webextensiontutorial.com" rel="noopener noreferrer"&gt;webextensiontutorial.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;14 lessons, all free, all on &lt;a href="https://www.youtube.com/playlist?list=PL73_IyyS-6PWIlsGL9c_dX7lTtBElzBPd" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;. You build a complete extension from scratch and publish it to both stores.&lt;/p&gt;

&lt;p&gt;Was it worth it? Absolutely. Despite the time investment, I enjoyed the process. Pushing myself to create something I hadn't done before - that's kind of the point, right?&lt;/p&gt;

&lt;p&gt;If you've been curious about browser extensions, check it out.&lt;/p&gt;

&lt;p&gt;And if you've created educational content yourself, I'd love to hear what surprised you most about the process.&lt;/p&gt;

</description>
      <category>extensions</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Oops, I Made a VS Code Extension</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Mon, 08 Jul 2024 07:26:51 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/oops-i-made-a-vs-code-extension-477d</link>
      <guid>https://dev.to/johnnyfekete/oops-i-made-a-vs-code-extension-477d</guid>
      <description>&lt;p&gt;Ever had one of those &lt;em&gt;"how did I get here?"&lt;/em&gt; moments in coding?  Well, buckle up, because I've got a story for you.&lt;/p&gt;

&lt;p&gt;It all started with localization. I was working on my mobile app - &lt;a href="https://apps.apple.com/app/social-aide/id6504584869" rel="noopener noreferrer"&gt;Social AIde&lt;/a&gt; &lt;em&gt;(an AI powered social-media response generator app built in Swift, thanks to AI because I never touched Swift before)&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
I wanted to add a bunch of new languages, but everything was hardcoded. Yikes 😬&lt;/p&gt;

&lt;p&gt;The problem wasn't finding the hardcoded strings, but replacing them with keys for a key-value pair JSON file. Fun times, right?&lt;br&gt;&lt;br&gt;
Copy-pasting 200+ strings manually? &lt;em&gt;No thanks.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
There had to be a better way.&lt;/p&gt;

&lt;p&gt;Spoiler: There wasn't.&lt;br&gt;&lt;br&gt;
At least, not in the VS Code marketplace. I looked for plugins, I tried to search online, but I was stuck with manually copying the string, then copying back the key.&lt;/p&gt;

&lt;p&gt;I'm too lazy to that.&lt;br&gt;
So I turned to my trusty sidekick, good friend, Claude AI.&lt;br&gt;
Described my problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Please help me creating a visual studio code macro/extension, whatever fits my use case. Here's what it should do:

You select a hardcoded text in the editor, and initiate the plugin (maybe as a command, called "replace with localized string key". It would ask for what should be the key (in a prompt?)

Then it replaces the selected text with that key in this format: String(localized: "my_key")

Then add/modify a JSON file in the project's root folder to add the key and the original hardcoded string in a key-value pair.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In 4 seconds it laid out everything: how to start a VS Code extension, what code to add:&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%2F7eqrpydlojiro0hl8por.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%2F7eqrpydlojiro0hl8por.png" alt="Claude's first response" width="800" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mind. Blown 🤯.&lt;br&gt;&lt;br&gt;
No Googling required – just follow the instructions.  &lt;/p&gt;

&lt;p&gt;But why stop there?  I figured, &lt;em&gt;"Hey, let's make this available to everyone!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I asked Claude to add configuration options. Boom. Done.&lt;br&gt;&lt;br&gt;
Then came the publishing process: API keys, descriptions, licenses – Claude had me covered.&lt;/p&gt;

&lt;p&gt;Of course, every cool extension needs a logo.&lt;br&gt;&lt;br&gt;
So I quickly jumped to recraft.ai and asked for a vector image of a &lt;em&gt;"magnifying glass zooming in on an arrow pointing up"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So the only manual thing left was to take a screen recording (because all cool VSCode plugins have a nice gif animation) - of course Claude helped me how to insert it into the readme (which was also written by Claude btw).&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%2Fujudhzxj0atzalg07pcu.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%2Fujudhzxj0atzalg07pcu.gif" alt="Claude's first response" width="720" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like that, in less than an hour, I had a &lt;a href="https://marketplace.visualstudio.com/items?itemName=johnnyfekete.hardcoded-string-replacer" rel="noopener noreferrer"&gt;Hardcoded String Replacer VS Code extension&lt;/a&gt; live.&lt;br&gt;&lt;br&gt;
Zero prior experience required.&lt;/p&gt;

&lt;p&gt;It's wild how AI is making our jobs faster.&lt;br&gt;&lt;br&gt;
Sure, you still need a basic understanding, but those entry barriers? They're crumbling.&lt;/p&gt;

&lt;p&gt;So here's to AI, our new coding buddy. Making the impossible possible, one accidental project at a time.&lt;br&gt;&lt;br&gt;
Who knows? Your next "oops, I made a thing" moment might be just around the corner.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vscode</category>
      <category>programming</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Connect Your Laravel Application to Digital Ocean's Managed MySQL 8 database</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Tue, 07 Sep 2021 15:17:45 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/how-to-connect-your-laravel-application-to-digital-ocean-s-managed-mysql-8-database-2khh</link>
      <guid>https://dev.to/johnnyfekete/how-to-connect-your-laravel-application-to-digital-ocean-s-managed-mysql-8-database-2khh</guid>
      <description>&lt;p&gt;If you're like me, you've spent way too much time trying to properly connect to a managed MySQL 8 database from Digital Ocean.&lt;/p&gt;

&lt;p&gt;Everything looks great, you believe you configured everything correctly, but then you receive an &lt;code&gt;SQLSTATE[HY000] [2002]&lt;/code&gt; &lt;code&gt;Doctrine\DBAL\Driver\PDO\Exception&lt;/code&gt; error from your application.&lt;/p&gt;

&lt;p&gt;Not a helpful error message, I've also seen it way too many times.  &lt;/p&gt;

&lt;p&gt;The last time I worked on &lt;a href="https://bonboarding.com" rel="noopener noreferrer"&gt;my company's web app&lt;/a&gt;, I ran into this, and decided to document the solution, for the sake of &lt;em&gt;future me&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you're in my shoes, don't worry, I have you covered. Here is a step-by-step guide on how to fix it and set up a working connection from Laravel to Digital Ocean's managed MySQL 8 database.&lt;/p&gt;




&lt;h2&gt;
  
  
  Digital Ocean Configurations
&lt;/h2&gt;

&lt;p&gt;Let's start by setting up the database.&lt;/p&gt;

&lt;p&gt;Create a managed database in Digital Ocean. Make sure to choose MySQL version 8, preferably to the same region where your Laravel server is.&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%2Fdgcfex8gde0ztp55nhhi.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%2Fdgcfex8gde0ztp55nhhi.png" alt="Choose MySQL version 8 at the Digital Ocean dashboard" width="489" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once it's done, I recommend creating a new user instead of using the default one.&lt;/p&gt;

&lt;p&gt;You can do it under the &lt;strong&gt;Users &amp;amp; Databases&lt;/strong&gt; tab:&lt;br&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%2Fb66be3ah1k5o2gxfcoru.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%2Fb66be3ah1k5o2gxfcoru.png" alt="Add a new user" width="800" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure to keep the password encryption as is (MySQL 8+).&lt;/p&gt;

&lt;p&gt;While you're here, you can also create a database for your project, it's up to you.&lt;/p&gt;

&lt;p&gt;The next thing you need to do is &lt;strong&gt;allowing traffic from various trusted sources&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
This means that only certain computers/servers can access your database.&lt;/p&gt;

&lt;p&gt;You can set this up under the &lt;strong&gt;Settings&lt;/strong&gt; tab's &lt;strong&gt;Trusted Sources&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Make sure to add the Laravel server's IP (or if it's hosted at Digital Ocean, you can refer to it by name) and your local IP as well, so you can connect from your computer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing the connection
&lt;/h2&gt;

&lt;p&gt;If everything went fine, you can see the connection details in the &lt;strong&gt;Overview&lt;/strong&gt; tab:&lt;br&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%2Fgk0hvkch6hzceniih5d5.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%2Fgk0hvkch6hzceniih5d5.png" alt="Connection details at Digital Ocean Overview page" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure your new user and your database are selected, so you see the correct login details.&lt;/p&gt;

&lt;p&gt;Also, check that you see the public host, as you're trying to connect from your computer.&lt;/p&gt;

&lt;p&gt;Now, download the &lt;em&gt;CA certificate&lt;/em&gt; file, and save it somewhere safe.&lt;br&gt;&lt;br&gt;
This file will be needed later when connecting from Laravel.  &lt;/p&gt;

&lt;p&gt;But for now, try connecting from your local MySQL client.&lt;/p&gt;

&lt;p&gt;Make sure to select a MySQL 8 connection, and use the following configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;host: &lt;code&gt;prod-do-user-XXXXXX.b.db.ondigitalocean.com&lt;/code&gt; &lt;strong&gt;replace with your database's public hostname&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;port: &lt;code&gt;25060&lt;/code&gt; &lt;strong&gt;it's not the default, 3306, make sure to update it!&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;username&lt;/li&gt;
&lt;li&gt;password &lt;strong&gt;make sure not to copy the extra space at the end of the password&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and finally, choose the option so you can add the CA certificate that you just downloaded.&lt;/p&gt;

&lt;p&gt;This is how a configuration can look like in TablePlus:&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%2Fwazg0t4g1zy4oxpvl31h.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%2Fwazg0t4g1zy4oxpvl31h.png" alt="MySQL 8 connection with CA certificate" width="500" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should be able to connect now. If there's still some issue, double-check if your IP is whitelisted in the Trusted Sources list in the Digital Ocean dashboard.&lt;/p&gt;
&lt;h2&gt;
  
  
  Connecting from Laravel
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Server Setup
&lt;/h3&gt;

&lt;p&gt;Before you configure anything, make sure that you have the MySQL extension enabled for your PHP.&lt;/p&gt;

&lt;p&gt;You can check this by running &lt;code&gt;php -i&lt;/code&gt; on your server, and if it's not enabled, install it.&lt;br&gt;&lt;br&gt;
&lt;em&gt;You can do it like this on an Ubuntu server:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;sudo apt install php8.0-mysql
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring the database connection]
&lt;/h3&gt;

&lt;p&gt;First, let's check our app's database configuration &lt;code&gt;config/database.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add the following to the end of the MySQL driver's configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'ssl_mode'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SSL_MODE'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="s1"&gt;'options'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;extension_loaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pdo_mysql'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
     &lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MYSQL_ATTR_SSL_CA&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MYSQL_ATTR_SSL_CA'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so it looks like this:&lt;br&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%2Ffex9y23grombdnsn92rc.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%2Ffex9y23grombdnsn92rc.png" alt="Final MySQL config in Laravel" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Uploading the CA certificate
&lt;/h3&gt;

&lt;p&gt;Next, upload the CA certificate to the server.&lt;/p&gt;

&lt;p&gt;I recommend adding it in your project's storage folder, eg.&lt;br&gt;
&lt;code&gt;[PATH TO MY PROJECT]/storage/certs/ca-certificate.crt&lt;/code&gt;. But it can be anywhere.&lt;/p&gt;

&lt;p&gt;What's important is that your Laravel application can access it. For that, you need to set some permissions on the file.&lt;/p&gt;

&lt;p&gt;I recommend very restrictive permissions, such as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;chmod 440 ca-certificate.crt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, make sure that the user who executes the PHP script (normally &lt;em&gt;www-data&lt;/em&gt;) has access to this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;chown www-data:www-data ca-certificate.crt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up the Environment Variables
&lt;/h3&gt;

&lt;p&gt;The last step is to set up the environment variables in Laravel.&lt;/p&gt;

&lt;p&gt;Simply edit your &lt;strong&gt;.env&lt;/strong&gt; file, and in the database area replace the values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB_CONNECTION=mysql
DB_HOST=private-XXXXX.b.db.ondigitalocean.com
DB_PORT=25060
DB_DATABASE=XXXXX
DB_USERNAME=XXXXX
DB_PASSWORD=XXXXX
SSL_MODE=required
MYSQL_ATTR_SSL_CA=[PATH TO MY PROJECT]/storage/certs/ca-certificate.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The connection, database, username, and password should be self-explanatory.
&lt;/li&gt;
&lt;li&gt;If you're connecting from another Digital Ocean server in the same region, use the private &lt;strong&gt;VPC network&lt;/strong&gt; host (you can find it in the Digital Ocean overview tab).
&lt;/li&gt;
&lt;li&gt;The port should be always 25060, and you should include the &lt;em&gt;SSL_MODE&lt;/em&gt; as &lt;code&gt;required&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;And make sure to include the full absolute path to your CA certificate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything's configured, make sure to clean Laravel's config cache, by running &lt;code&gt;php artisan config:cache&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If you followed these steps, you should be able to connect to your database.&lt;/p&gt;

&lt;p&gt;To test it quickly, use Laravel's Tinker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="n"&gt;php&lt;/span&gt; &lt;span class="n"&gt;artisan&lt;/span&gt; &lt;span class="n"&gt;tinker&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getDatabaseName&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should return with your database's name.&lt;/p&gt;

&lt;p&gt;If for some reason you still can't connect, double-check these common errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Laravel application's IP is not whitelisted in Digital Ocean's trusted source list&lt;/li&gt;
&lt;li&gt;incorrect path for the CA certificate&lt;/li&gt;
&lt;li&gt;the CA certificate is not readable by the Laravel application (permission, ownership issues)&lt;/li&gt;
&lt;li&gt;extra whitespace after the password&lt;/li&gt;
&lt;li&gt;old Laravel config is cached, refresh it with &lt;code&gt;php artisan config:cache&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>laravel</category>
      <category>mysql</category>
      <category>database</category>
      <category>php</category>
    </item>
    <item>
      <title>An Ode to JavaScript</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Thu, 08 Jul 2021 10:05:15 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/an-ode-to-javascript-4b2i</link>
      <guid>https://dev.to/johnnyfekete/an-ode-to-javascript-4b2i</guid>
      <description>&lt;p&gt;Oh JavaScript, please &lt;strong&gt;PROMISE&lt;/strong&gt; me&lt;br&gt;
That you'll treat me better.&lt;br&gt;
&lt;strong&gt;LET&lt;/strong&gt;'s end &lt;strong&gt;THIS&lt;/strong&gt; tragicomedy,&lt;br&gt;
So both of us can &lt;strong&gt;GET&lt;/strong&gt; happier.&lt;/p&gt;

&lt;p&gt;Every time I &lt;strong&gt;CATCH&lt;/strong&gt; up &lt;strong&gt;WITH&lt;/strong&gt; you,&lt;br&gt;
I feel pure joy and excitement.&lt;br&gt;
These are the moments I &lt;strong&gt;AWAIT&lt;/strong&gt;, to&lt;br&gt;
Fill my &lt;strong&gt;VOID&lt;/strong&gt; and reach enlightenment.&lt;/p&gt;

&lt;p&gt;IF only you could stay kind to me.&lt;br&gt;
What &lt;strong&gt;ELSE&lt;/strong&gt; could I ask for?&lt;br&gt;
Something goes wrong, you get gloomy&lt;br&gt;
You &lt;strong&gt;CLICK&lt;/strong&gt; and there's an &lt;strong&gt;ERROR&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WHILE&lt;/strong&gt; my spirit reaches &lt;strong&gt;OUTERHEIGHT&lt;/strong&gt;s&lt;br&gt;
Without an &lt;strong&gt;ALERT&lt;/strong&gt; you come to ruin it&lt;br&gt;
It's another &lt;strong&gt;CASE&lt;/strong&gt; of you picking a fight.&lt;br&gt;
I &lt;strong&gt;DOCUMENT&lt;/strong&gt; everything to myself to prove it.&lt;/p&gt;

&lt;p&gt;Anger &lt;strong&gt;RETURN&lt;/strong&gt;s, I lose &lt;strong&gt;FOCUS&lt;/strong&gt; and grieve&lt;br&gt;
&lt;strong&gt;DATE&lt;/strong&gt; and &lt;strong&gt;TIME&lt;/strong&gt; stops, another guilt trip&lt;br&gt;
Don't make me &lt;strong&gt;REPLACE&lt;/strong&gt; you and leave&lt;br&gt;
This non&lt;strong&gt;FUNCTION&lt;/strong&gt;al &lt;em&gt;love/hate&lt;/em&gt; relationship!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>poetry</category>
      <category>programming</category>
    </item>
    <item>
      <title>Animated CSS Sparkler</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Sun, 10 Jan 2021 11:49:14 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/animated-css-sparkler-5fbb</link>
      <guid>https://dev.to/johnnyfekete/animated-css-sparkler-5fbb</guid>
      <description>&lt;p&gt;CSS animations can be super powerful and combined in the correct way you can achieve pretty much anything.&lt;/p&gt;

&lt;p&gt;In this short tutorial, I explain how I created this animated sparkler with only HTML + CSS:&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%2Fi%2Fbbfs4trh5bnm0cz5heb5.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%2Fi%2Fbbfs4trh5bnm0cz5heb5.gif" alt="Alt Text" width="600" height="340"&gt;&lt;/a&gt;&lt;/p&gt;
Source code on &lt;a href="https://codepen.io/johnnyfekete/pen/MWjveNj?editors=1100" rel="noopener noreferrer"&gt;CodePen&lt;/a&gt;



&lt;p&gt;Let's dissect the animation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there's the stick that's burning with an ember and leaves the ashes behind&lt;/li&gt;
&lt;li&gt;the light, that's constantly pulsating, and moving together with the burn&lt;/li&gt;
&lt;li&gt;the sparks that are randomly showing up and fading out from the center, the burn&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Burning Stick
&lt;/h2&gt;

&lt;p&gt;The stick has two overlapping elements: one that is the ash and one with the animation. I used a &lt;code&gt;DIV&lt;/code&gt; and a &lt;code&gt;::before&lt;/code&gt; pseudo-class for the styling.&lt;/p&gt;

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

&lt;p&gt;As you can see in the example, the ember and the explosive cover is drawn by a linear gradient, and the burning effect happens by a keyframe animation, that simply decreases the height of the overlaying element in 20 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.gun-powder&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* this is needed so the burning starts 
                 at the top and ends at the bottom */&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;orange&lt;/span&gt; &lt;span class="m"&gt;0.3rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;gray&lt;/span&gt; &lt;span class="m"&gt;0.4rem&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sparkler-burn-anim&lt;/span&gt; &lt;span class="m"&gt;20s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;sparkler-burn-anim&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10rem&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pulsating Light
&lt;/h2&gt;

&lt;p&gt;The pulsating light is used for two things: besides showing a glow of the sparkles, it also functions as a container for them, and it moves together with the burn (so the sparks don't have to deal with moving while the burning takes place, just with the sparkling animation).&lt;/p&gt;

&lt;p&gt;The glow effect has been created with another keyframe animation on the &lt;code&gt;::after&lt;/code&gt; pseudo-class, and it uses a simple scale transform to grow and shrink, resulting the pulsating effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.sparkler-light&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;230&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sparkler-light-pulsating&lt;/span&gt; &lt;span class="m"&gt;2s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;sparkler-light-pulsating&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;25&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;75&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.sparkler-light&lt;/code&gt; moves together with the burn, and as I wrote, it contains all the sparks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sparkler-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  ... further sparks, 36 in total
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.sparkler-light&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-4.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-1.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sparkler-light-anim&lt;/span&gt; &lt;span class="m"&gt;20s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;sparkler-light-anim&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;11.5rem&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sparkling ✨
&lt;/h2&gt;

&lt;p&gt;It's not possible to use random values in CSS. But what's possible is to assign random numbers to CSS variables, and use those.&lt;/p&gt;

&lt;p&gt;I created 36 &lt;code&gt;DIV&lt;/code&gt;s for the sparkles, each with the same animation, but setting two CSS variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sparkler-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spark"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--spark-rotate: 10deg; --spark-delay: 223ms"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spark"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--spark-rotate: 20deg; --spark-delay: 844ms"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spark"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--spark-rotate: 360deg; --spark-delay: 559ms"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I could create the irregular order of the sparks by adding &lt;code&gt;animation-delay: var(--spark-delay)&lt;/code&gt; on them.&lt;/p&gt;

&lt;p&gt;I also rotated each one by 10 degrees (to make a full circle) with &lt;code&gt;transform: rotate(var(--spark-rotate))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, I used a keyframe-animation to create the explosive effect.&lt;br&gt;
It was important to use &lt;code&gt;transform-origin: bottom center&lt;/code&gt;, so the sparks all originate from the same point.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The Final Sparkler
&lt;/h2&gt;

&lt;p&gt;To polish everything, I applied a rotate transform on the whole sparkler, so it's not standing upwards.&lt;/p&gt;

&lt;p&gt;I used Pug templating engine, to facilitate the typing (especially with the 36 sparks, with their random values), you can read more about it in the &lt;a href="https://dev.to/johnnyfekete/learning-pug-templating-5ai1"&gt;Type Less! Use Pug Templates!&lt;/a&gt; article.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The CSS Christmas Calendar
&lt;/h2&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%2Fi%2Ff5h0yx5efjpehlb1gd5l.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%2Fi%2Ff5h0yx5efjpehlb1gd5l.jpg" alt="CSS Christmas Calendar" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This rotating 3D Christmas gift is part of my Holiday project, the &lt;a href="https://csschristmascalendar.com" rel="noopener noreferrer"&gt;CSS Christmas Calendar&lt;/a&gt;. I posted a new calendar item every single day from the 1st of December until Christmas Eve.&lt;/p&gt;

&lt;p&gt;I tried different techniques each day and learned a lot - and I share the most interesting techniques in this series.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/collection/nZPerV" rel="noopener noreferrer"&gt;All code&lt;/a&gt; is available on &lt;a href="https://codepen.io/johnnyfekete/full/eYdNvVL" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check it if you want to see other Christmassy CSS magic!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>dohackathon</category>
      <category>css</category>
      <category>animation</category>
    </item>
    <item>
      <title>3D Christmas Gift Box with HTML + CSS</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Thu, 07 Jan 2021 18:41:58 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/3d-christmas-gift-box-with-html-css-1kkl</link>
      <guid>https://dev.to/johnnyfekete/3d-christmas-gift-box-with-html-css-1kkl</guid>
      <description>&lt;p&gt;CSS is such a versatile language.&lt;br&gt;
You can use it to design web layouts, interactive buttons, style your website's text... or create a 3D Christmas gift box.&lt;/p&gt;

&lt;p&gt;In this tutorial, I'll demonstrate how I made one for my &lt;a href="https://csschristmascalendar.com" rel="noopener noreferrer"&gt;CSS Christmas Calendar&lt;/a&gt;, where I posted a new CSS art every day until Christmas.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Anatomy of the box
&lt;/h2&gt;

&lt;p&gt;To start creating the object, I had to analyze the different parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a box, which consists of 5 sides (4 sides and the bottom)&lt;/li&gt;
&lt;li&gt;a lid, which is similar except it doesn't have a bottom but a top part&lt;/li&gt;
&lt;li&gt;the loops of the ribbon (left and right)&lt;/li&gt;
&lt;li&gt;the two ends of the ribbon&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In the following examples I simplified the colors and sizes, in the final rotating box I used CSS variables to store them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3D box
&lt;/h2&gt;

&lt;p&gt;Although CSS is primarily used to style elements in 2 dimensions (X and Y axes), it actually knows about the third one (Z-axis, the depth).&lt;/p&gt;

&lt;p&gt;To create a 3D box, I had to design the 5 sides and move/rotate them in the 3D space to get to their desired place.&lt;/p&gt;

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

&lt;p&gt;As you can see, the sides already have the ribbon on them.&lt;br&gt;&lt;br&gt;
I achieved it with a linear gradient background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.side&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;red&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;white&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;white&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;red&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the trick is to have a horizontal gradient (from left to right) with sharp edges at 40% and 60%.&lt;/p&gt;

&lt;p&gt;For the lid, the concept is very similar, but there are 2 gradients: one horizontal and one vertical.&lt;br&gt;&lt;br&gt;
However, if both would start with red, the upper one would cover the other. So I used transparent instead for one of the directions, and also a slightly darker color for the ribbon, to help distinguish them from each other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.lid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;lightblue&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;lightblue&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;red&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;white&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;white&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;red&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to rotate the sides to their correct place.&lt;/p&gt;

&lt;p&gt;First, a container &lt;code&gt;div&lt;/code&gt; needs to get the &lt;code&gt;perspective&lt;/code&gt; property. This defines how &lt;em&gt;far&lt;/em&gt; the box is away from the user. I set it to 400px, which gives a realistic depth to it.&lt;/p&gt;

&lt;p&gt;To move the sides to the right place, I used the &lt;code&gt;transform&lt;/code&gt; property. Not only can it transform elements in 2D, but it can move and rotate in the 3D space as well (with &lt;code&gt;rotateZ&lt;/code&gt;, &lt;code&gt;translateZ&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.gift-box&lt;/span&gt; &lt;span class="nc"&gt;.gift-box-lid__side--top&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;90deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
             &lt;span class="n"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;120px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-10px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the sides were in their place, the whole box was moved to its correct place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.gift-box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-100px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;rotateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-15deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;rotateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-30deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did it with the box and the lid too, you can try it yourself, and see how it'd look with a different perspective or rotation:  &lt;/p&gt;

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

&lt;h2&gt;
  
  
  The ribbon
&lt;/h2&gt;

&lt;p&gt;The ribbon consists of two parts: the two ends with a V-cut, and the two loops.&lt;/p&gt;

&lt;p&gt;The end is actually a simple &lt;code&gt;div&lt;/code&gt;, with 2 gradient backgrounds overlapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.ribbon-end&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;110px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;45deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt; &lt;span class="m"&gt;72%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;72%&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-45deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt; &lt;span class="m"&gt;72%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;72%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;darkred&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-70deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-80px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-60px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&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%2Fi%2F1gd76qhgqd6x60azhvpy.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%2Fi%2F1gd76qhgqd6x60azhvpy.png" alt="Two ribbon ends" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;transform&lt;/code&gt; property I could move them to their correct place, just like with the sides of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rounded shapes in 3D
&lt;/h2&gt;

&lt;p&gt;So far the gift box consists only of rectangles.&lt;br&gt;&lt;br&gt;
But the loops of the ribbon aren't rectangular elements.&lt;/p&gt;

&lt;p&gt;CSS doesn't have the functionalities of a proper 3D rendering engine, thus there's a limitation of rounded shapes.&lt;/p&gt;

&lt;p&gt;Initially, I was playing with the idea to create 6-10 flat sections for the ribbon and rotate each one, but the result would've looked angular.&lt;/p&gt;

&lt;p&gt;Instead, I ended up with a little hack: it's easy to create the shape of the loop with a rounded rectangle and a skew transform.&lt;br&gt;&lt;br&gt;
What if I would simply have many of that rectangle, behind each other?  &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%2Fi%2F5wsgtpuihu12l26fjfo0.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%2Fi%2F5wsgtpuihu12l26fjfo0.png" alt="Multiple ribbons" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So that is exactly what I did!&lt;/p&gt;

&lt;p&gt;A 40px wide ribbon requires 40 &lt;code&gt;div&lt;/code&gt;s, each transposed with 1 extra pixel by the Z-axis.&lt;br&gt;
Also, to have the borders with dark red, the first and last 3 &lt;code&gt;div&lt;/code&gt;s should have the color of the border, the rest the color of the ribbon.&lt;/p&gt;

&lt;p&gt;I didn't want to write 40 CSS selectors for each side of the loops, so used another trick:&lt;/p&gt;

&lt;p&gt;You can set CSS variables on the HTML elements by editing their inline &lt;code&gt;style&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ribbon"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--gift-box-position: 0px; border-color: darkred"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ribbon"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--gift-box-position: 1px; border-color: white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;transform&lt;/code&gt; CSS property of the ribbon loop looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.ribbon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... styling of the ribbon ... */&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-5deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;90deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="c"&gt;/* use a CSS variable for the translate, defined in the inline style */&lt;/span&gt;
             &lt;span class="n"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;80px&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--gift-box-position&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;  
             &lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-30px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-110px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;skewY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: I could set the border color directly in the inline style, but I had to use a CSS variable, otherwise I would've had to list all parameters for the transform also).&lt;/p&gt;

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

&lt;p&gt;If you notice, from certain angles the loop is transparent and you can see the many layers of &lt;code&gt;div&lt;/code&gt;s showing up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rotating the box
&lt;/h2&gt;

&lt;p&gt;Just like opacity, color, or position, the &lt;code&gt;transform&lt;/code&gt; property can also be animated in CSS.&lt;/p&gt;

&lt;p&gt;To create an infinite animation, I had to set the keyframes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;rotation-3d&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-100px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-5deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-30deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-100px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;409deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-5deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-30deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: all transformed values have to be listed, even if only one changes)&lt;/p&gt;

&lt;p&gt;Finally, I applied this animation on the gift box element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.gift-box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotation-3d&lt;/span&gt; &lt;span class="m"&gt;20s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The CSS Christmas Calendar
&lt;/h2&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%2Fi%2Ff5h0yx5efjpehlb1gd5l.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%2Fi%2Ff5h0yx5efjpehlb1gd5l.jpg" alt="CSS Christmas Calendar" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This rotating 3D Christmas gift is part of my Holiday project, the &lt;a href="https://csschristmascalendar.com" rel="noopener noreferrer"&gt;CSS Christmas Calendar&lt;/a&gt;. I posted a new calendar item every single day from the 1st of December until Christmas Eve.&lt;/p&gt;

&lt;p&gt;I tried different techniques each day and learned a lot - and I share the most interesting techniques in this series.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/collection/nZPerV" rel="noopener noreferrer"&gt;All code&lt;/a&gt; is available on &lt;a href="https://codepen.io/johnnyfekete/full/eYdNvVL" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check it if you want to see other Christmassy CSS magic!&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>tutorial</category>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>Type less! Use Pug Templates!</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Thu, 07 Jan 2021 18:28:13 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/learning-pug-templating-5ai1</link>
      <guid>https://dev.to/johnnyfekete/learning-pug-templating-5ai1</guid>
      <description>&lt;p&gt;While I was working on the 24 CSS artworks for my &lt;a href="https://csschristmascalendar.com" rel="noopener noreferrer"&gt;CSS Christmas Calendar&lt;/a&gt; I quickly realized that I would have to write &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;/div&amp;gt;&lt;/code&gt; a million times.&lt;/p&gt;

&lt;p&gt;Besides being very monotone, it is also extremely error-prone. It's so easy to forget closing a &lt;code&gt;div&lt;/code&gt;, or other elements.&lt;/p&gt;

&lt;p&gt;I wanted to save myself from a lot of unnecessary keystrokes and headaches.&lt;br&gt;
That's why I decided to give Pug - a HTML templating engine - &lt;br&gt;
a try.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Is Pug?
&lt;/h2&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%2Fi%2F806qkeffssi8or6szn90.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%2Fi%2F806qkeffssi8or6szn90.jpg" alt="Pug in yellow jacket" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@charlesdeluvio" rel="noopener noreferrer"&gt;Charles Deluvio&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/pug" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;&lt;a href="https://pugjs.org" rel="noopener noreferrer"&gt;Pug.js&lt;/a&gt; is a HTML templating engine, which translates pug code to HTML tags.&lt;/p&gt;

&lt;p&gt;It requires some configuration in your backend, so it gets compiled. But luckily, it's easy to get started on &lt;a href="https://codepen.io" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt; without any local setup.&lt;/p&gt;

&lt;p&gt;Just create a new pen, go to the settings, and enable the Pug templating engine.&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%2Fi%2Fwml7hurwu1ujqnqe65ch.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%2Fi%2Fwml7hurwu1ujqnqe65ch.png" alt="Enable Pug templating engine on Codepen" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So how to write pug templates?&lt;/p&gt;

&lt;p&gt;Here's a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.hot-chocolate
  .mug
    .mug__grip
    .mug__contents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Code from the &lt;a href="https://codepen.io/johnnyfekete/pen/VwKKMKE" rel="noopener noreferrer"&gt;Hot chocolate with marshmallows&lt;/a&gt; CSS art





&lt;p&gt;that compiles to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hot-chocolate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mug__grip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mug__contents"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's highlight some important things here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;indentation is everything!&lt;/strong&gt; if you start the next line under the previous, it will be a sibling HTML element. But if you press a &lt;code&gt;TAB&lt;/code&gt;, it will be the children of it.&lt;/li&gt;
&lt;li&gt;no need for closing tags. The proper indentation will take care of them automatically.&lt;/li&gt;
&lt;li&gt;use your existing CSS knowledge: if something starts with a &lt;code&gt;.&lt;/code&gt;, it will be a class. &lt;code&gt;#&lt;/code&gt; stands for id.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Easy Peasy. Something More Complex?
&lt;/h2&gt;

&lt;p&gt;So far we only worked with empty &lt;code&gt;div&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;But you can add content to them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.title Santa Claus
.description.
  a plump, white-bearded, red-suited, and jolly old man
  in modern folklore who delivers presents to 
  children at Christmastime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you could see, multiline content needs to be indented one level, and the element definition needs a &lt;code&gt;.&lt;/code&gt; at the end.&lt;/p&gt;

&lt;p&gt;Changing the tag from &lt;code&gt;div&lt;/code&gt; is just as simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a.link I'm a link
span.highlight I'm a highlighted text
footer I don't even have a class
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can edit the elements attributes the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input(type='text' name='address' autofocus)
a(href='https://csschristmascalendar.com' target='_blank')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mixing Pug with JavaScript
&lt;/h2&gt;

&lt;p&gt;As I mentioned, Pug is a templating engine written in JavaScript.&lt;br&gt;
Therefore, you can combine it with any Javascript code, and that's where it's superpower begins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- let authenticated = false;

.login(class=authenticated ? 'hidden' : 'visible')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the class changed based on a JavaScript variable. &lt;em&gt;Notice, how &lt;code&gt;class&lt;/code&gt; is used as an attribute, and not in the selector&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also, the plain JavaScript line starts with a dash &lt;code&gt;-&lt;/code&gt; to mark that it's not a template&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- let length = 10;

ul.items(style=`height: ${Math.min(length * 2, 10)}rem`)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the height will be a maximum of 10rem.&lt;/p&gt;

&lt;p&gt;Finally, one thing I found quite useful is combining pug with the &lt;code&gt;random()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.star(style=`transform: rotate(${Math.random() * 360}deg)`)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will set a random rotate transform on the &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditional Rendering
&lt;/h3&gt;

&lt;p&gt;One of the cool things in Pug is that it can conditionally render elements.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- let authenticated = false;

.header
  if (authenticated)
    a(href='/logout') Sign out
  else
    a(href='/login') Sign in
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example will render either the &lt;em&gt;Sign in&lt;/em&gt; or &lt;em&gt;Sign out&lt;/em&gt; button, depending on the authenticated variable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loops That Will Save Your Time
&lt;/h3&gt;

&lt;p&gt;If you have to render the same block of code multiple times, loops will save you from unnecessary repetition.&lt;/p&gt;

&lt;p&gt;Let's start with a simple case, looping through an array of items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- const menuItems = ['intro', 'about', 'portfolio', 'contact'];

ul
  each menuItem in menuItems
    li
      a(href=`#${menuItem}`)= menuItem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will compile to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#intro"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;intro&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#about"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;about&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#portfolio"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;portfolio&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#contact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;contact&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Notice, how I added an equal character &lt;code&gt;=&lt;/code&gt; after the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag. That to tell Pug that what comes after is a JavaScript variable, so it doesn't print the word&lt;/em&gt; &lt;strong&gt;menuItem&lt;/strong&gt; &lt;em&gt;for each link.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For loop&lt;/em&gt; is another one that I used heavily in the CSS arts. It repeats the same content &lt;em&gt;n&lt;/em&gt; times.&lt;/p&gt;

&lt;p&gt;It's important to note that &lt;em&gt;for&lt;/em&gt; is not a Pug technique, but plain JavaScript. However, that's the beauty of it all: you can combine the two, without any problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.sky
  - for (var i = 1; i &amp;lt;= 10; i++)
    .star
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create 10 &lt;code&gt;&amp;lt;div class="star"&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; elements inside the sky &lt;/p&gt;.

&lt;p&gt;I used this trick in almost all items, often combined with more complex parameters:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.sparkler-light
  - for (var spark = 1; spark &amp;lt;= 36; spark++)
    .spark(
      class=`spark-${spark}`
      style=`--spark-rotate: ${spark * 10}deg;
        --spark-delay: ${Math.round(Math.random() * 1000)}ms`
    )
&lt;/code&gt;&lt;/pre&gt;
Code from the &lt;a href="https://codepen.io/johnnyfekete/pen/MWjveNj" rel="noopener noreferrer"&gt;Animated Christmas Sparkler&lt;/a&gt; CSS art





&lt;p&gt;This creates the following HTML&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sparkler-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spark spark-1"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--spark-rotate: 10deg;
    --spark-delay: 359ms"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spark spark-2"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--spark-rotate: 20deg;
    --spark-delay: 887ms"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  ...

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spark spark-36"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--spark-rotate: 360deg;
    --spark-delay: 121ms"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;With the &lt;em&gt;for loop&lt;/em&gt; I could add 36 sparks rotated by 10 degrees each, and with a random delay in their animation, that - combined with CSS - resulted 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%2Fi%2F3pd7q5led9vodf5mdu7e.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%2Fi%2F3pd7q5led9vodf5mdu7e.gif" alt="Christmas Sparkler made with CSS" width="600" height="349"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://codepen.io/johnnyfekete/pen/MWjveNj" rel="noopener noreferrer"&gt;Animated Christmas Sparkler&lt;/a&gt;



&lt;h2&gt;
  
  
  Sounds Cool! What's Next?
&lt;/h2&gt;

&lt;p&gt;Now you are ready to enable your Pug templating engine!&lt;/p&gt;

&lt;p&gt;You can make more use of it by including other pug files, creating complex layouts, or even including markdown files.&lt;/p&gt;

&lt;p&gt;Adding Pug to your projects is very easy, just follow the &lt;a href="https://pugjs.org/api/getting-started.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;. It has very straightforward steps about the setup and integration.&lt;/p&gt;

&lt;p&gt;If you just want to manually convert a Pug template to HTML, I found &lt;a href="https://www.ubercompute.com/pug-to-html" rel="noopener noreferrer"&gt;this online converter&lt;/a&gt; that did an amazing job for me, and it will certainly make it for you too.&lt;/p&gt;

&lt;p&gt;Finally, if you need a quick overview of the most common Pug syntaxes, check this &lt;a href="https://devhints.io/pug" rel="noopener noreferrer"&gt;cheat sheet&lt;/a&gt;.&lt;br&gt;
You can come back to it anytime you need a specific syntax, especially for the more complex elements.&lt;/p&gt;

&lt;p&gt;I'd love to hear from you, and see how you implemented Pug in your projects.&lt;/p&gt;

&lt;p&gt;Leave a comment below, and I'd be glad to answer your questions and comments.&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>tutorial</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating the CSS Christmas Calendar</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Wed, 06 Jan 2021 19:50:02 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/creating-the-css-christmas-calendar-40a0</link>
      <guid>https://dev.to/johnnyfekete/creating-the-css-christmas-calendar-40a0</guid>
      <description>&lt;p&gt;Creating the &lt;a href="https://csschristmascalendar.com" rel="noopener noreferrer"&gt;CSS Christmas Calendar&lt;/a&gt; items was a lot of fun, but before I could've even started on them, I actually had to build the calendar itself - only using HTML and CSS.&lt;/p&gt;

&lt;p&gt;It was a lot easier than I expected, with CSS techniques such as CSS grid, flexbox or 3D transforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Layout
&lt;/h2&gt;

&lt;p&gt;The layout can be achieved with CSS grid, which allows us to position the calendar doors to their desired position.&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%2Fi%2Fgbz6qarzbax10xuzhlfh.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%2Fi%2Fgbz6qarzbax10xuzhlfh.png" alt="Explanation of the CSS grid structure" width="800" height="759"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"calendar-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"day day-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"day day-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"day day-24"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple HTML can be turned into the given layout by just these few CSS rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;grid-template-areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;"title  title  title  day5   day17  day15"&lt;/span&gt;
      &lt;span class="s1"&gt;"title  title  title  day11  day20  day16"&lt;/span&gt;
      &lt;span class="s1"&gt;"title  title  title  day1   day18  day12"&lt;/span&gt;
      &lt;span class="s1"&gt;"day6   day22  day14  day24  day24  day4"&lt;/span&gt;
      &lt;span class="s1"&gt;"day10  day21  day2   day24  day24  day8"&lt;/span&gt;
      &lt;span class="s1"&gt;"day3   day9   day7   day13  day23  day19"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then assigning each calendar door &lt;code&gt;DIV&lt;/code&gt; to the corresponding template area:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.day-1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;day1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nc"&gt;.day-24&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;day24&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the &lt;em&gt;calendar-grid&lt;/em&gt;'s &lt;code&gt;display&lt;/code&gt; property is set to &lt;code&gt;grid&lt;/code&gt;, I could define the grid layout: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;grid-template-columns&lt;/code&gt; create 6 columns with equal width &lt;em&gt;(1 fraction)&lt;/em&gt; and the number of rows is &lt;code&gt;auto&lt;/code&gt;., so there can be as many as necessary.&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;grid-gap&lt;/code&gt; defines the space between the calendar doors (and title).&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;grid-template-areas&lt;/code&gt; shows, where each item should fit inside the grid layout. (these names must match the ones assigned to the individual items, as you could see).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice, that the &lt;code&gt;.title&lt;/code&gt; element takes up a 3x3 square. That is not a problem with CSS grid, it allows grid areas that take up more than one template areas.&lt;/p&gt;

&lt;h3&gt;
  
  
  What About Mobile?
&lt;/h3&gt;

&lt;p&gt;There's no way 6 columns could comfortably fit on mobile devices, but they don't need to.&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%2Fi%2Feyfmpusfnvn4mf7r04rz.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%2Fi%2Feyfmpusfnvn4mf7r04rz.jpg" alt="The calendar on an iPhone" width="800" height="451"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="py"&gt;grid-template-areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;"title  title  title"&lt;/span&gt;
    &lt;span class="s1"&gt;"day22  day3   day8"&lt;/span&gt;
    &lt;span class="s1"&gt;"day9   day18  day11"&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a &lt;code&gt;media-query&lt;/code&gt; I targeted smaller resolution devices and changed the grid template column count, and the area allocation.&lt;/p&gt;

&lt;p&gt;This time the title takes up the top 3 areas.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Title
&lt;/h2&gt;

&lt;p&gt;I wanted to create the title in the Christmassy spirit, so I used a Google font &lt;a href="https://fonts.google.com/specimen/Mountains+of+Christmas" rel="noopener noreferrer"&gt;Mountains of Christmas&lt;/a&gt;:&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%2Fi%2F2giqnay2c2givk3a4mxb.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%2Fi%2F2giqnay2c2givk3a4mxb.png" alt="The title" width="800" height="628"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url("https://fonts.googleapis.com/css2?family=Mountains+of+Christmas:wght@700&amp;amp;display=swap")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I broke up the whole title into 3 &lt;code&gt;span&lt;/code&gt;s, applied different colors on them and rotated them individually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;CSS&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Christmas&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Calendar&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.title-1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#9c163f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-10deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with the layout, I also used a slightly different style for the title for mobile devices: smaller fonts, and the 3 &lt;code&gt;span&lt;/code&gt;s with &lt;code&gt;inline-block&lt;/code&gt; display, so they fit next to each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Doors
&lt;/h2&gt;

&lt;p&gt;The doors can open, close, and show the day's surprise CSS art and its title.&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%2Fi%2F0r8zqd7mhtg59kdnfs7d.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%2Fi%2F0r8zqd7mhtg59kdnfs7d.gif" alt="Opening/closing the calendar doors" width="600" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To detect the opened/closed state without using JavaScript, I used a well-known trick: added an invisible checkbox, and a related label (that contains the CSS art). Clicking on the label will trigger the checkbox, and in CSS I can target it with the &lt;code&gt;input:checked&lt;/code&gt; selector.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"day day-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"door"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"front"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"back"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inside"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      ... CSS art of the day ...
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      ... title of the CSS art ...
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To hide the checkbox, simply add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;label&lt;/code&gt; has multiple functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gives a height to the door/item&lt;/li&gt;
&lt;li&gt;changes the mouse cursor to pointer&lt;/li&gt;
&lt;li&gt;creates a perspective to the door (so there's a 3D effect when it's opening or closing)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;preserve-3d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;136px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(there are some small changes in the height for mobile layout, where the height is calculated from the viewport's width: &lt;code&gt;height: calc(85vw / 3);&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.door&lt;/code&gt; div has the animation configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.door&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;preserve-3d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;300ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform-origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;further&lt;/span&gt; &lt;span class="err"&gt;styles&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;.door&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-180deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First I set the transform-style, so the door's child elements will also preserve the 3D effect.&lt;br&gt;&lt;br&gt;
I set that any change should be animated in 0.3 seconds.&lt;br&gt;&lt;br&gt;
Finally, defined that transform animation should start from the middle of the left side. This is needed for the flip effect, so the door opens in the 3D space:&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%2Fi%2F4tzhfm4nuqmteofft4gi.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%2Fi%2F4tzhfm4nuqmteofft4gi.jpg" alt="Transform origin with opening door" width="793" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the input is checked, I transform the rotation of the door, and the combination of this and the previously discussed styles end up with a nice opening/closing animation.&lt;/p&gt;

&lt;p&gt;However, the door has two sides: the outside with the number, and a gray back.&lt;br&gt;
To achieve this, I used the &lt;code&gt;backface-visibility: hidden&lt;/code&gt; style. This means that if an element is rotated in the 3D space, its back is not showing up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"door"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"front"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"back"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.door&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;-webkit-backface-visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;backface-visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;further&lt;/span&gt; &lt;span class="err"&gt;styles&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.door&lt;/span&gt; &lt;span class="nc"&gt;.back&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#384044&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#2e454f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-180deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By applying &lt;code&gt;transform: rotateY(-180deg)&lt;/code&gt; on the &lt;code&gt;.back&lt;/code&gt; div, I could flip it (the same way how the opening animation flips, but this time staying in the same place, to show up as the back of the door).&lt;/p&gt;

&lt;h3&gt;
  
  
  Ordering the Doors
&lt;/h3&gt;

&lt;p&gt;I realized that if I don't set the order of the doors, they can cover each other in a weird way, or the title of an open door can get covered by another door that is in the next row.&lt;/p&gt;

&lt;p&gt;To avoid these glitches, I manually set the &lt;code&gt;z-index&lt;/code&gt; on every door, starting from the bottom left corner:&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%2Fi%2Fogpim36zwcmlodnsklw5.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%2Fi%2Fogpim36zwcmlodnsklw5.jpg" alt="The z-index ordering" width="800" height="759"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.day-1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.day-2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.day-24&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to mention, that the &lt;code&gt;z-index&lt;/code&gt; ordering differs in the mobile layout, as the doors are in different locations. Therefore they need to be actualized by targeting the days with media queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Titles
&lt;/h2&gt;

&lt;p&gt;Each CSS art has a title, that's a link for the individual piece's CodePen.&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%2Fi%2Fmn51u8i8ja7f0ob4xwd5.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%2Fi%2Fmn51u8i8ja7f0ob4xwd5.png" alt="Title of a CSS polar bear" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make it easily noticeable that it's a link, it plays an animation to the striped background, once the mouse is over it.&lt;/p&gt;

&lt;p&gt;The titles are inside the label, next to the door:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"day day-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"door"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; ... &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inside"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; ... &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://codepen.io/johnnyfekete/pen/qBaRZXV"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Link to source code"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Polar bear
      &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To trigger the entering/leaving animations, I added these styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.title-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-1rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;400ms&lt;/span&gt; &lt;span class="n"&gt;ease-in-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;other&lt;/span&gt; &lt;span class="err"&gt;styles&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;positioning&lt;/span&gt; &lt;span class="err"&gt;and&lt;/span&gt; &lt;span class="err"&gt;layout&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nc"&gt;.title-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these styles, the title is initially hidden (&lt;code&gt;opacity: 0&lt;/code&gt;) and moved up (&lt;code&gt;transform: translateY(-1rem)&lt;/code&gt;). It doesn't detect any mouse events.&lt;/p&gt;

&lt;p&gt;Once the checkbox is checked, it animates the opacity and the position in 0.4 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Striped Background of the Titles
&lt;/h3&gt;

&lt;p&gt;The red-white striped border is not really a border, but the background of the title, positioned behind the white background.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.title-container&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* this is the actual title with white background */&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1d3557&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;additional&lt;/span&gt; &lt;span class="err"&gt;styles&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;text&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.title-container&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* pseudo class behind the title,
     with a negative offset in each direction */&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* the striped background uses repeating linear gradient */&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repeating-linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="m"&gt;-45deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;#f1faee&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;#f1faee&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;#e63946&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;#e63946&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.44rem&lt;/span&gt; &lt;span class="m"&gt;1.44rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, for the animation on &lt;code&gt;hover&lt;/code&gt; I defined a &lt;code&gt;keyframe-animation&lt;/code&gt; to move the background's position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;calendar-item-link&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.44rem&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This moves the background to the left with 1.44rem (exactly the same width as the width was for the stripe pattern &lt;em&gt;(fun fact, I used the Pythagorean theorem to calculate this number, as the stripes are each 0.5rem wide (1rem the red + white in total).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I applied this &lt;code&gt;keyframe-animation&lt;/code&gt; on the striped background pseudo-element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.title-container&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;rest&lt;/span&gt; &lt;span class="err"&gt;of&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;styles&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;

  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calendar-item-link&lt;/span&gt; &lt;span class="m"&gt;0.6s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so it's repeating forever, without any easing (and each cycle takes 0.6 seconds).&lt;br&gt;
I also paused the animation by default, and added a hover state that plays it once the mouse is over:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calendar-grid&lt;/span&gt; &lt;span class="nc"&gt;.title-container&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  And there you have it ✨
&lt;/h2&gt;

&lt;p&gt;Creating the calendar was quite challenging, but I also had a lot of fun with it!&lt;/p&gt;

&lt;p&gt;I learned the lesson that of course JavaScript is useful in many situations, but it is always easy for a developer to overuse it.&lt;br&gt;
This project demonstrated how much can be achieved by using CSS and HTML only.&lt;/p&gt;

&lt;p&gt;The full project is available on Github at &lt;a href="https://github.com/johnnyfekete/CSSChristmasCalendar/" rel="noopener noreferrer"&gt;https://github.com/johnnyfekete/CSSChristmasCalendar/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code in this article focused on the main features, and in the real calendar, I used more verbose CSS with vendor prefixes where necessary.&lt;/p&gt;

&lt;p&gt;Go ahead and give it a try!&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>css</category>
      <category>design</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CSS Christmas Calendar Submission</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Mon, 28 Dec 2020 21:41:50 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/css-christmas-calendar-submission-1m0a</link>
      <guid>https://dev.to/johnnyfekete/css-christmas-calendar-submission-1m0a</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I built CSS Christmas Calendar as an experiment, in which I published a new CSS art every day in December until Christmas.&lt;/p&gt;

&lt;p&gt;I created every artwork individually by using different CSS techniques, and all code is publicly available, so anyone can look &lt;em&gt;behind the scenes&lt;/em&gt; and get inspired.&lt;/p&gt;

&lt;p&gt;I wanted to keep the project challenging, so no JavaScript has been used in the whole calendar¹.&lt;/p&gt;

&lt;p&gt;To make the project more educative, I create a series of articles here on &lt;strong&gt;dev.to&lt;/strong&gt; about the highlights of what I learned during the creative process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Category Submission:
&lt;/h3&gt;

&lt;p&gt;Random Roulette&lt;/p&gt;

&lt;h3&gt;
  
  
  App Link
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://csschristmascalendar.com" rel="noopener noreferrer"&gt;https://csschristmascalendar.com&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&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%2Fi%2F91oqx7laq6x4ykhqm0hf.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%2Fi%2F91oqx7laq6x4ykhqm0hf.png" alt="CSS Christmas Calendar screenshot" width="800" height="447"&gt;&lt;/a&gt;&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%2Fi%2Fld0w8l9o8gzfnofvzdas.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%2Fi%2Fld0w8l9o8gzfnofvzdas.png" alt="CSS Christmas Calendar screenshot" width="800" height="502"&gt;&lt;/a&gt;&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%2Fi%2Ftizbnebqi7wvb4q8s0oy.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%2Fi%2Ftizbnebqi7wvb4q8s0oy.png" alt="CSS Christmas Calendar screenshot" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;For every day of the Christmas period, I created a new CSS-art, related to the Holidays. They ranged from Christmas stockings to animated toy locomotive and rotating 3D Christmas tree.&lt;br&gt;
Each item got inspired by winter and Christmas, and I used various techniques to bring them to life.&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Source Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/johnnyfekete/CSSChristmasCalendar" rel="noopener noreferrer"&gt;https://github.com/johnnyfekete/CSSChristmasCalendar&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissive License
&lt;/h3&gt;

&lt;p&gt;MIT License&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I was always fascinated by CSS-art.&lt;/p&gt;

&lt;p&gt;I saw many cool designs on Twitter, and even here on this platform; I also tried to create a few in the past - but never dug deep in the topic.&lt;br&gt;&lt;br&gt;
CSS was always a technology used to create pleasant UI's, maybe basic transition animations.&lt;/p&gt;

&lt;p&gt;Then this December I realized that my CSS skills could use some polishing, so I set a challenge to myself to create a Christmassy artwork every day, using totally different styles and techniques.&lt;/p&gt;

&lt;p&gt;This lead to CSS Christmas Calendar, in which I could learn a lot about CSS grids, 3D transformations, CSS variables and their scopes, custom gradient backgrounds, pug templating, and - of course - &lt;strong&gt;publishing to the Digital Ocean Apps platform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I wanted to keep the project open and useful for others too, so made all code available, and I created this &lt;/p&gt;

&lt;h3&gt;
  
  
  How I built it
&lt;/h3&gt;

&lt;p&gt;I was working on the project for a couple of hours every day during these 4 weeks.&lt;br&gt;&lt;br&gt;
I had to come up with an art idea every day, then identify its key parts and create the code.&lt;br&gt;&lt;br&gt;
I worked on the artworks in individual environments, and only added them to the calendar once they were polished there.&lt;/p&gt;

&lt;p&gt;Finally, I committed my changes on Github, and then the magic happened: Digital Ocean's App platform detected my changes and automatically published it to the web, so just a few seconds later it was publicly available to everyone.&lt;/p&gt;

&lt;p&gt;I was amazed by the simplicity of this deployment process, and the ease of configuration, so I will write an article in this series about how I set up the project in Digital Ocean.&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%2Fi%2F0yt7k1qk1a8a6jzidb1y.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%2Fi%2F0yt7k1qk1a8a6jzidb1y.gif" alt="Rotating 3D Christmas tree" width="600" height="404"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;¹ I had to use JavaScript on day 22 to play/pause a melody, but it was not used in any of the animations or graphics.&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>css</category>
      <category>html</category>
      <category>design</category>
    </item>
    <item>
      <title>Collapsing Page Effect</title>
      <dc:creator>Johnny Fekete</dc:creator>
      <pubDate>Sat, 28 Nov 2020 14:18:13 +0000</pubDate>
      <link>https://dev.to/johnnyfekete/collapsing-page-effect-332a</link>
      <guid>https://dev.to/johnnyfekete/collapsing-page-effect-332a</guid>
      <description>&lt;h2&gt;
  
  
  Fun with the Logout Animation
&lt;/h2&gt;

&lt;p&gt;The other day I was working on &lt;a href="https://bonboarding.com" rel="noopener noreferrer"&gt;my startup, Bonboarding&lt;/a&gt;, and wanted to spice things up&lt;br&gt;
a bit so I created a collapsing page animation for the logout functionality.&lt;br&gt;
Nothing fancy, some CSS transition animation. But when I &lt;a href="https://twitter.com/FullStackMaker/status/1280413902610796544?s=20" rel="noopener noreferrer"&gt;posted it on Twitter&lt;/a&gt;,&lt;br&gt;
it got viral, especially after it was retweeted by Smashing Magazine.&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%2Fa1oqrq5hodd0dqd800aq.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%2Fa1oqrq5hodd0dqd800aq.gif" alt="Collapsing page effect animation" width="600" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was totally mind-blown by the engagement, and all the positive feedback&lt;br&gt;
(this was my first viral content). Many of the people asked me to share the code,&lt;br&gt;
but instead of just publishing it on github (which I did, and you can access it as&lt;br&gt;
a &lt;a href="https://www.npmjs.com/package/collapsing-page" rel="noopener noreferrer"&gt;NPM package here&lt;/a&gt; - available both for React or plain JavaScript) I decided to write a brief article about it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Not-So-Complicated Code
&lt;/h2&gt;

&lt;p&gt;As a start, I wanted body's &lt;em&gt;all&lt;/em&gt; child elements to collapse, and also all &lt;code&gt;div&lt;/code&gt;'s.&lt;br&gt;
I didn't want to put animation on all elements (eg. headers, links, buttons etc)&lt;br&gt;
because I felt it would make the animation too fractured.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body &amp;gt; *, body div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure that the page doesn't get scrolled, I set the position to &lt;code&gt;fixed&lt;/code&gt;.&lt;br&gt;
I also disabled pointer events, so no clicks or other events get triggered&lt;br&gt;
during the animation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pointerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, before dealing with the actual, I had to measure the total height&lt;br&gt;
of the page (to know, how much should the items "fall" to ensure that all items&lt;br&gt;
will be out of the screen at the end):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the animation is actually super simple: just loop through the selected&lt;br&gt;
elements and generate some semi-random values, then add them as CSS attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// wait between 0 and 3 seconds&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// speed between 2 and 5 seconds&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rotate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// rotate with max 15 degrees to either direction&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moveX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// move with 80px to either direction&lt;/span&gt;

  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`transform &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms ease-out`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transitionDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`translateY(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px) translateX(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px) rotate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;deg)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This loop just goes through every element and assigns random values for them.&lt;/p&gt;

&lt;p&gt;All of the elements will be transitioned downward with the height of the screen,&lt;br&gt;
therefore even the ones at the top of your page will end up out of the screen at the end.&lt;/p&gt;

&lt;p&gt;Finally, I wanted to keep one item that stayed on the screen behind the collapsing page:&lt;/p&gt;

&lt;p&gt;There are a few important things with it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it should be a child of the body, so it's parent element is not collapsing&lt;/li&gt;
&lt;li&gt;it should have &lt;code&gt;fixed&lt;/code&gt; position&lt;/li&gt;
&lt;li&gt;to achieve the effect that it's in the background behind everything else, you can adjust the &lt;code&gt;z-index&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then just ignore it and it's children elements in the forEach loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Identify the logout screen that should stay in place&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logoutEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#logout-screen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Function that tells if an element is a&lt;/span&gt;
&lt;span class="c1"&gt;// descendant (children, grandchildren etc) of another element&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDescendant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// And the updated forEach loop:&lt;/span&gt;
&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;logoutEl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isDescendant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logoutEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pointerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// this element should detect clicks&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// don't continue adding the animation&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ... add the animation for the other items&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the basic logic, it's quite simple and all animations are handled by CSS transitions.&lt;/p&gt;

&lt;p&gt;Here's the final code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;collapsePage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body &amp;gt; *, body div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logoutEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#logout-screen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pointerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDescendant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;logoutEl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isDescendant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logoutEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pointerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pointerEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// wait between 0 and 3 seconds&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// speed between 2 and 5 seconds&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rotate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// rotate with max 10 degrees&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moveX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// move with 50px to either direction&lt;/span&gt;

    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`transform &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms ease-out`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transitionDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`translateY(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
      &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px) translateX(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px) rotate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;deg)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Things to Consider
&lt;/h2&gt;

&lt;p&gt;After the animation is done, all your elements will still be available in the DOM,&lt;br&gt;
just transitioned out of the screen. It is not a problem if you will navigate to&lt;br&gt;
another page after, but it might cause unexpected behavior if you use some&lt;br&gt;
libraries that handle the navigation for you (eg. react-router-dom).&lt;/p&gt;

&lt;p&gt;To solve this issue, I added a reset function to the component, that is triggered&lt;br&gt;
on unmounting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;br&gt;
You can grab the whole code as an &lt;a href="https://www.npmjs.com/package/collapsing-page" rel="noopener noreferrer"&gt;NPM package&lt;/a&gt; - it can be used both as a React component or as a standalone JavaScript function.&lt;br&gt;
&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While this animation can bring some unexpected delight to your users, be careful with it.&lt;br&gt;
Don't overuse, as the animation takes a few seconds each time. I recommend only using it for logouts,&lt;br&gt;
or when the user deletes something in your web-app (eg. a large project, or even the user's profile).&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>animation</category>
      <category>ux</category>
      <category>react</category>
    </item>
  </channel>
</rss>
