<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Eamonn Cottrell, Web Design and Development]]></title><description><![CDATA[I write about web development, design and tooling from a solopreneur's perspective.]]></description><link>https://blog.eamonncottrell.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 20 May 2026 23:00:35 GMT</lastBuildDate><atom:link href="https://blog.eamonncottrell.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Make a Lightbox in a Google Sheet]]></title><description><![CDATA[this originally appeared on my free weekly newsletter, Got Sheet.
In this article and accompanying video, I'll show you two ways to create a lightbox effect in a spreadsheet. The first will trigger the image to be displayed in a large area in the she...]]></description><link>https://blog.eamonncottrell.com/how-to-make-a-lightbox-in-a-google-sheet</link><guid isPermaLink="true">https://blog.eamonncottrell.com/how-to-make-a-lightbox-in-a-google-sheet</guid><category><![CDATA[google sheets]]></category><category><![CDATA[spreadsheets]]></category><category><![CDATA[excel]]></category><category><![CDATA[Html image]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Tue, 20 Aug 2024 19:43:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724183054064/cb013ab5-a1b7-4514-9101-ff9dbafca11d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>this originally appeared on my</em> <a target="_blank" href="https://www.gotsheet.xyz/c/hashnode"><em>free weekly newsletter, Got Sheet</em></a><em>.</em></p>
<p>In this article and accompanying video, I'll show you two ways to create a lightbox effect in a spreadsheet. The first will trigger the image to be displayed in a large area <strong>in the sheet</strong>. The second will be an actual HTML popup <strong>on top of the sheet</strong>.</p>
<h2 id="heading-what-is-an-image-lightbox"><strong>What is an Image Lightbox?</strong></h2>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/17705226-298d-4813-9700-21405d5d53cf/image.png?t=1715779845" alt /></p>
<p>An image lightbox is what we call it when we hover over or click on an image and it pops up into a bigger version on-screen.</p>
<p>It’s something we’re used to seeing on websites, and it gives things a nice, professional touch when done well.</p>
<p>What about in a spreadsheet, though?</p>
<p>Well, we’ve got two versions of a solution.</p>
<ol>
<li><p>Using built-in functions to display a larger version in a larger cell.</p>
</li>
<li><p>Using Apps Script to create a popup box on top of our spreadsheet.</p>
</li>
</ol>
<p>As a bonus to the first solution, we’ll also include an optional Apps Script to make things a little smoother…more on that below 😉.</p>
<p>Here’s the full walkthrough on <a target="_blank" href="https://youtu.be/J39nMbuycEk">YouTube</a>😀 </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/J39nMbuycEk">https://youtu.be/J39nMbuycEk</a></div>
<p> </p>
<p>Here’s our <a target="_blank" href="https://docs.google.com/spreadsheets/d/1Uz9sZJW1ts_YZc2-Ifd-UAQ8Pkgn23XLzmNdE_sDYrg/copy?gid=735811117&amp;utm_source=www.gotsheet.xyz&amp;utm_medium=referral&amp;utm_campaign=image-lightbox-in-google-sheets#gid=735811117"><strong>demo sheet</strong></a> if you want to follow along.</p>
<h2 id="heading-image-popup-with-built-in-functions"><strong>Image Popup With Built-In Functions</strong></h2>
<p>First, we need images in cells. From the top menu, <code>Insert - Image - Insert image in cell</code> will do the trick.</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/2750a472-e8dc-4668-a37f-a1a48eac0bc7/image.png?t=1715779915" alt /></p>
<p>Next, we need to merge some cells together so that there’s a larger container that will hold our larger picture after the next step.</p>
<p>You could use one cell and change its the width and height, but in my example sheet, the “lightbox” area is sharing rows with the rest of the data, so I didn’t want to do that.</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/351e6219-44d3-4d2f-bd23-94419d254092/image.png?t=1715779977" alt /></p>
<p>In the column next to my image thumbnails, I’ve put checkboxes by selecting <code>Data - Data validation - Criteria: Checkboxes</code> from the top menu.</p>
<p>This will let us select which image to display in our lightbox area.</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/d5ca1a3c-7574-4622-99a5-083eb86cc46e/image.png?t=1715780097" alt /></p>
<p>I’ve named the range <code>A2:A11</code> as <code>pics</code> and the range <code>B2:B11</code> as <code>checkboxes</code> to allow for easier readability in the function we’ll write next…</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/46ba5cda-7752-4d00-8788-da33d8623176/image.png?t=1715781954" alt /></p>
<p><em>named ranges in google sheets</em></p>
<p>Now all that remains is one <code>XLOOKUP()</code> function to put inside our lightbox.</p>
<p><code>=XLOOKUP(TRUE,checkboxes,pics,"")</code> is the function that searches for a check and then displays the corresponding image. By putting this in a big cell or range of merged cells, we can display whichever small image we select in the bigger area.</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/3143e8a6-7db2-4dcd-8f42-70742d0926c8/image.png?t=1715780186" alt /></p>
<p><em>xlookup function in google sheets</em></p>
<p>Remember, all a checkbox is doing is storing either a <code>TRUE</code> (checked) or a <code>FALSE</code> (unchecked) value.</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/f2d21278-2b3b-40b6-9d26-0b03ff0ac762/image.png?t=1715780326" alt /></p>
<p><em>checkboxes and image thumbnails in google sheets</em></p>
<p><strong>⚠️WARNING⚠️</strong></p>
<p>This does have one issue, though. Do you know what it is?</p>
<p><code>XLOOKUP()</code> is going to return whichever checkboxes it comes to first with a TRUE value. So if you have multiple images checked, it’s only going to display the <em>first one it gets to</em>, not the most <em>recently clicked</em> one.</p>
<p>To get around this, let’s write some code.</p>
<h2 id="heading-apps-script-improvement"><strong>Apps Script Improvement</strong></h2>
<p>Open up Apps Script by selecting <code>Extensions - Apps Script</code> from the top menu.</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/e310d543-85aa-458f-a55e-ca4ebb80c413/image.png?t=1715783060" alt /></p>
<p><em>opening apps script in google sheets</em></p>
<p>Delete the built-in function in the code editor that opens. We'll start from scratch with an onEdit function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onEdit</span>(<span class="hljs-params">e</span>) </span>{
</code></pre>
<p>We need to grab the range that we are currently editing.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> range = e.range
</code></pre>
<p>Then, get the checkboxes range.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> checkboxes = SpreadsheetApp.getActive().getRangeByName(<span class="hljs-string">"checkboxes"</span>)
</code></pre>
<p>Then, we need to check whether what we just edited is in that checkbox range.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (range.getColumn() == <span class="hljs-number">2</span> &amp;&amp; range.getRow() &gt;= <span class="hljs-number">2</span> &amp;&amp; range.getRow() &lt;= <span class="hljs-number">10</span>) {
</code></pre>
<p>If it was a checkbox, then we want to uncheck all the checkboxes and re-check the one we just checked.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Uncheck all other checkboxes in the range</span>
checkboxes.uncheck();
<span class="hljs-comment">// Check the edited cell</span>
range.check();
</code></pre>
<p>Now, there is a slight delay when you run the code. After clicking a checkbox, all of them are cleared right before the one you checked gets checked again.</p>
<p>Here’s what the full code looks like:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onEdit</span>(<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">var</span> range = e.range;
  <span class="hljs-keyword">var</span> checkboxes = SpreadsheetApp.getActive().getRangeByName(<span class="hljs-string">"checkboxes"</span>)

  <span class="hljs-comment">// Check if the edited cell is a checkbox in the desired range</span>
  <span class="hljs-keyword">if</span> (range.getColumn() == <span class="hljs-number">2</span> &amp;&amp; range.getRow() &gt;= <span class="hljs-number">2</span> &amp;&amp; range.getRow() &lt;= <span class="hljs-number">10</span>) {
    <span class="hljs-comment">// Uncheck all other checkboxes in the range</span>
    checkboxes.uncheck();
    <span class="hljs-comment">// Check the edited cell</span>
    range.check();
  }
}
</code></pre>
<h2 id="heading-a-real-pop-up-box-with-html"><strong>A Real Pop-Up Box with HTML</strong></h2>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/ba9eb102-9c45-462f-b9e6-11bfe9e088e8/image.png?t=1715780986" alt /></p>
<p><em>modal dialog box in google sheets</em></p>
<p>Okay, that’s all fine and dandy. What about the real thing, though?</p>
<p>This takes all Apps Script, but it’s doable thanks to the built-in method <code>showModalDialog</code>.</p>
<p>This is basically a pop-up window that can hold HTML. And since the internet is built with HTML, all that we need to do is use a little bit to plug in an image.</p>
<p>📌 This method does require an image to live on the internet somewhere. So, we cannot reference the image that we've embedded in our sheet and use it in the HTML we're going to write.</p>
<p>Weird, I know...</p>
<p>Let’s find an image URL we can use. I’ve grabbed an eagle off of <a target="_blank" href="https://unsplash.com/?utm_source=www.gotsheet.xyz&amp;utm_medium=referral&amp;utm_campaign=image-lightbox-in-google-sheets"><strong>unsplash</strong></a>.</p>
<p>We’ll hold this in a variable.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> imageURL = <span class="hljs-string">"https://images.unsplash.com/photo-1715002383611-63488b956401?q=80&amp;w=1887&amp;auto=format&amp;fit=crop&amp;ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"</span>
</code></pre>
<p>Then we need to build our HTML. In our case all we want is one element, so we won’t worry yourself with constructing a full, semantically correct page (although we certainly could 😉)</p>
<p>Another variable will hold this <code>img</code> element:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> html = <span class="hljs-string">'&lt;img src="'</span> + imageURL + <span class="hljs-string">'" style="max-width: 100%; max-height: 100%;"&gt;'</span>;
</code></pre>
<p>We have access to Class Ui in Apps Script where we can “…add features like menus, dialogs, and sidebars.”</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">var</span> ui = SpreadsheetApp.getUi();
</code></pre>
<p>And finally, by calling the showModalDialog() method, we can generate HTML from our <code>html</code> variable using the Class HtmlService.</p>
<pre><code class="lang-javascript">ui.showModalDialog(HtmlService.createHtmlOutput(html).setWidth(<span class="hljs-number">700</span>).setHeight(<span class="hljs-number">1000</span>), <span class="hljs-string">'Eagle 🦅'</span>);
</code></pre>
<h2 id="heading-make-the-image-a-button"><strong>Make the Image a Button</strong></h2>
<p>A final touch is to go add a thumbnail version of our eagle image into our spreadsheet so that it is inserted on top of our cells (this next bit won’t work if it’s embedded in a cell itself).</p>
<p>Once it’s in our sheet, we can click the three black dots in the top right corner and assign a script directly to the image.</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/697595e4-c5bd-4cfb-909d-398326f1c513/image.png?t=1715781440" alt /></p>
<p><em>assigning a script to image in google sheets</em></p>
<p>We named our script <code>displayImagePopup</code>, so this is what we enter. Make sure to leave off the parentheses when typing it into the image's script form.</p>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/80446bd0-c9c2-43bd-a60b-35c7a860049f/image.png?t=1715782276" alt /></p>
<p><em>assigning script</em></p>
<p>Now, anytime we click the small image of the eagle, a pop up box opens with the full image.</p>
<p>Here’s what the full code looks like:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">displayImagePopup</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Get the active sheet</span>
  <span class="hljs-keyword">var</span> sheet = SpreadsheetApp.getActiveSheet();
  <span class="hljs-keyword">var</span> imageURL = <span class="hljs-string">"https://images.unsplash.com/photo-1715002383611-63488b956401?q=80&amp;w=1887&amp;auto=format&amp;fit=crop&amp;ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"</span>
  <span class="hljs-comment">// Create an HTML string for the popup</span>
  <span class="hljs-keyword">var</span> html = <span class="hljs-string">'&lt;img src="'</span> + imageURL + <span class="hljs-string">'" style="max-width: 100%; max-height: 100%;"&gt;'</span>;

  <span class="hljs-comment">// Show the dialog</span>
  <span class="hljs-keyword">var</span> ui = SpreadsheetApp.getUi();
  ui.showModalDialog(HtmlService.createHtmlOutput(html).setWidth(<span class="hljs-number">700</span>).setHeight(<span class="hljs-number">1000</span>), <span class="hljs-string">'Eagle 🦅'</span>);
}
</code></pre>
<h2 id="heading-thanks">Thanks!</h2>
<p>Thanks for reading; let me know what you think.</p>
<p>Connect on <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">Linkedin</a><br />Videos on <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a></p>
]]></content:encoded></item><item><title><![CDATA[6 Ways to number in Excel]]></title><description><![CDATA[Since last we spoke I’ve been recording several new long and short form videos. Check out the YouTube Channel if you haven’t.
I’ve also wrapped up training for a marathon I’ll be racing on March 3rd… 🤞🤞
Now to the sheets…
Do you ever feel lost in E...]]></description><link>https://blog.eamonncottrell.com/6-ways-to-number-in-excel</link><guid isPermaLink="true">https://blog.eamonncottrell.com/6-ways-to-number-in-excel</guid><category><![CDATA[excel]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[spreadsheets]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Thu, 29 Feb 2024 13:26:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1709212836313/fa92b70b-ba90-48e6-b828-0dae5bc6c964.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Since last we spoke I’ve been recording several new long and short form videos. Check out the <a target="_blank" href="https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3d3dy55b3V0dWJlLmNvbS9AZWFtb25uY290dHJlbGw_dXRtX3NvdXJjZT1nb3Qtc2hlZXQuYmVlaGlpdi5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249Ni13YXlzLXRvLW51bWJlci1pbi1leGNlbCIsInBvc3RfaWQiOiJlNDVmNmUzMS1lZGIyLTRkNjYtYTYxMi04YTY0MjJiMWM0YTMiLCJwdWJsaWNhdGlvbl9pZCI6IjA2MWZiMmRhLWIxYTItNGU4My04MWI4LTdkNjc2NzhjNTM2NiIsInZpc2l0X3Rva2VuIjoiY2MxZDUwZGUtMjNlMy00NmQxLTliMTMtODQ1OGRmOGU2YmNjIiwiaWF0IjoxNzA5MjEyNzk0LCJpc3MiOiJvcmNoaWQifQ.ghV8a1dCtLY00Hqdq72Pwz3NVFjjyH5rirVfvyjacLs">YouTube Channel</a> if you haven’t.</p>
<p>I’ve also wrapped up training for a marathon I’ll be racing on March 3rd… 🤞🤞</p>
<p>Now to the sheets…</p>
<p>Do you ever feel lost in Excel? Like you should know a lot but struggle to grasp the basics?</p>
<p>Fear not. Today's tips are all about some fundamentals that will be useful and speed up your spreadsheet work.</p>
<p>Here's the brief video walkthrough if you're a visual learner:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/awNe9MEO8no">https://youtu.be/awNe9MEO8no</a></div>
<p> </p>
<h2 id="heading-6-ways-to-number-things"><strong>6 Ways to Number Things</strong></h2>
<ol>
<li><p><strong>Manual</strong> - You’re familiar with this, though you hang your head in shame at how often you use it. This is when you manually type everything in. Don’t worry, we all start here.</p>
</li>
<li><p><strong>Drag</strong> - Level 1. At the bottom right corner of an active cell or range you can click and drag down. Excel is pretty smart. It will expand the sequence of numbers (or dates, or days) and fill in the missing values.</p>
</li>
<li><p><strong>Double Click</strong> - Now we’re getting up to speed. By double clicking that bottom right corner, Excel will fill down just like when you dragged down. Where it stops will depend on other values in your workbook. If you don’t have other values, or a table you’re working in, this may not work.</p>
<p> <img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/d425da4c-2c51-4ce4-aa0d-c06a7a1157e3/arrow.png?t=1709064606" alt /></p>
</li>
<li><p><strong>Formula</strong> - The power of spreadsheets lies behind the scenes in all the gritty, delicious minutia. It’s here where the wild things live. It’s here where we can write custom formulas to do darn near anything. And I do mean anything - someone <a target="_blank" href="https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PTVyZzd4dlRKOFNVJnV0bV9zb3VyY2U9Z290LXNoZWV0LmJlZWhpaXYuY29tJnV0bV9tZWRpdW09cmVmZXJyYWwmdXRtX2NhbXBhaWduPTYtd2F5cy10by1udW1iZXItaW4tZXhjZWwiLCJwb3N0X2lkIjoiZTQ1ZjZlMzEtZWRiMi00ZDY2LWE2MTItOGE2NDIyYjFjNGEzIiwicHVibGljYXRpb25faWQiOiIwNjFmYjJkYS1iMWEyLTRlODMtODFiOC03ZDY3Njc4YzUzNjYiLCJ2aXNpdF90b2tlbiI6ImNjMWQ1MGRlLTIzZTMtNDZkMS05YjEzLTg0NThkZjhlNmJjYyIsImlhdCI6MTcwOTIxMjc5NCwiaXNzIjoib3JjaGlkIn0.op98vj7D4eJv3ZvdHuSWg8qDozHl2KMTJMmeFZiY9Mc">built a 16-bit CPU inside an Excel workbook</a> recently.<br /> For our purposes, we can simply reference the first cell and add something to it. Then if we drag that formula down just like in option 3, we can quickly expand the sequence of dates.</p>
<p> <img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/735d8a54-2da4-43ab-9545-b1eabd8fe711/image.png?t=1709064401" alt /></p>
</li>
<li><p><strong>Sequence</strong> - This is an incredibly handy built-in function. It’s like a custom formula, only it’s already baked in to Excel. It allows you to generate sequences of values over a specified number of rows and/or columns. You let Excel know how many of each to use, what value to start with and then what value to increment by. *Bonus: to count down, you can use a negative value in the final “step” variable.</p>
<p> <img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/8697f0a0-2af9-447f-9a7f-e326307a65a5/image.png?t=1709064268" alt /></p>
</li>
<li><p><strong>Row</strong> - Another nifty function, and lesser known. Row isn’t explicitly for numbering or sequencing things, but we can use it for this. Row will return whatever the numerical value of the current row is. We can use this as is, but if we worry about rows being deleted, we can also explicitly reference a cell using ROW(A1) or something similar. From here we can sequence subsequent rows using one of the above methods if need be.</p>
</li>
</ol>
<p><img src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/5bb74059-16f2-4b80-be25-88fc81b28ef4/image.png?t=1709064710" alt /></p>
<p><strong><mark>What's your favorite method?</mark></strong></p>
<h2 id="heading-thank-you-so-much"><strong>Thank you so much!</strong></h2>
<p>It means a lot that you’ve read this, and I hope it’s informed and/or entertained you for a few moments today!</p>
<p>Would love to say hi. Here are the best places to find me:</p>
<ul>
<li><p>📺️ Videos on <a target="_blank" href="https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3d3dy55b3V0dWJlLmNvbS9AZWFtb25uY290dHJlbGw_dXRtX3NvdXJjZT1nb3Qtc2hlZXQuYmVlaGlpdi5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249Ni13YXlzLXRvLW51bWJlci1pbi1leGNlbCIsInBvc3RfaWQiOiJlNDVmNmUzMS1lZGIyLTRkNjYtYTYxMi04YTY0MjJiMWM0YTMiLCJwdWJsaWNhdGlvbl9pZCI6IjA2MWZiMmRhLWIxYTItNGU4My04MWI4LTdkNjc2NzhjNTM2NiIsInZpc2l0X3Rva2VuIjoiY2MxZDUwZGUtMjNlMy00NmQxLTliMTMtODQ1OGRmOGU2YmNjIiwiaWF0IjoxNzA5MjEyNzk0LCJpc3MiOiJvcmNoaWQifQ.ghV8a1dCtLY00Hqdq72Pwz3NVFjjyH5rirVfvyjacLs">YouTube</a></p>
</li>
<li><p>💾 Products on <a target="_blank" href="https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL2VhbW9ubi5ndW1yb2FkLmNvbS8_dXRtX3NvdXJjZT1nb3Qtc2hlZXQuYmVlaGlpdi5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249Ni13YXlzLXRvLW51bWJlci1pbi1leGNlbCIsInBvc3RfaWQiOiJlNDVmNmUzMS1lZGIyLTRkNjYtYTYxMi04YTY0MjJiMWM0YTMiLCJwdWJsaWNhdGlvbl9pZCI6IjA2MWZiMmRhLWIxYTItNGU4My04MWI4LTdkNjc2NzhjNTM2NiIsInZpc2l0X3Rva2VuIjoiY2MxZDUwZGUtMjNlMy00NmQxLTliMTMtODQ1OGRmOGU2YmNjIiwiaWF0IjoxNzA5MjEyNzk0LCJpc3MiOiJvcmNoaWQifQ.D3D-ngTkLNlO5krX5XLZ_rhbccSA3VXZWN3ly9M5Wfg">Gumroad</a></p>
</li>
<li><p>✉️ Connections on <a target="_blank" href="https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3d3dy5saW5rZWRpbi5jb20vaW4vZWFtb25uY290dHJlbGwvP3V0bV9zb3VyY2U9Z290LXNoZWV0LmJlZWhpaXYuY29tJnV0bV9tZWRpdW09cmVmZXJyYWwmdXRtX2NhbXBhaWduPTYtd2F5cy10by1udW1iZXItaW4tZXhjZWwiLCJwb3N0X2lkIjoiZTQ1ZjZlMzEtZWRiMi00ZDY2LWE2MTItOGE2NDIyYjFjNGEzIiwicHVibGljYXRpb25faWQiOiIwNjFmYjJkYS1iMWEyLTRlODMtODFiOC03ZDY3Njc4YzUzNjYiLCJ2aXNpdF90b2tlbiI6ImNjMWQ1MGRlLTIzZTMtNDZkMS05YjEzLTg0NThkZjhlNmJjYyIsImlhdCI6MTcwOTIxMjc5NCwiaXNzIjoib3JjaGlkIn0.SA3RqUwdSxcGen6dlMwIzG86CMt0JWHbfeNdw8e9e4A">LinkedIn</a></p>
</li>
</ul>
<p>It's been a while since I've posted one of my articles on Hashnode. Never fear - I'll be repurposing some of my content from the past year or so to keep you in the loop!</p>
]]></content:encoded></item><item><title><![CDATA[Google Sheets Tutorial – How to Enable Multiple Selection Data Validation Using Apps Script]]></title><description><![CDATA[Originally published for freeCodeCamp

In this article I will show you how to allow for multiple items to be selected using the drop-down data validation feature in Google Sheets.
Here's the Google Sheet we'll use for the example. You can make a copy...]]></description><link>https://blog.eamonncottrell.com/google-sheets-tutorial-how-to-enable-multiple-selection-data-validation-using-apps-script</link><guid isPermaLink="true">https://blog.eamonncottrell.com/google-sheets-tutorial-how-to-enable-multiple-selection-data-validation-using-apps-script</guid><category><![CDATA[data validation]]></category><category><![CDATA[google sheets]]></category><category><![CDATA[spreadsheets]]></category><category><![CDATA[google apps script]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Mon, 06 Feb 2023 15:01:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675868474866/c1567d24-eb49-430e-8e43-b75b4de1dee1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Originally published for <a target="_blank" href="https://www.freecodecamp.org/news/google-sheets-multiple-data-validation-selections/">freeCodeCamp</a></p>
</blockquote>
<p>In this article I will show you how to allow for multiple items to be selected using the drop-down data validation feature in Google Sheets.</p>
<p><a target="_blank" href="https://docs.google.com/spreadsheets/d/1yxc4k1x5idcS_moQ1Bq8LnHGZF2nm9dpARglY7Rv0UI/edit#gid=390071620">Here's the Google Sheet</a> we'll use for the example. You can make a copy of this to edit yourself by clicking <code>File -&gt; Make a copy</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-13.png" alt="image-13" /></p>
<p>At the bottom of the article is a video walkthrough I recorded for this solution. 👇</p>
<h2 id="heading-the-problem"><strong>The Problem 🤔</strong></h2>
<p>My five-year-old son posed a question that triggered a deep dive into Google Apps Script. He wanted to have multiple items selected from a data validation drop-down list.</p>
<p>Leave it to a five-year-old to send me to Google, YouTube, and beyond in search of a spreadsheet solution! 😅</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/michelangelo.gif" alt="Gif of Michaelengelo from Teenage Mutant Ninja Turtles" /></p>
<p>We had built a spreadsheet that showed information about the Teenage Mutant Ninja Turtles. Names, birthdays, ages, favorite colors...</p>
<p>I was highlighting the amazing power of spreadsheets to organize, calculate, and visualize information. Typical five-year-old parenting stuff.</p>
<p>For the favorite color column, we used a drop-down list to select from a list of colors.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-3.png" alt="image-3" /></p>
<p>This is a fairly simple feature to use in Google Sheets. To create a drop-down list, select <code>Data -&gt; Data validation</code> from the menu:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-4.png" alt="Screenshot of Google Sheet's data menu" /></p>
<p>Update: the same feature is now also available when you right-click a cell:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-5.png" alt="Screenshot of right-clicking menu in Google Sheets" /></p>
<p>In either case, a data validation menu will appear where you may set your conditions.</p>
<p>We had our list of colors in the cells <code>H2:H9</code> so we selected <code>Dropdown (from a range)</code> in the Criteria section and then entered that range.</p>
<p>We wanted this to be copied to other cells without affecting that range, so we locked it in place, using the $ signs: <code>=$H$2:$H$9</code>.</p>
<p>This allows for the validation to be copied to other cells while retaining those cell references for the list of color values.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-7.png" alt="Screenshot of Google Sheets' data validation options menu" /></p>
<h2 id="heading-google-sheets-allows-one-selection-only"><strong>Google Sheets Allows One Selection Only 🚩</strong></h2>
<p>The problem is, Google Sheets only allows for a single selection. We wanted for Leonardo to have multiple favorite colors!</p>
<p>Fortunately, Google Apps Script allows custom code to be written within Google Sheets, and we used this to solve our problem.</p>
<p>I came across the code for this from a YouTube <a target="_blank" href="https://www.youtube.com/watch?v=dm4z9l26O0I">video</a> by Alexander Ivanov and set out to update the information with a more clearly laid out video and explanation of my own.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/problem-solved.gif" alt="Problem Solved gif" /></p>
<h2 id="heading-how-to-use-apps-script"><strong>How to Use Apps Script🧑‍💻</strong></h2>
<p>Open the Apps Script screen by selecting <code>Extensions -&gt; Apps Script</code> from the menu bar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-9.png" alt="image-9" /></p>
<p>You'll be able to create files using the plus <code>+</code> icon. For this project we need a <a target="_blank" href="http://Code.gs"><code>Code.gs</code></a> file and a <code>Page.html</code> file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-8.png" alt="image-8" /></p>
<h2 id="heading-the-codegshttpcodegs-file"><strong>The</strong> <a target="_blank" href="http://Code.gs"><strong>Code.gs</strong></a> <strong>File</strong></h2>
<p>Start in the <a target="_blank" href="http://Code.gs">Code.gs</a> file.</p>
<p>The first thing we want is a drop-down from the Google Sheets toolbar to run our code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-14.png" alt="Screenshot of Google Sheets' custom drop-down bar" /></p>
<p>To do this, we use the following code to add the UI option <code>Show dialog</code> to the menu bar. Upon clicking, it will run the <code>showDialog</code> function. By wrapping these methods in the built-in <code>onOpen</code> function, this menu in the toolbar gets added as soon as we open the spreadsheet.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onOpen</span>(<span class="hljs-params">e</span>) </span>{                                
    SpreadsheetApp.getUi()                                
    .createMenu(<span class="hljs-string">'Multiple Select Data Validation'</span>)
    .addItem(<span class="hljs-string">'Show dialog'</span>, <span class="hljs-string">'showDialog'</span>)                                
    .addToUi();                                
}
</code></pre>
<p>There is often a short delay of a few seconds before custom menus are visible in the menu. Give it a few seconds and it will appear.</p>
<p>The <code>showDialog</code> function will create an HTML variable from a template that we will create in a moment. It then uses the built-in method <code>.showSidebar</code> to create a sidebar with that html.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">showDialog</span>(<span class="hljs-params"></span>) </span>{                                
    <span class="hljs-keyword">var</span> html = HtmlService.createTemplateFromFile(<span class="hljs-string">'Page'</span>).evaluate();
    SpreadsheetApp.getUi().showSidebar(html);                                
}
</code></pre>
<p>Then we have another function, <code>valid</code>, which will check the current cell for any data validation criteria and return those values in a 2-dimensional array.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> valid = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{                                
    <span class="hljs-keyword">try</span>{                                
        <span class="hljs-keyword">return</span> SpreadsheetApp.getActiveRange().getDataValidation().getCriteriaValues()[<span class="hljs-number">0</span>].getValues();                                
    }<span class="hljs-keyword">catch</span>(e){                                
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>                                
    }                                
}
</code></pre>
<p>And our final function, <code>fillCell</code>, will create an array to hold the resultant list of values we want the cell to contain. It then pushes these strings to the array and separates them by commas.</p>
<p>Lastly, it uses the built-in method, <code>setValue</code> to fill the current cell with the comma separated values (our favorite colors).</p>
<p>(<code>fillCell</code> and <code>valid</code> are called in the Page.html code we are about to go over.)</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fillCell</span>(<span class="hljs-params">e</span>)</span>{                                
    <span class="hljs-keyword">var</span> s = [];                                
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i <span class="hljs-keyword">in</span> e){                                
        <span class="hljs-keyword">if</span>(i.substr(<span class="hljs-number">0</span>, <span class="hljs-number">2</span>) == <span class="hljs-string">'ch'</span>) s.push(e[i]);                                
}                                
    <span class="hljs-keyword">if</span>(s.length) SpreadsheetApp.getActiveRange().setValue(s.join(<span class="hljs-string">', '</span>));
}
</code></pre>
<p>Here is the full code for the <a target="_blank" href="http://Code.gs"><code>Code.gs</code></a> file:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onOpen</span>(<span class="hljs-params">e</span>) </span>{                                
    SpreadsheetApp.getUi()                                
    .createMenu(<span class="hljs-string">'Multiple Select Data Validation'</span>)
    .addItem(<span class="hljs-string">'Show dialog'</span>, <span class="hljs-string">'showDialog'</span>)                                
    .addToUi();                                
}                                
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">showDialog</span>(<span class="hljs-params"></span>) </span>{                                
    <span class="hljs-keyword">var</span> html = HtmlService.createTemplateFromFile(<span class="hljs-string">'Page'</span>).evaluate();
    SpreadsheetApp.getUi().showSidebar(html);                                
}                                
<span class="hljs-keyword">var</span> valid = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{                                
    <span class="hljs-keyword">try</span>{                                
        <span class="hljs-keyword">return</span> SpreadsheetApp.getActiveRange().getDataValidation().getCriteriaValues()[<span class="hljs-number">0</span>].getValues();                                
    }<span class="hljs-keyword">catch</span>(e){                                
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>                                
    }                                
}                                
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fillCell</span>(<span class="hljs-params">e</span>)</span>{                                
    <span class="hljs-keyword">var</span> s = [];                                
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i <span class="hljs-keyword">in</span> e){                                
        <span class="hljs-keyword">if</span>(i.substr(<span class="hljs-number">0</span>, <span class="hljs-number">2</span>) == <span class="hljs-string">'ch'</span>) s.push(e[i]);                                
}                                
    <span class="hljs-keyword">if</span>(s.length) SpreadsheetApp.getActiveRange().setValue(s.join(<span class="hljs-string">', '</span>));
}
</code></pre>
<h2 id="heading-the-pagehtml-file"><strong>The Page.html File</strong></h2>
<p>Now create and open a file called <code>Page.html</code>.</p>
<p>This will control a pop-up sidebar where we will handle our multiple selections, and will contain:</p>
<ol>
<li><p>a form with checkboxes next to each option</p>
</li>
<li><p>a button to fill in the current cell</p>
</li>
<li><p>a button to get validation from the current cell</p>
</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-10.png" alt="Screenshot of our custom Google Sheet html sidebar" /></p>
<p>We will use templated HTML for our example. First, create a variable, <code>data</code>, by calling the <code>valid()</code> function we created above. We use <code>&lt;? CODE_GOES_HERE ?&gt;</code> syntax to write code within the html template.</p>
<pre><code class="lang-javascript">&lt;? <span class="hljs-keyword">var</span> data = valid(); ?&gt;
</code></pre>
<p>Then we create a form to house all of the data pulled from the <code>valid()</code> function:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"form"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"form"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>And we'll run some code to build our list of checkboxes within this form. First, we check if the data in the cell is of type <code>[object Array]</code>.</p>
<pre><code class="lang-javascript">&lt;? <span class="hljs-keyword">if</span>(<span class="hljs-built_in">Object</span>.prototype.toString.call(data) === <span class="hljs-string">'[object Array]'</span>) { ?&gt;
</code></pre>
<p><code>valid()</code> returns a two-dimensional array because it's using the built-in method <code>getValues()</code>. So, you can picture this being returned as an array of arrays with each individual one being one of the favorite colors:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//for illustration only; this is the type of 2D array that valid() will return</span>

favoriteColors = [
    [<span class="hljs-string">"purple"</span>],
    [<span class="hljs-string">"red"</span>],
    [<span class="hljs-string">"white"</span>],
    [<span class="hljs-string">"black"</span>]
]
</code></pre>
<p>This will help as we look at the next bit of code which can appear overwhelming.</p>
<p>We need to access each color – the strings. We do this by nesting <code>for</code> loops. The first loop iterates through each position in the favorite colors array.</p>
<pre><code class="lang-javascript">&lt;? <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; data.length; i++) { ?&gt;
</code></pre>
<p>The second loop iterates through each element in the interior arrays. In our case, this will always be one element since each of these arrays are of length 1.</p>
<pre><code class="lang-javascript">&lt;? <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> j = <span class="hljs-number">0</span>; j &lt; data[i].length; j++) { ?&gt;
</code></pre>
<p>Remember, <code>["purple"]</code> is an array of length 1. The <code>j</code> loop does not count the letters in the string <em>within</em> the array – we are just counting the length <em>of</em> the array.</p>
<p>So, we're looping through each item in the 2D array and creating a checkbox input for each one:</p>
<pre><code class="lang-javascript">&lt;input type=<span class="hljs-string">"checkbox"</span> id=<span class="hljs-string">"ch&lt;?= '' + i + j ?&gt;"</span> name=<span class="hljs-string">"ch&lt;?= '' + i + j ?&gt;"</span> value=<span class="hljs-string">"&lt;?= data[i][j] ?&gt;"</span>&gt;&lt;?= data[i][j] ?&gt;<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span></span>
</code></pre>
<p>This adds an <code>id</code> and <code>name</code> that start with "ch" and then adds the element's array position. It also pulls the value (the color) itself as a <code>value</code> in addition to the displayed text.</p>
<p>These screenshots may help connect the mental dots:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-11.png" alt="screenshot of inspected element: the input with an id" /></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-12.png" alt="screenshot of inspected element: the form and input elements" /></p>
<p>If our initial <code>if</code> statement fails, we'll display a <code>&lt;p&gt;</code> indicating that perhaps there are no data validation rules in that cell while linking to a support article showing how to create an in-cell drop down list.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">else</span> { ?&gt;                                
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Maybe current cell doesn't have <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://support.google.com/drive/answer/139705?hl=en"</span>&gt;</span>Data validation...<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>                                
&lt;? } ?&gt;
</code></pre>
<h3 id="heading-how-to-code-the-buttons"><strong>How to Code the Buttons</strong></h3>
<p>Then we need our two buttons.</p>
<p>The first button pulls the data validation from the cell. It runs the <code>showDialog()</code> function we created in <a target="_blank" href="http://Code.gs"><code>Code.gs</code></a> and builds the form of checkbox entries if there exist data validation values.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// get validation from the current cell</span>
&lt;input type=<span class="hljs-string">"button"</span> value=<span class="hljs-string">"get validation from current"</span> onclick=<span class="hljs-string">"google.script.run.showDialog()"</span> /&gt;
</code></pre>
<p>The second button fills the selected values into the current cell. <strong>This is what we were after all along!</strong> This runs the <code>fillCell</code> function in <a target="_blank" href="http://Code.gs"><code>Code.gs</code></a>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// fills values into current cell</span>
&lt;input type=<span class="hljs-string">"button"</span> value=<span class="hljs-string">"fill current"</span> onclick=<span class="hljs-string">"google.script.run.fillCell(this.parentNode)"</span> /&gt;
</code></pre>
<p>Here is the full code for <code>Page.html</code>:</p>
<pre><code class="lang-javascript">&lt;div&gt;                                
&lt;? <span class="hljs-keyword">var</span> data = valid(); ?&gt;                                
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"form"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"form"</span>&gt;</span>                                
<span class="hljs-tag">&lt;<span class="hljs-name">?</span> <span class="hljs-attr">if</span>(<span class="hljs-attr">Object.prototype.toString.call</span>(<span class="hljs-attr">data</span>) === <span class="hljs-string">'[object Array]'</span>) { ?&gt;</span>                                
<span class="hljs-tag">&lt;<span class="hljs-name">?</span> <span class="hljs-attr">for</span> (<span class="hljs-attr">var</span> <span class="hljs-attr">i</span> = <span class="hljs-string">0;</span> <span class="hljs-attr">i</span> &lt; <span class="hljs-attr">data.length</span>; <span class="hljs-attr">i</span>++) { ?&gt;</span>                                
    <span class="hljs-tag">&lt;<span class="hljs-name">?</span> <span class="hljs-attr">for</span> (<span class="hljs-attr">var</span> <span class="hljs-attr">j</span> = <span class="hljs-string">0;</span> <span class="hljs-attr">j</span> &lt; <span class="hljs-attr">data</span>[<span class="hljs-attr">i</span>]<span class="hljs-attr">.length</span>; <span class="hljs-attr">j</span>++) { ?&gt;</span>                                
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ch&lt;?= '' + i + j ?&gt;"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"ch&lt;?= '' + i + j ?&gt;"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"&lt;?= data[i][j] ?&gt;"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">?=</span> <span class="hljs-attr">data</span>[<span class="hljs-attr">i</span>][<span class="hljs-attr">j</span>] ?&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>                                
    <span class="hljs-tag">&lt;<span class="hljs-name">?</span> } ?&gt;</span>                                
<span class="hljs-tag">&lt;<span class="hljs-name">?</span> } ?&gt;</span>                                
<span class="hljs-tag">&lt;<span class="hljs-name">?</span> } <span class="hljs-attr">else</span> { ?&gt;</span>                                
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Maybe current cell doesn't have <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://support.google.com/drive/answer/139705?hl=en"</span>&gt;</span>Data validation...<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>                                
<span class="hljs-tag">&lt;<span class="hljs-name">?</span> } ?&gt;</span>                                
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"fill current"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"google.script.run.fillCell(this.parentNode)"</span> /&gt;</span>                                
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"get validation from current"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"google.script.run.showDialog()"</span> /&gt;</span>                                
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>                                
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>Yes, this is quite a cumbersome solution to something relatively simple. Maybe Google will add this to the native functionality at some point. They have recently updated Data Validation to include more modern and easy to use functionality, so it's not out of the question.</p>
<p>Until then, this has been a great solution for me, and I'm grateful to <a target="_blank" href="https://www.youtube.com/watch?v=dm4z9l26O0I">Alexander</a> for his initial code.</p>
<p>I hope this article has helped you understand the code a bit better and that it empowers you to create your own custom Google Sheets solutions!</p>
<p>As promised, here's my walkthrough video:</p>
<h2 id="heading-video-walkthrough"><strong>Video Walkthrough 📽️</strong></h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/41ydIPKZezE">https://youtu.be/41ydIPKZezE</a></div>
<p> </p>
<h2 id="heading-thanks"><strong>Thanks!</strong></h2>
<p>Thanks for reading! If you found this helpful, I'd love it if you followed me and said hey 👋 over on <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a> and <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a> where you can find more content like this.</p>
<p>Have a great one!</p>
]]></content:encoded></item><item><title><![CDATA[Excel Tutorial – How to Clean Data with the TRIM() and CLEAN() Functions]]></title><description><![CDATA[Originally published for freeCodeCamp

Without clean data, your spreadsheet is knocking on death's door. In this tutorial, I will show you two fast ways to clean up the data in your Excel or Google Sheets spreadsheet.
When dealing with data sets, esp...]]></description><link>https://blog.eamonncottrell.com/excel-tutorial-how-to-clean-data-with-the-trim-and-clean-functions</link><guid isPermaLink="true">https://blog.eamonncottrell.com/excel-tutorial-how-to-clean-data-with-the-trim-and-clean-functions</guid><category><![CDATA[data]]></category><category><![CDATA[excel]]></category><category><![CDATA[google sheets]]></category><category><![CDATA[clean-data]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Fri, 27 Jan 2023 14:43:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675867468404/02b9aa4b-d3cf-40ee-8aed-b590aac9c3ef.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><a target="_blank" href="https://www.freecodecamp.org/news/excel-tutorial-clean-data-with-the-trim-and-clean-functions/">Originally published for freeCodeCamp</a></p>
</blockquote>
<p>Without clean data, your spreadsheet is knocking on death's door. In this tutorial, I will show you two fast ways to clean up the data in your Excel or Google Sheets spreadsheet.</p>
<p>When dealing with data sets, especially large ones and/or those that you didn't create, it is likely that you will have to clean the data in large or small ways to get it fully functional.</p>
<p>Both of the built-in functions we'll discuss – <code>=TRIM()</code> and <code>=CLEAN()</code> – are available in Microsoft Excel as well as Google Sheets. And both of them have the potential to save you a lot of head-scratching.</p>
<p>Let's examine:</p>
<ol>
<li><p>What do we mean by clean data?</p>
</li>
<li><p>Why isn't it already clean?</p>
</li>
<li><p>How do we clean it fast and thoroughly?</p>
</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/clean-desk.gif" alt="a man flipping his messy desk and it becoming magically organized" /></p>
<p><em>pssst</em>: I have a video walkthrough at the bottom 👇 of the article too 😉</p>
<h2 id="heading-what-is-clean-data"><strong>What is Clean Data?</strong></h2>
<p>In Excel and Google Sheets, the data that we work with is located in cells. In a perfect world, these cells contain properly formatted data like numbers, amounts, names, and other pieces of information.</p>
<p>However, we often encounter things in the cells which don't belong and which will prevent us from using that data in the way we need.</p>
<p>Things like non-printable characters, extra white spaces and letters in cells that should contain numbers are a few examples of unclean data which will negatively affect our work.</p>
<p>What are non-printable characters, you ask? They are the first 32 control characters in the ASCII table.</p>
<p>Check out the table below of the ASCII characters. The first 32 are non-printable control codes. These can cause issues if they somehow make it into your data set.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/char-1.png" alt="screenshot of ascii table in Excel" /></p>
<p>You can generate this table by using the character function for all the numbers from 0 to 255: <code>=CHAR(&lt;number&gt;)</code>.</p>
<p>The other common offenders are spaces that shouldn't be there – leading or trailing spaces in a cell. Or simply spaces in the middle that shouldn't be there.</p>
<p>Compound these finicky cells throughout a spreadsheet that contains thousands or millions of cells, and we've got quite a mess on our hands.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/a-mess.gif" alt="a woman waving hands and saying, &quot;this is a mess&quot;" /></p>
<h2 id="heading-why-isnt-the-data-clean"><strong>Why Isn't the Data Clean?</strong></h2>
<p>Because we live in a fallen world.</p>
<p>🤣 Well, it's not that dramatic. But there can be many reasons the data isn't already clean. Many times human error is the culprit.</p>
<p>Whoever or wherever you're getting your data from simply made some mistakes with it before you got your hands on it.</p>
<p>Or maybe you messed it up when you started to manipulate the data.</p>
<p>As we'll see in the example below, the data could be perfectly fine wherever you're getting it from on the internet. But then when you import it into your spreadsheet, the conversion from HTML to Spreadsheet brings in a bunch of non-printable characters and spaces.</p>
<p>And, of course, because we're dealing with computers and people and data, we may never figure out why the data we've received isn't clean. It simply isn't. And despite our confusion, we have to clean it up to use it and extract meaning from it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/confusion.gif" alt="Obi Wan visibly confused" /></p>
<h2 id="heading-how-to-clean-data-in-excel-and-google-sheets"><strong>How to Clean Data in Excel and Google Sheets</strong></h2>
<p>Let's first get some data. In both Excel and Google Sheets, we can import data from the web. I want to bring in a table of Recipes and Ingredients from a video game website that looks like this online.</p>
<p>Here's Link...I mean, <a target="_blank" href="https://www.ign.com/wikis/the-legend-of-zelda-breath-of-the-wild/All_Recipes_and_Cookbook">here's the link</a>. ⚔️</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-153.png" alt="Zelda recipe table" /></p>
<p>To address the obvious question first: yes, I could just copy and paste the table. And, yes, it will in this case paste the table straight into Excel.</p>
<p>But it'll also bring in the pictures which I don't need, the links which I don't want, some formatting that I'll have to reset, and potentially some of the non-printable characters and/or spaces that we'll discuss below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-154.png" alt="Table of Recipe ingredients copied straight off website" /></p>
<p>Above is what it will look like after copying and pasting. But we are interested in preserving data and cleaning it, so we'll import it another way for the sake of the rest of our discussion.</p>
<p>If you're using Excel, it has some pretty robust cleaning features out of the box. When importing data from IGN, we enter the address where it lives, and it will pull down any data that it detects as available for import.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/import.png" alt="screenshot of data import from web in Excel" /></p>
<p>Let's get the table of Poultry and Meat Entrée receipts from Zelda: Breath of the Wild.</p>
<p>The Navigator window shows us a handy list of data tables detected on the page as well as a preview pane on the right that can be toggled between table and website views.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/recipe-table-1.png" alt="Excel data import navigator window screenshot" /></p>
<h3 id="heading-excel-power-query"><strong>Excel Power Query</strong></h3>
<p>Once we've selected our data and loaded it, we'll have imported a table into our spreadsheet just fine. Excel's Power Query features allow us to then go into this particular table and extract the values in the Ingredients column into a list of items delimited by a selector of our choice.</p>
<p>In other words, Excel is smart enough to extract the individual list items in the Ingredients column and put them in a cell one by one and separated by commas (or whatever we select to separate them).</p>
<p>The following three screenshots show this process:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/expand-values.png" alt="Screenshot of Excel Power Query menu" /></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/delimiter.png" alt="Screenshot of delimiter for expanded values" /></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/extracted.png" alt="Screenshot of Expanded list values in Ingredient column" /></p>
<p>The end result is a comma separated list of values that is seemingly pretty clean. We can then split it up by using the <code>=SPLIT()</code> function into separate cells if we choose, or simply use it as is.</p>
<p>This is an ideal situation. But what if we encounter those spaces and non-printable characters?</p>
<h3 id="heading-clean-and-trim-functions"><strong>Clean and Trim Functions</strong></h3>
<p>Here's a screenshot of the same table when imported into Google Sheets with <code>=IMPORTHTML()</code>. It pulls the data correctly, but you can see the extra spaces that were also brought into the sheet.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/sheets-trim.png" alt="Imported data from webpage in Google Sheets" /></p>
<p>By using <code>=CLEAN()</code> on the ingredients cells, we can get rid of some non-printable carriage returns causing the line breaks.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/justclean.png" alt="Table after Cleaning cells" /></p>
<p>By using <code>=TRIM()</code> on the cells, we can get rid of all the leading spaces.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/justtrim.png" alt="Table after Trimming white spaces" /></p>
<p>And by nesting the two with <code>=CLEAN(TRIM())</code>, we can do both. The result is a list of values separated by dashes. Similarly to our resultant table in Excel where we were left with a comma-separated list, we can then go and <code>=SPLIT()</code> these values further if need be.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/both.png" alt="Table after cleaning and trimming at once" /></p>
<p>These functions work the same in Excel, but for illustrative purposes we used Google Sheets since it wasn't able to import the data as neatly there.</p>
<h2 id="heading-sample-spreadsheet-amp-video-walkthrough"><strong>Sample Spreadsheet &amp; Video Walkthrough</strong></h2>
<p><a target="_blank" href="https://docs.google.com/spreadsheets/d/11j6ajxvN6dSd9U7y6LQT6IhAJvM99jRGPFcDIBtBMkM/edit?usp=sharing">Here is a link to the sample Google Sheet</a> that I made for this tutorial.</p>
<p>The first page is a table of the non-printable characters.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-155.png" alt="image-155" /></p>
<p>And the second page is the Zelda example we talked about. Feel free to make a copy of this spreadsheet if you'd like to mess around with it further (File -&gt; Make a copy).</p>
<p>We'll be building a really cool project with some of this Zelda data soon...<a target="_blank" href="https://www.youtube.com/@eamonncottrell">follow me on YouTube</a> to keep an eye out for that.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/mgAHQWrTcCg">https://youtu.be/mgAHQWrTcCg</a></div>
<p> </p>
<h2 id="heading-thanks"><strong>Thanks</strong></h2>
<p>Thanks for reading, and I hope this has been useful for you. Come say hey 👋 on <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a> and <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a>, and I'll talk to you in the next article!</p>
]]></content:encoded></item><item><title><![CDATA[Accounting Number Format in Excel – How to Apply it to Selected Cells]]></title><description><![CDATA[Originally published for freeCodeCamp

In this article I will show you how to format cells in Microsoft Excel. We'll be looking particularly at the Accounting format for cells with numbers.
At the end of the article I'll give you two bonuses: a short...]]></description><link>https://blog.eamonncottrell.com/accounting-number-format-in-excel-how-to-apply-it-to-selected-cells</link><guid isPermaLink="true">https://blog.eamonncottrell.com/accounting-number-format-in-excel-how-to-apply-it-to-selected-cells</guid><category><![CDATA[excel]]></category><category><![CDATA[spreadsheets]]></category><category><![CDATA[formatting]]></category><category><![CDATA[currency]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Wed, 18 Jan 2023 14:36:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675866960119/65aec515-46f4-4fbe-99df-2fd2008adc64.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Originally published for <a target="_blank" href="https://www.freecodecamp.org/news/accounting-number-format-in-excel-how-to-apply-it-to-selected-cells/">freeCodeCamp</a></p>
</blockquote>
<p>In this article I will show you how to format cells in Microsoft Excel. We'll be looking particularly at the <code>Accounting</code> format for cells with numbers.</p>
<p>At the end of the article I'll give you two bonuses: a short video walkthrough of the methods we go over, as well as a breakdown of the differences between the <code>Accounting</code> format and the <code>Currency</code> format.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/currency-vs-accounting.png" alt="Excel screenshot with differently formatted columns of numbers" /></p>
<p>We'll go over these three primary ways to format cells in Excel, and how to use each one to add the <code>Accounting</code> format to cells.</p>
<ol>
<li><p>Point and click on Home Tab in Ribbon</p>
</li>
<li><p>Ctrl + 1 opens format cells window</p>
</li>
<li><p>Keyboard Combos</p>
</li>
</ol>
<h2 id="heading-easy-mode-point-and-click"><strong>Easy Mode: Point and Click</strong></h2>
<p>As with most things in Excel, you can indeed simply select the cell(s) you'd like to apply the <code>Accounting</code> format to and click the appropriate selection.</p>
<p>Select the Home tab from the toolbar at the top. The formatting options are found in the Number section on the Ribbon.</p>
<p>The <code>Accounting</code> format actually has its own shortcut button marked by the dollar sign.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/ribbon.png" alt="Excel screenshot of Ribbon" /></p>
<p>You may also select the drop-down menu to see the list of options:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-25.png" alt="Excel screenshot of number format options" /></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/easy.gif" alt="easy as that gif" /></p>
<h2 id="heading-how-to-use-the-format-cell-menu"><strong>How to Use the Format Cell Menu</strong></h2>
<p>The selections above included the most common number formats, but you can also open the full Format Cells window by clicking the arrow in the bottom right corner of the Number section on the Ribbon.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/arrow.png" alt="Excel screenshot of format cell menu selection" /></p>
<p>As with all good things in computer life, there is also a shortcut to open the Format cell menu.</p>
<p>Press <code>CTRL + 1</code> to open the full menu.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-27.png" alt="Excel screenshot of Format Cells menu" /></p>
<p>From here you can again select the <code>Accounting</code> format and also adjust additional options like what type of money symbol and how many decimal places to use.</p>
<h2 id="heading-how-to-use-the-keyboard-combo"><strong>How to Use the Keyboard Combo</strong></h2>
<p>Excel includes keyboard combos for every imaginable action. In many cases this can save time spent searching for and clicking the buttons on the Ribbon.</p>
<p>Keyboard combos start by pressing the <code>ALT</code> key. As soon as you press it, you'll see a ton of letters pop up on the toolbar and Ribbon.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-28.png" alt="Excel screenshot of ALT keyboard combo letters in Ribbon" /></p>
<p>The Keyboard Combo for <code>Accounting</code> Format is <code>ALT, H,AN</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-30.png" alt="image-30" /></p>
<p>And after you press those buttons (one after the other), you'll have selected the drop-down for <code>Accounting</code> format. The most commonly used currency symbols are displayed. <code>ENTER</code> will select the English dollar sign, or you can arrow down to another selection.</p>
<p>You can see from the screenshot above that you can also press the combo <code>ALT,H,FM</code> to open the full Format Cells window.</p>
<p>The keyboard combos offer the most nimble way to access operations and menus in Excel. Learning the ones you'll use most often can greatly improve your productivity.</p>
<p>As promised, here's an overview video walking through the topics I've discussed in this article:</p>
<h2 id="heading-video-walkthrough"><strong>Video Walkthrough</strong></h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/o_cNy4JPXCA">https://youtu.be/o_cNy4JPXCA</a></div>
<p> </p>
<h2 id="heading-accounting-vs-currency-format"><strong>Accounting vs Currency Format</strong></h2>
<p>So, why use <code>Accounting</code> format? Isn't it just formatting the numbers with dollar signs?</p>
<p>That's a half truth. Yes, it adds a dollar sign, but so does the <code>Currency</code> format. If we open up the Format Cells window again by pressing <code>CTRL + 1</code>, we'll see that the main difference is one of alignment.</p>
<p>The <code>Currency</code> format is used for "general monetary values" and puts the dollar sign directly in front of the left-most number. The <code>Accounting</code> format, on the other hand, lines up both the dollar sign as well as the decimal in the column of numbers.</p>
<p>In terms of formatting, <code>Currency</code> will also give you options for how to display negative numbers. But in the <code>Accounting</code> format, the negative numbers will always be displayed within parenthesis.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/currency-options.png" alt="Format cells menu in Excel" /></p>
<h2 id="heading-thanks-for-reading"><strong>Thanks for reading!</strong></h2>
<p>I hope this has been helpful for you.</p>
<p>Come find me on <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a> where I'm growing my coding and spreadsheet tutorial channel. And come follow and say hey over on <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a>. 👋</p>
<p>Have a great one!</p>
]]></content:encoded></item><item><title><![CDATA[How to Clear Formatting in Excel – Remove Format From a Cell]]></title><description><![CDATA[Originally published for freeCodeCamp

In this article I will show you how to clear formatting from a cell in Excel. If you want the quick and dirty version, it's first up and will require you to simply click a couple times.
If you want to become a s...]]></description><link>https://blog.eamonncottrell.com/how-to-clear-formatting-in-excel-remove-format-from-a-cell</link><guid isPermaLink="true">https://blog.eamonncottrell.com/how-to-clear-formatting-in-excel-remove-format-from-a-cell</guid><category><![CDATA[excel]]></category><category><![CDATA[formatting]]></category><category><![CDATA[spreadsheets]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Thu, 12 Jan 2023 14:34:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675866757999/97e11b13-bd2a-49aa-b0cc-ae87ed34f678.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Originally published for <a target="_blank" href="https://www.freecodecamp.org/news/how-to-clear-formatting-in-excel/">freeCodeCamp</a></p>
</blockquote>
<p>In this article I will show you how to clear formatting from a cell in Excel. If you want the quick and dirty version, it's first up and will require you to simply click a couple times.</p>
<p>If you want to become a spreadsheet speedrunner, read this whole, brief article. I'll show you a couple keyboard combos and open the door for you to the WIDE world of Excel shortcuts.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/opening-odor.gif" alt="A gif of a man opening door" /></p>
<h2 id="heading-how-to-clear-formatting-in-excel-with-just-a-click"><strong>How to Clear Formatting in Excel with Just a Click</strong></h2>
<p>This is easy mode. Highlight the cells you'd like to clear formatting on and click <code>Clear -&gt; Clear Formats</code> from the Home menu in the Excel Ribbon:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/clear-format-1-3.jpg" alt="Excel screenshot of the Ribbon, Home Tab and Clear selection" /></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/clear-format-2-2.jpg" alt="Excel screenshots of the editing window" /></p>
<p>If you don't see the ribbon at all, it will appear when you click any of the menu tabs (Home, for example). You can then click the arrow over in the bottom right corner. This will allow you to select which Show Ribbon option you'd prefer.</p>
<p>If you're using the web app version of Excel, this arrow will simply toggle the full Ribbon display on and off.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-162.png" alt="Excel screenshot of Showing the Ribbon" /></p>
<p>If you found this helpful, follow me over on <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a> and <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a>. But I hope you'll stick around a couple more minutes, because the real fun stuff is below! 👇</p>
<h2 id="heading-how-to-use-excel-keyboard-shortcuts-to-clear-formatting"><strong>How to Use Excel Keyboard Shortcuts to Clear Formatting</strong></h2>
<p>Excel is a powerhouse.</p>
<p>You know this. Mostly, though, it simply rears its head and tramples us with its density. Those who have mastered it have done so only through mental grit and by using a peculiarly large set of obscure keyboard combinations in lieu of a mouse.</p>
<p>You too could be one of these keyboard shortcutting pioneers.</p>
<p>Think of the glory.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/darksouls-1.gif" alt="Dark Souls praise the sun gif" /></p>
<p>Most modern software has at least a few keyboard shortcuts to make life easier for their users. Who among us has not readily used <code>CTRL + C</code> and <code>CTRL + V</code> for the better part of our lives?</p>
<p>In Excel, though, there lies an ocean of possibility.</p>
<p>On the surface live the usual suspects of copying, pasting and undoing. Things we are used to seeing everywhere.</p>
<p>But as we go underneath the water a bit, we find that there's a universe of keyboard shortcuts. In fact, virtually every command is accessible through a combination of keystrokes in Excel.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/diver-gif.gif" alt="Deep sea diver gif" /></p>
<h2 id="heading-just-press-alt"><strong>Just Press ALT</strong></h2>
<p>Go ahead. Fire up Excel. Or go to <a target="_blank" href="http://excel.new">excel.new</a> in your browser.</p>
<p>Press <code>ALT</code><strong>:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/clear-format-3.jpg" alt="Excel screenshot highlighting keyboard combos" /></p>
<p>You should see letters appear next to all the menu tabs. Pressing the letter will open that menu tab.</p>
<p>Press <code>H</code><strong>:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/clear-format-4.jpg" alt="Excel screenshot of ALT Ribbon shortcuts" /></p>
<p>Neat, right? Pressing <code>ALT</code> opens up a gigantic array of keyboard combinations that avoid using the mouse altogether.</p>
<p>And for productivity's sake, this can be a huge time saver. How many seconds do you think you've spent hunting for a menu, clicking into it and then hunting for the next menu option? It doesn't take long, but a few keystrokes takes three, four, five times less time.</p>
<p>(And it's a lot more fun!!) 😆</p>
<p>You've still got the <code>Home</code> tab selected with the new list of letters, right?</p>
<p>Press <code>E</code><strong>:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/clear-5.jpg" alt="Excel screenshot highlighting the clear selection" /></p>
<p>Press <code>F</code><strong>:</strong></p>
<p>This will clear the format of any cells you'd selected just like the point and click version. But with the swift <code>ALT + H + E + F</code> keyboard combination, it takes less than a second.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/clear-6.jpg" alt="Excel screenshot of clear formats command" /></p>
<p>It's important to remember that unlike CTRL + C for copying, the ALT shortcuts are keyboard <strong>combos</strong>. So, you press the keys <strong>consecutively</strong> rather than all at once.</p>
<h2 id="heading-can-you-use-these-shortcuts-in-google-sheets"><strong>Can You Use These Shortcuts in Google Sheets?</strong></h2>
<p>If you're a Google Sheets purist, you may be wondering if this all works the same there.</p>
<p>And yes, it does.</p>
<p>First of all, there is a simple keyboard shortcut for the Clear Formatting in Google Sheets. Pressing <code>CTRL + \</code> will clear the formatting.</p>
<p>If you want to enable the ALT keyboard combos, press <code>CTRL + /</code> to open the Keyboard Shortcuts menu in Google Sheets.</p>
<p>At the bottom, toggle on the <strong>Enable compatible spreadsheet shortcuts</strong> feature. Our clear formatting is available and it tells us the three keyboard shortcut and combo options to access it (just as they appear in Excel).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/clear-7.jpg" alt="Google Sheets Screenshot of format and keyboard shortcuts menus" /></p>
<p>There is one difference between using the <code>ALT</code> combos in Google Sheets vs Excel. In order for Google Sheets to know that you're trying to use <code>ALT</code> combos, you have to press the first two keys at once.</p>
<p>Pressing <code>ALT</code> on its own does not bring up the letter options like Excel, and Google Sheets will not start listening for further keystrokes.</p>
<p>However, once you press <code>ALT + H</code> together, you'll see it registering the combo in the bottom left of the screen. After pressing those two together, you proceed to press each of the next keys consecutively like in Excel:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/clear-8.jpg" alt="Google Sheets Screenshot of Shortcut Keys Pressed" /></p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>I hope this has been helpful for you! Keyboard shortcuts and combos have leveled up my productivity over the years, and I encourage you to continue expanding your own repertoire.</p>
<p>If you enjoyed this article, follow me on <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a> and <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a> for more content and videos like it.</p>
<p>Have a great one! 👋</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/goodbye.gif" alt="gif of Jake Gyllenhaal waving goodbye" /></p>
]]></content:encoded></item><item><title><![CDATA[I Made 📃Goals and 💲Money This Year | 👉Dev Retro 2022]]></title><description><![CDATA[📈 Spreadsheets and Goals
I found it immensely helpful to create a spreadsheet outlining some of my goals this past year. I'll reference it throughout this article, and here's the link if you'd like to check out the View Only version.

This year has ...]]></description><link>https://blog.eamonncottrell.com/i-made-goals-and-money-this-year-dev-retro-2022</link><guid isPermaLink="true">https://blog.eamonncottrell.com/i-made-goals-and-money-this-year-dev-retro-2022</guid><category><![CDATA[#DevRetro2022]]></category><category><![CDATA[Freelancing]]></category><category><![CDATA[spreadsheets]]></category><category><![CDATA[Soft Skills]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Wed, 28 Dec 2022 17:19:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672249553035/6b64491e-0a5e-4a3d-9e36-3575ed5c0ce1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-spreadsheets-and-goals">📈 Spreadsheets and Goals</h2>
<p>I found it immensely helpful to create a spreadsheet outlining some of my goals this past year. I'll reference it throughout this article, and <a target="_blank" href="https://docs.google.com/spreadsheets/d/1oTtfOsWAH90mRvHYD0TWkdij9zSo7iZvTmtJpcjSVFo/edit#gid=0">here's the link</a> if you'd like to check out the View Only version.</p>
<blockquote>
<p>This year has been both a joyful exercise in learning and content creation as well as an endurance test.</p>
</blockquote>
<p>I began the year reenergized for my continued pathway into computer programming and content creation. I'd been consistently learning to code for a couple of years while working full-time, but I was not getting much clarity in terms of what direction I was headed in.</p>
<p>I've got some videos 📺 of some projects I've worked on this year toward the end of the article. Make sure you read all the way down! 👇</p>
<h2 id="heading-good-job">⚒Good Job</h2>
<p>My job was (and is) great.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672173032034/768d48d9-3aa7-4d81-a7d9-70c6782f4584.gif" alt="I love my job gif" class="image--center mx-auto" /></p>
<p>I was not unemployed or even looking hard for new employment. In many ways, this made things trickier than they would have otherwise been. Unlike others who may need the fastest route to employment in the field, most of my endeavors for the past several years have been for intrinsic gain.</p>
<p>Sure, I'd like to find some alternate revenue sources, though. And at the onset of the year, this was one of my expressed purposes.</p>
<h2 id="heading-goals-at-the-start">✅Goals at the Start</h2>
<p>I committed in January to do a few things pertaining to my continued education:</p>
<ol>
<li><p>Continue learning to code by building real things.</p>
</li>
<li><p>Complete Quincy Larson's (freeCodeCamp) <a target="_blank" href="https://www.freecodecamp.org/news/2022-become-a-dev-new-years-resolution-challenge/">Become a Developer in 2022 challenge</a></p>
</li>
<li><p>Participate in the <a target="_blank" href="https://www.100daysofcode.com/">100 Days of Code challenge</a></p>
</li>
</ol>
<p>Within these general guidelines, were specific goals:</p>
<ol>
<li><p>Complete the <a target="_blank" href="https://www.freecodecamp.org/learn/relational-database/">Relational Database Certification</a> on freeCodeCamp</p>
</li>
<li><p>Configure my <a target="_blank" href="https://blog.eamonncottrell.com/">homepage's subdomain</a> to my Hashnode blog and have written 3 blogs by the end of the 100 Days of Code</p>
</li>
<li><p>Become an <a target="_blank" href="https://www.freecodecamp.org/news/author/eamonn">author at freeCodeCamp</a></p>
</li>
<li><p>Make actual, sellable <a target="_blank" href="https://eamonn.gumroad.com/">products on Gumroad</a></p>
</li>
<li><p><a target="_blank" href="https://iwant2hack.transistor.fm/">Podcast</a> about my journey</p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/@eamonncottrell">Make videos</a> along the course of my journey</p>
</li>
</ol>
<p>This is a lot, but I had experience with many of the more daunting aspects already.</p>
<p>I am a seasoned writer; I enjoy the craft and have written many things for fun - <a target="_blank" href="https://www.amazon.com/Swansong-Pirata-Isle-Eamonn-Cottrell-ebook/dp/B00J1NUGP6/ref=sr_1_1?crid=1QH7H53OVRWAY&amp;keywords=jon+swansong+and+the+pirata+isle&amp;qid=1672151978&amp;sprefix=jon+swansong+and+the+pirata+isle%2Caps%2C66&amp;sr=8-1">including a novel</a> - in the past.</p>
<p>I have been <a target="_blank" href="https://www.eamonncottrell.com/podcasts/">podcasting</a> on and off since 2006.</p>
<p>I've made an assortment of videos over the past decade for fun.</p>
<p>And, of course, I have been learning to code already for a few years.</p>
<h2 id="heading-combine-goals-with-progress">🧪Combine Goals With Progress</h2>
<p>Just getting started is one of the biggest hurdles. Many times, I find myself not knowing how to start or what to do next.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672173616697/19e9167e-8387-4cc0-83b2-c6bffe649961.gif" alt class="image--center mx-auto" /></p>
<p>One of my core weaknesses has been following rabbit trails. If you're at all familiar with the choose-your-own-adventure style of learning to code online, you've probably had your fair share of these alluring rabbit trails pop up in front of you.</p>
<p>One second you're learning the basics of flexbox 🤔 and the next thing you know you're watching a 20 hour tutorial on BigQuery. 😵</p>
<p>These things happen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672152289898/bfc5b11b-3d13-483c-8f94-0cb57cb32892.gif" alt="it happens gif" class="image--center mx-auto" /></p>
<p>I know this about myself, and I wanted to fight against that.</p>
<blockquote>
<p>A critical component to my success in leveling up my developer skills this year has been combining my many interests around thematic goals.</p>
</blockquote>
<p>By repurposing content in multiple media, I was able to write, video and podcast about my learning journey and solidify the concepts I was learning along the way.</p>
<p>Now, overachievement can also lead to burnout, stress and the copious anxiety of seeking to produce content for its own sake - a burden I was eager to avoid. And as a result, not every piece of content was accompanied by every media outlet.</p>
<p>But when it made sense to write an article, record a video or podcast, I seized the moment.</p>
<p>Simply pressing record or typing the opening paragraph of an article is often all it takes to get over the insecurities of creation.</p>
<p><strong><mark>I've never regretted writing, recording or learning.</mark></strong></p>
<h2 id="heading-i-kept-going-anyway">👊I Kept Going Anyway</h2>
<p>Here's the tough truth: even knowing this, I spent the first several months of 2022 doing exactly the things that I was trying to guard against.</p>
<p>I veered off course by trying to learn multiple things at once.</p>
<p>I procrastinated learning the things I'd written down as goals in favor of checking a box on other certifications.</p>
<p>I didn't follow through daily with 100 Days of Code. Look through my <a target="_blank" href="https://docs.google.com/spreadsheets/d/1oTtfOsWAH90mRvHYD0TWkdij9zSo7iZvTmtJpcjSVFo/edit#gid=0">spreadsheet of tweets</a>.</p>
<p>The first one was on <em>January 10th</em>; the last one was on <strong><em>August 19th</em></strong>! That's six months, y'all! 😱</p>
<p>I launched a few products but then lost steam for several months.</p>
<p>I podcasted pretty regularly right up until I stopped for a while.</p>
<p>Here's the thing, though. I just kept going even when I missed a day or a week. And I didn't beat myself up when I didn't keep perfect track of all I was working on. Maybe I learned some cool stuff for work one day but didn't tweet it out. No big deal. Oftentimes perfection is the enemy.</p>
<p>And I never stopped. Momentum has been so important for me. Even as I've zigged and zagged I've kept forward momentum.</p>
<p>Even when I felt like throwing in the towel. Even when I felt like I was not making progress. Even when I felt like my learning and content was purposeless....I didn't stop.</p>
<p>I realize that <strong>it doesn't matter what I feel like</strong>. Results come after the crappy feelings...even when they take twice as long as you planned.🤣</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672174364267/3336b2d6-41e4-43e5-a576-89b61413c06f.gif" alt="actually it does not matter gif" class="image--center mx-auto" /></p>
<h2 id="heading-mission-accomplished">💥Mission Accomplished</h2>
<p>And you know what? By the end of those six months, I'd annihilated my original goals.</p>
<p>Not only had I completed every one of them, but I'd begun to learn:</p>
<ul>
<li><p>how to <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-trello-to-manage-freelance-web-projects/">better organize my time</a> by managing projects like a professional.</p>
</li>
<li><p>how to <a target="_blank" href="https://blog.eamonncottrell.com/sacred-geometry-with-adobe-illustrator">combine my design interests with my technical writing and developer pathway</a></p>
</li>
<li><p>how to <a target="_blank" href="https://blog.eamonncottrell.com/journey-to-the-center-of-the-code">quantify why I was doing this</a> in the first place.</p>
</li>
<li><p>how to write proficiently. I have published over 30 articles this year on <a target="_blank" href="https://blog.eamonncottrell.com/">Hashnode</a>, <a target="_blank" href="https://www.freecodecamp.org/news/author/eamonn/">freeCodeCamp</a> and <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a></p>
</li>
<li><p>how to <a target="_blank" href="https://blog.eamonncottrell.com/infinite-memory">participate in a hackathon</a>. <a target="_blank" href="https://blog.eamonncottrell.com/series/writeathon-2022">I won $500</a> as one of the runner ups in Hashnode's Writeathon this fall!</p>
</li>
<li><p>how to work with clients in a freelance capacity. By talking and writing about my progress, I was able to work with two clients who reached out to me for web development work.</p>
</li>
<li><p>how to reach out to people I admire. I've met <a target="_blank" href="https://www.freecodecamp.org/news/author/quincylarson/">Quincy</a> (founder of freeCodeCamp) twice this year to discuss his platform, my contributions to it, and opportunities for future growth</p>
</li>
<li><p>how to release <a target="_blank" href="https://eamonn.gumroad.com/">real products</a> into the world on Gumroad.</p>
</li>
</ul>
<p>Check out my first tweet in January:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/EamonnCottrell/status/1480721431596093443">https://twitter.com/EamonnCottrell/status/1480721431596093443</a></div>
<p> </p>
<p>By the end of the 100 tweets in August, after meandering through a myriad of topics, my final tweet was a milestone for me. I released a legit <a target="_blank" href="https://unmove.netlify.app/">open-source animation library</a> 💥:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/EamonnCottrell/status/1560699132439941120">https://twitter.com/EamonnCottrell/status/1560699132439941120</a></div>
<p> </p>
<p>That project alone rolled together project management, open-source infrastructure, version releases on GitHub, making proper documentation...not to mention coding the actual product.</p>
<p>Much of the year had me refining my project management as much as my raw coding skills.</p>
<h2 id="heading-priorities">🏃‍♂️Priorities</h2>
<p>I've also learned where to pull back. One of the reasons my "100" Days of code challenge didn't go as planned was that we had our fourth child this spring.</p>
<p>I've had to learn where to pump the brakes, slow down and reprioritize.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672174794400/867cfc7f-fb88-45fd-aae1-011963603071.gif" alt="man gesturing to slow down" class="image--center mx-auto" /></p>
<p>Correct. I have a job, a wife and four children.</p>
<p>And on top of that, I love running. Not 5Ks either. <a target="_blank" href="https://ultrasignup.com/results_participant.aspx?fname=Eamonn&amp;lname=Cottrell">I run ultra-marathons</a>. That takes a ton of time.</p>
<p>So, rather than bemoan all the free time I no longer have that I was spoiled with in my twenties, I have had to learn how to prioritize things. How to give up things. How to focus on what's truly important.</p>
<p>Everyone has the same amount of time and I can do anything I want as long as I'm willing to sacrifice something else.</p>
<p>It may come as no surprise then that I don't watch a ton of TV or sports. That I don't go out on weekends. That I'm not doing end-game raids anymore.</p>
<p>Know what? Some of the simplest ways to consistently level up my coding and content creation cadences has been to:</p>
<ol>
<li><p>Get good sleep</p>
</li>
<li><p>Wake up early</p>
</li>
<li><p>Eat healthily</p>
</li>
<li><p>Exercise regularly</p>
</li>
<li><p>Be in community with others</p>
</li>
</ol>
<p>How bout that for boring and oversimplified general life advice?! 🤣</p>
<p>But it's so true. When I can prioritize these things, every other aspect of my life has benefited.</p>
<h2 id="heading-what-is-important">🙋‍♂️What is Important?</h2>
<p>Computers are not important. ❌</p>
<p>Coding is not important. ❌</p>
<p>Writing is not important. ❌</p>
<p>Content is not important. ❌</p>
<p>Relationships are important. ✅</p>
<p>People are important. ✅</p>
<p>Family is important. ✅</p>
<p>Selflessness is important. ✅</p>
<p>My kids don't give a rip about what new framework has inspired me to watch a YouTube analysis.</p>
<p>My wife doesn't read <s>any</s> many of the articles I write.</p>
<p>What I've created matters far less than who it can help. After a year of diligently expanding my technical toolkit and writing repertoire, I am convinced that the best thing I've done has been to try and help others.</p>
<p>There will always be someone who can learn from what I've just done.</p>
<p>The thing is, learning for its own sake is also a ton of fun. I'd probably spend a significant portion of my life in college forever if I had infinite resources. So it can be easy to slide off into misaligned priorities that completely miss the point.</p>
<p>The point is that I'm learning and creating content to benefit others. By teaching, I get better and I help others get better.</p>
<p>I was beginning to understand this even at the start of the year when I thought I'd make a new product every month.</p>
<h2 id="heading-solving-specific-problems">🧩Solving Specific Problems</h2>
<p>Solving specific problems that I was experiencing gave birth to the <a target="_blank" href="https://eamonn.gumroad.com/l/personalfinance?layout=profile">2022 Personal Finance Tracker</a>, the <a target="_blank" href="https://eamonn.gumroad.com/l/sacred-geometry?layout=profile">Sacred Geometry</a> collection, and the <a target="_blank" href="https://infinite-memory.netlify.app/">Infinite Memory</a> web application.</p>
<p>These products couldn't be categorically further from one another, but they were all things I was deeply interested in as well as passionate about sharing with others.</p>
<p>The Finance Tracker was a polished version of a Google Sheets tool that my wife and I have been using for the past decade every year.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=E9oeEXdJFbg">https://www.youtube.com/watch?v=E9oeEXdJFbg</a></div>
<p> </p>
<p>The Sacred Geometry collection was more an art project than anything. I combined my Adobe Illustrator hacks with my childhood interests in geometric drawings to create an iPhone wallpaper pack.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=yKP609MObYY">https://www.youtube.com/watch?v=yKP609MObYY</a></div>
<p> </p>
<p>The Infinite Memory project was a tool I made for a men's bible study to help memorize scripture.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=qgInM6FH8Lk">https://www.youtube.com/watch?v=qgInM6FH8Lk</a></div>
<p> </p>
<p>Making videos or writing articles about specific things I was struggling with or had figured out became my most popular content.</p>
<blockquote>
<p>Helping others is the best way to increase exposure.</p>
</blockquote>
<p>My personal YouTube channel is tiny because it contains a diverse amount of irregularly posted content. (<strong>I have a dedicated brand channel that will be launching in the coming weeks that will focus on spreadsheets and coding</strong>!)</p>
<p>But even with the minuscule amount of views I ordinarily garner from my personal channel's posts, I had one video amass 10,000+ views since publishing it in January.</p>
<p>And the coolest part: I made it on a whim after talking to my 5-year-old son about building a spreadsheet that kept track of the Teenage Mutant Turtles' favorite colors.</p>
<p>This was not a high-concept piece, it was simply something that I did for fun with my son, and which presented a <strong><mark>curious</mark>, <mark>small</mark>, <mark>specific </mark> problem</strong> that I had to figure out.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=41ydIPKZezE">https://www.youtube.com/watch?v=41ydIPKZezE</a></div>
<p> </p>
<p>The other two videos that gained multiple thousand views were answering very specific, and pretty basic questions about a point-of-sale software that my company uses.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=L8PBv2k76jU">https://www.youtube.com/watch?v=L8PBv2k76jU</a></div>
<p> </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=HB4OQF9r98c">https://www.youtube.com/watch?v=HB4OQF9r98c</a></div>
<p> </p>
<p>People benefit most from answers to concise questions. I am still learning how to get better at keeping things simple in my content and in my learning path.</p>
<h2 id="heading-you-gotta-make-poop-before-pearls">💩You Gotta Make Poop Before Pearls</h2>
<p>Why the bit on YouTube? Why not give some Hashnode stats? First, I'm happy to share my meager Hashnode numbers below. This article will be the 30th I've published here, and of those, 5 were originally published on freeCodeCamp.</p>
<p>I have <strong>169</strong> videos on YouTube. Of those, <strong><mark>only 10 have more than 100 views</mark></strong>. 👀</p>
<p>For Hashnode, I have only been putting out content for a year, and my readership is not large. Here's a snapshot from the advanced analytics before publishing this article:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672155436152/59926267-aa95-43b9-aff6-f526460de859.png" alt class="image--center mx-auto" /></p>
<p>The most viewed article I have written was about <a target="_blank" href="https://blog.eamonncottrell.com/responsive-youtube-lessiframegreater-embedding-with-bootstrap">embedding video in a Bootstrap 5 site</a>.</p>
<p>The next two articles were about doing very specific things too: <a target="_blank" href="https://blog.eamonncottrell.com/animate-svgs-for-github-readmes">Animating SVGs for GitHub ReadMEs</a> and <a target="_blank" href="https://blog.eamonncottrell.com/how-to-quickly-create-a-lightbox-photo-gallery-in-your-bootstrap-5-site">Creating Lightbox Photo Galleries in a Bootstrap 5 site</a>.</p>
<p>So, you've got to make crap first. Or at least I do.</p>
<p>It takes writing and recording and putting out a bunch of crap to refine my voice.</p>
<p>It takes fearlessly releasing things into the wild and persevering past negative (or even worse, zero) reactions to them.</p>
<p>It takes time to develop a skillset and the ability to craft content that is at once <strong>concise</strong> and <strong>helpful</strong>.</p>
<p>But it has been and will continue to be worth it.</p>
<p>I love helping and teaching others. I love creating content that can provide shortcuts to solutions I've discovered. I love platforms that connect developers and content creators around the world.</p>
<p>I love these things because I love people. Sure I love software and learning and challenges and puzzles and videos and writing and all that.</p>
<p>But at the end of the day, I love connecting with others. Creating content has helped me do more of that this year. I love that I can expand my technical toolkit by helping someone else expand theirs.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I could go on about the cool stuff I've learned and been able to write about this year. Mostly, though, it's given me further insight into the viability of being a professional creator. I love connecting with people through the content I make, and I look forward to an impactful year of doing more of exactly that in 2023!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672156102565/688050ed-54a8-4cc1-9e67-805d82c408d3.gif" alt class="image--center mx-auto" /></p>
<p>I hope you found this recap helpful and inspiring! Come follow me on <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a> and <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a>! I'd love it if you said hey! 👋</p>
]]></content:encoded></item><item><title><![CDATA[What is a ROM? ROM Price and Cost Estimate]]></title><description><![CDATA[Originally published for freeCodecamp

ROM stands for Rough Order of Magnitude. It is a project management guideline to determine the estimated range of costs for a project.
This article will explain:

Who should use a ROM

When to use a ROM

How to ...]]></description><link>https://blog.eamonncottrell.com/what-is-a-rom-rom-price-and-cost-estimate</link><guid isPermaLink="true">https://blog.eamonncottrell.com/what-is-a-rom-rom-price-and-cost-estimate</guid><category><![CDATA[project management]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Fri, 23 Dec 2022 14:13:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675865496656/dfdf776b-c9c8-4a3a-a64c-92844e2e4a2a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><a target="_blank" href="https://www.freecodecamp.org/news/what-is-a-rom-price-and-cost-estimate-2/">Originally published for freeCodecamp</a></p>
</blockquote>
<p>ROM stands for <strong>Rough Order of Magnitude</strong>. It is a project management guideline to determine the estimated range of costs for a project.</p>
<p>This article will explain:</p>
<ol>
<li><p>Who should use a ROM</p>
</li>
<li><p>When to use a ROM</p>
</li>
<li><p>How to calculate a ROM</p>
</li>
<li><p>Simple example of a ROM</p>
</li>
</ol>
<h2 id="heading-who-should-use-a-rom"><strong>Who Should Use a ROM</strong></h2>
<p>Project Managers use rough orders of magnitude to determine initial cost ranges for upcoming projects. Many organizations and most software organizations use project managers to lead projects.</p>
<p>Their role involves leading their project team, defining the goals of the project, reporting on the progress to the company stakeholders, and seeing the project through to completion.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/pm.gif" alt="man with blank stare listening to man trying to explain something" /></p>
<p>Who else can use a ROM? You! 👊 As you'll see, the concept of a ROM is very simple to grasp and can be useful in myriad circumstances.</p>
<h2 id="heading-when-to-use-a-rom"><strong>When to Use a ROM</strong></h2>
<p>ROMs are extremely important for project managers in the <strong>early stages</strong> of a project's planning. They're used to decide whether or not a project is going to be feasible or not.</p>
<p>Rough Orders of Magnitude can be used to prepare a project business case (an analysis of the potential benefits of the project) before a project is committed to.</p>
<h2 id="heading-how-to-calculate-a-rom"><strong>How to Calculate a ROM</strong></h2>
<p>Calculating a ROM is pretty straightforward. It is meant to provide a <strong>range of the possible cost of a project</strong> and you can find it using the following formulas:</p>
<p><code>Lower Bound = ROM Estimate x 0.75</code></p>
<p><code>Upper Bound = ROM Estimate x 1.75</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/fred.gif" alt="Fred Flinstone counting on fingers" /></p>
<p>During the planning phases of a project, the project manager can come up with an estimate of the costs involved. From here, using the above formulas, they can then calculate a lower boundary in case the project ends up being less expensive and an upper boundary in case it goes over budget.</p>
<p>Since it is helpful to have more room on the upper boundary to avoid running out of funding if it does go over budget, the rough order of magnitude's upper boundary is considerably higher.</p>
<h2 id="heading-rom-example"><strong>ROM Example</strong></h2>
<p>You've probably calculated a ROM without knowing it. 💡</p>
<p>Say you're going to the movies with a friend. You figure there will be ticket costs plus refreshment costs. Maybe $20 a ticket since you're going to the latest Nolan IMAX film and then another $30 a person since popcorn is more expensive than steaks at the movies.</p>
<p>This puts you at $100 total for both of you.</p>
<p>The Lower Bound in a ROM calculation would be <code>$100 x 0.75 = $75</code>.</p>
<p>The Upper Bound would be <code>$100 x 1.75 = $175</code>.</p>
<p>You probably don't do this exact math in your head, but you likely have figured there could be extra expenses. You go into the date thinking you could spend as much as $150, for instance.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/movie.gif" alt="Angry Bird eating popcorn at movies" /></p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Rough Orders of Magnitude are extremely important in organizational projects. It is vital to decide at the onset whether or not the project is feasible. And the principals behind ROMs are both common sense and easy to calculate.</p>
<p>I hope this has been helpful to you!</p>
<p>You can find and follow me on <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a> &amp; <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a>. I'd love it if you said hey! 👋</p>
]]></content:encoded></item><item><title><![CDATA[How to Calculate Percentage Differences Between Two Numbers in Excel - Cell Percentage Change Tutorial]]></title><description><![CDATA[Originally published for freeCodeCamp

Spreadsheets are powerful and awesome. 💪
In this tutorial I will show you four ways to find the percentage difference between two numbers in Excel. I'll also show you how to use custom functions in Google Sheet...]]></description><link>https://blog.eamonncottrell.com/how-to-calculate-percentage-differences-between-two-numbers-in-excel-cell-percentage-change-tutorial</link><guid isPermaLink="true">https://blog.eamonncottrell.com/how-to-calculate-percentage-differences-between-two-numbers-in-excel-cell-percentage-change-tutorial</guid><category><![CDATA[excel]]></category><category><![CDATA[spreadsheets]]></category><category><![CDATA[VBA]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[google sheets]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Tue, 20 Dec 2022 14:14:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675865372965/11c3e646-c730-4d23-97cd-216024769ae0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><a target="_blank" href="https://www.freecodecamp.org/news/how-to-calculate-percentage-differences-between-two-numbers-in-excel-cell-percentage-change-tutorial/">Originally published for freeCodeCamp</a></p>
</blockquote>
<p>Spreadsheets are powerful and awesome. 💪</p>
<p>In this tutorial I will show you four ways to find the percentage difference between two numbers in Excel. I'll also show you how to use custom functions in Google Sheets. 👍</p>
<p>The four techniques (and one bonus) we'll use are:</p>
<ol>
<li><p>Using a Formula (lvl 1️⃣ easy mode)</p>
</li>
<li><p>Using the LAMBDA FUNCTION + Name Manager (lvl 2️⃣ normal mode)</p>
</li>
<li><p>Using Visual Basic for Applications (VBA) (lvl 3️⃣ hard mode)</p>
</li>
<li><p>Using the Office JavaScript API (lvl 4️⃣ ultra-mode)</p>
</li>
<li><p>Using Custom Functions with Google Sheets (💥 bonus level)</p>
</li>
</ol>
<h2 id="heading-formula-for-percentage-change-between-two-numbers-in-excel"><strong>Formula for Percentage Change Between Two Numbers in Excel</strong></h2>
<p>The formula is the first thing most people will reach for when making a calculation in Excel. It allows us to make explicit calculations using data in cells.</p>
<p>Suppose we have sales data for one year in cell <code>B3</code> and sales data for the second year in cell <code>C3</code>. By typing the formula below we can calculate the percentage difference from the first year to the second:</p>
<pre><code class="lang-javascript">=(C3-B3)/B3
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-62.png" alt="formula for percentage change between two numbers in Excel" /></p>
<p>Typing a custom formula has the advantage of being quick and straightforward, especially for simple calculations.</p>
<p>Additionally, formulas may be copied down and/or across cell ranges for quick reuse. And formulas are used in exactly the same fashion in Google Sheets as in Microsoft Excel.</p>
<p>However, when calculations become lengthy and/or complex, it can be helpful to know about some alternative methods.</p>
<h2 id="heading-how-to-use-a-lambda-function-and-name-manger"><strong>How to Use a LAMBDA Function and Name Manger</strong></h2>
<p>Building on our first example, the LAMBDA function allows us to take a custom operation and codify it for reuse throughout our worksheet.</p>
<p>Using the same data as before (this time in cells <code>B4</code> and <code>C4</code>) we write the LAMBDA Function like so:</p>
<pre><code class="lang-javascript">=LAMBDA(year1,year2,(year2-year1)/year1)(B4,C4)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-63.png" alt="Screenshot of Excel Lambda function" /></p>
<p>At first glance you might wonder why on earth we should type out this lengthy mess, but hang with me and you'll see it is arguably cleaner for reuse than simply defining a function.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/confused.gif" alt="Gif of woman scratching head and looking confused" /></p>
<p>Here's what's happening:</p>
<p>The first thing we're doing is defining the parameters of our function and separating them by commas. You can define as many of those as you need (well, up to 253 that is 🤣). We only have two: <code>year1</code> and <code>year2</code>.</p>
<p>After listing the parameters, we write the formula we want Excel to calculate. This is the same thing we did in the first Formula section – only this time we're using our parameter names instead of the explicit cell names: <code>(year1-year2)/year1</code>.</p>
<p>Last, we close the parenthesis of the LAMBDA function and then call it by writing the actual cells being used: <code>B4,C4</code>. This is telling the function that it needs to use the value in cell <code>B4</code> for the parameter <code>year1</code> and the value in cell <code>C4</code> for the parameter <code>year2</code>.</p>
<p>What a mess, right!? Yes, and technically, writing out the whole LAMBDA function here is simply good practice to make sure the thing works before we do the next step...</p>
<p>This is the cool part. Click the Formula tab in the Ribbon at the top and select Name Manager.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/name-manager.png" alt="Excel Ribbon accessing Name Manager in Formula Tab" /></p>
<p>Select New.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/name-manager-new.png" alt="Screenshot of Name Manager in Excel" /></p>
<p>Then enter the Name of your formula and write an optional description in the Comments. In the Refers to line, you'll copy in the LAMBDA Function.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/name-manager2.png" alt="Screenshot of Named Function in Excel" /></p>
<p>You'll be able to use it in the same way you would use a built-in function by typing the following into a cell</p>
<pre><code class="lang-javascript">=Percentage_Change(B5,C5)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/percentage-change-lambda.png" alt="Screenshot of Excel Formula" /></p>
<p>Now we have all the ease of a regular built-in function at our disposal. Google Sheets has similar functionality, which we'll discuss at the end of this article.</p>
<h2 id="heading-how-to-use-visual-basic-for-applications"><strong>How to Use Visual Basic for Applications</strong></h2>
<p>If you're using the desktop version of Excel, you have access to Visual Basic for Applications. This is an event-driven programming language by Microsoft and you can use it to do almost anything you can dream (and code) up.</p>
<p>If you wanted to find the percentage difference between two numbers using VBA, you would go to the Developer tab in the Ribbon (or press <code>alt + F11</code>).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/vba1.png" alt="Screenshot of Developer Tab in Excel" /></p>
<p>If you don't see the Developer tab, you may need to enable it by selecting File -&gt; Options. Then look for Customize Ribbon. From here you can select the box next to Developer.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/vba3.png" alt="Screenshot of Excel Options" /></p>
<p>Bonus: If <code>ALT + F11</code> doesn't work, GeForce Experience may be interfering with the built-in shortcuts. Change the shortcut for whatever is using <code>ALT + F11</code> in the settings. For me it was the Toggle comments on/off while broadcasting to Facebook setting.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/vba2.png" alt="screenshot of GeForce Experience settings" /></p>
<p>Once you've opened the VBA window, select Insert -&gt; Module from the menu. This will open up a blank window where we will write our program. Think of this like an IDE inside Excel. We'll program here and then utilize that program in our worksheet.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/vba4.png" alt="Screenshot of VBA menu" /></p>
<p>Here we can enter the same type of commands we used with the Named LAMBDA function above.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">Function</span> PERCENTFUNCTION(year1, year2)
    PERCENTFUNCTION = (year2 - year1) / year1
End <span class="hljs-built_in">Function</span>
</code></pre>
<p>And voilà! We can now use <code>PERCENTFUNCTION</code> in our Excel Sheet in the same way we used the <code>Percentage_Change</code> named function.</p>
<p>VBA is useful for more complicated programs and would be overkill for our example. Incidentally, Google Sheets does not have VBA functionality.</p>
<h2 id="heading-how-to-use-the-office-javascript-api"><strong>How to Use the Office JavaScript API</strong></h2>
<p>Now the real good stuff! Did you know you can write JavaScript and TypeScript within Excel? Me either. But you can.</p>
<p>Script Lab is an Add-on by Microsoft that allows us to explore the JavaScript API within Office Apps as well as declare custom functions by writing them as scripts.</p>
<p>You can add it to Excel <a target="_blank" href="https://learn.microsoft.com/en-us/office/dev/add-ins/overview/explore-with-script-lab">here</a>. And read more about it <a target="_blank" href="https://learn.microsoft.com/en-us/office/dev/add-ins/overview/explore-with-script-lab">here</a>.</p>
<p>Unlike VBA, this is usable on the web version of Excel too.</p>
<p>Once it's installed, select it from the Ribbon and click Code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/script1.png" alt="Screenshot of Script Lab in Excel Ribbon" /></p>
<p>This will bring up a legit code editor on the sidebar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-66.png" alt="Screenshot of Script Labs Code Editor in Excel" /></p>
<p>We can create a custom function using JavaScript by selecting a New Snippet from the Hamburger menu at the top left of the Scripts Lab window.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-67.png" alt="Screenshot of New Snippet menu " /></p>
<p>By typing the following function we can again define a percent difference function, but this time using JavaScript.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/** <span class="hljs-doctag">@CustomFunction </span>*/</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">percent_change_javacscript</span>(<span class="hljs-params">year1, year2</span>) </span>{
  <span class="hljs-keyword">return</span> (year2 - year1) / year1;
}
</code></pre>
<p>In order to use this function, select Script Lab -&gt; Functions from the Ribbon:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/script2.png" alt="Screenshot of Script Lab Menu in Ribbon" /></p>
<p>This will open another sidebar tab and because of the first line in the snippet: <code>/** @CustomFunction */</code> it will register that custom function.</p>
<p>In the worksheet, you'll be able to use it just like we've been using custom defined functions. This time, though, when you start typing the title, you'll see it registered with a scriptlab prefix on the name. Select this, and it will return the percent change just like the other methods.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/script3.png" alt="Screenshot of Script Lab Custom Function in Excel" /></p>
<p>Once again, this is major overkill for a simple function, but quite handy to put in your toolbelt nonetheless! 👍</p>
<h2 id="heading-how-to-use-custom-functions-with-google-sheets"><strong>How to Use Custom Functions with Google Sheets</strong></h2>
<p>As promised, here's a bonus for how to create a Named Function in Google Sheets. This is very similar to using the Name Manager in Excel.</p>
<p>Select Data -&gt; Named Functions in Google Sheets.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/sheets1.png" alt="Screenshot of Google Sheets Data Menu" /></p>
<p>This will prompt you to name and describe your function as well as provide arguments, if applicable.</p>
<p>Last, you'll define the function's operation.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/sheets2.png" alt="Screenshot of naming a custom function in Google Sheets" /></p>
<p>The next screen prompts you to add argument descriptions and examples if you'd like. This is optional, but will be included in the drop down help menu when you use the function in your sheet.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/sheets4.png" alt="Screenshot of custom function argument descriptions in Google Sheets" /></p>
<p>Then it's as easy as typing in your custom function and selecting the cells. You can see below how the help menu is displayed with the information you provided.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/sheets5.png" alt="Screenshot of using a custom function in Google Sheets" /></p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Yes, often you will opt for simplicity's sake to use a quick formula in Excel or Google Sheets. But now you know several other ways to find the percentage change between two numbers.</p>
<p>I hope you've found this useful, and good luck in your own spread-sheeting adventures!</p>
<p>You can find and follow me on <a target="_blank" href="https://www.youtube.com/@eamonncottrell">YouTube</a> &amp; <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a>. I'd love it if you said hey! 👋</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/hey.gif" alt="Jimmy Fallon saying haaaaay and holding up some hay" class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Email Alias – How to Set Up a Professional Email for Free]]></title><description><![CDATA[Originally published for freeCodeCamp

I needed to log in to AWS. But my main email address was rejected. I'd done this in the past and the account had been irreversibly deleted.
No reset option – just a message saying the account was permanently del...]]></description><link>https://blog.eamonncottrell.com/email-alias-how-to-set-up-a-professional-email-for-free</link><guid isPermaLink="true">https://blog.eamonncottrell.com/email-alias-how-to-set-up-a-professional-email-for-free</guid><category><![CDATA[email]]></category><category><![CDATA[alias]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Fri, 02 Dec 2022 19:37:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670009776586/0f468b24-4423-4c26-953f-c43e1e219e6c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Originally published for <a target="_blank" href="https://www.freecodecamp.org/news/email-alias-set-up-a-professional-email-for-free/">freeCodeCamp</a></p>
</blockquote>
<p>I needed to log in to AWS. But my main email address was rejected. I'd done this in the past and the account had been irreversibly deleted.</p>
<p>No reset option – just a message saying the account was permanently deleted:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-2.17.29-PM.png" alt="Screen-Shot-2022-10-12-at-2.17.29-PM" /></p>
<p>There are about a million ways to get a new email address, but I wanted to set up one of the domains I own with email.</p>
<p>And I figured there would probably be a way to have that forwarded to my Gmail.</p>
<p>I was right!</p>
<p>In this quick guide, I'll walk you through:</p>
<ul>
<li><p>How to create an email alias</p>
</li>
<li><p>Forwarding mail from the alias to a Gmail account</p>
</li>
<li><p>Sending mail as the Email Alias</p>
</li>
</ul>
<h2 id="heading-how-to-create-an-email-alias"><strong>How to Create an Email Alias</strong></h2>
<p>I'm using Google products: <a target="_blank" href="https://domains.google.com/registrar/">Google Domains</a> and <a target="_blank" href="https://gmail.com/">Gmail</a>. All these steps should apply generally to other Domain and Email services.</p>
<p>First, login to your domain provider and select the <strong>Email</strong> menu. You will be able to select "<strong>Add email alias</strong>" from the menu options.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-2.37.40-PM.png" alt="Screen-Shot-2022-10-12-at-2.37.40-PM" /></p>
<h2 id="heading-how-to-forward-email-to-another-address"><strong>How to Forward Email to Another Address</strong></h2>
<p>Note: if you add the asterisk (*) symbol, this will create a wildcard alias that forwards any email to the specified address. We'll be adding a specific alias in this tutorial.</p>
<p>Add whatever email you choose and enter where you'd like it to be forwarded to. I'll have <a target="_blank" href="mailto:hi@sieis.com"><strong>hi@sieis.com</strong></a> forwarded to my main Gmail address.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-2.41.16-PM.png" alt="Screen-Shot-2022-10-12-at-2.41.16-PM" /></p>
<p>If you are using Google Name servers, then Google Domains will automatically set up the correct mail records (MX).</p>
<p>Otherwise, you'll need to set up these MX records. The process is exactly like setting up name servers if you've ever done that to host your website somewhere different than where you purchased the domain.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>NAME/HOST/ALIAS</strong></td><td><strong>TYPE</strong></td><td><strong>TIME-TO-LIVE (TTL)</strong></td><td><strong>PRIORITY</strong></td><td><strong>VALUE/ANSWER/DESTINATION</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Blank or @</td><td>MX</td><td>1H</td><td>5</td><td><a target="_blank" href="http://gmr-smtp-in.l.google.com">gmr-smtp-in.l.google.com</a></td></tr>
<tr>
<td>Blank or @</td><td>MX</td><td>1H</td><td>10</td><td><a target="_blank" href="http://alt1.gmr-smtp-in.l.google.com">alt1.gmr-smtp-in.l.google.com</a></td></tr>
<tr>
<td>Blank or @</td><td>MX</td><td>1H</td><td>20</td><td><a target="_blank" href="http://alt2.gmr-smtp-in.l.google.com">alt2.gmr-smtp-in.l.google.com</a></td></tr>
<tr>
<td>Blank or @</td><td>MX</td><td>1H</td><td>30</td><td><a target="_blank" href="http://alt3.gmr-smtp-in.l.google.com">alt3.gmr-smtp-in.l.google.com</a></td></tr>
<tr>
<td>Blank or @</td><td>MX</td><td>1H</td><td>40</td><td><a target="_blank" href="http://alt4.gmr-smtp-in.l.google.com">alt4.gmr-smtp-in.l.google.com</a></td></tr>
</tbody>
</table>
</div><p>Bonus: I've done this for a site hosted on Netlify, and it is very straightforward. From the Netlify dashboard, select <strong>Options</strong>, <strong>Go to DNS panel</strong>, and then enter the MX records:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.42.25-PM.png" alt="Screen-Shot-2022-10-12-at-3.42.25-PM" /></p>
<p>👇</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.42.53-PM-2.png" alt="Screen-Shot-2022-10-12-at-3.42.53-PM-2" /></p>
<p>Google will send a one-time verification email here, but if you've previously done this verification it may not send another one.</p>
<p>Send yourself an email from a different address and check it out!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.02.53-PM-1.png" alt="Screen-Shot-2022-10-12-at-3.02.53-PM-1" /></p>
<p>Email sent to Alias and forwarded to main Gmail</p>
<p>Note: sending from your main Gmail address doesn't show up as an unread message like it normally would when you email yourself. Send it from a different address at this point. After the rest of the steps, it will behave as normal when we complete the "send email as alias" part.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.04.35-PM.png" alt="Screen-Shot-2022-10-12-at-3.04.35-PM" /></p>
<h2 id="heading-how-to-send-email-as-the-alias"><strong>How to Send Email as the Alias</strong></h2>
<p>If you reply from your main Gmail currently, the recipient will see that address when you reply instead of the alias. That may not be a big deal depending on the use case, but we can certainly set it up so that sent mail acts like it's coming from the custom domain as well.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.08.37-PM.png" alt="Screen-Shot-2022-10-12-at-3.08.37-PM" /></p>
<p>You'll need to go to your <a target="_blank" href="https://myaccount.google.com/security"><strong>Google Account Security</strong></a> and click <strong>App passwords</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.12.46-PM.png" alt="Screen-Shot-2022-10-12-at-3.12.46-PM" /></p>
<p>Select <strong>Mail</strong> for the app dropdown and <strong>Other</strong> for the device dropdown.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.14.18-PM.png" alt="Screen-Shot-2022-10-12-at-3.14.18-PM" /></p>
<p>Enter the name of your domain and click <strong>Generate</strong>. It will give you a 16 digit password. Save this for use over in Gmail...</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.15.34-PM.png" alt="Screen-Shot-2022-10-12-at-3.15.34-PM" /></p>
<p>In Gmail, go to settings -&gt; <strong>Accounts and Import</strong> and click <strong>Add another email address</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.17.49-PM.png" alt="Screen-Shot-2022-10-12-at-3.17.49-PM" /></p>
<p>This will pop up a new small window where you will enter in the details of the alias. Enter the <strong>name</strong> you want recipients to view and the <strong>address</strong> of the alias. Make sure the "<strong>Treat as an alias</strong>" box is checked.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.21.46-PM.png" alt="Screen-Shot-2022-10-12-at-3.21.46-PM" /></p>
<p>On the next screen, you'll need to change the <strong>SMTP server</strong> to <a target="_blank" href="http://smtp.gmail.com">smtp.gmail.com</a>, the <strong>username</strong> to your Gmail username, and then paste in the <strong>16-digit password</strong> you generated from Google Security in the steps above.</p>
<p>The <strong>port</strong> should be 587, and the <strong>TLS</strong> radial button should be checked.</p>
<p>Click <strong>Add Account</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.23.33-PM.png" alt="Screen-Shot-2022-10-12-at-3.23.33-PM" /></p>
<p>This will prompt a verification code being sent to the email alias...which should in turn go to the Gmail account. Enter that in, and you'll now be good to go!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.27.09-PM-1.png" alt="Screen-Shot-2022-10-12-at-3.27.09-PM-1" /></p>
<p>Back in our email thread in Gmail, you'll have the dropdown option when composing new messages to select which account you would like it to send from.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.29.10-PM.png" alt="Screen-Shot-2022-10-12-at-3.29.10-PM" /></p>
<p>Now we can see from our full thread that our email forwards to and sends from our Gmail account using the email alias and display name we selected.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen-Shot-2022-10-12-at-3.35.15-PM.png" alt="Screen-Shot-2022-10-12-at-3.35.15-PM" /></p>
<h2 id="heading-thanks-for-reading"><strong>Thanks for reading!</strong></h2>
<p>This was enlightening for me, and I hope it's helpful for you too.</p>
<p>Come say hey on Twitter: <a target="_blank" href="https://twitter.com/EamonnCottrell">https://twitter.com/EamonnCottrell</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/goodtogo.gif" alt="goodtogo" /></p>
]]></content:encoded></item><item><title><![CDATA[Animate SVGs for GitHub READMEs]]></title><description><![CDATA[Holy backslash! We can program SVG animations directly within the file! 😲

What's an SVG?
From Wikipedia: SVGs are scalable vector graphics. They are an XML-based vector image format for defining 2d graphics that supports interactivity and animation...]]></description><link>https://blog.eamonncottrell.com/animate-svgs-for-github-readmes</link><guid isPermaLink="true">https://blog.eamonncottrell.com/animate-svgs-for-github-readmes</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[#week4]]></category><category><![CDATA[SVG]]></category><category><![CDATA[CSS Animation]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Wed, 07 Sep 2022 16:04:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1662565538160/NVUVFEnuX.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Holy backslash! We can program SVG animations directly within the file! 😲</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662565610555/g-6GLxgru.gif" alt="amazing.gif" class="image--center mx-auto" /></p>
<h2 id="heading-whats-an-svg">What's an SVG?</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics">From Wikipedia</a>: SVGs are scalable vector graphics. They are an XML-based vector image format for defining 2d graphics that supports interactivity and animation. They can be scaled in size without loss of quality.</p>
<p>So, SVGs are, in essence, a list of programatic instructions describing a shape or collection of shapes. The file itself is an XML document and when you open one up, it looks a lot like html at first glance. In fact, as we'll see shortly, you can add ids and classes to the different elements in order to manipulate them with CSS just like html elements!</p>
<p>Cool, right?! 😁</p>
<h2 id="heading-use-case-github-readme">Use Case - GitHub README</h2>
<p>I recently updated the README for one of my GitHub projects, <a target="_blank" href="https://github.com/sieis/unmove">unMove</a>. This was what caused me to go down the .svg rabbit hole. I wanted an animation on there without having to resort to something on giphy. </p>
<p>This is what I used first, but it was pretty rough:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/rqXk2WsgiDGHoHMU8l/giphy.gif">https://media.giphy.com/media/rqXk2WsgiDGHoHMU8l/giphy.gif</a></div>
<p>That's when I realized that I could "embed" the animation into an .svg and then just add that .svg straight to the README like any other image file.</p>
<p>Below is the graphic I created in Illustrator with the embedded code followed by the full .svg code block:</p>
<div class="hn-embed-widget" id="sacred-spinner"></div><pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"b"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">xmlns:xlink</span>=<span class="hljs-string">"http://www.w3.org/1999/xlink"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 322.01 322.01"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">defs</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
            <span class="hljs-selector-class">.e</span>{
                <span class="hljs-attribute">fill</span>:<span class="hljs-built_in">url</span>(#d);
            }
            <span class="hljs-selector-class">.rotate</span>{
                <span class="hljs-attribute">transform-origin</span>: center;
                <span class="hljs-attribute">animation</span>: rotation <span class="hljs-number">20s</span> infinite linear;
            }
            <span class="hljs-keyword">@keyframes</span> rotation{
                <span class="hljs-selector-tag">from</span>{
                    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>);
                }
                <span class="hljs-selector-tag">to</span>{
                    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">359deg</span>);
                }
            }
        </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">radialGradient</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"d"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"161.01"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"161.01"</span> <span class="hljs-attr">fx</span>=<span class="hljs-string">"161.01"</span> <span class="hljs-attr">fy</span>=<span class="hljs-string">"161.01"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"161.01"</span> <span class="hljs-attr">gradientUnits</span>=<span class="hljs-string">"userSpaceOnUse"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">stop</span> <span class="hljs-attr">offset</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">stop-color</span>=<span class="hljs-string">"red"</span>/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">stop</span> <span class="hljs-attr">offset</span>=<span class="hljs-string">".22"</span> <span class="hljs-attr">stop-color</span>=<span class="hljs-string">"#ff0"</span>/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">stop</span> <span class="hljs-attr">offset</span>=<span class="hljs-string">".4"</span> <span class="hljs-attr">stop-color</span>=<span class="hljs-string">"#f7931e"</span>/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">stop</span> <span class="hljs-attr">offset</span>=<span class="hljs-string">".73"</span> <span class="hljs-attr">stop-color</span>=<span class="hljs-string">"#f0f"</span>/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">stop</span> <span class="hljs-attr">offset</span>=<span class="hljs-string">"1"</span> <span class="hljs-attr">stop-color</span>=<span class="hljs-string">"#aca1c9"</span>/&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">radialGradient</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">defs</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"c"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"rotate"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"e"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M322.01,161c-88.92,0-161.01,72.09-161.01,161.01,0-88.92-72.08-161.01-161-161.01C88.92,161,161,88.92,161,0c0,88.92,72.09,161,161.01,161Z"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<p>Pretty neat, eh? 😀</p>
<p>Let's build one of our own...</p>
<h2 id="heading-get-svg-from-undraw">Get SVG from unDraw</h2>
<p>You don't have to create a graphic from scratch, although that's a fun option too if you're so inclined!</p>
<p>One of my favorite places to get free, open-sourced illustrations in .svg or .png format is <a target="_blank" href="https://undraw.co/">unDraw</a>. We'll be using one of their illustrations for our example file.</p>
<p>When you select an image on unDraw, you'll be able to change it's color as well as select the download format.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662486593294/oS_bPTtfj.png" alt="Screen Shot 2022-09-06 at 1.49.33 PM.png" class="image--center mx-auto" /></p>
<p>I'm going to use the <em>Blooming</em> illustration.  </p>
<p>If you search "flower", it should pop up easily for you. 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662486532292/o1iHhFw8n.png" alt="Screen Shot 2022-09-06 at 1.47.03 PM.png" class="image--center mx-auto" /></p>
<h2 id="heading-edit-the-svg">Edit the SVG</h2>
<p>Once you've downloaded the SVG, open it up in your code editor of choice. I use <a target="_blank" href="https://code.visualstudio.com/">VSCode</a>, but any editor will do the trick.</p>
<p><em>Pro-tip</em>: if you're so inclined, you can also open the .svg in a graphics program like <a target="_blank" href="https://www.adobe.com/products/illustrator.html">Adobe Illustrator</a> (not free) or <a target="_blank" href="https://www.gimp.org/">GIMP</a> (free) and make edits or additions graphically. Once you've done that, though, we'll want to end up looking at the actual code.</p>
<p>I've created a folder for our example</p>
<pre><code class="lang-bash"><span class="hljs-variable">$mkdir</span> svg-sample
</code></pre>
<p>And then I've placed the svg in the folder</p>
<pre><code class="lang-bash"><span class="hljs-variable">$ls</span>
<span class="hljs-comment"># blooming.svg</span>
</code></pre>
<p>This makes it simple to open in VSCode</p>
<pre><code class="lang-bash"><span class="hljs-variable">$code</span> .
</code></pre>
<p>Once in VSCode, though, you'll be confronted with an impenetrable wall of text when you open the .svg file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662487027120/qZ2VFmKD8.png" alt="Screen Shot 2022-09-06 at 1.56.33 PM.png" class="image--center mx-auto" /></p>
<p>Take heart! 💚 </p>
<p>We'll just need to clean up the code a bit to make examination easier.</p>
<h2 id="heading-svg-elements">SVG Elements</h2>
<p>As always, MDN is an incredible resource for information. The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element">documentation on SVGs</a> is immense, and I encourage you to dig deeper on your own.</p>
<p>As we examine our code, we can see that is made up of several elements: <code>&lt;svg&gt;</code>, <code>&lt;path&gt;</code>, <code>&lt;rect&gt;</code>, <code>&lt;polygon&gt;</code>, <code>&lt;circle&gt;</code>, and <code>&lt;g&gt;</code></p>
<p>The first thing we'll do is get things a little more readable so we can decipher what's what and manipulate certain elements.</p>
<h3 id="heading-clean-up">Clean Up</h3>
<p>In VSCode, <code>SHIFT + CMD + L</code> will select all occurrences of the highlighted text, so I will select all the beginning of all the various elements, backspace to the start of them, and hit <code>RETURN</code> to get all of them at the start of a line:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662488458518/uRKR116ns.gif" alt="svg vscode.gif" class="image--center mx-auto" /></p>
<p>Now we can begin to see what's going on a little easier.</p>
<h2 id="heading-experimentation">Experimentation</h2>
<p>In simple SVGs, you may have only one or two elements. This makes it simple to label and apply styling to. However, in our example, there are many elements and nothing to let us know what is what. </p>
<p>By poking around with some styling, we can figure out what is what. The shapes themselves will tell us a little, but by applying a fill color to different things, we can then label everything in our SVG code.</p>
<p>I'm using the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer">Live Server extension</a> in VSCode, so I'm able to see the effects of changes instantly by opening the file while I'm making the styling edits.</p>
<p>You can see in the screenshot below that the first path in the .svg is the path for the woman's right hand in the illustration. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662489180434/KiJUt-zpg.png" alt="Screen Shot 2022-09-06 at 2.31.17 PM.png" /></p>
<h3 id="heading-svg-elements">SVG Elements</h3>
<p>We start out with the <code>&lt;svg&gt;</code> element itself. This is a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg">container element</a> and sets up the coordinate system. Think of it as our canvas where the rest of the graphics will live.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"598.25299"</span> 
<span class="hljs-attr">height</span>=<span class="hljs-string">"581.49793"</span> 
<span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 598.25299 581.49793"</span> 
<span class="hljs-attr">xmlns:xlink</span>=<span class="hljs-string">"http://www.w3.org/1999/xlink"</span>&gt;</span>
</code></pre>
<p>Then, we go straight into the paths, polygons, rectangles and circles. All of the following code blocks are individual pieces of the illustration. I've made note of which piece is which in the comments:</p>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- right hand --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"uuid-207e1ec5-f316-41d7-bdda-8b0dd0a2bd60-198"</span> 
<span class="hljs-attr">d</span>=<span class="hljs-string">"M317.37575,441.36116c-8.89671,1.36364-15.40463,7.05944-14.53588,12.72122,.86876,5.66178,8.78434,9.14461,17.68405,7.7793,3.56255-.49662,6.95436-1.83918,9.89166-3.91538l37.62277-6.25382-2.511-14.87153-37.53792,5.31177c-3.42552-1.10114-7.06497-1.36572-10.61369-.77157Z"</span> 
<span class="hljs-attr">fill</span>=<span class="hljs-string">"#ffb6b6"</span>/&gt;</span>
</code></pre>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- right sleeve --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"479.50058 395.00508 464.91227 425.96119 391.57489 450.64909 330.71947 453.89578 329.19546 439.2792 395.14843 425.48908 462.82463 393.50814 479.50058 395.00508"</span> 
<span class="hljs-attr">fill</span>=<span class="hljs-string">"#e6e6e6"</span>/&gt;</span>
</code></pre>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- middle flower stem  --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">rect</span>  <span class="hljs-attr">x</span>=<span class="hljs-string">"313.0222"</span> 
<span class="hljs-attr">y</span>=<span class="hljs-string">"330.24459"</span> 
<span class="hljs-attr">width</span>=<span class="hljs-string">"2.0005"</span> 
<span class="hljs-attr">height</span>=<span class="hljs-string">"173.77644"</span> 
<span class="hljs-attr">transform</span>=<span class="hljs-string">"translate(-9.1013 7.00975) rotate(-1.26066)"</span>/&gt;</span>
</code></pre>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- flower center on middle flower --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flower"</span> 
<span class="hljs-attr">cx</span>=<span class="hljs-string">"314.6595"</span> 
<span class="hljs-attr">cy</span>=<span class="hljs-string">"306.0531"</span> 
<span class="hljs-attr">r</span>=<span class="hljs-string">"21.23894"</span> 
<span class="hljs-attr">fill</span>=<span class="hljs-string">"#3f3d56"</span>/&gt;</span>
</code></pre>
<p>In this manner, you will be able to go through and label any part of the illustration that you need to use in your animations.</p>
<h2 id="heading-full-example-animation">Full Example Animation</h2>
<p>Now, look at the full example below. I've made the graphic quite lively to show how you can make an illustration come to life with some basic css.</p>
<p>All of this can be achieved by writing the css in a <code>&lt;style&gt;</code> section directly within the .svg file itself.</p>
<div class="hn-embed-widget" id="svg-animate"></div><blockquote>
<p>Writing the css into the .svg file itself was actually very important for our GitHub README use case. This is what allows us to have the animated .svg rendered in our markdown file on GitHub.</p>
</blockquote>
<h2 id="heading-piece-by-piece">Piece by Piece</h2>
<p>There is not a lot of complicated things going on here, tbh. The most difficult part in this example was simply going through each of the .svg elements to label all the parts. </p>
<p>From there, I built a spinning animation for the flower petals. One clockwise and one counterclockwise:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.spin</span>{
    <span class="hljs-attribute">transform-box</span>:fill-box;
    <span class="hljs-attribute">transform-origin</span>:center;
    <span class="hljs-attribute">animation</span>:spin <span class="hljs-number">7s</span> infinite linear;
}
<span class="hljs-selector-class">.spin-counter</span>{
    <span class="hljs-attribute">transform-box</span>:fill-box;
    <span class="hljs-attribute">transform-origin</span>:center;
    <span class="hljs-attribute">animation</span>:spin-counter <span class="hljs-number">7s</span> infinite linear;
}
<span class="hljs-keyword">@keyframes</span> spin{
    0%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>)}
    100%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">359deg</span>)}
}
<span class="hljs-keyword">@keyframes</span> spin-counter{
    0%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>)}
    100%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">359deg</span>)}
}
</code></pre>
<p>Then I found the code for those petals and added <code>class="spin"</code>. They were the most lengthy elements in this file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662512819701/SMDTG0TWL.png" alt="Screen Shot 2022-09-06 at 9.05.19 PM.png" class="image--center mx-auto" /></p>
<p>Similarly, I animated the flower stems and leaves to flash and the center of the flowers to scale up and down while changing colors.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.flash</span>{
    <span class="hljs-attribute">animation</span>:flash .<span class="hljs-number">2s</span> infinite <span class="hljs-built_in">steps</span>(<span class="hljs-number">5</span>,jump-none);
}
<span class="hljs-selector-class">.flower</span>{
    <span class="hljs-attribute">transform-box</span>:fill-box;
    <span class="hljs-attribute">transform-origin</span>:center;
    <span class="hljs-attribute">animation</span>:flower <span class="hljs-number">4s</span> infinite ease-in-out;
}
<span class="hljs-keyword">@keyframes</span> flash{
    0%{<span class="hljs-attribute">fill</span>:yellow}
    20%{<span class="hljs-attribute">fill</span>:pink}
    40%{<span class="hljs-attribute">fill</span>:blue}
    60%{<span class="hljs-attribute">fill</span>:orange}
    80%{<span class="hljs-attribute">fill</span>:red}
    100%{<span class="hljs-attribute">fill</span>:purple}
}
<span class="hljs-keyword">@keyframes</span> flower{
    0%{
        <span class="hljs-attribute">transform</span>:<span class="hljs-built_in">scale</span>(<span class="hljs-number">1.0</span>);
        <span class="hljs-attribute">fill</span>:<span class="hljs-number">#3f3d56</span>;
    }
    50%{
        <span class="hljs-attribute">transform</span>:<span class="hljs-built_in">scale</span>(<span class="hljs-number">1.3</span>);
        <span class="hljs-attribute">fill</span>: yellow;
    }
    100%{
        <span class="hljs-attribute">transform</span>:<span class="hljs-built_in">scale</span>(<span class="hljs-number">1.0</span>);
        <span class="hljs-attribute">fill</span>: <span class="hljs-number">#3f3d56</span>;
    }
}
</code></pre>
<p>But, we're not limited to harsh, obvious animations. I tried to mimic a slight breeze through the woman's hair by adding a very slight rotation with <code>ease-in-out</code> to it:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.hair</span>{
    <span class="hljs-attribute">transform-box</span>:fill-box;
    <span class="hljs-attribute">transform-origin</span>:top-left;
    <span class="hljs-attribute">animation</span>:hair <span class="hljs-number">3s</span> infinite ease-in-out;
}
<span class="hljs-keyword">@keyframes</span> hair{
    0%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>)}
    50%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">2deg</span>)}
    100%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>)}
}
</code></pre>
<p>And similarly, for her foot...a little tapping by doing this:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.tap</span>{
    <span class="hljs-attribute">transform-box</span>:fill-box;
    <span class="hljs-attribute">transform-origin</span>:right;
    <span class="hljs-attribute">animation</span>:tap <span class="hljs-number">1.3s</span> infinite <span class="hljs-built_in">cubic-bezier</span>(.<span class="hljs-number">2</span>,<span class="hljs-number">1.4</span>,.<span class="hljs-number">7</span>,<span class="hljs-number">2</span>);
}
<span class="hljs-keyword">@keyframes</span> tap{
    0%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">2deg</span>)}
    50%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">5deg</span>)}
    100%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">2deg</span>)}
}
</code></pre>
<h2 id="heading-the-full-code">The Full Code</h2>
<p>There are other ways to put an .svg on a website. For instance, you can insert the element directly into the html and then move all your styling to a separate css file.</p>
<p>This makes things cleaner, but we focused on having everything together for our project today. This way, you can simply upload the .svg to your GitHub project and then throw it into your README like you would any other picture</p>
<pre><code class="lang-md">![<span class="hljs-string">svg-animation</span>](<span class="hljs-link">&lt;path-to-svg&gt;</span>)
</code></pre>
<p>Here is the full code file with everything within the .svg:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"598.25299"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"581.49793"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 598.25299 581.49793"</span> <span class="hljs-attr">xmlns:xlink</span>=<span class="hljs-string">"http://www.w3.org/1999/xlink"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
    <span class="hljs-selector-class">.flash</span>{
        <span class="hljs-attribute">animation</span>:flash .<span class="hljs-number">2s</span> infinite <span class="hljs-built_in">steps</span>(<span class="hljs-number">5</span>,jump-none);
    }
    <span class="hljs-selector-class">.spin</span>{
        <span class="hljs-attribute">transform-box</span>:fill-box;
        <span class="hljs-attribute">transform-origin</span>:center;
        <span class="hljs-attribute">animation</span>:spin <span class="hljs-number">7s</span> infinite linear;
    }
    <span class="hljs-selector-class">.spin-counter</span>{
        <span class="hljs-attribute">transform-box</span>:fill-box;
        <span class="hljs-attribute">transform-origin</span>:center;
        <span class="hljs-attribute">animation</span>:spin-counter <span class="hljs-number">7s</span> infinite linear;
    }
    <span class="hljs-selector-class">.flower</span>{
        <span class="hljs-attribute">transform-box</span>:fill-box;
        <span class="hljs-attribute">transform-origin</span>:center;
        <span class="hljs-attribute">animation</span>:flower <span class="hljs-number">4s</span> infinite ease-in-out;
    }
    <span class="hljs-selector-class">.hair</span>{
        <span class="hljs-attribute">transform-box</span>:fill-box;
        <span class="hljs-attribute">transform-origin</span>:top-left;
        <span class="hljs-attribute">animation</span>:hair <span class="hljs-number">3s</span> infinite ease-in-out;
    }
    <span class="hljs-selector-class">.tap</span>{
        <span class="hljs-attribute">transform-box</span>:fill-box;
        <span class="hljs-attribute">transform-origin</span>:right;
        <span class="hljs-attribute">animation</span>:tap <span class="hljs-number">1.3s</span> infinite <span class="hljs-built_in">cubic-bezier</span>(.<span class="hljs-number">2</span>,<span class="hljs-number">1.4</span>,.<span class="hljs-number">7</span>,<span class="hljs-number">2</span>);
    }
    <span class="hljs-keyword">@keyframes</span> flash{
        0%{<span class="hljs-attribute">fill</span>:yellow}
        20%{<span class="hljs-attribute">fill</span>:pink}
        40%{<span class="hljs-attribute">fill</span>:blue}
        60%{<span class="hljs-attribute">fill</span>:orange}
        80%{<span class="hljs-attribute">fill</span>:red}
        100%{<span class="hljs-attribute">fill</span>:purple}
    }
    <span class="hljs-keyword">@keyframes</span> spin{
        0%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>)}
        100%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">359deg</span>)}
    }
    <span class="hljs-keyword">@keyframes</span> spin-counter{
        0%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>)}
        100%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">359deg</span>)}
    }
    <span class="hljs-keyword">@keyframes</span> flower{
        0%{
            <span class="hljs-attribute">transform</span>:<span class="hljs-built_in">scale</span>(<span class="hljs-number">1.0</span>);
            <span class="hljs-attribute">fill</span>:<span class="hljs-number">#3f3d56</span>;
        }
        50%{
            <span class="hljs-attribute">transform</span>:<span class="hljs-built_in">scale</span>(<span class="hljs-number">1.3</span>);
            <span class="hljs-attribute">fill</span>: yellow;
        }
        100%{
            <span class="hljs-attribute">transform</span>:<span class="hljs-built_in">scale</span>(<span class="hljs-number">1.0</span>);
            <span class="hljs-attribute">fill</span>: <span class="hljs-number">#3f3d56</span>;
        }
    }
    <span class="hljs-keyword">@keyframes</span> hair{
        0%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>)}
        50%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">2deg</span>)}
        100%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>)}
    }
    <span class="hljs-keyword">@keyframes</span> tap{
        0%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">2deg</span>)}
        50%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">5deg</span>)}
        100%{<span class="hljs-attribute">transform</span>:<span class="hljs-built_in">rotate</span>(-<span class="hljs-number">2deg</span>)}
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>

<span class="hljs-comment">&lt;!-- right hand --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"uuid-207e1ec5-f316-41d7-bdda-8b0dd0a2bd60-198"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M317.37575,441.36116c-8.89671,1.36364-15.40463,7.05944-14.53588,12.72122,.86876,5.66178,8.78434,9.14461,17.68405,7.7793,3.56255-.49662,6.95436-1.83918,9.89166-3.91538l37.62277-6.25382-2.511-14.87153-37.53792,5.31177c-3.42552-1.10114-7.06497-1.36572-10.61369-.77157Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ffb6b6"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- right sleeve --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"479.50058 395.00508 464.91227 425.96119 391.57489 450.64909 330.71947 453.89578 329.19546 439.2792 395.14843 425.48908 462.82463 393.50814 479.50058 395.00508"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#e6e6e6"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- middle flower --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flash"</span>&gt;</span>

<span class="hljs-comment">&lt;!-- middle flower stem  --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">rect</span>  <span class="hljs-attr">x</span>=<span class="hljs-string">"313.0222"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"330.24459"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"2.0005"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"173.77644"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"translate(-9.1013 7.00975) rotate(-1.26066)"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span>  <span class="hljs-attr">d</span>=<span class="hljs-string">"M313.1538,407.16797c-.18115-.61621-4.32715-15.26855,4.65088-29.6709l1.69727,1.05859c-8.48975,13.61816-4.47021,27.90723-4.42871,28.05078l-1.91943,.56152Z"</span> /&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M318.50651,378.60915s-2.72937-18.65506,22.41425-23.77492c5.23825-1.06664,10.16278-1.79561,14.67694-2.28132,5.32851-.57333,7.40701,6.72083,2.59337,9.0768-6.97123,3.41197-13.52893,7.71085-16.94694,12.80161-9.42681,14.04022-22.73764,4.17784-22.73764,4.17784Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M315.74023,461.11523l-1.91943-.56152c.0415-.14355,4.06104-14.43262-4.42871-28.05078l1.69727-1.05859c8.97803,14.40234,4.83203,29.05469,4.65088,29.6709Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M310.3877,432.55605s2.72937-18.65506-22.41425-23.77492c-5.23825-1.06664-10.16278-1.79561-14.67694-2.28132-5.32851-.57333-7.40701,6.72083-2.59337,9.0768,6.97123,3.41197,13.52893,7.71085,16.94694,12.80161,9.42681,14.04022,22.73764,4.17784,22.73764,4.17784Z"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- flower petals on middle --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"spin"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M345.38308,306.12152c5.06107-1.33443,9.79382-3.65962,9.63686-5.95984-.18646-2.73252-7.21292-4.56342-13.27465-4.84202,4.57215-3.57699,8.77317-8.52168,7.48846-10.75799-1.1811-2.05594-6.67554-1.25956-11.74486,.5512,2.99995-4.28898,5.10906-9.12193,3.50282-10.77588-2.00087-2.06027-9.10218,1.49577-13.9485,5.42651,.94344-6.1237,.46649-13.8534-2.2801-14.55274-2.29776-.58506-5.55432,3.91139-7.78706,8.80952-.98679-5.1402-2.98444-10.02026-5.29-10.02026-3.28439,0-5.9469,9.9025-5.9469,16.35398,0,.36468,.00967,.70797,.02632,1.03597-.32965-.38218-.70745-.78145-1.1383-1.19988-4.62815-4.49466-13.58691-9.48355-15.8751-7.12744-1.60625,1.65395,.50287,6.4869,3.50282,10.77588-5.06932-1.81075-10.56375-2.60714-11.74486-.5512-1.58701,2.76251,5.19664,9.65864,10.71068,13.00291-.33147-.03993-.67971-.07355-1.05033-.09883-6.4365-.43917-16.49728,1.54306-16.72087,4.81983-.15696,2.30022,4.57578,4.62541,9.63686,5.95984-5.03878,1.89411-9.74642,4.83707-9.31913,7.16931,.51858,2.8306,8.42853,3.82148,14.64789,3.24148-4.64633,4.4579-9.68446,12.44383-7.5819,14.78429,1.54078,1.71511,6.50608-.06012,10.98934-2.76114-2.15166,4.93429-3.32024,10.36176-1.34945,11.68009,2.60449,1.74223,9.74743-4.32147,13.51348-9.53904-.38374,6.38314,1.14359,15.15229,4.17408,15.49836,2.29068,.2616,4.82911-4.36033,6.39273-9.35537,1.66258,5.11982,4.38795,9.95671,6.73724,9.63613,3.05486-.41687,4.38668-9.40546,3.81684-15.79319,3.88469,5.00287,10.42248,10.35339,12.88884,8.70356,1.9708-1.31833,.80222-6.7458-1.34945-11.68009,4.48325,2.70102,9.44855,4.47625,10.98934,2.76114,2.10256-2.34046-2.93557-10.3264-7.5819-14.78429,6.21936,.58,14.12931-.41088,14.64789-3.24148,.42729-2.33224-4.28035-5.2752-9.31913-7.16931Zm-27.25118,18.05535c-1.30094,.17752-2.49049,.43103-3.39512,1.07838-.96104-.95404-2.3647-1.2446-3.92539-1.42282-1.74749-.19956-3.33551-.21882-4.54651,.78962-.44311-1.23164-1.53269-2.1066-2.80192-2.95564-1.09135-.73004-2.14972-1.3293-3.2561-1.44485-.08644-1.35139-.94416-2.4999-1.99392-3.66846-.66934-.7451-1.33902-1.41125-2.08213-1.85683,1.07214-1.3054,.94852-2.9553,.61679-4.76601-.2366-1.2915-.54407-2.46827-1.23197-3.34246,.90927-1.00353,1.13558-2.41899,1.24252-3.98616,.11675-1.7112,.06824-3.26269-.91948-4.41797,2.55254,.32468,3.78435-1.26552,4.92929-3.25854,.65405-1.13848,1.17989-2.23522,1.21986-3.3469,1.34239-.17824,2.42983-1.11217,3.52418-2.23903,.93486-.96262,1.73007-1.91578,2.02877-3.01283,1.0712,.90022,2.53281,1.03646,4.14527,1.03646,1.57083,0,2.99839-.12942,4.06148-.96827,.82534,.7458,1.97845,1.13265,3.25084,1.45663,1.21463,.30928,2.36117,.51612,3.4036,.30795,.38052,.86931,1.05707,1.65872,1.82981,2.4544,1.09435,1.12687,2.18179,2.06079,3.52418,2.23903,.03998,1.11167,.56582,2.20841,1.21986,3.3469,.76989,1.34014,1.57848,2.49889,2.80151,3.01589-.1428,.77844-.12753,1.63528-.06603,2.53672,.10695,1.56718,.33326,2.98266,1.24252,3.98616-.68791,.87419-.99537,2.05096-1.23197,3.34246-.33173,1.8107-.45535,3.46061,.61679,4.76601-.7431,.44557-1.41278,1.11173-2.08213,1.85683-1.04976,1.16856-1.90749,2.31707-1.99392,3.66846-1.10638,.11555-2.16475,.71482-3.2561,1.44485-1.49056,.99708-2.73218,2.03061-2.97065,3.63515-1.11854-.54256-2.45752-.47248-3.90393-.27513Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ff6863"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- flower center on middle flower --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flower"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"314.6595"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"306.0531"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"21.23894"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#3f3d56"</span>/&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>

<span class="hljs-comment">&lt;!-- ground --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M58.50921,563.59732c0,.66003,.53003,1.19,1.19006,1.19H581.98925c.65997,0,1.19-.52997,1.19-1.19,0-.65997-.53003-1.19-1.19-1.19H59.69927c-.66003,0-1.19006,.53003-1.19006,1.19Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#3f3d56"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- right leg skin --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"344.23894 512.546 331.14162 545.67451 314.19215 534.11806 324.20774 499.44869 344.23894 512.546"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ffb6b6"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- left leg skin --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"289.53838 500.21912 254.86901 533.34763 268.73676 547.9858 304.17655 520.25031 289.53838 500.21912"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ffb6b6"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- back of hair --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"485.99813 392.35886 485.61291 389.66236 476.75296 372.32767 447.47661 380.03198 452.86962 403.91532 485.99813 392.35886"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ffb6b6"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- skirt right leg--&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span>  <span class="hljs-attr">d</span>=<span class="hljs-string">"M473.67124,507.92342l.90036,11.57162s3.72223,48.52195-26.32456,52.3741c-30.04679,3.85215-43.1441,7.7043-60.864-22.34248l-27.73549-49.30754-16.20027,19.27593-21.55082-16.96463s15.40861-60.09357,27.73549-62.40486c1.54086-.28891,3.08172-.43337,4.59851-.46497,10.20508-.21261,19.82492,4.77897,25.93085,12.95861l34.95723,46.82949,12.7121-5.77823,45.84061,14.25296Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#2f2e41"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- right foot --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M307.25828,544.13365l6.93387-13.09732,20.03119,13.86775s4.62258,16.94947-2.31129,19.26076c-6.93387,2.31129-26.19463-.77043-26.19463-.77043,0,0-35.4398,7.7043-36.21023,.77043s16.17904-8.47473,16.17904-8.47473l21.57205-11.55646Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#2f2e41"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- skirt left leg --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M382.76045,534.88849l-53.54491-33.73818-27.35028,24.49301-18.49033-21.57205s33.89894-50.07798,43.91453-52.38927,26.19463-3.85215,26.19463-3.85215l49.30754,45.45539-20.03119,41.60324Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#2f2e41"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- left foot --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tap"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M254.09858,530.2659l17.7199,19.26076-2.33678,1.99887s-4.59709,20.02209-15.38312,13.24898c-10.78603-6.77311-11.55646-8.31398-11.55646-8.31398,0,0-30.81722,6.93387-33.89894-5.39301-3.08172-12.32689,5.39301-10.0156,5.39301-10.0156l7.6952,4.62258,32.36718-15.40861Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#2f2e41"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- face --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">circle</span>  <span class="hljs-attr">cx</span>=<span class="hljs-string">"456.72177"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"363.85294"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"24.65377"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ffb6b6"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- chest --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span>  <span class="hljs-attr">d</span>=<span class="hljs-string">"M450.94354,398.90752l35.4398-10.78603s13.86775,32.35808,11.55646,46.99625c-2.31129,14.63818-23.3682,84.37729-23.3682,84.37729l-46.74096-25.82458s-.77043-15.40861-3.08172-22.34248-9.63038-28.12071,6.54866-45.84061c16.17904-17.7199,19.64597-26.57985,19.64597-26.57985Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#e6e6e6"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- big flower --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flash"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"107.34447"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"155.95049"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"1.99952"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"409.09901"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"translate(-7.90461 2.47077) rotate(-1.26058)"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M107.59862,336.66016c-.10254-.34961-10.01758-35.3584,10.79932-68.75146l1.69727,1.05811c-20.34814,32.6416-10.67773,66.78906-10.57764,67.13086l-1.91895,.5625Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span>  <span class="hljs-attr">d</span>=<span class="hljs-string">"M118.90075,269.80903s-6.42539-43.91713,52.76688-55.97014c12.33172-2.51104,23.92488-4.22717,34.55197-5.3706,12.54419-1.34971,17.43734,15.82195,6.10524,21.36829-16.41144,8.03236-31.84935,18.15263-39.89591,30.13712-22.19228,33.05303-53.52818,9.83533-53.52818,9.83533Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M111.08984,463.66016l-1.91895-.5625c.09961-.34082,9.75146-34.52051-10.57764-67.13086l1.69727-1.05859c20.81689,33.39355,10.90186,68.40234,10.79932,68.75195Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span>  <span class="hljs-attr">d</span>=<span class="hljs-string">"M99.78771,396.80903s6.42539-43.91713-52.76688-55.97014c-12.33172-2.51104-23.92488-4.22717-34.55197-5.3706-12.54419-1.34971-17.43734,15.82195-6.10524,21.36829,16.41144,8.03236,31.84935,18.15263,39.89591,30.13712,22.19228,33.05303,53.52818,9.83533,53.52818,9.83533Z"</span>/&gt;</span>

<span class="hljs-comment">&lt;!-- big flower petals --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"spin-counter"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M182.17266,99.16107c11.91461-3.14148,23.05627-8.61536,22.68677-14.03046-.43896-6.4328-16.98041-10.74304-31.25073-11.39893,10.76361-8.42084,20.6535-20.06146,17.62909-25.32611-2.78052-4.84003-15.71533-2.96521-27.64935,1.29761,7.06238-10.09698,12.02759-21.47455,8.24622-25.36823-4.71039-4.85022-21.42804,3.5213-32.8371,12.7749,2.22101-14.4162,1.09821-32.61322-5.36774-34.25958-5.4093-1.37732-13.07581,9.20807-18.33203,20.73907-2.32306-12.10089-7.02588-23.58936-12.45355-23.58936-7.73199,0-14,23.31213-14,38.5,0,.85852,.02277,1.66669,.06195,2.43884-.77606-.89972-1.66547-1.83966-2.67975-2.82471-10.89545-10.58118-31.98584-22.32587-37.37262-16.77917-3.78137,3.89368,1.18384,15.27124,8.24622,25.36823-11.93402-4.26282-24.86884-6.13763-27.64935-1.29761-3.73608,6.50342,12.23376,22.73804,25.21472,30.61102-.78033-.09399-1.60016-.17316-2.47266-.23267-15.15259-1.03387-38.83734,3.63263-39.36371,11.34668-.36951,5.4151,10.77216,10.88898,22.68677,14.03046-11.86212,4.45905-22.9447,11.38727-21.93878,16.87775,1.22083,6.6637,19.84216,8.9964,34.48358,7.63098-10.93823,10.49463-22.79883,29.29486-17.84906,34.80469,3.62726,4.03766,15.31641-.14154,25.87073-6.50018-5.06537,11.61615-7.81641,24.39331-3.17682,27.49689,6.13141,4.1015,22.94708-10.17346,31.81299-22.45648-.90338,15.02698,2.6922,35.67102,9.82648,36.48572,5.39264,.61584,11.36853-10.26495,15.04956-22.02411,3.914,12.05292,10.32996,23.43976,15.8606,22.68506,7.19165-.98138,10.32697-22.14203,8.98547-37.17981,9.1452,11.77759,24.53625,24.3736,30.34247,20.48962,4.63959-3.10358,1.88855-15.88074-3.17682-27.49689,10.55432,6.35864,22.24347,10.53784,25.87073,6.50018,4.94977-5.50983-6.91083-24.31006-17.84906-34.80469,14.64142,1.36542,33.26276-.96729,34.48358-7.63098,1.00592-5.49048-10.07666-12.4187-21.93878-16.87775Zm-64.15381,42.50531c-3.06262,.41791-5.86304,1.01471-7.99268,2.5387-2.26245-2.24597-5.56689-2.92999-9.24103-3.34955-4.11389-.46979-7.85236-.51514-10.70325,1.85889-1.04315-2.89948-3.60822-4.95929-6.59619-6.95807-2.56921-1.71863-5.06079-3.12939-7.66541-3.40143-.20349-3.1814-2.22272-5.88519-4.69403-8.63617-1.57574-1.75409-3.15228-3.32233-4.90167-4.37128,2.52399-3.07312,2.23297-6.95728,1.45203-11.21997-.55701-3.04041-1.28082-5.81073-2.90027-7.86871,2.14056-2.36249,2.67334-5.6947,2.92511-9.38409,.27484-4.02844,.16064-7.68091-2.16461-10.40063,6.00909,.76434,8.909-2.97925,11.60437-7.67114,1.53973-2.68018,2.77765-5.26208,2.87177-7.87915,3.16022-.41962,5.72021-2.61823,8.29651-5.27106,2.20081-2.26617,4.07288-4.51007,4.77606-7.09271,2.52179,2.11926,5.96265,2.44,9.75867,2.44,3.698,0,7.05872-.30469,9.5614-2.27948,1.94299,1.75574,4.65759,2.66644,7.65302,3.42914,2.85944,.72809,5.55859,1.21503,8.01263,.72498,.89581,2.04651,2.48853,3.90491,4.30768,5.77808,2.57629,2.65283,5.13629,4.85144,8.29651,5.27106,.09412,2.61707,1.33203,5.19897,2.87177,7.87915,1.81244,3.15491,3.716,5.88281,6.59521,7.09991-.33618,1.83258-.30023,3.84973-.15546,5.97186,.25177,3.68939,.78455,7.02167,2.92511,9.38409-1.61945,2.05798-2.34326,4.82831-2.90027,7.86871-.78094,4.2627-1.07196,8.14685,1.45203,11.21997-1.74939,1.04895-3.32593,2.61719-4.90167,4.37128-2.47131,2.75098-4.49054,5.45477-4.69403,8.63617-2.60461,.27203-5.09619,1.6828-7.66541,3.40143-3.50903,2.34729-6.43201,4.7804-6.99341,8.55774-2.63324-1.27728-5.7854-1.1123-9.19049-.64771Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ff6863"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flower"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"109.84423"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"99"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#3f3d56"</span>/&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>


<span class="hljs-comment">&lt;!-- small flower --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flash"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"570.88768"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"466.26258"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"2.00002"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"97.74925"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"translate(-11.19664 12.70857) rotate(-1.26084)"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M570.979,509.65527c-.104-.35449-2.48779-8.7793,2.66504-17.04492l1.69727,1.05859c-4.66504,7.48242-2.46533,15.34668-2.44287,15.4248l-1.91943,.56152Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M574.40991,493.46765s-1.53527-10.49347,12.60802-13.3734c2.94652-.59998,5.71656-1.01003,8.25578-1.28324,2.99728-.3225,4.16644,3.78047,1.45877,5.1057-3.92132,1.91924-7.61002,4.33735-9.53265,7.2009-5.30258,7.89763-12.78992,2.35003-12.78992,2.35003Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M573.27392,540l-1.91943-.56152c.02246-.07812,2.22217-7.94141-2.44287-15.42383l1.69727-1.05859c5.15283,8.26562,2.76904,16.68945,2.66504,17.04395Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M569.84308,523.81278s1.53527-10.49347-12.60802-13.3734c-2.94652-.59998-5.71656-1.01003-8.25578-1.28324-2.99728-.3225-4.16644,3.78047-1.45877,5.1057,3.92132,1.91924,7.61002,4.33735,9.53265,7.2009,5.30258,7.89763,12.78992,2.35003,12.78992,2.35003Z"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"spin-counter"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M589.52798,452.69335c2.84685-.75062,5.50902-2.05854,5.42073-3.35241-.10489-1.53704-4.05727-2.56692-7.46699-2.72364,2.57184-2.01206,4.93491-4.79345,4.21226-6.05137-.66437-1.15647-3.75499-.7085-6.60648,.31005,1.68747-2.41255,2.87385-5.13109,1.97033-6.06143-1.12549-1.1589-5.11997,.84137-7.84603,3.05241,.53068-3.44458,.2624-7.79254-1.28256-8.18592-1.29249-.32909-3.12431,2.20016-4.38022,4.95535-.55507-2.89136-1.67875-5.63639-2.97563-5.63639-1.84747,0-3.34513,5.57016-3.34513,9.19912,0,.20513,.00544,.39823,.0148,.58273-.18543-.21498-.39794-.43956-.64029-.67493-2.60334-2.52825-7.64263-5.3345-8.92974-4.00918-.90351,.93035,.28286,3.64888,1.97033,6.06143-2.85149-1.01855-5.94211-1.46651-6.60648-.31005-.89269,1.55391,2.92311,5.43298,6.02476,7.31414-.18645-.02246-.38234-.04137-.59081-.05559-3.62053-.24703-9.27972,.86797-9.40549,2.71115-.08829,1.29387,2.57388,2.60179,5.42073,3.35241-2.83431,1.06544-5.48236,2.72085-5.24201,4.03274,.2917,1.59221,4.74105,2.14958,8.23944,1.82333-2.61356,2.50757-5.44751,6.99966-4.26482,8.31616,.86669,.96475,3.65967-.03382,6.1815-1.55314-1.21031,2.77554-1.86764,5.82849-.75906,6.57005,1.46503,.98,5.48293-2.43083,7.60133-5.36571-.21585,3.59052,.64327,8.52316,2.34792,8.71783,1.28851,.14715,2.71637-2.45269,3.59591-5.2624,.9352,2.8799,2.46822,5.60065,3.7897,5.42032,1.71836-.23449,2.4675-5.29057,2.14697-8.88367,2.18514,2.81411,5.86264,5.82378,7.24997,4.89575,1.10857-.74156,.45125-3.79451-.75906-6.57005,2.52183,1.51932,5.31481,2.51789,6.1815,1.55314,1.18269-1.31651-1.65126-5.8086-4.26482-8.31616,3.49839,.32625,7.94774-.23112,8.23944-1.82333,.24035-1.31188-2.4077-2.9673-5.24201-4.03274Zm-15.32879,10.15614c-.73178,.09985-1.4009,.24245-1.90975,.60659-.54059-.53665-1.33014-.70009-2.20803-.80033-.98297-.11225-1.87623-.12309-2.55741,.44416-.24925-.69279-.86214-1.18496-1.57608-1.66255-.61388-.41065-1.20922-.74773-1.83156-.81273-.04862-.76016-.53109-1.4062-1.12158-2.06351-.37651-.41912-.7532-.79383-1.1712-1.04446,.60308-.73429,.53354-1.66236,.34694-2.68088-.13309-.72647-.30604-1.3884-.69298-1.88014,.51146-.56449,.63876-1.36068,.69892-2.24222,.06567-.96255,.03838-1.83526-.51721-2.48511,1.4358,.18263,2.1287-.71186,2.77273-1.83293,.3679-.6404,.66369-1.25731,.68617-1.88263,.7551-.10026,1.36678-.62559,1.98235-1.25946,.52586-.54148,.97317-1.07763,1.14118-1.69472,.60255,.50637,1.4247,.58301,2.33172,.58301,.88359,0,1.6866-.0728,2.28458-.54465,.46426,.41951,1.11288,.63711,1.8286,.81935,.68323,.17397,1.32816,.29032,1.91452,.17322,.21404,.48899,.5946,.93303,1.02927,1.3806,.61557,.63386,1.22726,1.15919,1.98235,1.25946,.02249,.62532,.31827,1.24223,.68617,1.88263,.43306,.75383,.88789,1.40563,1.57585,1.69644-.08033,.43787-.07174,.91985-.03714,1.42691,.06016,.88154,.18746,1.67774,.69892,2.24222-.38695,.49173-.55989,1.15367-.69298,1.88014-.1866,1.01852-.25613,1.94659,.34694,2.68088-.418,.25063-.79469,.62535-1.1712,1.04446-.59049,.65731-1.07296,1.30335-1.12158,2.06351-.62234,.065-1.21767,.40209-1.83156,.81273-.83844,.56086-1.53685,1.14222-1.67099,2.04477-.62918-.30519-1.38235-.26577-2.19596-.15476Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ff6863"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flower"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"572.24597"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"452.65487"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"11.9469"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#3f3d56"</span>/&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">g</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"uuid-fcc96ddd-8a9b-4008-9e3e-ba4d42e134c9-199"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M372.9429,560.6837c-5.76763,6.90981-6.87749,15.48672-2.47944,19.15655,4.39805,3.66983,12.6373,1.04277,18.40606-5.87027,2.34226-2.72987,3.99672-5.98097,4.82492-9.48131l24.06795-29.58576-11.72388-9.48778-24.62774,28.82333c-3.29686,1.44132-6.20051,3.65139-8.46786,6.44523Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#ffb6b6"</span>/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">polygon</span>  <span class="hljs-attr">points</span>=<span class="hljs-string">"478.12744 414.451 486.50417 447.63127 445.50568 513.25891 403.65242 555.32266 390.08694 544.06849 432.29671 491.54901 464.29146 423.87933 478.12744 414.451"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#e6e6e6"</span>/&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>


<span class="hljs-comment">&lt;!-- hair --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hair"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M433.99408,359.61557c6.32276-.01514,6.68173,.02699,7.7043,0,7.84218-.20681,9.89286-3.2921,13.86775-3.08172,7.09451,.37553-2.26916,35.94511-6.16344,52.38927-3.81924,16.12741,9.08811,30.5789,10.0156,31.58765,13.10117,14.24835,36.11364,16.85373,40.06238,10.78603,3.19618-4.91121-8.13109-12.37871-5.39301-23.11291,2.67657-10.49307,15.03562-9.3757,18.49033-19.26076,3.61798-10.35219-8.54038-15.57338-16.17904-40.83281-4.3869-14.50661-3.61083-17.20961-7.7043-26.19463-10.94177-24.01645-64.57358-13.7909-58.55271,8.47473,1.16467,4.307-1.93482,9.25899,3.85215,9.24516Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#2f2e41"</span>/&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<h2 id="heading-thanks-for-reading">Thanks for Reading! 🙏</h2>
<p>I love tweaking illustrations like this and making them animated. A little animation goes a long way.</p>
<p>I hope this has been helpful and fun for you to learn about as well!</p>
<p>Come say hey 👋 and let me know if you add an animated .svg to your GitHub; you can find me on Twitter: https://twitter.com/EamonnCottrell</p>
]]></content:encoded></item><item><title><![CDATA[Build an Open Source CSS Library]]></title><description><![CDATA[Meet unMove: my latest project where I built my first open-source library using vanilla CSS, GitHub Projects, Issues, tags and releases, semantic versioning and jsDelivr.

I set out building this to better learn good project management and the ins & ...]]></description><link>https://blog.eamonncottrell.com/build-an-open-source-css-library</link><guid isPermaLink="true">https://blog.eamonncottrell.com/build-an-open-source-css-library</guid><category><![CDATA[Open Source]]></category><category><![CDATA[CSS Animation]]></category><category><![CDATA[4articles4weeks]]></category><category><![CDATA[#week3]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Thu, 01 Sep 2022 18:52:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1661971411411/2GxO1dL2i.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Meet <a target="_blank" href="https://github.com/sieis/unmove">unMove</a>: my latest project where I built my first open-source library using vanilla CSS, <a target="_blank" href="https://github.com/features/issues">GitHub Projects</a>, Issues, <a target="_blank" href="https://blog.eamonncottrell.com/version-releases-and-git-tags-for-beginners">tags</a> and <a target="_blank" href="https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository">releases</a>, <a target="_blank" href="https://semver.org/">semantic versioning</a> and <a target="_blank" href="https://www.jsdelivr.com/">jsDelivr</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661886510165/WI4BjANVg.png" alt="image.png" /></p>
<p>I set out building this to better learn good project management and the ins &amp; outs of maintaining an open source project.</p>
<p>If you're interested in open source or project management...or just staying organized and on task amidst a never ending stream of ideas, this may be helpful for you!</p>
<p>I'll walk you through all the pieces of this project in the order that I built them. I've managed to put together a good boilerplate for a project, and look forward to continuing to contribute to its growth!</p>
<h2 id="heading-readme">README 📖</h2>
<p>Please create a readme file. 🙏</p>
<p>You probably know it, but don't forget to do this. It is the best place to showcase your project. And GitHub will automatically prompt you to initiate the repository with a readme, so there's no excuse to leave this out.</p>
<p>Check out <a target="_blank" href="https://readme.so/">readme.so</a> by <a target="_blank" href="https://twitter.com/katherinecodes">Katherine Peterson</a>: she's made an incredibly easy to use browser-based tool. It lets you to create a clearly sectioned readme using a drag and drop template editor that you can then copy and paste into your repository.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661887480616/X5an1_gRO.png" alt="readmeso.png" class="image--center mx-auto" /></p>
<p>If you don't have enough info at the beginning, make the readme anyway and come back later to fill it in. </p>
<p>Here's what mine looked like initially; it wasn't perfect, but it had some of the bones of what the project would become. I copied and pasted all of it from readme.so except for the gif. I actually designed that graphic and made it spin on giphy so I could have something animated quickly on the readme.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661889742463/u0MLIhQw3T.png" alt="unmove alpha readme.png" class="image--center mx-auto" /></p>
<p>Bonus: In the current, updated <a target="_blank" href="https://github.com/sieis/unmove#readme">readme</a> I used an svg and edited the inline <code>&lt;style&gt;</code> to add the rotation animation. It made it a lot cleaner than the giphy version. 😊</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661890055266/mY8gcS9Tj.gif" alt="sacred spinner.gif" class="image--center mx-auto" /></p>
<h2 id="heading-license">License 🎓</h2>
<p>Another easy addition. You'll be prompted for this too. MIT License is an easy way to make the project completely open sourced. Go <a target="_blank" href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository#choosing-the-right-license">here</a> to check out how to choose a license for your repository.</p>
<p>I simply selected MIT for <a target="_blank" href="https://github.com/sieis/unmove/blob/main/LICENSE">unMove's license</a> and am good to go without any further edits: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661890165536/ztAbvljq_.png" alt="license.png" class="image--center mx-auto" /></p>
<h2 id="heading-descriptions-andampandamp-tags">Descriptions &amp;&amp; Tags</h2>
<p>Don't neglect the right sidebar. Here's where you should add a description for your project as well as tags. We'll come back later when we create a new release too 😊.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661890318689/sldhhRhEh.png" alt="tags.png" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661890360550/Fuj-3Dr5L.png" alt="image.png" /></p>
<h2 id="heading-issues-andampandamp-projects">Issues &amp;&amp; Projects</h2>
<p>I've recently been using Trello for <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-trello-to-manage-freelance-web-projects/">freelance project management</a>, and it's been a great solution to keep me and my client on task.</p>
<p>For this project, though, we'll use the <a target="_blank" href="https://github.com/features/issues">built in capabilities from GitHub</a>. 🙌 </p>
<p>GitHub projects allows you to create board or table-view task lists for your project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661890462075/9SmL7y3m9.png" alt="github projects.png" class="image--center mx-auto" /></p>
<p>Here's what they'll look like when you create some:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661890584977/WqBuWofWk.png" alt="image.png" class="image--center mx-auto" /></p>
<p>And you can convert them to Issues so they show up in your Issues Tab for assignment to team members 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661890652037/XRiy0Gakr.png" alt="convert.png" class="image--center mx-auto" /></p>
<p>Alternatively, you can create an issue from the Issues tab and then assign it to a Project 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661971635139/YfqwVlI8U.png" alt="github-issues-edit.png" class="image--center mx-auto" /></p>
<p>Since this project consists of just me, I'm assigning everything to myself, but I'm also going through the small step of adding labels like enhancement, feature, good first issue, etc.</p>
<p>Even though these first small issues don't require much documentation, I want to get in the habit of creating well documented comments and reviewing the changes before merging into main.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661971704531/Sx8NtK3R9.png" alt="commenting issues.png" class="image--center mx-auto" /></p>
<p>You don't need to have grand plans or incredible features listed here. Simple things are fine. It's important to simply set good habits of documentation, assignments and code review.</p>
<h2 id="heading-branches-main-vs-other">Branches: Main vs Other</h2>
<p>So I've known branches were a thing for a while, but I've never bothered using one in any of my personal projects.</p>
<p>Turns out they're a real good idea (shocker), and a real helpful tool even on small projects. I'll use development branches in this project any time I make changes to the codebase. This way I can check that everything is working without effecting the current (already working) main branch.</p>
<p>Simply click the branches tab and type the name of the branch you'd like to create.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661971821059/j8XYW-jUb.jpg" alt="branches.jpg" class="image--center mx-auto" /></p>
<p>Now once you clone the repo to work locally, you can <code>$git checkout develop</code> to switch to the develop branch. This will allow for changes to be tested without affecting the main branch.</p>
<p>This is a new habit for me, and I am happy to be leveling up my developing processes. </p>
<h3 id="heading-pull-requests-briefly">Pull Requests, Briefly</h3>
<p>More often, though, after forking and cloning the repo, you'll want to make your own branch locally:</p>
<pre><code class="lang-bash"><span class="hljs-variable">$git</span> checkout -b &lt;new-branch-name&gt;
</code></pre>
<p>Then, when you push this <code>&lt;new-branch-name&gt;</code> to your forked repo, you'll have the option to open a pull request against the main branch.</p>
<h2 id="heading-launch">Launch</h2>
<p>I suppose we need to make something functional before launching. At the very least, I want this library to have a couple CSS animations available immediately. I've developed these in a develop branch to test out, and once satisfied, I merged those changes to the main branch.</p>
<pre><code class="lang-bash"><span class="hljs-variable">$git</span> checkout main
<span class="hljs-variable">$git</span> merge &lt;new-branch-name&gt;
</code></pre>
<h2 id="heading-versioning-andampandamp-git-tags">Versioning &amp;&amp; Git Tags</h2>
<p>Here's another thing I've known about for a while, but only actually used once before. I want to launch with an actual version number. In this case, v0.1.0-alpha. In order to do this, we'll need two things:</p>
<ol>
<li><a target="_blank" href="https://git-scm.com/book/en/v2/Git-Basics-Tagging">Git Tag</a></li>
<li><a target="_blank" href="https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository">GitHub Release Version</a></li>
</ol>
<p>The release version won't work unless you have a git tag in your codebase, so we'll do that first.</p>
<p>Git Tags denote spots in your commit history where big things happen...like a version release! </p>
<p>This will show your commit history:</p>
<pre><code class="lang-bash"><span class="hljs-variable">$git</span> <span class="hljs-built_in">log</span> --pretty=oneline
</code></pre>
<p>This will tag the spot you choose in your history. Replace the hashmarks with the first digits in the checksum from your commit history.</p>
<pre><code class="lang-bash"><span class="hljs-variable">$git</span> tag -a v0.1.0-alpha <span class="hljs-comment">#####</span>
</code></pre>
<p>Pushing this change works differently too. If you run <code>$git status</code> everything will look like it's up to date.
Instead, run <code>$git push origin v0.1.0-alpha</code> and we're good to go!</p>
<p>Now, back in GitHub, we can make a version release by clicking <em>Create a new release</em> in the righthand side bar.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661972410340/VvJ4jLrB-.jpg" alt="release.jpg" class="image--center mx-auto" /></p>
<h2 id="heading-website">Website</h2>
<p>Building a website is usually the second order of business...right in line with purchasing a sweet domain. I've focused first on building up a framework of good processes.</p>
<p>But the time has now come. We do indeed need a website. It doesn't have to be much, but it does have to be clean and concise.</p>
<p>Enter: Netlify. Or a host of other...hosts. 😁 Use whatever you're comfortable with, but I have been very impressed using Netlify especially for personal projects. It's allowed me to hone my git and GitHub skills and incorporate that into the development workflow. </p>
<p>I love how deployment works: push a change to my GitHub repository, and it deploys/updates automatically.</p>
<p>Bonus: Netlify has an open source plan if you meet certain criteria. <a target="_blank" href="https://www.netlify.com/legal/open-source-policy/">Here's the link</a> to that, and the basics are below:</p>
<ol>
<li>Include open source compatible license ✅</li>
<li>Have a code of conduct</li>
<li>Link to Netlify ✅</li>
<li>Cannot be a commercial project ✅</li>
</ol>
<p>As you can see, my site (https://unmove.netlify.app/) is currently a bare bones display of the first few animation classes available.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661972612339/1g48Cw9SE.png" alt="image.png" class="image--center mx-auto" /></p>
<h2 id="heading-jsdelivr-cdn">JSdelivr CDN</h2>
<p>JSdelivr is, simply put, a free CDN for Open Source. </p>
<p>Content Delivery Networks (CDN) help deliver content <strong>fast</strong> by using globally distributed servers and help cache content at the network edge. 🚀</p>
<p>CDNs are widely used, and it will allow our users the option of using our stylesheet via a JSdelivr link rather than downloading it into their project.</p>
<p>And since our project is on GitHub, we can simply load any release, commit or branch by using this link template:</p>
<pre><code>// <span class="hljs-keyword">load</span> <span class="hljs-keyword">any</span> GitHub <span class="hljs-keyword">release</span>, <span class="hljs-keyword">commit</span>, <span class="hljs-keyword">or</span> branch
// note: we recommend <span class="hljs-keyword">using</span> npm <span class="hljs-keyword">for</span> projects that support it
https://cdn.jsdelivr.net/gh/<span class="hljs-keyword">user</span>/repo@<span class="hljs-keyword">version</span>/<span class="hljs-keyword">file</span>
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661972890610/R3rFg-VxK.png" alt="image.png" class="image--center mx-auto" /></p>
<p>I updated our README file's <a target="_blank" href="https://github.com/sieis/unmove#quick-install-instructions">Quick Install Instructions</a> to reflect these changes once I had the first working CSS distribution.</p>
<p>In our case, adding the following to the html page's <code>&lt;head&gt;</code> allows us to use unMove! 😃</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/gh/sieis/unmove@main/dist/unmove.css"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>unmove demo<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-css">CSS</h2>
<p>What's going on? 1300 words in and we're just now at the actual product. We were writing a CSS library after all, remember!?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661976006896/-gSWdFmMv.gif" alt="whatsgoingon.gif" class="image--center mx-auto" /></p>
<p>Here's the thing: code is important, but getting a project and/or product organized can be just as cumbersome a process as writing the code. And if it's not done up front, if standards aren't set, it's liable to get real messy real quickly. 🤔</p>
<p>So, yes, here we are at the end...and just beginning to review the code.</p>
<p>But so much is in place now! And hopefully, you've picked up some knowledge that you can take into your own projects.</p>
<p>One of the reasons I picked a CSS animation library was precisely so it wouldn't be too heavily code-intensive. I wanted a quick way to get a working MVP into the world.</p>
<p>And we did exactly that.</p>
<p><a target="_blank" href="https://cdn.jsdelivr.net/gh/sieis/unmove@main/dist/unmove.css">Here is a link to the CSS from JSdelivr</a>. Pull this up in another tab to examine the code and I'll walk through the classes below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661974776539/htKWefPU_.png" alt="image.png" class="image--center mx-auto" /></p>
<h3 id="heading-spinning-animation">Spinning Animation</h3>
<p>Here's a look at the Spinner Section from <a target="_blank" href="https://unmove.netlify.app/">unMove's example page</a>:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-center container border border-dark border-5 rounded-3 p-5 my-2"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Spinner Animation<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-2  text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>An infinite spin. Add the class <span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>um-spin<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span> to anything you want to spin
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"height:200px;width:200px"</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"my-5 mx-auto spin border border-dark border-5 rounded-5 shadow-lg um-spin"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-2  text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Crank up the speed. Add the class <span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>um-spin-fast<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span> to go twice as fast.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"height:200px;width:200px"</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"my-5 mx-auto border border-dark border-5 rounded-5 shadow-lg um-spin-fast"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-2  text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Chill out. Add the class <span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>um-spin-slow<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span> to calm down and go real slow.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"height:200px;width:200px"</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"my-5 mx-auto border border-dark border-5 rounded-5 shadow-lg um-spin-slow"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>And the CSS:</p>
<pre><code class="lang-css">
<span class="hljs-comment">/* Infinite Spinning Animation */</span>
<span class="hljs-selector-class">.um-spin</span> {
    <span class="hljs-attribute">animation</span>: spin <span class="hljs-number">3s</span> linear infinite;
}
<span class="hljs-selector-class">.um-spin-fast</span> {
    <span class="hljs-attribute">animation</span>: spin <span class="hljs-number">1.5s</span> linear infinite;
}
<span class="hljs-selector-class">.um-spin-slow</span> {
    <span class="hljs-attribute">animation</span>: spin <span class="hljs-number">6s</span> linear infinite;
}
<span class="hljs-keyword">@keyframes</span> spin {
    <span class="hljs-selector-tag">from</span> {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">0</span>);
    }
    <span class="hljs-selector-tag">to</span> {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">360deg</span>);
    }
}
</code></pre>
<p>This is fairly simple. We are using <code>transform: rotate()</code> to rotate the 200px square divs. All that the <code>.um-spin</code> classes do is apply the animation <code>spin</code>. One rotation will take either 1.5sec(<code>.um-spin-slow</code>), 3sec(<code>.um-spin</code>) or 6sec (<code>.um-spin-fast</code>). And then <code>infinite</code> simple tells it not to stop.</p>
<p>The keyframes are very simple. We go from 0 degrees to 360 degrees.</p>
<h3 id="heading-news-ticker-animation">News Ticker Animation</h3>
<p>Many of the solutions for the news ticker style animation involved writing JavaScript. I've got nothing against that, but I simply wanted a pure CSS version. After looking at a few CSS builds, I modeled unMove's off of a solution I found on <a target="_blank" href="https://nolte.io/blog/a-continuous-image-ticker-built-with-pure-css/">Nolte here</a>.</p>
<p>Here is the html from the example site:</p>
<pre><code class="lang-html">        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-wrap"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-tick"</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- free handdrawn vectors used from https://goodstuffnononsense.com/ --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/earth@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/saturn@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/mars@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/jupiter@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/venus@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/uranus@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/neptune@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/earth@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/saturn@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/mars@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/jupiter@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/venus@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/uranus@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./img/neptune@4x.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"um-news-item"</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>and the News Ticker CSS:</p>
<pre><code class="lang-css">
<span class="hljs-comment">/* Scrolling News Ticker Animation */</span>
<span class="hljs-selector-class">.um-news-wrap</span> {
    <span class="hljs-attribute">overflow</span>: hidden;
}
<span class="hljs-selector-class">.um-news-tick</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">animation</span>: news-ticker <span class="hljs-number">6s</span> linear infinite;
}
<span class="hljs-selector-class">.um-news-tick</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">animation-play-state</span>: paused;
}
<span class="hljs-selector-class">.um-news-tick-no-hover</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">animation</span>: news-ticker <span class="hljs-number">6s</span> linear infinite;
}
<span class="hljs-selector-class">.um-news-tick-reverse</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: flex-end;
    <span class="hljs-attribute">animation</span>: news-ticker-reverse <span class="hljs-number">6s</span> linear infinite
}
<span class="hljs-selector-class">.um-news-tick-reverse-no-hover</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: flex-end;
    <span class="hljs-attribute">animation</span>: news-ticker-reverse <span class="hljs-number">6s</span> linear infinite
}
<span class="hljs-selector-class">.um-news-tick-reverse</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">animation-play-state</span>: paused;
}
<span class="hljs-selector-class">.um-news-item</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">16rem</span>;
    <span class="hljs-comment">/* min-width helps svgs behave properly in the scroll without having to manually set their height or width */</span>
    <span class="hljs-attribute">min-width</span>: <span class="hljs-number">16rem</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;
}
<span class="hljs-keyword">@keyframes</span> news-ticker {
    0% {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate3d</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    }
    100% {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate3d</span>(-<span class="hljs-number">126rem</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    }
}
<span class="hljs-keyword">@keyframes</span> news-ticker-reverse {
    0% {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate3d</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    }
    100% {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate3d</span>(<span class="hljs-number">126rem</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    }
</code></pre>
<p>It is not as elegant as a JavaScript solution, but it works for our purposes. The first div with <code>class=um-news-wrap</code> sets 'overflow:hidden'. This hides everything outside the edges of the div.</p>
<p>The second div is what will move from side to side. This is the actual ticker. It's going to be twice as wide as the container because we're putting two copies of the items in it. This is so there's no stuttering in the animation. </p>
<p>As the first set of icons goes from right to left, the second set will then start scrolling across the viewable area. At the end of the animation duration, the first set will be ready to display immediately as the second set is scrolling off the viewable area. </p>
<p>Without the duplication, halfway through the animation duration, there would be nothing scrolling across and then at the start of the next iteration, the original strip of images would pop into place. Below is a side by side example. The seamless setup with duplicate images is on the left, and the non-duplicate version is on the right 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662057975749/qYaDkEmJ4.gif" alt="comparison.gif" class="image--center mx-auto" /></p>
<p>Setting that overflow to hidden lets us manipulate the div in a way that looks seamless.</p>
<p>You'll notice that <code>.um-news-item</code> sets width and margin for the items. This is the hacky part. We are hardcoding widths both here and in the keyframe transform in order for the scrolling to work as intended. This only works with 7 items in the example because that's how I've calculated the widths. </p>
<p>16rem * 7 items + 2rem * 7 items = 126rem total</p>
<p>So, yeah, JS would be simpler. But this is a CSS project, and we like hacky things! 😂</p>
<h2 id="heading-contributions">Contributions</h2>
<p>Last but not least, the beauty and the joy of open-source is the community. I've written up a <a target="_blank" href="https://github.com/sieis/unmove/blob/main/CONTRIBUTING.md">CONTRIBUTING guidelines file</a>, and welcome your input on this project.</p>
<p>This was a big motivation for taking on this project - I want to see what it's like maintaining a public, open-source project. </p>
<p>So, please hop over to the <a target="_blank" href="https://github.com/sieis/unmove">repo</a>, give it a star, make a fork, read the guidelines, check out the issues, and contribute away! 🙌</p>
<h2 id="heading-thank-you">Thank You</h2>
<p>I appreciate you reading through this, and hope that you learned something helpful! If you did, let me know! </p>
<p>You can find me on Twitter, and I'd love to say hey 👋 https://twitter.com/EamonnCottrell</p>
]]></content:encoded></item><item><title><![CDATA[📚 Two Inspiring Technology Companies 💻]]></title><description><![CDATA[No other online platforms have had as big an impact in my life as Khan Academy && freeCodeCamp. I'm forever grateful to the wealth of knowledge they provide, and their missions:

Our mission is to provide a free, world‑class education for anyone, any...]]></description><link>https://blog.eamonncottrell.com/two-inspiring-technology-companies</link><guid isPermaLink="true">https://blog.eamonncottrell.com/two-inspiring-technology-companies</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[#week2]]></category><category><![CDATA[khan academy]]></category><category><![CDATA[freeCodeCamp.org]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Sat, 27 Aug 2022 17:24:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1661617709918/lElBplKBC.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>No other online platforms have had as big an impact in my life as <a target="_blank" href="https://www.khanacademy.org/">Khan Academy</a> &amp;&amp; <a target="_blank" href="https://www.freecodecamp.org/">freeCodeCamp</a>. I'm forever grateful to the wealth of knowledge they provide, and their missions:</p>
<blockquote>
<p>Our mission is to provide a free, world‑class education for anyone, anywhere.</p>
</blockquote>
<p>-<a target="_blank" href="https://www.khanacademy.org/about">Khan Academy</a></p>
<blockquote>
<p>freeCodeCamp's mission is to help people learn to code for free.</p>
</blockquote>
<p>-<a target="_blank" href="https://www.freecodecamp.org/news/freecodecamp-press-kit">freeCodeCamp</a></p>
<h2 id="heading-khan-academy">Khan Academy</h2>
<p>Khan Academy is an online learning platform offering practice exercises, videos and more. It is primarily for K-14 material as well as free tools for parents and instructors.</p>
<blockquote>
<p>It's staggering to me the amount of material present on the platform, and the quality with which it's presented.</p>
</blockquote>
<p>Khan Academy was <a target="_blank" href="https://support.khanacademy.org/hc/en-us/articles/202483180-What-is-the-history-of-Khan-Academy-">founded by Sal Khan</a> after he began tutoring family members online. He started posting his videos on YouTube in 2006, and incorporated as a non-profit in 2008.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661456105217/A3XPz57fG.png" alt="KA_Wordmark_RGB_Dark.png" class="image--center mx-auto" /></p>
<p>The pre-funding early days of Khan Academy ran parallel to my return to college in the late 00's. Shortly after significant grant money began to bolster the platform, I began to use it extensively.</p>
<p>I took my first Calculus course in the spring of 2012, and it was a system-shock experience. I quickly realized I'd have to relearn substantial chunks of trigonometry in tandem with the Calculus work to keep up with our professor.</p>
<p>Dr. Shive, like many college professors at the time, recommended Khan Academy to any (all) of us who were struggling to keep pace with his instruction.</p>
<p>I spent many hours on Khan Academy relearning the trig and reinforcing the Calculus that semester.</p>
<p>And you know what happened?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661453729478/DRYf-gBun.gif" alt="light.gif" class="image--center mx-auto" /></p>
<ol>
<li>I fell in love with both subjects</li>
<li>I fell in love with learning, in general</li>
<li>I realized the potential for educational content online</li>
</ol>
<h3 id="heading-new-school">🏫 New School</h3>
<p>I'd had some wonderful professors through the years. Having returned for my first degree at age 25, I was more receptive to subjects and instruction than before.</p>
<p>Profs. Bishop, Switzer, Kelly, Prenshaw, Fiser, Culpepper, Brister and Parakkal were a few standouts that had life-long impacts on my education and my career afterward.</p>
<p>Add Sal Khan to the list. Particularly because of his early instructional videos in mathematics and physics. I was so impressed with the quality of content he was producing even before the videos themselves became as polished as they are today.</p>
<p>As a result of my experiences that final semester during my business undergraduate degree, my love of learning was renewed. And my love for math and science was birthed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661458492215/oc0aVaukw.gif" alt="justwannalearn.gif" class="image--center mx-auto" /></p>
<p>I went back through two years of an engineering degree two years after graduating, and my love of web development and technology today stems from those formative years.</p>
<p><strong>Khan Academy inspires me to</strong></p>
<ol>
<li>Continue learning</li>
<li>Help others in their journey</li>
<li>Leverage the power of the internet to do good</li>
</ol>
<h2 id="heading-freecodecamp">freeCodeCamp</h2>
<p>Not long after my experiences with Khan Academy, freeCodeCamp was founded in 2014. This was right when I returned to college in an engineering program. </p>
<p>freeCodeCamp is a free online coding bootcamp with challenges, projects and verified certifications. In addition to the curriculum, there are hundreds of tutorials and videos available on their news blog and YouTube channel. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661456472840/PQPL9lRpJ.jpg" alt="fcc_primary_large.jpg" class="image--center mx-auto" /></p>
<p>I dabbled in Python and Java over the course of two years. I found freeCodeCamp myself in 2016 after my first son was born and I chose to exit formal university in favor of self-study.</p>
<p>I also used Codecademy and edX a bit, but was always drawn to freeCodeCamp because of its crystal clear mission.</p>
<p>The for-profit market is very crowded, especially as bootcamps have become a mainstream, albeit expensive, route to learn many specific skills. They can be a useful tool, but I couldn't help but feel that the value proposition was skewed. </p>
<p>Through a combination of listening to <a target="_blank" href="https://changelog.com/podcast/431">Quincy's personal story</a> and motivations for beginning freeCodeCamp and my own experience working through a bit of the curriculum, I could tell that it was a special platform.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661458575523/Til6FZPKl.gif" alt="special.gif" class="image--center mx-auto" /></p>
<p>The impetus for growth is placed on the individual. And all the certifications are project-based. I was able to go through the tutorial sections just like I have come to expect from any number of sites. </p>
<p>But rather than getting stuck in tutorial hell, the projects are just difficult enough to force you to go beyond the lessons into external documentation to solve.</p>
<h3 id="heading-deeper-learning">Deeper Learning</h3>
<p>Being pushed outside my comfort zone is a healthy thing. I've recently completed the <a target="_blank" href="https://www.freecodecamp.org/learn/relational-database/">Relational Database certification</a>, and it was very challenging at times.</p>
<p>It is still in beta as of this writing (08/2022), and was available either as a <a target="_blank" href="https://www.freecodecamp.org/news/how-to-run-freecodecamps-relational-databases-curriculum-using-docker-vscode-and-coderoad/">VSCode + Docker</a> experience or in a <a target="_blank" href="https://www.freecodecamp.org/learn/relational-database/">web-based course</a> delivered via integration with <a target="_blank" href="https://codeally.io/">CodeAlly.</a></p>
<p>This caused all kinds of problems. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661619740108/TEMQGNplj.gif" alt="laugh.gif" /></p>
<p>But you know what? </p>
<p>It was incredibly impactful. The problems I encountered deepened my learning when I was able to set frustrations aside and come to grips with the fact that web development and computer programming is wrought with bugs and problem solving.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661458438962/QPYxPohvX.gif" alt="learning.gif" class="image--center mx-auto" /></p>
<p>It was fantastic practice in finding solutions rather than having them handed to me.</p>
<p>This continues to be my experience with freeCodeCamp as a whole. <strong>It inspires me to</strong></p>
<ol>
<li>Not give up in the face of seemingly impossible problems</li>
<li>Pay it forward as a contributor to the news blog</li>
<li>Be mission-based in my online ventures</li>
</ol>
<h2 id="heading-online-education">Online Education</h2>
<p>It's no secret that it's easy to start an online course, and it's often even easier to quit one.</p>
<p>My own start-stop relationship with both of these platforms is testament to that. But, I've kept coming back. </p>
<p>I've "wanted to learn how to code" since about 2013 when I <a target="_blank" href="https://share.transistor.fm/s/80d46ef6">first wrote some custom Google Apps Scripts</a> for a spreadsheet at work.</p>
<p>But it wasn't until about 2019 that I got really serious and began to make progress past the introduction-level.</p>
<p>Don't give up.</p>
<p>Even when you give up!</p>
<p>Here's a screenshot of my first GitHub commit. It was in December 2012. My next commit was in July of 2019! </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661458712692/8iIpCx4hy.png" alt="image.png" class="image--center mx-auto" /></p>
<p>That's right, years went by. I'm pretty sure I started Harvard's CS50 like 5 times.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661458806120/PAYLiCjh5.gif" alt="what.gif" class="image--center mx-auto" /></p>
<p>I did some coding in there, sure, but I largely was focused on my career, my family and traditional education.</p>
<p>But I never shut the door. And I'm so glad I didn't. These resources were there waiting for me when I came back.</p>
<p>Services like Khan Academy and freeCodeCamp always have open doors to free learning for anyone, anywhere!</p>
<p>Seeing how two people leveraged their own unique skillsets through technology to make the world a better place and to help millions of learners continues to inspire me.</p>
<h2 id="heading-future-hope">Future Hope</h2>
<p>I hope that I can pass on what has been freely given to me. It's one of the reasons that I've begun writing about my journey as a developer this year. I want to be able to help others along this journey by sharing my own experiences.</p>
<p>And I hope, whatever my future holds, that I can contribute to and build impactful tools and resources that serve others well!</p>
<p>Thanks for reading.</p>
<p>Come say hey 👋 over on Twitter: https://twitter.com/EamonnCottrell</p>
]]></content:encoded></item><item><title><![CDATA[Journey to the Center of the Code]]></title><description><![CDATA[Beginning and re-beginning
My first experiences with coding were in middle school. I didn't quite realize it at the time, but I was writing rudimentary programs on our graphing calculators and having a ball programing conditional statements in a choo...]]></description><link>https://blog.eamonncottrell.com/journey-to-the-center-of-the-code</link><guid isPermaLink="true">https://blog.eamonncottrell.com/journey-to-the-center-of-the-code</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[week1]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Mon, 22 Aug 2022 15:29:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1661182136580/f4VUIXCUM.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-beginning-and-re-beginning">Beginning and re-beginning</h2>
<p>My first experiences with coding were in middle school. I didn't quite realize it at the time, but I was writing rudimentary programs on our graphing calculators and having a ball programing conditional statements in a choose-your-own-adventure fashion.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661179762464/EKW-z5CVB.png" alt="drug wars game ti-83 screenshot" class="image--center mx-auto" /></p>
<p>Nothing really stuck, though. I had a penchant for accounting and numbers came easily, but I didn't invest into my math and engineering interests until much later.</p>
<p>My first college major was piano. That didn't last long. I took a long sabbatical during the truly pretentious days of the early 00's coffee scene to work as a barista and fantasize about owning my cafe.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661179958497/C_9LzQoXi.gif" alt="barista.gif" class="image--center mx-auto" /></p>
<p>When the froth settled, I returned to college. This time taking an assortment of histories, creative writing, statistics and anything else that seemed halfway interesting. Naturally, this leads to one of two places: English teachers and business majors.</p>
<p>I chose the later and it was not until the final semester of my senior year that I began second guessing this seemingly logical and well-rounded pathway.</p>
<h2 id="heading-derivatives-and-starting-over">Derivatives and starting over</h2>
<p>I'd put off Calculus until the absolute last semester, and once the class began, it became apparent that a) I was going to have to re-learn trigonometry, and b) I really liked math.</p>
<p>So, while my classmates complained about the rigor with which our professor held us to, I soaked up the integrals and the sounds of chalk-on-slate while supplementing his lessons with Khan Academy material to fill in all the trigonometric blanks in my head.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661180032766/zw1JpOTEP.gif" alt="math.gif" class="image--center mx-auto" /></p>
<p>This taught me another two things:</p>
<ol>
<li>I really missed the boat when picking business as my major</li>
<li>There was a world of free educational material on the internet </li>
</ol>
<h2 id="heading-the-real-world">The real world</h2>
<p>My business degree gave me access to a "real job" at the company I had worked at through college. After a couple years, though, I began to get curious about other options. </p>
<p>I took a Calculus II summer class. (like you do)</p>
<p>I talked to a friend about cyber security.</p>
<p>I took one of the first MOOC's over at edX: an intro to computer programming with Python from MITx.</p>
<p>I decided I was supposed to be an engineer.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661180161953/l2NU1r_Oc.gif" alt="engineer.gif" class="image--center mx-auto" /></p>
<h2 id="heading-school-and-kids">School and kids 👶</h2>
<p>Another degree seemed the logical step if I was going to hop careers into engineering. So I went back part time while working and took the first two years' worth of an engineering degree. You know: all the easy stuff...Cal III &amp; IV, Physics, Chemistry, Differential Equations, Linear Algebra, Engineering Mechanics...oh, and a Java course that was absolutely dreadful.</p>
<p>I knew engineers designed and built stuff. And I knew they got paid well. I was loving learning, and began to try and figure out what branch of engineering I would go into.</p>
<p>I decided Civil. Then Computer. Then Electrical. Then Nuclear. Then Civil again...maybe Environmental. Then Software.</p>
<p>I really didn't have a clue, and I was continuing to manage the chain of cafes I had been employed at for years. My previous pipe dream of ownership seemed like a possible future. But did I want that any more?</p>
<p>My wife and I had our first child 👶 during the final semester of that two year pre-engineering degree in the spring of 2016, and I began to take a hard look at the options I had:</p>
<ol>
<li>Continue in a state college engineering program-probably in Knoxville, TN as we were about to move.</li>
<li>Transfer into ASU's online program</li>
<li>Pivot into either software development or a hybrid path</li>
</ol>
<p>I was pretty sold on a traditional degree being valuable to my situation. I enrolled in ASU, but at the same time got a promotion at work with some solid assurances for my family. I pulled out of ASU in order to move from Mississippi to Tennessee, and began to scout out programs at UT.</p>
<p>Over the course of all this time, I'd tinkered online with Codecademy, freeCodeCamp, Coursera and edX. I'd learned enough of HTML, CSS, and basic programming concepts with Python, Java and Javascript to understand the basics but I wasn't really sold on going the software route yet.</p>
<p>Opportunities continued to open up at my day job, and we had our second child 👶👶 after moving to Knoxville. I was unable to devote my full time to pursuing the last two years' of a degree, and I was unable to start over in a new career. </p>
<p>I felt pretty stuck: I really wanted to pivot, but it just didn't seem possible. I looked at a masters program in Data Analytics, but the timing and logistics didn't work out for it either.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661180496707/iTYAD_dly.gif" alt="stuck.gif" class="image--center mx-auto" /></p>
<h2 id="heading-web-development">Web Development</h2>
<p>Every six months or so, I'd dabble back in <a target="_blank" href="https://www.freecodecamp.org/sieis">freeCodeCamp</a> or Udemy and get my creative juices flowing for web development. I stumbled across the <a target="_blank" href="https://www.indiehackers.com/">Indie Hackers</a> community. I began listening to entrepreneurial podcasts. I became intrigued with a new idea: web development as a pathway into engineering and/or entrepreneurship. </p>
<p>Maybe I wouldn't have to wait until timing permitted me to return to school.</p>
<p>When the 2020 pandemic hit, I dove back into freeCodeCamp with the expressed intent of honing my web development skills while I continued to manage my business. I began to hear many stories of people successfully learning on the side and eventually landing jobs or starting companies.</p>
<p>We had another baby 👶👶👶 in 2020 and were all-in on raising a family. My job was fully remote, and I had enough margin to study when and where I could on the side. My previous formal education pathway and traditional engineering school slipped further into the background. </p>
<p>I'd still love to go through a masters program some day, but it doesn't serve an explicit purpose right now.</p>
<p>I began to actually put together <a target="_blank" href="https://www.eamonncottrell.com/projects/">some small projects</a>, I began to find ways to automate spreadsheets at work using code, I began to <a target="_blank" href="https://blog.eamonncottrell.com/">document</a> some of my processes and progress.</p>
<p>I began to have a blast.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661192244296/B7hzhBbBX.gif" alt="havingablast.gif" class="image--center mx-auto" /></p>
<p>I really enjoyed the process of building things. I was fascinated by the stories of ordinary people building extraordinary things on the internet using little more than the skills I saw that were within my grasp.</p>
<p>Furthermore, I saw the opportunity to create <a target="_blank" href="https://eamonn.gumroad.com/">digital products</a> for a potential worldwide audience pool rather than relying on physical good in a fixed place and time.</p>
<h2 id="heading-keep-leaning-forward">Keep leaning forward 📈</h2>
<p>I'm in a unique situation. I have a great job with benefits that would be hard to replace in an entry level position. Throughout the course of the last two years, I've worked at optimizing my role there by automating what I can and then using any free time to continue to learn and develop my technical skill set. </p>
<p>Certifications are certainly a slippery slope. It's easy to get caught in a pattern of hopping from one to another, but I have found value in pursing a few, and ultimately using them as guide rails to learn specific things I'm interested in through projects.</p>
<p>freeCodeCamp is great at this: all their programs use project based learning techniques where you learn enough to build a user-story-specified project at the end. I just wrapped up their <a target="_blank" href="https://www.freecodecamp.org/learn/relational-database/">Relational Database</a> course and it was a wonderful learning experience in SQL and troubleshooting. </p>
<p>Google has <a target="_blank" href="https://grow.google/certificates/#?modal_active=none">several programs</a> they've launched over the past couple years, and I completed their <a target="_blank" href="https://www.credly.com/badges/c8f8a2ff-cc12-4737-a169-34036c7602b2/public_url">data analytics</a> one to sharpen my skills earlier this year.</p>
<p>Hashnode's hackathons have been a great tool as well, giving opportunities to flex and stretch skills while competing. I built a <a target="_blank" href="https://blog.eamonncottrell.com/infinite-memory">very small project</a> for my very first hackathon experience earlier this year.</p>
<p>In January of 2022, Quincy (founder of freeCodeCamp) <a target="_blank" href="https://www.freecodecamp.org/news/2022-become-a-dev-new-years-resolution-challenge/">posted a challenge</a> which I've just finished up. It included blogging as part of the developer path he laid out, and I've found that writing has been a great tool to utilize in my learning process.</p>
<h2 id="heading-my-222-day-version-of-100daysofcode">My 222 day version of #100DaysOfCode</h2>
<p>After accepting the "Become a Dev" challenge, I did what all good students do: I built a new spreadsheet. 😂 </p>
<p><a target="_blank" href="https://docs.google.com/spreadsheets/d/1oTtfOsWAH90mRvHYD0TWkdij9zSo7iZvTmtJpcjSVFo/edit#gid=0">Here's my track record</a> for the last 222 days' worth of that journey. And it's a testament to how long I can take to complete 100 days of coding!</p>
<p>To be fair, we had our fourth (and final? 👶👶👶👶) child in May, so I knew that I was going to likely be having forced breaks in my coding streak. </p>
<p>Even so, I managed to stick with it. And I think I've been able to maintain a healthy balance of work + family + exercise + learning.</p>
<p>Not plowing through to the point of exhaustion has actually been a critical lesson learned from these last 222 days. It's important to keep margin in my life. I need healthy relationships in and outside my family. I need to be reading books (fiction, not coding), listening to music, running, ect. </p>
<p>Those extra-curricular activities kept me from coding every single day, but they also kept me more sane and healthy than I would have otherwise been.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661181372003/1Mg6-Ib7r.gif" alt="insane.gif" class="image--center mx-auto" /></p>
<h2 id="heading-next">Next</h2>
<p>I don't know what's next, career-wise. But I know I'm going to be coding along the way.</p>
<p>I don't know if I'll truly seek another job or keep doing this on the side. But the more I code, the more intrigued I am with roles outside of just a "programmer".</p>
<p>Developer relations, product management, startup founder, data scientist and more all interest me. </p>
<p>I know this: I'm going to keep leaning forward and learning. </p>
<p>I'm currently writing on <a target="_blank" href="https://www.freecodecamp.org/news/author/eamonn/">Hashnode</a> and <a target="_blank" href="https://www.freecodecamp.org/news/author/eamonn/">freeCodeCamp</a>.</p>
<p>I've just released the v0.1.0-alpha of <a target="_blank" href="https://github.com/sieis/unmove">unMove</a>, a css animation library that I'm building to learn a few things:</p>
<ol>
<li>Proper use of GitHub, PR's and codebases</li>
<li>Maintaining an open source project</li>
<li>Structured project management</li>
<li>Collaboration with others</li>
</ol>
<p>As disparate as my journey to code has been so far, I'm truly excited to see where it takes me next, and I look forward to the doors that open...especially the ones I didn't realize were there to begin with!</p>
<p>Come say hey on Twitter. You can find me here: https://twitter.com/EamonnCottrell</p>
]]></content:encoded></item><item><title><![CDATA[Google Sheets Tutorial – How to Use Regex and VLOOKUP to Display Images from Google Drive]]></title><description><![CDATA[Originally published on freeCodeCamp.

Images make many things better. And Google Sheets is one of those things.
The easiest way to add an image to Google Sheets is to simply insert one into your sheet.
But if you have added many images this way, you...]]></description><link>https://blog.eamonncottrell.com/google-sheets-tutorial-how-to-use-regex-and-vlookup-to-display-images-from-google-drive</link><guid isPermaLink="true">https://blog.eamonncottrell.com/google-sheets-tutorial-how-to-use-regex-and-vlookup-to-display-images-from-google-drive</guid><category><![CDATA[google sheets]]></category><category><![CDATA[Regex]]></category><category><![CDATA[image]]></category><category><![CDATA[vlookup]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Mon, 15 Aug 2022 17:15:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583664899/YN5iOtZlm.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><a target="_blank" href="https://www.freecodecamp.org/news/google-sheets-use-regex-and-vlookup-to-display-images-from-google-drive/">Originally published on freeCodeCamp.</a></p>
</blockquote>
<p>Images make many things better. And Google Sheets is one of those things.</p>
<p>The easiest way to add an image to Google Sheets is to simply insert one into your sheet.</p>
<p>But if you have added many images this way, you'll quickly tire of the multiple clicks it takes to do so. Especially if you have to add images often, or if you have to add the same images to multiple sheets.</p>
<p>In this article, you'll learn how to add many images from their URLs that you can dynamically toggle between in a dropdown list. We'll cover:</p>
<ul>
<li>Data Validation for creating a dropdown list</li>
<li>Named Ranges to make formula references easier and cleaner</li>
<li>The VLOOKUP function to display the right image from the dropdown list</li>
<li>The REGEXEXTRACT function to extract a string from a URL (don't worry, it'll make sense 😉)</li>
<li>The IMAGE function to display the image from a URL address</li>
<li>We'll use the ampersand (&amp;) operator as well as regular expressions (Regex)</li>
<li>We'll also make our sheet look good by removing gridlines, changing the font, adding borders, colors, and a drop shadow effect behind tables</li>
</ul>
<h2 id="heading-how-to-setup-the-project">How to Setup the Project 📐</h2>
<p>You can follow along with the sheet I'm using for everything we'll discuss:</p>
<p>https://docs.google.com/spreadsheets/d/1rFU2gPy6rU8IKFDmsxKHYCf0KGVHkcumQ5O5QCf156M/edit?usp=sharing</p>
<p>Make a copy if you want to edit it yourself.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660582895178/mxl3Kw73G.png" alt="copy.png" class="image--center mx-auto" /></p>
<p>All cell and range references below will be from this sheet so you can easily look and see what I'm talking about.</p>
<p>I've also made a folder of images <a target="_blank" href="https://drive.google.com/drive/folders/1na_BdarFXheF5t6YssKY2qPfTEDLYlSF">here</a> that is publicly shared so all this works. You don't have to make a copy of this unless you just want to 😀.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660582951355/UK2E1pKBe.png" alt="visible.png" class="image--center mx-auto" /></p>
<h2 id="heading-how-to-use-named-ranges-in-google-sheets">How to Use Named Ranges in Google Sheets 📛</h2>
<p>Named ranges make life easier.</p>
<p>You don't have to use them, but it makes references in functions easier since you'll be writing the name of something instead of a sterile cell reference.</p>
<p>We'll use three of them:</p>
<ol>
<li><code>B4</code> = <code>itemSelect</code> This is the cell where our dropdown list will live.</li>
<li><code>B8:G13</code> = <code>pictureMatch</code> This is the range for our VLOOKUP function. It contains the names of the pictures we'll display followed by their respective URLs.</li>
<li><code>B8:B16</code> = <code>pictureName</code> This is the first column of the pictureMatch range for referencing just the names in our data validation cell.</li>
</ol>
<p>To create a named range, simply highlight the range, select Data -&gt; Named ranges from the toolbar, and name it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583020313/yDTJcPt2O.png" alt="named.png" class="image--center mx-auto" /></p>
<h2 id="heading-how-to-perform-data-validation">How to Perform Data Validation 📃</h2>
<p>We'll use data validation to create a dropdown list in B4. Same deal here – just highlight the cell (or range) and select Data -&gt; Data validation from the toolbar:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583042017/08sjRkQcE.png" alt="validation.png" class="image--center mx-auto" /></p>
<p>Select List from a range, and then <code>=pictureName</code> (because we named that range) for the range. Alternatively, you can declare the range explicitly.</p>
<p>There are additional options to configure if you want to change anything:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583143128/U6Tte-o85.png" alt="image-8.png" class="image--center mx-auto" /></p>
<p>If you select reject input, you can have a custom message pop up whenever an invalid choice is entered:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583161620/rmugzBaN3.png" alt="image-7.png" class="image--center mx-auto" /></p>
<blockquote>
<p>You might want to make your message more helpful than this one.</p>
</blockquote>
<h2 id="heading-how-to-use-vlookup">How to Use VLOOKUP 📊</h2>
<p>VLOOKUP is an incredibly useful function. It takes four arguments:</p>
<pre><code class="lang-javascript">=VLOOKUP(search_key, range, index, [is_sorted])

=VLOOKUP(itemSelect,pictureMatch,<span class="hljs-number">3</span>,<span class="hljs-number">0</span>)
</code></pre>
<p>We'll use <code>itemSelect</code> for our <code>search_key</code> and <code>pictureMatch</code> for the range because we want to find <code>itemSelect</code> in that range. Then the 3 for index gets the value in the third column in that range.</p>
<p>(It's 3 in our example because we merged the cells in columns B &amp; C for our formatting, but VLOOKUP still counts both of them).</p>
<p>Finally, the zero sets <code>is_sorted</code> to <code>FALSE</code>. Our data is not sorted, and we want an exact match.</p>
<h2 id="heading-how-to-use-regexextract">How to Use REGEXEXTRACT 💾</h2>
<p>It happened: I found a real world use for Regular Expressions. 😳</p>
<p><a target="_blank" href="https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/regular-expressions/">This section of freeCodeCamp's Javascript certification</a> was particularly confusing for me, and it was good to revisit a small portion of it here in the wild.</p>
<p>Because Google Drive is quirky, and we're sort of hacking a free option here, we need to alter the URLs to our images in order for the IMAGE function to work properly.</p>
<p><a target="_blank" href="https://stackoverflow.com/questions/60287504/how-display-images-from-google-drive-on-gsheet-cell">This</a> Stack Overflow answer was helpful for me.</p>
<p>We need to build a URL by taking this:</p>
<pre><code><span class="hljs-attribute">https</span>:<span class="hljs-comment">//drive.google.com/uc?export=download&amp;id=###</span>
</code></pre><p>and replacing the ### part at the end with the ID we extract with the <code>REGEXEXTRACT</code> function.</p>
<p>Looking at the URLs we copied over, we can see a pattern. Everything after the <code>/d/</code> and then before the next <code>/</code> is the ID.</p>
<p>Here's an example of one of our image URLs: https://drive.google.com/file/d/1IaO08gj3GWIUQDAnzKEob62Gcl87ufuN/view?usp=sharing</p>
<p>You can see this at work by itself in <code>B26</code> of the example spreadsheet as the function grabs everything between those two markers:</p>
<p><code>=REGEXEXTRACT(D9,".*/d/(.*)/")</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583400445/pmDmy1RTD.png" alt="image-9.png" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583416461/pdJrFxjyz.png" alt="image-10.png" class="image--center mx-auto" /></p>
<blockquote>
<p>This extracts everything between the /d/ and the /</p>
</blockquote>
<h2 id="heading-how-to-use-the-image-function">How to Use the IMAGE Function 📷</h2>
<p>Okay. We've got the disparate pieces figured out. I know the pieces fit. 🎵</p>
<p>Let's put them together.</p>
<p>All of our work was to get one cell ( <code>B4</code> ) to provide data to the <code>IMAGE</code> function.</p>
<p>Image takes one argument and three other optional ones:</p>
<pre><code><span class="hljs-selector-tag">IMAGE</span>(<span class="hljs-selector-tag">url</span>, <span class="hljs-selector-attr">[mode]</span>, <span class="hljs-selector-attr">[height]</span>, <span class="hljs-selector-attr">[width]</span>)
</code></pre><p>We build the URL by combining the required beginning of the URL which I've got in <code>J17</code> using the ampersand (&amp;) operator with our <code>REGEXEXTRACT</code> function. And within our <code>REGEXEXTRACT</code> function we use our <code>VLOOKUP</code> function to get the URL of whatever image we've selected in the <code>itemSelect</code> cell.</p>
<p>Whew.</p>
<p>But, cool, right!?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583479296/Ob56FXPu_.gif" alt="giphy.gif" class="image--center mx-auto" /></p>
<p>If you feel lost in a recursive nightmare, I encourage you to pull up the <a target="_blank" href="https://docs.google.com/spreadsheets/d/1rFU2gPy6rU8IKFDmsxKHYCf0KGVHkcumQ5O5QCf156M/edit#gid=0">example spreadsheet</a> and examine the parts of the function in <code>F4</code> piece by piece. 👍</p>
<h2 id="heading-how-to-format-your-sheet-ftw">How to Format Your Sheet FTW 💯</h2>
<p>These few details can turn up the volume 📣 on an otherwise mundane spreadsheet.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583522507/eMAxNJxHT.gif" alt="nin2.gif" class="image--center mx-auto" /></p>
<blockquote>
<p>This is likely the only place you'll find a NIN gif in an article about spreadsheets today.</p>
</blockquote>
<p>I love a hard drop shadow, and we can achieve this by manipulating the row and column sizes around a particular cell or range, using the merge cell option for our main range, and then using a fill color around the right side and bottom.</p>
<p>Click the lines between the column headers to drag and adjust the widths and heights of the columns and rows.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583543662/W_IG-zW42.png" alt="width.png" class="image--center mx-auto" /></p>
<p>Cells are the main appeal of spreadsheets, but in some cases hiding the gridlines can make your sheet standout. I opted for this approach in this project.</p>
<p>Select View-&gt;Show-&gt;Gridlines.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583557544/LUKU6yOgI.png" alt="gridlines.png" class="image--center mx-auto" /></p>
<p>As much as I appreciate Arial, I will typically opt out of the default font immediately.</p>
<p>Click the Font Dropdown in the Toolbar. It's usually smack dab in the middle:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583570006/jGCSWz7g8.png" alt="fonts.png" class="image--center mx-auto" /></p>
<p>And just choose whatever font you'd like.</p>
<p>There you have it!</p>
<h2 id="heading-thanks-for-reading">Thanks for Reading! 🙏</h2>
<p>Follow me on Twitter to see more content like this: https://twitter.com/EamonnCottrell</p>
<p>Thanks!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660583599929/vonZKfD6Y.gif" alt="thankyou.gif" class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[How to Check for Null in Javascript]]></title><description><![CDATA[Sometimes you've gotta check to make sure that nothing isn't actually...nothing. 😲❗❓
In JavaScript, null is a primitive type intentionally containing the value of null. Undefined is a primitive type and represents a variable you declare without init...]]></description><link>https://blog.eamonncottrell.com/how-to-check-for-null-in-javascript</link><guid isPermaLink="true">https://blog.eamonncottrell.com/how-to-check-for-null-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Null]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Mon, 01 Aug 2022 17:32:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659375089245/EIEilJfOX.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sometimes you've gotta check to make sure that nothing isn't actually...nothing. 😲❗❓</p>
<p>In JavaScript, null is a primitive type intentionally containing the value of null. Undefined is a primitive type and represents a variable you declare without initiating a value.</p>
<p>So, null is nothing and undefined is just missing something. 🤣</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659374582053/fNcavufhL.gif" alt="nothing.gif" /></p>
<p>Not super helpful, I know. Let's dive deeper.</p>
<h2 id="heading-how-to-define-null-and-undefined-values">How to Define Null and Undefined Values</h2>
<p>An example will help. Below we declare two variables. Let's keep it simple and use null and undefined to compare outcomes since they are sometimes confused because of their similarities.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//declaring two variables: one null and one undefined.</span>

<span class="hljs-keyword">let</span> leviticus = <span class="hljs-literal">null</span>;
<span class="hljs-comment">// leviticus is null</span>

<span class="hljs-keyword">let</span> dune;
<span class="hljs-comment">// dune is undefined</span>
</code></pre>
<p><code>leviticus</code> is intentionally without an object value (null). Whereas <code>dune</code> is declared, yet it's unintentionally missing a value (undefined).</p>
<h2 id="heading-how-to-check-for-null-with-typeof">How to Check for Null with typeof()</h2>
<p>You can check for null with the <code>typeof()</code> operator in JavaScript.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// typeof() will return 'object' when called on a null variable</span>

<span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">typeof</span>(leviticus))
<span class="hljs-comment">// object</span>

<span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">typeof</span>(dune))
<span class="hljs-comment">// undefined</span>
</code></pre>
<p>Curiously, if you check with <code>typeof()</code>, a null variable will return object. This is because of a <a target="_blank" href="https://www.turbinelabs.com/blog/the-odd-history-of-javascripts-null">historic bug</a> in JavaScript.</p>
<h2 id="heading-how-to-check-for-null-with-equality-operators">How to Check for Null with Equality Operators</h2>
<p>Another curiosity is that when you loosely check for equality using double equals <code>==</code>, <code>null</code> and <code>undefined</code> will return <code>true</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(leviticus == dune)
<span class="hljs-comment">// true</span>

<span class="hljs-built_in">console</span>.log(leviticus === dune)
<span class="hljs-comment">// false</span>

<span class="hljs-built_in">console</span>.log(leviticus == <span class="hljs-literal">null</span>)
<span class="hljs-comment">// true (but not as good a habit to use as strict equality shown in next example)</span>
</code></pre>
<p>But when you strictly check for equality using triple equals <code>===</code>, null and undefined will return <code>false</code>.</p>
<p>This is because null and undefined are both <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy">falsy</a> in JavaScript. Falsy means that a value is considered <code>false</code> when encountered in a Boolean (<code>true</code> or <code>false</code>) context.</p>
<p>JavaScript uses coercion to coerce values from one type to another in order to be able to use them in a Boolean context.</p>
<p>But by strictly checking for equality, you can see that they are in fact not equal.</p>
<p>##How to to Check for Null with Strict Equality</p>
<p>The best way to check for null is to use strict and explicit equality:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(leviticus === <span class="hljs-literal">null</span>)
<span class="hljs-comment">// true</span>

<span class="hljs-built_in">console</span>.log(dune === <span class="hljs-literal">null</span>)
<span class="hljs-comment">// false</span>
</code></pre>
<h2 id="heading-how-to-check-for-null-with-the-objectis-method">How to Check for Null with the <code>Object.is()</code> Method</h2>
<p>An equally foolproof way to check for null is to use the built-in <code>Object.is()</code> method:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Object</span>.is(leviticus, <span class="hljs-literal">null</span>)
<span class="hljs-comment">// true</span>

<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Object</span>.is(dune, <span class="hljs-literal">null</span>)
<span class="hljs-comment">// false</span>
</code></pre>
<h2 id="heading-summary">Summary</h2>
<ul>
<li><code>null</code> is a primitive type of a variable which evaluates falsy, has a <code>typeof()</code> of object, and is typically intentionally declared as <code>null</code></li>
<li><code>undefined</code> is a primitive type of a variable which evaluates falsy, has a typeof() of undefined, and represents a variable that is declared but missing an initial value.</li>
<li><code>null == undefined</code> evaluates as true because they are loosely equal.</li>
<li><code>null === undefined</code> evaluates as false because they are not, in fact, equal.</li>
<li><code>&lt;null_variable&gt; === null</code> is the<strong> best way</strong> to strictly check for null.</li>
<li><code>Object.is(&lt;null_variable&gt;,null)</code> is an <strong>equally reliable way</strong> to check for null.</li>
</ul>
<p>Take heart! As you've probably gathered, there are a plethora of brain teasers in the JavaScript ecosystem like this. But when you break it down, you can confidently understand them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659374974230/hO3LNZeVz.gif" alt="denzel.gif" class="image--center mx-auto" /></p>
<h2 id="heading-thanks-for-reading">Thanks for Reading!</h2>
<p>I hope this was a helpful breakdown for you. Keep coding, and keep leaning forward!</p>
<p>Come say hey to me over on Twitter: https://twitter.com/EamonnCottrell</p>
<p>Have a great one 👋.</p>
]]></content:encoded></item><item><title><![CDATA[JavaScript String to Date - Date Parsing in JS]]></title><description><![CDATA[This article originally appeared on freeCodeCamp here.

Dates are a pretty fundamental concept. We use them all the time. And computers use them all the time. But parsing dates using JavaScript can be a little...well, interesting.
In this article, we...]]></description><link>https://blog.eamonncottrell.com/javascript-string-to-date-date-parsing-in-js</link><guid isPermaLink="true">https://blog.eamonncottrell.com/javascript-string-to-date-date-parsing-in-js</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[date]]></category><category><![CDATA[string]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Tue, 05 Jul 2022 19:31:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657048880862/IArscQdlN.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This article originally appeared on freeCodeCamp <a target="_blank" href="https://www.freecodecamp.org/news/javascript-string-to-date-date-parsing-in-js/">here</a>.</p>
</blockquote>
<p>Dates are a pretty fundamental concept. We use them all the time. And computers use them all the time. But parsing dates using JavaScript can be a little...well, interesting.</p>
<p>In this article, we'll:</p>
<ol>
<li>Discuss date formatting</li>
<li>Turn a wee ol' string into a proper date object using JavaScript.</li>
<li>Parse a date object into a number</li>
<li>Show an alternative way to use arguments instead of strings to create a date object.</li>
</ol>
<p>Dates are tricky, but they're also incredibly helpful to use. And once you spend a little time going over the basics, your confidence will grow.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657048936615/LyUXb8JKz.gif" alt="date.gif" class="image--center mx-auto" /></p>
<h2 id="heading-what-is-the-date-format-in-javascript">What is the Date Format in JavaScript?</h2>
<p>ISO 8601, of course! This is the name of the international standard for communicating date and time data. We need to be using this format when dealing with dates in JavaScript</p>
<p>Here's what this format looks like. You're familiar with it already – it just combines a date and time into one big piece of info that JavaScript can get cozy with.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// YYYY-MM-DDTHH:mm:ss.sssZ</span>
<span class="hljs-comment">// A date string in ISO 8601 Date Format</span>
</code></pre>
<h2 id="heading-how-to-use-the-new-date-constructor-in-javascript">How to Use the new Date() Constructor in JavaScript</h2>
<p><code>new Date()</code> is the constructor to create a new date in JavaScript. Shocker! 😂</p>
<p>shocker
If you don't pass anything into the new date constructor, it will give you a date object of whatever the current date and time is.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
<span class="hljs-comment">// Thu Jun 23 2022 20:35:51 GMT-0400 (Eastern Daylight Time)</span>
</code></pre>
<blockquote>
<p>A new date created without any arguments returns the current date and time.
Note that a date object can, and often should, contain a time down to the millisecond in addition to the month, day, and year.</p>
</blockquote>
<h2 id="heading-how-to-create-a-new-date-with-a-string">How to Create a New Date With a String</h2>
<p>You may pass a date string into <code>new Date()</code> to create a date object.</p>
<p>You don't have to specify a time when creating a date object.</p>
<p><code>new Date('2022-06-13')</code> is perfectly valid. However, when you console log this new date, you'll see that a time will be automatically assigned even though we didn't declare one.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> aDate = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-string">'2022-06-13'</span>)
<span class="hljs-comment">// Sun Jun 12 2022 20:00:00 GMT-0400 (Eastern Daylight Time)</span>
</code></pre>
<blockquote>
<p>A string date without a declared time will still be assigned one.</p>
</blockquote>
<p>This can create schisms in the matrix, and it is best to include a full date. For instance, since the local system time is used to interpret the date, depending upon where in the world your computer is, you could get different results from the same non-specific date.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657049121506/aOxfz-46R.gif" alt="glitch.gif" class="image--center mx-auto" /></p>
<p>So, when passing a string into <code>new Date()</code>, use a full date with hours:minutes.milliseconds.</p>
<p>A capital <strong>T</strong> separates the day component from the time component as shown below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-string">'2022-05-14T07:06:05.123'</span>)
<span class="hljs-comment">// Sat May 14 2022 07:06:05 GMT-0400 (Eastern Daylight Time)</span>
</code></pre>
<blockquote>
<p>Use the full day and time when possible</p>
</blockquote>
<h2 id="heading-how-to-create-a-new-date-with-a-number">How to Create a New Date With a Number</h2>
<p>You can also pass a number into a <code>new Date()</code> constructor. More on what the numbers represent below – but <code>new Date(1656033105000)</code>, for instance, will return a legitimate date:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Date</span>(<span class="hljs-number">1656033105000</span>))
<span class="hljs-comment">// Thu Jun 2022 21:12:06 GMT-0400 (Eastern Daylight Time)</span>
</code></pre>
<blockquote>
<p>Giant numbers represent dates too</p>
</blockquote>
<h2 id="heading-how-to-create-a-new-date-with-arguments">How to Create a New Date With Arguments</h2>
<p>More on this below too...You many pass up to seven arguments into <code>new Date()</code> as well, creating a simpler way to represent a date and time to the Date constructor.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">2022</span>,<span class="hljs-number">03</span>,<span class="hljs-number">14</span>,<span class="hljs-number">07</span>,<span class="hljs-number">33</span>,<span class="hljs-number">245</span>)
<span class="hljs-comment">// Thu Apr 14 2022 07:37:05 GMT-0400 (Eastern Daylight Time)</span>
</code></pre>
<blockquote>
<p>A descending list of arguments starting with the year and ending with milliseconds</p>
</blockquote>
<h2 id="heading-what-is-dateparse">What is Date.parse()?</h2>
<p>So, an interesting thing happens if you were to use the parse method on a date object. It spits out a huge number.</p>
<p><code>Date.parse()</code> tells us the number of milliseconds that have elapsed since January 1, 1970. This is helpful when comparing multiple dates. It's easier to compare and measure differences in dates when they are converted to numbers rather than strings.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> anotherDate = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">2012</span>,<span class="hljs-number">07</span>,<span class="hljs-number">12</span>,<span class="hljs-number">12</span>,<span class="hljs-number">00</span>,<span class="hljs-number">234</span>)
<span class="hljs-built_in">Date</span>.parse(anotherDate)
<span class="hljs-comment">// 1344787434000</span>
</code></pre>
<blockquote>
<p>Date.parse returns the number of milliseconds since January 1, 1970</p>
</blockquote>
<h2 id="heading-which-is-better-dates-made-with-arguments-or-strings">Which is Better – Dates Made With Arguments or Strings?</h2>
<p>When dating, learn to argue well for long term success. When using dates in JavaScript, use arguments over strings for long term success.</p>
<p><code>new Date(2022, 00, 12, 8, 01, 33, 456)</code></p>
<p>This can be a bit easier than creating a date using a string. The arguments are simply entered in descending order starting with the year, and ending with the milliseconds.</p>
<p>Only tricky part here: the month is zero indexed. So, January is 00.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">2022</span>,<span class="hljs-number">00</span>,<span class="hljs-number">12</span>,<span class="hljs-number">8</span>,<span class="hljs-number">01</span>,<span class="hljs-number">33</span>,<span class="hljs-number">456</span>)
<span class="hljs-comment">// Wed Jan 12 2022 08:01:33 GMT-0500 (Eastern Standard Time)</span>
</code></pre>
<blockquote>
<p>Don't forget that the month is zero indexed when using arguments for date construction</p>
</blockquote>
<h2 id="heading-how-to-go-deeper-with-javascript-dates">How to Go Deeper With Javascript Dates</h2>
<p>This only scratches the surface of the Date object. Check out <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">MDN for a deep dive</a>. As with all things, there is a treasure trove of information there.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657049359771/aR3dxSDrS.gif" alt="deep.gif" class="image--center mx-auto" /></p>
<p>You've got the basics now, though. Go put it into practice. You now know how to create a date object in JavaScript with <code>new Date()</code>. You can grab the current date and time by not passing anything into the constructor, or you can pass in a string, a number or arguments.</p>
<p>Thanks for Reading
Thanks for reading! I write about design and development here: https://blog.eamonncottrell.com/</p>
<p>And you can find me on <a target="_blank" href="https://twitter.com/EamonnCottrell">Twitter</a> and <a target="_blank" href="https://www.linkedin.com/in/eamonncottrell/">LinkedIn</a>.</p>
<p>Have a great one!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657049491903/LzyofMwff.gif" alt="thank-you.gif" class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[More PostgreSQL Commands for freeCodeCamp Projects]]></title><description><![CDATA[More Commands 🌟
We're creating more bash files to do some of the heavy lifting and querying for us. Here are a list of commands used in Learn SQL by Building a Student Database: Part 2. 
They'll be useful to (definitely) my future self and (hopefull...]]></description><link>https://blog.eamonncottrell.com/more-postgresql-commands-for-freecodecamp-projects</link><guid isPermaLink="true">https://blog.eamonncottrell.com/more-postgresql-commands-for-freecodecamp-projects</guid><category><![CDATA[SQL]]></category><category><![CDATA[freeCodeCamp.org]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Fri, 17 Jun 2022 19:44:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657043535143/viTh9bEqf.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-more-commands">More Commands 🌟</h2>
<p>We're creating more bash files to do some of the heavy lifting and querying for us. Here are a list of commands used in Learn SQL by Building a Student Database: Part 2. 
They'll be useful to (definitely) my future self and (hopefully) you! 😁👊</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655401208251/I2a8KSAGX.gif" alt="more.gif" class="image--center mx-auto" /></p>
<h3 id="heading-conditions">Conditions ❔</h3>
<p>There are all sorts of ways to ...sort 😊 and specify query results </p>
<ol>
<li><code>LIKE</code> &amp;&amp; <code>NOT LIKE</code>: use this to query a table and find matches to certain criteria. <code>SELECT * FROM courses WHERE course LIKE '_lgorithms'</code>; will match any row with a word that has exactly one letter before the 'lgorithms' part. </li>
<li><code>...LIKE '%lgorithms'</code> will not constrain it to one letter in front of the 'lgorithms' part. So <code>%</code> lets anything come before.</li>
<li><code>ILIKE</code> will ignore case. So you don't need to specify capital or lower case.</li>
<li><code>AND</code> and <code>OR</code> conditions will enable you to combine query criteria. e.g. <code>SELECT * FROM courses WHERE course LIKE '% %' AND course NOT ILIKE '%b%';</code>. This will select rows with a space in it and without a capital or lowercase 'b' in it.</li>
<li><code>echo -e</code>: this lets you use the escape character like '\n' to create a newline.</li>
<li><code>IS NULL</code>: let's you query fields that are blank or empty. e.g. <code>SELECT &lt;column_name&gt; FROM &lt;table&gt; WHERE &lt;column&gt; IS NULL</code>.</li>
<li><code>IS NOT NULL</code>: let's you query fields that are not blank or empty.</li>
<li><code>ORDER BY &lt;column_name&gt;</code>: specifies the order in which results are displayed. Defaults to ascending (<code>ASC</code>) order. add <code>DESC</code> (descending) to the end of the query to reverse that displayed order. e.g. <code>SELECT * FROM &lt;table&gt; ORDER BY &lt;column&gt; DESC</code>.</li>
<li>To order by multiple columns, add those columns separated by commas at the end of the <code>ORDER BY</code> query. e.g. <code>...ORDER BY &lt;column_name1&gt;,&lt;column_name2&gt;</code>. Now, if there are matching values from ordering by column1, they will order by column2. <em>if you specify <code>DESC</code> while using multiple ordering columns, you will do that immediately following each column name in the query.</em> <code>ORDER BY &lt;column_name1&gt; DESC, &lt;column_name2&gt; DESC</code>.</li>
<li><code>LIMIT</code>: limits the returned number of rows from a query. The order of keywords matters in the query, too: You cannot put <code>LIMIT</code> before <code>ORDER BY</code> or either of them before <code>WHERE</code>.</li>
</ol>
<h3 id="heading-mathematics">Mathematics ➗</h3>
<ol>
<li><code>MIN</code> &amp;&amp; <code>MAX</code>: two of many mathematic functions. MIN &amp; MAX find the lowest or highest values, respectively, in a column. e.g. <code>SELECT MIN(&lt;column_name&gt;) FROM &lt;table&gt;</code></li>
<li><code>SUM</code>: sums the values of a column. </li>
<li><code>AVG</code>: averages the values in a column.</li>
<li><code>CEIL</code> &amp;&amp; <code>FLOOR</code>: rounds results to the nearest whole number. <code>CEIL</code> rounds up, <code>FLOOR</code> rounds down.</li>
<li><code>ROUND</code> simply rounds to the nearest whole number. Add a comma and a number to round to a specified number of decimal places. e.g. <code>SELECT ROUND(AVG(major_id),5)</code></li>
<li><code>COUNT</code>: how many entries in a table for the column. e.g. <code>SELECT COUNT(*) FROM majors</code> counts total rows and <code>SELECT COUNT(gpa) FROM students</code> counts only those rows with non-null values in the gpa column.</li>
<li><code>DISTINCT</code>: shows only the unique values from a query. e.g. <code>SELECT DISTINCT(&lt;column_name&gt;) FROM &lt;table&gt;</code>.</li>
<li><code>GROUP BY</code>: works similarly to <code>DISTINCT</code>. e.g. <code>SELECT &lt;column_name&gt; FROM &lt;table&gt; GROUP BY &lt;column_name&gt;</code>. The advantage of using <code>GROUP BY</code> is that it allows you to add any of the aggregate functions like <code>MIN</code>, <code>MAX</code>, and <code>COUNT</code> to get more info from your results. <code>SELECT COUNT(*) FROM &lt;table&gt; GROUP BY &lt;column_name&gt;</code> will count the number of distinct column values in that query.</li>
<li><code>HAVING</code>: use at the end of a query where condition must be an aggregate function with a test. e.g. <code>HAVING COUNT(*) &gt; 0</code></li>
<li><code>AS</code>: rename a column. e.g. <code>SELECT &lt;column&gt; AS &lt;new_column_name&gt;</code></li>
</ol>
<h2 id="heading-joins">JOINS</h2>
<p>We tackled only FULL JOINS (and only those briefly) in my initial list of commands <a target="_blank" href="https://blog.eamonncottrell.com/a-list-of-postgresql-commands-for-beginners">here</a>. As you might imagine, there's plenty more to do with JOINS.</p>
<ol>
<li><code>LEFT JOIN</code>: gets all the rows from the left table (the table mentioned first (furthest to the left on the line) in the query). e.g. <code>SELECT * FROM students LEFT JOIN majors ON students.major_id = majors.major_id</code></li>
<li><code>RIGHT JOIN</code>: same thing only returns rows from right table.</li>
<li><code>INNER JOIN</code>: combines the two above. Will return rows from LEFT table if they have a RIGHT table counterpart and visa versa.</li>
<li><code>USING</code>: keyword you can use in the event that the foreign key has the same name in each table. e.g. <code>SELECT * FROM &lt;table_1&gt; FULL JOIN &lt;table_2&gt; USING(&lt;column&gt;)</code></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[How to Query Data in Google Sheets]]></title><description><![CDATA[This article appeared first on freeCodeCamp's news publication.

I built a spreadsheet and wanted to display some of the data in a small table which would update based on the day of the week.
After some digging, querying seemed the best option to pul...]]></description><link>https://blog.eamonncottrell.com/how-to-query-data-in-google-sheets</link><guid isPermaLink="true">https://blog.eamonncottrell.com/how-to-query-data-in-google-sheets</guid><category><![CDATA[google sheets]]></category><category><![CDATA[SQL]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Fri, 17 Jun 2022 17:51:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1655401119554/IkCkRrSNf.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This article appeared first on <a target="_blank" href="https://www.freecodecamp.org/news/querying-like-an-sql-boss-in-google-sheets/">freeCodeCamp's news publication</a>.</p>
</blockquote>
<p>I built a spreadsheet and wanted to display some of the data in a small table which would update based on the day of the week.</p>
<p>After some digging, querying seemed the best option to pull this off.</p>
<p>In this article, you'll learn several things about Google Sheets tables, functions, queries, data validation and formatting including:</p>
<ol>
<li>Creating a clean, usable data table.</li>
<li>Creating a data validation drop down list</li>
<li>Naming ranges for easier use and cleaner data management</li>
<li>Querying basics including SELECT, WHERE and the TEXT() and TODAY() functions</li>
<li>The funky syntax to reference cells within Google Sheet queries</li>
<li>Where to go in the official docs for further information.</li>
</ol>
<h2 id="heading-google-sheets-is-similar-to-sql">Google Sheets is Similar to SQL</h2>
<p>Google Sheets indeed has a built in "Google Visualization API Query Language". Check out the docs <a target="_blank" href="https://developers.google.com/chart/interactive/docs/querylanguage">here</a>.</p>
<p>Some of the syntax is the same and much of the functionality is similar to SQL, so you should pick it up quickly if you are familiar with SQL at all.</p>
<p>I know but a little when it comes to SQL, but I was able to parse my way through some basic queries with little trouble.</p>
<p>In fact, the thing that gave me the most trouble was <strong>cell referencing syntax</strong>. And I hope this post can save you some head scratching that I went through today.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655486619777/ZYWm1JZLk.gif" alt="barney-head-scratch.gif" /></p>
<h2 id="heading-setup-your-table">Setup Your Table</h2>
<p>First things first: get all the data in a well organized table. This is sometimes the hardest part of any data analysis: simply getting the data in an orderly, usable format.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655487140364/iQWEG--mO.png" alt="neat.png" /></p>
<p>I used data validation for the days of the week to ensure I didn't have a typo in there since our query will depend on those days.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655487787872/sZeOuw67B.png" alt="validate.png" /></p>
<h2 id="heading-create-a-named-range">Create a Named Range</h2>
<p>Select the entire table to create a named range. This will make things cleaner and easier in the next step. It's good practice to keep data as tidy as possible.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655487816830/7_j_wDbGu.png" alt="query.png" /></p>
<p>Confirm and name the range to save it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655487940991/g98mgMZWk.png" alt="confirm.png" /></p>
<h2 id="heading-how-to-write-the-query">How to Write the Query</h2>
<p>Now, let's make another sheet to put our query into. I only need two columns for our data: one for the person and one for the task. Make room for more if your data requires it.</p>
<p>The first step requires a little trick to make the current day display when the sheet is loaded and for this to be used in our query.</p>
<p>There is a built-in <code>=Today()</code> function, but using it alone is not enough even when you change the formatting. They query won't recognize it as matching the days of the week text in our table.</p>
<p>Instead, wrap the <code>Today()</code> function inside the <code>Text()</code> function as shown below. The <code>Text()</code> function accepts a number and a format as arguments. When we pass it <code>Today()</code>, it can convert that date number into text using the format "dddd". Neat, right? 😊</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655488026267/BLgJRauSw.png" alt="functions.png" /></p>
<p>In Google Sheets, <code>=QUERY</code> is also a built in function. As you type it in the cell (<code>B22</code> in my example) you can click the drop down arrow in the top right to get more information on the parameters accepted.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655488054236/23ycBqL7V.png" alt="more info.png" /></p>
<p>We'll select our data. Type the name of the range you created and it will automatically select that table.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655488085416/UOfYkGIrj.png" alt="range.png" /></p>
<p>You can manually select the range for the table to query if you choose. But you want to be as awesome as possible, so use that named range! 🙌</p>
<p>We want to select and return the first two columns (the names and tasks) in our data sheet based on the fourth column (the day of the week).</p>
<p>The query reads <code>=QUERY(heat,"select B,C where D='"&amp;B21&amp;"'",1)</code>.</p>
<p>And yes, that nastiness with the single quotes, double quotes and ampersands is necessary for the conditional statement we are creating.</p>
<p>We're telling the query to match the D column in our data sheet with the value in <code>B21</code> which is now text thanks to our previous formula manipulating the day of the week.</p>
<p>The syntax to match the text in the cell is gross, but it's just how you have to do it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655488113233/-vxa-yH5c.png" alt="query-function.png" /></p>
<p>That last parameter <code>1)</code> is letting the query know that there is one row of headers to display (name, task).</p>
<p>And, voilà! We've got ourselves an automatically updating task list based on the current day of the week. All that's left is cleaning up the final result! 🎁</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655488142221/w6pMMF_dS.png" alt="format.png" /></p>
<p>##Official Examples</p>
<p>I referenced <a target="_blank" href="https://support.google.com/docs/answer/3093343?hl=en">this article</a> from the QUERY function page on Google Support to learn, and it has a Google Sheet you can copy with a bunch of examples in it. Super helpful.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655488178956/fFt7ObLQf.png" alt="ex.png" /></p>
<h2 id="heading-my-example-spreadsheet">My Example Spreadsheet</h2>
<p><a target="_blank" href="https://docs.google.com/spreadsheets/d/1oO5SMyVlk2KbqXmW534JH2kYSoqfP1IgVf80KRwpQgY/edit#gid=0">Here is the sheet</a> I built for this article. You are free to make a copy of it and use yourself.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655488214144/bQzdWJeF6.png" alt="copy.png" /></p>
<h2 id="heading-thanks-for-reading">Thanks for Reading!</h2>
<p>I write about web design and development from a solopreneur's perspective at https://blog.eamonncottrell.com/ and you can find me on Twitter: https://twitter.com/EamonnCottrell</p>
<p>Have a great one! 🎉</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655488225692/RweE5Hosk.gif" alt="bye.gif" /></p>
]]></content:encoded></item><item><title><![CDATA[Learn SQL By Building A Student Database]]></title><description><![CDATA[Back to the SSQL 🎶
I'm glad to be back in SQL for this course. Bash was great, but let's make some more databases!
https://media.giphy.com/media/Rm1p7xp3Odl2o/giphy.gif
If you, like me, need a cheat sheet for some of the PostgreSQL commands from the...]]></description><link>https://blog.eamonncottrell.com/learn-sql-by-building-a-student-database</link><guid isPermaLink="true">https://blog.eamonncottrell.com/learn-sql-by-building-a-student-database</guid><category><![CDATA[PostgreSQL]]></category><category><![CDATA[freeCodeCamp.org]]></category><category><![CDATA[Certification]]></category><category><![CDATA[review]]></category><dc:creator><![CDATA[Eamonn Cottrell]]></dc:creator><pubDate>Wed, 08 Jun 2022 19:29:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1654716460291/kqLIQBLZg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-back-to-the-ssql">Back to the SSQL 🎶</h2>
<p>I'm glad to be back in SQL for this course. Bash was great, but let's make some more databases!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/Rm1p7xp3Odl2o/giphy.gif">https://media.giphy.com/media/Rm1p7xp3Odl2o/giphy.gif</a></div>
<p>If you, like me, need a cheat sheet for some of the PostgreSQL commands from the first course, check out my previous article in this series, <a target="_blank" href="https://blog.eamonncottrell.com/a-list-of-postgresql-commands-for-beginners">A List of PostgreSQL Commands for Beginners</a>. I'm pulling it up in a second tab now.</p>
<p>This course has us making a four table student database using data in two .csv files. One has a bunch of student data and the other a bunch of course data:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650653499862/WoF2TT6so.png" alt="image.png" /></p>
<p>The first 30% of the course is a rehash of setting up the tables and beginning to insert data into them.</p>
<p>Then we combine what we learned in the bash course to create a script file.</p>
<h2 id="heading-new-commands">New Commands</h2>
<ul>
<li><code>cat &lt;filename&gt;</code>: prints the contents of a file...can also be used to pipe the contents into a loop to do stuff to each line! 😃<ul>
<li>for instance, this will read the major and course values on each row and then echo the majors </li>
</ul>
</li>
</ul>
<pre><code class="lang-bash">cat courses.csv | <span class="hljs-keyword">while</span> IFS=<span class="hljs-string">","</span> <span class="hljs-built_in">read</span> MAJOR COURSE
<span class="hljs-keyword">do</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-variable">$MAJOR</span>
<span class="hljs-keyword">done</span>
</code></pre>
<ul>
<li><code>IFS</code>: Internal Field Separator. Used to determine word boundaries, and defaults to spaces. So if we want to echo more than the first word of a major, we need to add this to our loop:</li>
</ul>
<pre><code class="lang-bash">cat courses.csv | <span class="hljs-keyword">while</span> IFS=<span class="hljs-string">","</span> <span class="hljs-built_in">read</span> MAJOR COURSE
<span class="hljs-keyword">do</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-variable">$MAJOR</span>
<span class="hljs-keyword">done</span>
</code></pre>
<ul>
<li><code>psql</code> command can be used to run a single command and exit </li>
<li><code>TRUNCATE &lt;table_name&gt;</code>: delete all the data in a table. You can do this for multiple tables at once by separating  with commas i.e. <code>TRUNCATE &lt;table_one&gt;,&lt;table_two&gt;</code>.</li>
</ul>
<p>For me, the most difficult part in this course has been getting used to the syntax of writing in bash and not making silly mistakes that the CodeAlly tester doesn't like. It continues to be very particular that everything is written precisely as intended.</p>
<h2 id="heading-finish-line-andamp-one-issue">Finish Line &amp; One Issue</h2>
<p>By the end of this course, we've successfully programmed a bash file to loop through .csv files and add the items therein into a PostgreSQL database.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1654715984759/19QN5kqKd.png" alt="image.png" /></p>
<p>There is a part two that promises to get a little more advanced by combining information from multiple tables with join commands. </p>
<p>On the whole, this was a good exercise in a more practical example of building a database as most data will need to come from somewhere like a .csv file in the real world.</p>
<p>I have encountered a nagging issue that is addressed in the freeCodeCamp forums <a target="_blank" href="https://forum.freecodecamp.org/t/running-the-relational-database-curriculum-in-your-browser/500231">here</a>. I've been unable to get a checkmark for completion back on my freeCodeCamp page after finishing the project.</p>
<p>There are several steps to take to remedy this, and it's happened I believe on every project in this certification. However, this is the first time that I've gone through all the steps a few times to no avail. Not a huge deal as it's not measured in the formal certification, but I hate not getting those progress checkmarks! ✔</p>
<h2 id="heading-thanks-for-reading">Thanks for Reading!</h2>
<p>Come say hey 👋 on Twitter, I'd love to meet you and get your feedback: twitter.com/EamonnCottrell</p>
]]></content:encoded></item></channel></rss>