<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stanley Lim</title>
    <description>The latest articles on DEV Community by Stanley Lim (@spiderpig86).</description>
    <link>https://dev.to/spiderpig86</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F140204%2Ffb507fb0-66ff-420a-bf20-2f68406fa00d.png</url>
      <title>DEV Community: Stanley Lim</title>
      <link>https://dev.to/spiderpig86</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/spiderpig86"/>
    <language>en</language>
    <item>
      <title>🚀 Cirrus 0.8.0 launched!</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Tue, 18 Feb 2025 16:02:09 +0000</pubDate>
      <link>https://dev.to/spiderpig86/cirrus-080-launched-18m4</link>
      <guid>https://dev.to/spiderpig86/cirrus-080-launched-18m4</guid>
      <description>&lt;p&gt;Cirrus 0.8.0! Lots of under the hood changes that have been in the works for quite a long time, but very excited it is finally out. This is a very crucial step before resuming work on Cirrus Blocks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cirrus-ui.com/" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Spiderpig86/Cirrus" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎉 Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Major rewrite of class generation system.&lt;/li&gt;
&lt;li&gt;New utility mixins to replace old ones:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;generator.utility&lt;/code&gt; -&amp;gt; &lt;code&gt;generator_v2.utility&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;generator.utility-with-body&lt;/code&gt; -&amp;gt; &lt;code&gt;generator_v2.utility-with-body&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Class generator now supports class grouping via &lt;code&gt;group:[pseudo]&lt;/code&gt; and &lt;code&gt;group-[pseudo]&lt;/code&gt; CSS classes. These allow developers to use events on a parent element to affect the styles of child elements.
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;generator_v2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;$base-class-name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$class-value-pairs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'blue'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'color'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$variants&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'group-hover'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$generate-viewports&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$override&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'!important'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.group&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="nc"&gt;.group-hover&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nd"&gt;:u-text-blue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"group"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"group-hover:u-text-blue"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;this is blue on hover&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;this is also blue on hover&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Class generator now supports many more variants (pseudos):

&lt;ul&gt;
&lt;li&gt;'responsive',&lt;/li&gt;
&lt;li&gt;'dark', 'light',&lt;/li&gt;
&lt;li&gt;'reduce-motion',&lt;/li&gt;
&lt;li&gt;'first-of-type',&lt;/li&gt;
&lt;li&gt;'last-of-type',&lt;/li&gt;
&lt;li&gt;'portrait', 'landscape',&lt;/li&gt;
&lt;li&gt;'hover', 'group-hover',&lt;/li&gt;
&lt;li&gt;'focus', 'group-focus', 'focus-visible', 'focus-within',&lt;/li&gt;
&lt;li&gt;'active',&lt;/li&gt;
&lt;li&gt;'visited',&lt;/li&gt;
&lt;li&gt;'checked',&lt;/li&gt;
&lt;li&gt;'disabled'
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;generator_v2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;$base-class-name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$class-value-pairs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'blue'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'color'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$variants&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'dark'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'hover'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'reduce-motion'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'group-hover'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'group-focus'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$generate-viewports&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$override&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'!important'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.u-text-blue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.hover&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nd"&gt;:u-text-blue:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.group&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="nc"&gt;.group-hover&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nd"&gt;:u-text-blue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.group&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="nc"&gt;.group-focus&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nd"&gt;:u-text-blue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;640px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.sm&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nd"&gt;:u-text-blue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.sm&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nd"&gt;:u-text-blue:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update grid class utilities to support generation with viewports and modifiers &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/b3be15f8cc05311176c167f1047676d0c426b5be" rel="noopener noreferrer"&gt;b3be15f&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Enable hover pseudo selector for v2 colors &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/839cd47baec8c76567dc0136c60c3a7081231738#diff-2be740eb6ef25e141bd74e2ce850db08d2a30ba64f5e58ad333f1b7f2a2c4f87" rel="noopener noreferrer"&gt;839cd47&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add custom button behaviors for v1 color classes (only apply on button selectors and add hover behavior) &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/d4a6b6a73444b04c25f08a2055fd32fda7d04a83" rel="noopener noreferrer"&gt;d4a6b6a&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add cursor util classes &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/32e08971cd9e85bc54d070ec46f6a6e36945c95e" rel="noopener noreferrer"&gt;32e0897&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add tooltip--visible class to show tooltips outside of :hover and :focus &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/9b77f7844c6d60bc433f99ca8a02f54ecaf27868" rel="noopener noreferrer"&gt;9b77f78&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new pseudo-variants config with integration to existing class generation &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/459dbba4928af89a547b7c9c2a33d27161d536f3" rel="noopener noreferrer"&gt;459dbba&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"cirrus-ext"&lt;/span&gt; &lt;span class="nt"&gt;with&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nt"&gt;pseudo-variants&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nt"&gt;CLEARFIX&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'responsive'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'hover'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;...&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nt"&gt;FLEX&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'responsive'&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;...&lt;/span&gt;
        &lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🐛 Fixes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Remove unnecessary forward for cirrus-all build &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/3dfa6e64904b2c1502c244e1975929a3cce25be7" rel="noopener noreferrer"&gt;3dfa6e6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Migrate to ESM for Gulp, update Gulp deps, update CI to use Node 20 &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/3697dee9736db599239529947a6f4ae317d6007a" rel="noopener noreferrer"&gt;3697dee&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix left padding for form-ext inputs &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/645394359a5e8601f9176d0052845e1a1f36c58f#diff-65d54f869ab57e4907682b0ca168f65b93bc614bab3d64e7726cc9105993c446" rel="noopener noreferrer"&gt;6453943&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove inconsistent padding for table header and footer th &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/3842fcef09d456769f0df65bd98b0f8c8ad48396#diff-41fccbff8bfc8e273de5f1f31df3c00b9e47d155e014bec5e901f6a84d24f944" rel="noopener noreferrer"&gt;3842fce&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix incorrect class name and property for italic font util class &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/6591ca1ce109dcc85798327d163b7e98396aabc4#diff-d3a01f0b6c089e08ab968240addc2bec463b547d14f253fb55c72a59d56674edR720" rel="noopener noreferrer"&gt;6591ca1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove extra letter spacing from all text elements &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/6cb8f768ef3d34e9eca9574f8f07287da836dc64" rel="noopener noreferrer"&gt;6cb8f76&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add missing viewport config entries &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/ea038befb811081b804e0656823ac006067e99c3" rel="noopener noreferrer"&gt;ea038be&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add missing grid-row styles &lt;code&gt;grid-rows-&lt;/code&gt;, &lt;code&gt;grid-r-&lt;/code&gt;, &lt;code&gt;grid-rs-&lt;/code&gt;, and &lt;code&gt;grid-re-&lt;/code&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/12950165240b1d53cc4a3e803155759cde1de50f#diff-bcee8f5354ad9844acbf1140b90dbdb15e5793f65ec20bdc6c5d737687fb6346" rel="noopener noreferrer"&gt;1295016&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix grid row column style &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/c95fe41cade7d0b6a5cb4ce41cd6d23c941f39cf#diff-bcee8f5354ad9844acbf1140b90dbdb15e5793f65ec20bdc6c5d737687fb6346" rel="noopener noreferrer"&gt;c95fe41&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Link font weight and color adjustments no longer apply on nested input elements &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/a6b5e256210d24fc014bad504ddbb85b18959c6a#diff-f6186573c0b149acb95d8b889d6da98865c652fbc2da9fadc20a5edd0c557a6d" rel="noopener noreferrer"&gt;a6b5e25&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove background color from avatar when there is a child image element &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/7d4ea82fe6b5d30862d98e48dab4130ff18f4d46" rel="noopener noreferrer"&gt;7d4ea82&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix text color with a tag and &lt;code&gt;.btn&lt;/code&gt; class &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/c1c7fa560c7af168ef31d770b3aaeb04936c5a4a" rel="noopener noreferrer"&gt;c1c7fa5&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove button shadow on focus for &lt;code&gt;.btn--disabled&lt;/code&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/867eefff491e0ae4b45173c028ffae676adb840c" rel="noopener noreferrer"&gt;867eeff&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix broken tab styles, remove remaining hardcoded colors &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/330" rel="noopener noreferrer"&gt;#330&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/bfa64c6d0bdd687d3aac4e778530f42c53319cf5" rel="noopener noreferrer"&gt;bfa64c6&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💥 Breaking Changes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;New viewport syntax across all classes that support different viewports, unifying the fractured viewport system. 

&lt;ul&gt;
&lt;li&gt;The main difference from before is now all viewport modifiers will use the same syntax instead of inconsistent system from before. All classes that are meant to apply at a certain viewport size and above will be in the form of &lt;code&gt;[sm|md|lg|xl]:classname&lt;/code&gt;. This will be the case for all classes whether it is for columns or utility classes.&lt;/li&gt;
&lt;li&gt;Below are the main syntax changes:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;col-sm-1&lt;/code&gt; -&amp;gt; &lt;code&gt;sm:col-1&lt;/code&gt; - column classes will all have the viewport modifier prefixed at the front.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;col-6&lt;/code&gt; -&amp;gt; &lt;code&gt;md:col-6&lt;/code&gt; - all column classes that did not have the modifier class must be prefixed with &lt;code&gt;md:&lt;/code&gt; to get the same behavior as before. This is to address the confusing implicit behavior where a class like &lt;code&gt;col-6&lt;/code&gt; would have 50% width for &lt;code&gt;md&lt;/code&gt; and above but 100% width for viewports below &lt;code&gt;md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;u-block-md&lt;/code&gt; -&amp;gt; &lt;code&gt;md:u-block&lt;/code&gt; - for all utility classes, the viewport modifier will be prefixed at the front.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Deprecated &lt;code&gt;margin:1 rem 0;&lt;/code&gt; style for &lt;code&gt;p, article, blockquote&lt;/code&gt; since it leads to unexpected behaviors for users &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/9bcda3a4f1351aa4f4fe9736c473768e62fabc31" rel="noopener noreferrer"&gt;9bcda3a&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Deprecating row.no-space classes &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/8e802f923df81d2e611c670f13fa175a1bf4c79f#diff-28c9e4afc4df9ed98a503dd39511da564502d2be96a5e8cd8f80b2ba096edf1e" rel="noopener noreferrer"&gt;8e802f9&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Update Modal class names for clarity &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/9da499f60e3f3f818cf8ccba628fb89dbcb85727" rel="noopener noreferrer"&gt;9da499f&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Deprecated &lt;code&gt;.modal.small&lt;/code&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/9da499f60e3f3f818cf8ccba628fb89dbcb85727" rel="noopener noreferrer"&gt;9da499f&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Deprecating placeholder.scss &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/39e663bdc24923073145232e61c7aed7d9c21a5d" rel="noopener noreferrer"&gt;39e663b&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Deprecated &lt;code&gt;.title&lt;/code&gt; and &lt;code&gt;.subtitle&lt;/code&gt; classes &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/b07c76df0a56520c625c64ddac253838f8665133" rel="noopener noreferrer"&gt;b07c76d&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Deprecated viewports config in favor of using 'responsive' entry in pseudo-variant config &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/1f33d83c6a559315e81871d423a7f4aa380c3e6d" rel="noopener noreferrer"&gt;1f33d83&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>showdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>🚀 Cirrus 0.7.2 Released</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Sat, 30 Dec 2023 17:21:39 +0000</pubDate>
      <link>https://dev.to/spiderpig86/cirrus-072-released-44nl</link>
      <guid>https://dev.to/spiderpig86/cirrus-072-released-44nl</guid>
      <description>&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;Just released 0.7.2 for Cirrus. Check it out! This release adds fixes to a lot of user requested issues.&lt;/p&gt;

&lt;p&gt;💎 &lt;a href="https://github.com/Spiderpig86/Cirrus/"&gt;Cirrus&lt;/a&gt;&lt;br&gt;
📝 &lt;a href="https://cirrus-ui.com/"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Cirrus?
&lt;/h2&gt;

&lt;p&gt;Cirrus is a component and utility centric SCSS framework&lt;br&gt;
designed for rapid prototyping. I started this project back in late 2016, but never really wrote about it. The main reason why you should use it is if you need to proto-type a project ASAP with many pre-styled component classes. There's a lot of CSS frameworks already out there, but Cirrus comes with my (and those who contributed in the past) take on what a CSS framework should have and a distinct style.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onto the Updates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🎉 Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Link pseudo elements will now use &lt;code&gt;currentColor&lt;/code&gt; instead of fixed link color &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/bd9ab44b93dd5dd0c6f5aa08b84eb12611c4399a"&gt;bd9ab44&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Buttons now completely support color modifier classes (background, text, and border). &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/5962ca4d02028f57b39b15eb70c3e9b33125ad14"&gt;5962ca4&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add px (1px) to default sizing system. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/14970c42a0aac517635fa01a4e7e4e461de95eb9"&gt;14970c4&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add 48 and 64 to default sizing system to close the gap with &lt;code&gt;xs&lt;/code&gt; viewport. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/31f80f0b7ef73f372573959dad30c1d1ccc66a45"&gt;31f80f0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Border color util classes will now set button shadow color, taking precedence over bg color classes. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/46fcc01a58312e1d9566bfe9c704e1e073c42b04"&gt;46fcc01&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new &lt;code&gt;normal-case&lt;/code&gt; utility class. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/81d36aa3c2a90adf140813920307b6fd4e1188af"&gt;81d36aa&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;vertical-align&lt;/code&gt; util classes. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/ee67b56eb92509cfdb3bef7372f2fbe42b91c80c"&gt;ee67b56&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Update utility mixin to handle prefers dark/light and reduced motion variants. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/49073812ae3efb1e2689e68fc8c5789a45717c66"&gt;4907381&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add common class name field to utility api, add new function to convert old class map to new class mapping for utility mixin. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/3b6d75341fa362fb4ace8f7ecfa6ecca5fc69fb2"&gt;3b6d753&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reorder style generation to minimize minified size with gulp-clean-css. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/b5dd955eef5fd4bfc978d8d9e87eb07d1a59fc74"&gt;b5dd955&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new gap classes for row or column only &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/5c317e09ea8ec60231d69495c1659277b7ba8648"&gt;5c317e0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new font style classes &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/192"&gt;#192&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/83463e12d184ab91fc0856339c976396a5489116"&gt;83463e1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Move language label for code component to styling for 'pre' &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/206"&gt;#206 &lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/22486311ec3cecbb7fca8ee012ed89b440cea932"&gt;2248631&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new helper class to toggle tree-nav visibility &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/623a19d8d71f8c3fc9095c8ee14dfdf30f6e074d"&gt;623a19d&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Integrate native hover color generation for buttons using bg- modifier classes &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/7b55b6ddbba6bc316af676656612682c21974375"&gt;7b55b6d&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new utility classes for grid auto flow row and column. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/f0349a180189e61dcac0f29e9fa49e1aca8ea9e9"&gt;f0349a1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🐛 Fixes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Remove margins from label HTML element. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/010146f2e742525b0f5117f6110a5a5b137c1226"&gt;010146f&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove margins for &lt;code&gt;btn-close&lt;/code&gt;. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/4bfb7c0dbd6dd9e4f0ded4e056e769383781070b"&gt;4bfb7c0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Last border of accordion item should be invisible. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/a95694b017bafe98ccb8a9721ceef79ec9654c7f"&gt;a95694b&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add missing button variables to v1 color classes. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/8e01988cddf6bb722561e5224b44d2267b869762"&gt;8e01988&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reset cursor of &lt;code&gt;nav-item&lt;/code&gt; children. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/c706fcecd7c20d425054da2ec4806262e4178620"&gt;c706fce&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Inline condition for generating classes to avoid conflicting variable names. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/50dc1b58c3ff783907e151bfce598911c6ae4caf"&gt;50dc1b5&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix incorrect viewport being used to apply fill-height styles &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/163"&gt;#163&lt;/a&gt;. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/7ef825b2f7520d4fd904384f30a98e99995379dd"&gt;7ef825b&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add left text alignment for list in dropdown menu. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/6997266506d65cf68f5ab8768b50aa84a981b6fe"&gt;6997266&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix horizontal centering for default spinner. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/50af67499c054a0416e6440f7a05dd8646c69428"&gt;50af674&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;All semantic color references to _theme.scss will now use _config.scss for customization &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/174"&gt;#174&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/d3e0f9a92ad54e5794e1f48e9fa58699b61dab07"&gt;d3e0f9a&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove duplicate generation of blur util classes &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/c60a2378022f96a4fdae4bccc41bc4ad091496ee"&gt;c60a237&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix grid row support, grid rows will now have a max of 6 &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/74cdab9f343984bff51e7e808030893625ceb17f"&gt;74cdab9&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove extra padding on anchor tags &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/9bd4aec64929454ca83fcb54aa39dbee70e8970e"&gt;9bd4aec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Move pointer cursor to &lt;code&gt;tag__close-btn&lt;/code&gt; specifically instead of colored tags &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/193"&gt;#193&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/0a6c23f563d5fccab588f0bd9296e8686aebe871"&gt;0a6c23f&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hide overflow for frame component &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/195"&gt;#195&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/7fd9209c1488a2f91dd82a6c6a8c00dcdebc5bd2"&gt;7fd9209&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Restrict transition animation to only box shadow &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/194"&gt;#194&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/4731e12e2d26357cded418d40214ab461d365ebc"&gt;4731e12&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix specifier for tree-nav class for small viewports &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/875a5ce5c16e56cd326f2fa656d3095308c0ce7a"&gt;875a5ce&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix issue where range slider does not slide from start to end &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/217"&gt;#217&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/77582e7203b99d8869dc6019b60616877218959c"&gt;77582e7&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix accordion rendering on Safari &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/216"&gt;#216&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/ab69a6cfd4118a92856d8eb0cb0efa99ca695cc4"&gt;ab69a6c&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Set pointer as default cursor for pagination-item a tag &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/211"&gt;#211&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/d9ea857dc9ef61d55a6ffb52f2397bf56b513ac8"&gt;d9ea857&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vertical center spinner fix (&lt;a href="https://github.com/Spiderpig86/Cirrus/pull/242"&gt;#242&lt;/a&gt;) &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/856461d3540862d826d1c3925ecef68b69528a42"&gt;856461d&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix bug where list dropdown menu flashes briefly during page load &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/207"&gt;#207&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/2cbc17a00f8979bc348385e468143830fd6db03e"&gt;2cbc17a&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Use default browser focus styling for elements that don't have custom styling &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/b16448501295d758ea17bbbe7e816334afb0fc0b"&gt;b164485&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove unused focus styling &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/68eb3a2a2a5197a84d377cf490eda4bc5460322d"&gt;68eb3a2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix incorrect property used for column gap &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/81336a84267cfdf0c177d923993ab2c8118e74f8"&gt;81336a8&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix bug where webkit default focus styles were applied instead of input, textarea, and select focus styles, refactor forms code &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/cff0e97632c661c231dec4b90b6790391fb1b951"&gt;cff0e97&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix incorrect selector used in commit &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/a95694b017bafe98ccb8a9721ceef79ec9654c7f"&gt;a95694b&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/7090712d5340d92dea790d1c5ef5030ad7322b02"&gt;7090712&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Form border styling should respect --border-opacity var if present &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/95bb80bba0f46a06cc7dffa1cece9688074e9d3e"&gt;95bb80b&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix horizontal alignment of loading spinner &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/f093e8fab94e7f44e6cf677e993731dbe9783a03"&gt;f093e8f&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💥 Breaking Changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Rename &lt;code&gt;tree-nav--shown&lt;/code&gt; to &lt;code&gt;tree-nav--visible&lt;/code&gt; to follow new class conventions &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/37290afa9f320deffaaf551abd75c5db53794ee7"&gt;37290af&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deprecate &lt;code&gt;.menu-item.right&lt;/code&gt;, revert to applying list stylings to child link to support nested lists, expand list stylings to child divs and spans first children only &lt;a href="https://github.com/Spiderpig86/Cirrus/issues/198"&gt;#198&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/889f0fec8f0ac1aa6532bbd08cfb09bbdbb93be4"&gt;889f0fe&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deprecating &lt;code&gt;.usquare&lt;/code&gt; and &lt;code&gt;.usquare.delay&lt;/code&gt; classes due to incompatible color styling &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/161bf52e8eb3bdc898f55545ec8c311c57364af4"&gt;161bf52&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>showdev</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>🚀Maximizing Efficiency and Impact - Why I choose Mermaid for Graph Creation</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Tue, 20 Jun 2023 15:00:00 +0000</pubDate>
      <link>https://dev.to/spiderpig86/maximizing-efficiency-and-impact-why-i-choose-mermaid-for-graph-creation-1kl4</link>
      <guid>https://dev.to/spiderpig86/maximizing-efficiency-and-impact-why-i-choose-mermaid-for-graph-creation-1kl4</guid>
      <description>&lt;p&gt;Before I talk about what Mermaid is and why you should use it, let's take a step back and discuss why diagrams are essential for engineers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Diagram?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2MLRFM4W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evcuuuar641cak2hmmtr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2MLRFM4W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evcuuuar641cak2hmmtr.png" alt="Image description" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Diagrams are an invaluable resource for conveying ideas with clarity and ease. In software engineering, all facets of our work involve the transfer of information over different mediums to make informed decisions. Like slideshows and newsprint, readers tend to be more captivated by images and graphics over long walls of text. In design reviews, diagrams are used to represent complex system architectures and relationships from a birds-eye view. Engineers use these to effectively convey their vision. With large walls of text, readers can be more disengaged, causing them to haphazardly scan through the document. Logic errors, bottlenecks, and security risks that could have been caught in the design phase may one day wreak havoc on production.&lt;/p&gt;

&lt;p&gt;Diagrams also play a pivotal role in documentation, where they serve as visual anchors for complex concepts. A boon to engineers old and new, diagrams depict structures that anyone can reference at any time. It fosters better understanding, easier knowledge transfer, and improved operations. The next someone asks you about where your team sits in a vast network of services, you can send them an image that depicts a high-level overview of your upstream and downstream callers.&lt;/p&gt;

&lt;p&gt;One of the biggest problems with diagrams, like any other documentation, is that it presents an overhead, sometimes large, to engineers when they don't want to maintain it. The danger comes when engineers forget to update a diagram when changes occur, forget to include a diagram in the first place, or reference a stale diagram as the source of truth. These situations can result in extra engineering time to verify its validity, or worse, making the wrong decisions under incorrect assumptions. If left untouched for even longer, it will have overreaching impacts of slowing the ramp-up of new hires and the productivity of the team.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://mermaid.js.org/"&gt;Mermaid&lt;/a&gt; comes in, a lightweight diagramming and charting tool designed to tackle stale diagrams.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Mermaid?
&lt;/h2&gt;

&lt;p&gt;Mermaid provides a lightweight syntax akin to Markdown for rendering all sorts of chart types. As of writing, Mermaid supports the following chart types:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/flowchart.html"&gt;Flowchart&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/sequenceDiagram.html"&gt;Sequence Diagram&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/classDiagram.html"&gt;Class Diagram&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/stateDiagram.html"&gt;State Diagram&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/entityRelationshipDiagram.html"&gt;Entity Relationship Diagram&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/userJourney.html"&gt;User Journey&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/gantt.html"&gt;Gantt&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/pie.html"&gt;Pie Chart&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/quadrantChart.html"&gt;Quadrant Chart&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/requirementDiagram.html"&gt;Requirement Diagram&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/gitgraph.html"&gt;Gitgraph (Git) Diagram&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/c4c.html"&gt;C4C Diagram (Context) Diagram&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/mindmap.html"&gt;Mindmaps&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/timeline.html"&gt;Timeline&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mermaid.js.org/syntax/zenuml.html"&gt;Zenuml&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The list above continues to grow with more functionality added each day. Because it is open-sourced, we can dig into issues and report/fix them ourselves if needed.&lt;/p&gt;

&lt;p&gt;Mermaid is designed to make diagramming easier by taking away the cognitive load that comes with positioning boxes, drawing and styling arrows, and other details that don't necessarily make the diagram any clearer -- helping you get things done faster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR

A[Hard] --&amp;gt;|Text| B(Round)
B --&amp;gt; C{Decision}
C --&amp;gt;|One| D[Result 1]
C --&amp;gt;|Two| E[Result 2]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of the neat features it has include CSS class support and integration from any webpage straight from their CDN.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph LR
    classDef terminal font-weight:bold,fill:#0066ff,color:white

    styled:::terminal
    unstyled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mermaid&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mermaid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    graph LR
    A --- B
    B--&amp;gt;C[fa:fa-ban forbidden]
    B--&amp;gt;D(fa:fa-spinner);
&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Mermaid?
&lt;/h2&gt;

&lt;p&gt;I wouldn't say this is a full-on replacement for heavier graphing applications like LucidChart, but it does solve some of the biggest problems with using these heavier tools. One of the biggest drawbacks of more involved diagramming tools is the amount of busy work that comes with it once you have a rough idea of what parts are needed. This is one reason that would cause people not to diagram in the first place.&lt;/p&gt;

&lt;p&gt;Mermaid takes that overhead and tosses it out the window by making all diagrams powered by its JS library and its intuitive syntax. There is no need to export your diagram as an image or some proprietary, editable file format anymore as these diagrams are self-documenting. With the proprietary format, you can edit and view it, but only in an editor that supports that format. With images, anyone can view them, but you cannot edit them. With Mermaid's markup, anyone can easily edit yet understand what the graph is depicting with a simple text editor. &lt;/p&gt;

&lt;p&gt;These diagrams are also immensely portable. They can live in your design docs or your source code. You can audit changes to the diagram via source control to know precisely what things have changed. For complicated visuals, engineers can pinpoint new lines, labels, etc. to make their comprehension faster.&lt;/p&gt;

&lt;p&gt;Another reason to try Mermaid for your next diagram is the growing community and third-party integration. With growing support from platforms like Github's Markdown renderer, Google Docs, Jupyter, and more, anyone can easily get started with a diagram by typing a few lines of markup in their favorite SaaS. Today, you can find Mermaid powering Kubernetes's documentation throughout the entire website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hVT6ugng--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g3sv7cfbz53vtioc7y62.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hVT6ugng--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g3sv7cfbz53vtioc7y62.png" alt="Image description" width="800" height="222"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Mermaid rendering on GitHub.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last but not least, the tooling is some of the easiest to get started with. Just hop onto &lt;a href="https://mermaid.live"&gt;mermaid.live&lt;/a&gt; to try Mermaid out yourself. The editor comes with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax highlighting&lt;/li&gt;
&lt;li&gt;Live previews&lt;/li&gt;
&lt;li&gt;Static URLs to share with others&lt;/li&gt;
&lt;li&gt;Export as JPG, PNG, SVG, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the live editor is not your cup of tea, you can try out their &lt;a href="https://github.com/mermaid-js/mermaid-cli"&gt;CLI&lt;/a&gt;. With a simple command, you can convert a &lt;code&gt;.mmd&lt;/code&gt; file to the format you want by specifying a couple of flags. Below is an example of generating an SVG from a Mermaid file with custom CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mmdc &lt;span class="nt"&gt;--input&lt;/span&gt; test-positive/flowchart1.mmd &lt;span class="nt"&gt;--cssFile&lt;/span&gt; test-positive/flowchart1.css &lt;span class="nt"&gt;-o&lt;/span&gt; docs/animated-flowchart.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Mermaid is a simple tool that takes away a lot of the overhead when it comes to creating diagrams. By eliminating the overhead associated with traditional diagramming software, Mermaid allows you to focus on content creation rather than struggling with complex tools. Its lightweight nature, intuitive syntax, and language-agnostic approach make diagramming a seamless and enjoyable experience. Moreover, Mermaid's integration with numerous third-party tools and platforms enhances its versatility and accessibility, allowing you to effortlessly incorporate captivating visualizations into websites, documentation, and other mediums. This is a tool I would gladly recommend the next time you are looking to diagram something.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Thanks for reading!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>TIL How Casing Can Break Netlify Functions</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Mon, 27 Feb 2023 15:00:00 +0000</pubDate>
      <link>https://dev.to/spiderpig86/til-how-casing-can-break-netlify-functions-4c4f</link>
      <guid>https://dev.to/spiderpig86/til-how-casing-can-break-netlify-functions-4c4f</guid>
      <description>&lt;p&gt;Did you know Netlify Functions could break when renamed to the same name with a different casing? I sure didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Netlify Functions?
&lt;/h2&gt;

&lt;p&gt;If you know what AWS Lambda or GCP Cloud Functions are, then Netlify Functions won't be a stranger to you. Netlify functions are serverless functions powered by Netlify's infrastructure. These functions are designed to allow you and me to quickly build and deploy serverless applications without the need for managing infrastructure or scaling concerns.&lt;/p&gt;

&lt;p&gt;One of the common ways we can use Netlify Functions is by serving API routes defined in frameworks like Next.js. Suppose I wanted to create some endpoints for my frontend application to call, like &lt;code&gt;/api/toast/&amp;lt;id&amp;gt;&lt;/code&gt;. In that case, Next.js enables you to create your route within the same root directory under the &lt;code&gt;/pages/api&lt;/code&gt; directory. You can read more about it &lt;a href="https://nextjs.org/docs/api-routes/introduction" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this a bug?
&lt;/h2&gt;

&lt;p&gt;I have been toying around with Netlify Functions to test a few APIs here and there to see how easy it is to integrate into a project. Over time, one of the routes I defined stopped working on my live deployment. &lt;em&gt;And the worst part was that it worked locally.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;The route was a &lt;a href="https://nextjs.org/docs/api-routes/dynamic-api-routes" rel="noopener noreferrer"&gt;dynamic API route&lt;/a&gt; that took in an id called &lt;code&gt;toastId&lt;/code&gt; so I can fetch a piece of Toast by id (this is a simple test app, don't worry about it).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toastId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Fetch the piece of toast by id and do something with it...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking through the Git history to see what change may have broke, it didn't give too many hints as to how I broke it -- neither did logging out the value on my local deployment. &lt;/p&gt;

&lt;p&gt;What did help was logging out the value on the deployed instance of my test app. Turns out, my renaming of the dynamic route file from &lt;code&gt;[toastid].ts&lt;/code&gt; to &lt;code&gt;[toastId].ts&lt;/code&gt; broke what I initially deployed to Netlify. When I ran the server locally, the casing change took effect as expected. However, it was not reflected when deployed to Netlify. As a result, my route was not able to extract the path parameter using the following  code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toastId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// `toastid` was being populated instead&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After renaming the file from &lt;code&gt;[toastId].ts&lt;/code&gt; to &lt;code&gt;[toastid].ts&lt;/code&gt;, the route worked once more.&lt;/p&gt;

&lt;p&gt;Now, is this a bug or just an undocumented behavior? Unfortunately, I have yet to find an answer for this in the Netlify and Next.js documentation or StackOverflow and Github Discussions. Please let me know if any of you do!&lt;/p&gt;

&lt;p&gt;From my perspective, naming in the Windows file system is not case-sensitive, but many Unix systems are. I'm curious to know if this influenced their decision on how they handled names with different casing. One possibility is that they wanted to map all names regardless of casing to the same function to avoid things breaking when deployed by different developers running different operating systems. But that's just a thought.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tl;dr
&lt;/h2&gt;

&lt;p&gt;The name you choose for your routes in your Next.js app matters. It may not be apparent when you're working on it locally, but quirks such as this one on Netlify can throw a wrench into your application deployed in prod.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Thanks for reading!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>lambda</category>
      <category>streaming</category>
      <category>scalability</category>
    </item>
    <item>
      <title>GoDaddy Redirect Hack</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Wed, 21 Dec 2022 14:00:00 +0000</pubDate>
      <link>https://dev.to/spiderpig86/godaddy-redirect-hack-3mko</link>
      <guid>https://dev.to/spiderpig86/godaddy-redirect-hack-3mko</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Over the last few days, website owners, specifically those hosting on GoDaddy, have been experiencing strange redirects to various websites. These redirects don’t happen all the time, but they seem to happen when a user first visits a page or refreshes it enough times to trigger it. I first stumbled upon this issue when visiting a website of a favorite restaurant of mine. Instead of seeing a page with pictures of food, menus, etc., I was presented with a &lt;a href="https://arstechnica.com/information-technology/2014/02/what-a-fake-antivirus-attack-on-a-trusted-website-looks-like/"&gt;fake AV page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Like with other security issues I discover, I end up jumping down the rabbit hole to investigate what this is, how it works, and whether there is a way to avoid it. Unfortunately, for this incident, the best perspective I can give is as an outsider. A compromised GoDaddy website would make this investigation easier, but it isn’t impossible without it.&lt;/p&gt;

&lt;p&gt;At a high level, the sequence of events is as follows based on testing and other observations:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The user visits a compromised website hosted on GoDaddy.
&lt;/h3&gt;

&lt;p&gt;Like me, users could visit a local business’s website they are familiar with.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Initial redirect occurs
&lt;/h3&gt;

&lt;p&gt;Through some black box logic (usually for first-time visitors or by chance), GoDaddy servers send a &lt;code&gt;302&lt;/code&gt; to the user to redirect them to the attacker’s website. Every compromised website I’ve found redirects to &lt;code&gt;46.4.68.136&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Currently, there is speculation that GoDaddy’s load balancers are compromised. Specific requests will redirect you to the attacker’s page, but this occurs less often than expected. The cached versions of these compromised sites show a redirect in the HTTP response’s header. This header will cause the browser to redirect to the website specified in the &lt;code&gt;path&lt;/code&gt; variable. Below are some examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="ne"&gt;Found&lt;/span&gt;
&lt;span class="na"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fri, 16 Dec 2022 22:23:24 GMT&lt;/span&gt;
&lt;span class="na"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache&lt;/span&gt;
&lt;span class="na"&gt;P3P&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"&lt;/span&gt;
&lt;span class="na"&gt;Cache-Control&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-cache&lt;/span&gt;
&lt;span class="na"&gt;Pragma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-cache&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;b64618f79bd8f79428b7f1f80c1abceb=qtjmovs3948hi5t1m2shhambr4; path=/&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://46.4.68.136/[REDACTED]?DOM=www.vocationalvisions.com&amp;amp;URI=%2findex.php&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;span class="na"&gt;Vary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/HTML; charset=utf-8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="ne"&gt;Found&lt;/span&gt;
&lt;span class="na"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sat, 17 Dec 2022 17:21:14 GMT&lt;/span&gt;
&lt;span class="na"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache&lt;/span&gt;
&lt;span class="na"&gt;X-Pingback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://www.bookkeepingservicesclt.com/xmlrpc.php&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://46.4.68.136/[REDACTED]?DOM=www.bookkeepingservicesclt.com&amp;amp;URI=%2findex.php&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;span class="na"&gt;Vary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html; charset=UTF-8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="ne"&gt;Found&lt;/span&gt;
&lt;span class="na"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fri, 16 Dec 2022 14:43:04 GMT&lt;/span&gt;
&lt;span class="na"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://46.4.68.136/[REDACTED]?DOM=www.daria-snadowsky.com&amp;amp;URI=%2findex.php&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;span class="na"&gt;Vary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html; charset=UTF-8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="ne"&gt;Found&lt;/span&gt;
&lt;span class="na"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tue, 13 Dec 2022 14:00:31 GMT&lt;/span&gt;
&lt;span class="na"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache&lt;/span&gt;
&lt;span class="na"&gt;X-Powered-By&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PHP/5.6.40&lt;/span&gt;
&lt;span class="na"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;http://saslist.com/wp-json/&amp;gt;; rel="https://api.w.org/"&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://46.4.68.136/[REDACTED]?DOM=saslist.com&amp;amp;URI=%2findex.php&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;span class="na"&gt;Vary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html; charset=UTF-8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="ne"&gt;Found&lt;/span&gt;
&lt;span class="na"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wed, 14 Dec 2022 00:20:15 GMT&lt;/span&gt;
&lt;span class="na"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache&lt;/span&gt;
&lt;span class="na"&gt;X-Powered-By&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PHP/5.6.40&lt;/span&gt;
&lt;span class="na"&gt;X-Pingback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://paypointeinc.com/xmlrpc.php&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://46.4.68.136/[REDACTED]?DOM=paypointeinc.com&amp;amp;URI=%2findex.php&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;span class="na"&gt;Vary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html; charset=UTF-8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These websites receive a response from an Apache-powered server and will redirect the user to the same malicious IP address -- whether they use WordPress or not doesn't play a factor.&lt;/p&gt;

&lt;p&gt;Every user that ends up being redirected to the attacker's host is then tagged with a cookie set from the response header. Every subsequent request on that host will also include that cookie to identify the victim.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Set-Cookie: a8163=&amp;lt;JWT looking token&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The second redirect occurs
&lt;/h3&gt;

&lt;p&gt;Redirect takes the user to a specific page on the attacker's website. This page then runs JavaScript to redirect them to phishing or pornographic content. The basic structure of the page we see is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://bad.website&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://bad.website&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                   &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
            The Document has moved &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://bad.website"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;here&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notably, you can find very similar snippets on Github. The redirect above isn't a new tactic used to redirect users. After this redirect, the user is at the attacker's final destination.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the attacker's site
&lt;/h2&gt;

&lt;p&gt;I've played around with the attacking site. Fun fact: the redirect page changes depending on your IP/location. The page would redirect me to the fake AV scanner if I were in North America. If I were in Europe, Asia, South America, etc., each would redirect me to different pages showing less-than-desirable content. Is this done intentionally to specialize attack payloads based on some level of effectiveness according to location, or is it just done randomly?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is serious
&lt;/h2&gt;

&lt;p&gt;In general, the attack is widespread, where we can find many compromised websites with a simple Google search of the attacking website's IP address. Many website owners are affected by this, as evinced by this &lt;a href="https://community.cloudflare.com/t/redirecting-to-unwanted-sites/445551"&gt;Cloudflare&lt;/a&gt; support question. Notably, even this &lt;a href="https://github.com/sveltejs/kit/issues/8187"&gt;issue&lt;/a&gt; found its way to one of the links in the SvelteKit docs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kH9Ocu1N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://pbs.twimg.com/media/FkX09HcWAAA7R39%3Fformat%3Djpg%26name%3Dlarge" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kH9Ocu1N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://pbs.twimg.com/media/FkX09HcWAAA7R39%3Fformat%3Djpg%26name%3Dlarge" alt="Google search results showing impact" width="800" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is not apparent that the attack impacts WordPress plugins, as &lt;a href="https://twitter.com/ColinQuarello"&gt;@ColinQuarello&lt;/a&gt; demonstrated. Newly provisioned accounts are also affected even with no content uploaded to the website. From this, we can assume that this could be a system-wide issue for the hosting provider.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://twitter.com/GoDaddyHelp?ref_src=twsrc%5Etfw"&gt;@GoDaddyHelp&lt;/a&gt; &lt;a href="https://twitter.com/GoDaddy?ref_src=twsrc%5Etfw"&gt;@GoDaddy&lt;/a&gt; 302 redirects are happening from your load balancers to spam/porn sites on the shared hosting infrastructure. I provisioned a new account and immediately got a 302 redirect when there was &lt;em&gt;no content on the site&lt;/em&gt;. Headers looked forged too. This is serious.&lt;/p&gt;

&lt;p&gt;— Colin Quarello (@ColinQuarello) &lt;a href="https://twitter.com/ColinQuarello/status/1604956452451504130?ref_src=twsrc%5Etfw"&gt;December 19, 2022&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once a user is in the hands of the attacker by being on the attacker's website, they can succumb to social engineering, phishing, etc. &lt;a href="https://www.inky.com/en/blog/phishing-scams-cost-companies-billions"&gt;Phishing scams&lt;/a&gt; alone cost companies and the general public billions per year.&lt;/p&gt;

&lt;p&gt;On the human side, this incident can lead to a loss in user trust not only for GoDaddy but also for small to medium-sized businesses that rely on the platform to host their company's website, e-commerce store, etc. These days, user trust is more important than ever to foster user growth and business expansion. According to a study done by &lt;a href="https://www.digicert.com/campaigns/digital-trust-survey"&gt;DigiCert&lt;/a&gt;, "84% of consumers would switch to a competitor if they lost trust in the enterprise". A single incident, let alone repeated occurrences, will degrade user trust faster than a business can regain it quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latest Developments
&lt;/h2&gt;

&lt;p&gt;As of 2022-12-20, GoDaddy is currently working on this incident (INC-5492776). I am unsure if this is an internal incident tracking number or something GoDaddy customers can access.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We're aware of the issue and working to correct it now. I can assure you that our teams are diligently working to restore service as soon as possible. ^CG&lt;/p&gt;

&lt;p&gt;— GoDaddy Help (@GoDaddyHelp) &lt;a href="https://twitter.com/GoDaddyHelp/status/1605268691951689756?ref_src=twsrc%5Etfw"&gt;December 20, 2022&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Previously, it was suspected to be a firewall-related issue, but this has since been disproven. GoDaddy engineers and users have found this issue occurring regardless of firewall configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Thanks for reading!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>🚀 Cirrus 0.7.1 Released - New utility classes, Accordion, and more</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Mon, 31 Oct 2022 19:39:43 +0000</pubDate>
      <link>https://dev.to/spiderpig86/cirrus-071-released-new-utility-classes-accordion-and-more-46gh</link>
      <guid>https://dev.to/spiderpig86/cirrus-071-released-new-utility-classes-accordion-and-more-46gh</guid>
      <description>&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;Just released 0.7.1 for Cirrus. Check it out! The focus for this update was to add new utility classes for absolutes, transitions, etc, add the new Accordion component, and more.&lt;/p&gt;

&lt;p&gt;💎 &lt;a href="https://github.com/Spiderpig86/Cirrus/"&gt;Cirrus&lt;/a&gt;&lt;br&gt;
📝 &lt;a href="https://cirrus-ui.com/"&gt;Documentation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Cirrus?
&lt;/h2&gt;

&lt;p&gt;Cirrus is a component and utility centric SCSS framework&lt;br&gt;
designed for rapid prototyping. I started this project back in late 2016, but never really wrote about it. The main reason why you should use it is if you need to proto-type a project ASAP with many pre-styled component classes. There's a lot of CSS frameworks already out there, but Cirrus comes with my (and those who contributed in the past) take on what a CSS framework should have and a distinct style.&lt;/p&gt;
&lt;h2&gt;
  
  
  Onto the Updates
&lt;/h2&gt;
&lt;h3&gt;
  
  
  🎉 Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add configurations for specifying custom font families for primary and secondary fonts. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/dfaadb19914e92612f0f775ec13d4efc6c7ac7dc"&gt;dfaadb1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Removed auto-imports for Google fonts for GDPR compliance. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/dfaadb19914e92612f0f775ec13d4efc6c7ac7dc"&gt;dfaadb1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;btn--disabled&lt;/code&gt; class to support diabled state for &lt;code&gt;div&lt;/code&gt; and &lt;code&gt;a&lt;/code&gt; tags. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/85651388cfe90219a8d51c4cef94f47c1ac68eba"&gt;8565138&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Button shadows now rely on CSS variables. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/d9fc6ac98c892b711ddd070b4a8db50887973e0e"&gt;d9fc6ac&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;New map added to &lt;code&gt;_config.scss&lt;/code&gt; to include fg and bg colors. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/faac886ecb62c2bcbdb7e937b8a7c9430925a558"&gt;faac886&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new &lt;code&gt;modal--visible&lt;/code&gt; class to replace &lt;code&gt;shown&lt;/code&gt; class to display modal dialogs. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/2c3c6d7ab93a6c18c40dbc4ca140ed1d66c55b9c"&gt;2c3c6d7&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new &lt;code&gt;flex-basis&lt;/code&gt; utility classes. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/23925ed3c62795bbff22f4c692b6f1ae86c2846d"&gt;23925ed&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/b070926d3d8e73f0f94fb80b3d35da2463d7d16a"&gt;b070926&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new blur filter utility classes with viewport variants disabled. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/f723636792ab2978e66570c11f1ca62fff78e2cf"&gt;f723636&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add utility classes for fixed widths based on default sizing system. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/21e1b5a69f03a96888931c252b5be950c4007571"&gt;21e1b5a&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add new utility classes for min/max width using breakpoints. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/6a2b469c97c4068c8d5dddd66e095d498fcd836e"&gt;6a2b469&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;accordion&lt;/code&gt; component. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/5347b9f61b7ba1671c2a63f2566b6c9f8a443b83"&gt;5347b9f&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add border color and border opacity utility classes. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/ae26f16cbb0a022f9c00c659ea56c233426f7755"&gt;ae26f16&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add utility classes for fixed heights based on default sizing system. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/7111de8a52f054c1d6dd00823c15739aca0b34bd"&gt;7111de8&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add Gzipped build option for Cirrus for Gulp. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/8ad20517dfa10161ef36ee10ee0a0626b878510d"&gt;8ad2051&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add utility classes for transition durations. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/d0814bf5fe8670d3d597711feb040a753e9c2873"&gt;d0814bf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add utility classes for line heights. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/6ac14a31548425ec7f3b3fc49ecef68a70cebc71"&gt;6ac14a3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;25p&lt;/code&gt;, &lt;code&gt;75p&lt;/code&gt; and negative versions of absolute util classes. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/4fc644dd7f7d6bb6cd94c389b39ee231b14ab760"&gt;4fc644d&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Add absolute values for absolute util classes based on default sizing system (limited to 0-4rem). &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/1e83295b2fefcfbae0e8a53334c5614b10c05cd9"&gt;1e83295&lt;/a&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/7dfa71f2809367a3cfb4f19de3f5bd2af0126b95"&gt;7dfa71f&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;border-width&lt;/code&gt; util classes. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/374cc0ec05e757d189cd67298cb91db8eff8ff53"&gt;374cc0e&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  🐛 Fixes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fix bug with non-vertically aligned toggle component for different zoom levels. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/368a6dbbc6a38043843f1031a63125bbc865fa58"&gt;368a6db&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Move inline image values to constants. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/6b5a99961f986abcbbe52e2e4825cd4089fe8970"&gt;6b5a999&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;generate-classes-for-viewport&lt;/code&gt; should reuse logic from &lt;code&gt;generate-classes-for-viewport-with-map&lt;/code&gt;. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/37c5f8e09bd80e6f48315d8fc7aa365507faa438"&gt;37c5f8e&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Transparent border added to default style for all elements so border util classes will show up. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/b6e3a9fc359b00528b3bcb008068ea8346225ead"&gt;b6e3a9f&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;leading-none&lt;/code&gt; should have a line height of 1 instead of 0. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/ee293bea11df97cf2c02e0541748bb2dcba2ee25"&gt;ee293be&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  💥 Breaking Changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Themed button classes now respect changes in &lt;code&gt;control-themes&lt;/code&gt; map inside &lt;code&gt;_config.scss&lt;/code&gt;. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/faac886ecb62c2bcbdb7e937b8a7c9430925a558"&gt;faac886&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;_config.scss&lt;/code&gt; to have components (e.g. avatars, tabs, etc.) as top level attributes in the config instead of having &lt;code&gt;avatar-sizes&lt;/code&gt; for example.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Before&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="err"&gt;...
&lt;/span&gt;&lt;span class="na"&gt;avatar-sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;breakpoints&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;button-sizes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;After&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="err"&gt;...
&lt;/span&gt;&lt;span class="na"&gt;avatars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sizes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;breakpoints&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;widths&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;breakpoint-pairs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;buttons&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sizes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Remove &lt;code&gt;ms&lt;/code&gt; vendor prefix for flexbox due to high CSS3 standard adoption. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/34c838b9ab87302947cd38b2a9869f361bce786e"&gt;34c838b&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove prefixes for remaining styles that have &amp;gt; 98% unprefixed adoption according to caniuse. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/b1d1fb140ad875f6acb1dc09578ac132cff0b4fc"&gt;b1d1fb1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove all remaining CSS prefixes except &lt;code&gt;-webkit-tap-highlight-color&lt;/code&gt;. &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/eff211e6b008f0cb3289d51eb338913e0ffaa8a3"&gt;eff211e&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed viewport classes for &lt;code&gt;col&lt;/code&gt; where it was shifted 1 level wider than expected in 0.7.0&lt;/strong&gt; &lt;a href="https://github.com/Spiderpig86/Cirrus/commit/628a815832e195b048bd71da250e225853a1ef85"&gt;628a815&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Airpods Not Charging on Windows ⚡</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Tue, 23 Aug 2022 02:50:07 +0000</pubDate>
      <link>https://dev.to/spiderpig86/airpods-not-charging-on-windows-1a0i</link>
      <guid>https://dev.to/spiderpig86/airpods-not-charging-on-windows-1a0i</guid>
      <description>&lt;p&gt;Airpods are pretty cool, especially when you never have to worry about the tangled mess of wired headphones. What’s not cool is when you own a Windows machine and plugging the USB wire to charge your Airpod doesn’t work. The steps below should apply to all Airpods, but I have personally tested it using a first-generation Airpods Pro. I only realized this was a problem when after leaving the Airpods to charge via my laptop USB port for days, it never seemed to charge.&lt;/p&gt;

&lt;p&gt;This guide is for anyone who has tried to charge their Airpods to no avail despite many resets.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was not the issue
&lt;/h2&gt;

&lt;p&gt;If you look online, most of the guides will suggest you reset your Airpods in case there was some other internal failure, such as the firmware for both Airpods being out of sync. Before you proceed with the rest of this guide, I recommend following those steps &lt;a href="https://support.apple.com/en-us/HT209463"&gt;here&lt;/a&gt; first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;The root cause of this issue is a driver problem. To fix it, all you have to do is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect your Airpods case with your Windows machine using the charging cable you have.&lt;/li&gt;
&lt;li&gt;Open Control Panel and click 'Devices and Printers'.&lt;/li&gt;
&lt;li&gt;Under the 'Unspecified' dropdown group, you should see 'AirPod Case'. Double click this item.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wj75307T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/Spiderpig86/blog/master/images/Airpods%2520Not%2520Charging%2520on%2520Windows/Step3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wj75307T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/Spiderpig86/blog/master/images/Airpods%2520Not%2520Charging%2520on%2520Windows/Step3.PNG" alt="" width="395" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the 'Hardware' tab and double click 'HID-compliant vendor-defined device'.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MWUDMx6G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/Spiderpig86/blog/master/images/Airpods%2520Not%2520Charging%2520on%2520Windows/Step4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MWUDMx6G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/Spiderpig86/blog/master/images/Airpods%2520Not%2520Charging%2520on%2520Windows/Step4.PNG" alt="" width="432" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on 'Change settings'.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ChDOQppU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/Spiderpig86/blog/master/images/Airpods%2520Not%2520Charging%2520on%2520Windows/Step5.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ChDOQppU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/Spiderpig86/blog/master/images/Airpods%2520Not%2520Charging%2520on%2520Windows/Step5.PNG" alt="" width="422" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click 'Disable Device'.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xn9jV-hc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/Spiderpig86/blog/master/images/Airpods%2520Not%2520Charging%2520on%2520Windows/Step6.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xn9jV-hc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/Spiderpig86/blog/master/images/Airpods%2520Not%2520Charging%2520on%2520Windows/Step6.PNG" alt="" width="422" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finally, unplug and plug in your AirPod case. At this time, your case should charge correctly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope these steps fixed the issue. If not, feel free to consult other guides or give it a good old slap to get things working again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>airpods</category>
      <category>tutorial</category>
      <category>debug</category>
    </item>
    <item>
      <title>⚡The Fastest Way to Develop and Deploy Your Next Project</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Thu, 09 Jun 2022 17:47:34 +0000</pubDate>
      <link>https://dev.to/spiderpig86/the-fastest-way-to-develop-and-deploy-your-next-project-1dbp</link>
      <guid>https://dev.to/spiderpig86/the-fastest-way-to-develop-and-deploy-your-next-project-1dbp</guid>
      <description>&lt;p&gt;👋 Hey everyone!&lt;/p&gt;

&lt;p&gt;Do you like launching stuff? I like launching stuff! A few months ago, I released one of the largest updates to my CSS framework, &lt;a href="https://www.cirrus-ui.com/"&gt;Cirrus&lt;/a&gt;. Cirrus is a CSS framework designed for rapid prototyping and production use with a wide variety of ready-to-use components to plug into your app and utility classes to customize them. This makes building anything new with Cirrus a breeze with dead simple configuration and little overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Cirrus-UI/Cirrus-Start"&gt;Cirrus Start&lt;/a&gt; is my next project that pushes Cirrus to the next level. The first step with any project is to read the setup section of the documentation. This involves hours or even days to get things going the way you want them. Sometimes the documentation is bad, confusing, or both. Even with good documentation, who wants to spend all that time learning a whole new framework in-depth to get the ball going?&lt;/p&gt;

&lt;p&gt;Introducing ⚡ Cirrus Start, the fastest way to bring your idea to life with a framework designed for rapid prototyping. Out of the box, Cirrus Start comes with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💎 Direct integration with &lt;strong&gt;Cirrus&lt;/strong&gt; using &lt;strong&gt;Sass&lt;/strong&gt; configuration. This allows you to customize what features you want in Cirrus and which classes to add/remove during project generation.&lt;/li&gt;
&lt;li&gt;⚙️ &lt;strong&gt;Webpack&lt;/strong&gt; to bundle all your assets in a fast and configurable manner. This includes a few standard Webpack plugins and a live reload server for fast iteration.&lt;/li&gt;
&lt;li&gt;⚗️ &lt;strong&gt;PurgeCSS&lt;/strong&gt; is included by default to minimize build sizes and remove all Cirrus classes that are not used for builds.&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Surge.sh&lt;/strong&gt; to deploy and tear down your project with ease.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How do you use it?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wc6JfdpA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wc6JfdpA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/1.gif" alt="Step 1 Clone the repo" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;First clone the repository.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W3vONX7d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W3vONX7d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/2.gif" alt="Step 2 Open in Editor" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Open the cloned repository in your favorite editor.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eb1lsYTq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eb1lsYTq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/3.gif" alt="Step 3 Try running local server" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Try running the local server.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QhMdIsAa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QhMdIsAa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/4.gif" alt="Step 4 Make your edits" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make any edits you want to make.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qeJJynTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qeJJynTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://github.com/Cirrus-UI/Cirrus-Start/raw/main/gifs/5.gif" alt="Step 5 Deploy" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Deploy your site to Surge.sh!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I customize Cirrus?
&lt;/h2&gt;

&lt;p&gt;Cirrus is loaded within &lt;code&gt;src/index.scss&lt;/code&gt;. The file contents is quite simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Configure &lt;/span&gt;
&lt;span class="c1"&gt;// https://www.cirrus-ui.com/getting-started/configuration&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"cirrus-ui/src/cirrus-ext"&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;with&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nt"&gt;extend&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Other Styles&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('https://raw.githubusercontent.com/Spiderpig86/Cirrus/gh-pages/cirrus-docs-next/static/img/gradient.jpg')&lt;/span&gt; &lt;span class="nb"&gt;no-repeat&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-attachment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I strongly recommend reading more about how to customize Cirrus using the configuration mapping &lt;a href="https://www.cirrus-ui.com/getting-started/configuration"&gt;here&lt;/a&gt; to configure it the way you want. However, configuration is &lt;strong&gt;not necessary&lt;/strong&gt; and you can use the framework as is!&lt;/p&gt;

&lt;p&gt;If you'd like to have Cirrus specific configs placed in a separate file, you could move it to a file like &lt;code&gt;src/cirrus.config.scss&lt;/code&gt;. Just remember to add &lt;code&gt;import './cirrus.config.scss'&lt;/code&gt; inside &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;This pretty much ends my spiel on Cirrus Start. I can't wait to see what all of you will build with this! And of course, feedback will be appreciated. 😊&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>beginners</category>
      <category>css</category>
    </item>
    <item>
      <title>The Browser in the Browser (BITB) Attack: Lies, Deceit, and CSS</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Wed, 30 Mar 2022 16:33:28 +0000</pubDate>
      <link>https://dev.to/spiderpig86/the-browser-in-the-browser-bitb-attack-lies-deceit-and-css-1kio</link>
      <guid>https://dev.to/spiderpig86/the-browser-in-the-browser-bitb-attack-lies-deceit-and-css-1kio</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The attack, possible enhancements to the attack, and any attached source code are to be used for &lt;strong&gt;educational purposes only&lt;/strong&gt;. By no means use them for illegal activities such as phishing. &lt;strong&gt;You are responsible for your own actions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Restating the disclaimer mentioned in mr.d0x's repository: Usage of these templates for attacking targets without prior consent is illegal. It's the end user's responsibility to obey all applicable laws. The developer is not responsible for any misuse of these templates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“Beware the Ides of March”, they say; and we should for good reason.&lt;/p&gt;

&lt;p&gt;On March 15th, a security researcher by the name of &lt;em&gt;mr.d0x&lt;/em&gt; published an &lt;a href="https://mrd0x.com/browser-in-the-browser-phishing-attack/" rel="noopener noreferrer"&gt;article&lt;/a&gt; a &lt;strong&gt;nearly undetectable&lt;/strong&gt; phishing attack that most users would quickly overlook as a legitimate sign-in dialog. This form of phishing, coined as the &lt;em&gt;Browser in the Browser&lt;/em&gt; attack, presents a large complication to the web’s growing reliance on SSO and OAuth dialogs to authorize and authenticate users to online services like social media, cloud storage, and other platforms that may store sensitive user information. Common examples of this we see today are signing into other services with our Google Account, Microsoft Account, etc. which we would automatically trust due to reputation.&lt;/p&gt;

&lt;p&gt;This attack takes advantage of the trust users built with these sign-in processes to steal credentials by replicating a fake OAuth modal using plain old HTML, CSS and JS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Origins
&lt;/h2&gt;

&lt;p&gt;While this attack is making headlines today, it is a variation of something that has existed since the late 2000s. Back around when Windows XP still had over 80% market share and Internet Explorer was the most used browser in the world, &lt;a href="https://security.googleblog.com/2010/04/rise-of-fake-anti-virus.html" rel="noopener noreferrer"&gt;fake online antivirus scanners&lt;/a&gt; started to pop up at every corner of the internet. Since Windows wasn’t known for its stellar security features, selling fake and harmful malware disguised as antiviruses was a very lucrative business, with &lt;a href="https://sites.cs.ucsb.edu/~chris/research/doc/weis11_fakeav.pdf" rel="noopener noreferrer"&gt;3 companies racking up a total of $130 million during their existence&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The goal of these fake antivirus scanners was to scare users into purchasing their “antivirus” since their machine was magically infected one day. These phishing sites flooded your screens with Windows XP or 7 looking windows consisting of flashing red text, a large list of viruses, and giant call-to-actions coercing you to register the product to save your computer from certain destruction. For Windows XP, you would see something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSpiderpig86%2Fblog%2Fraw%2Fmaster%2Fimages%2FBrowser%2520in%2520the%2520Browser%2520%28BITB%29%2520Attack%2FFakeAV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSpiderpig86%2Fblog%2Fraw%2Fmaster%2Fimages%2FBrowser%2520in%2520the%2520Browser%2520%28BITB%29%2520Attack%2FFakeAV.png" alt="FakeAV"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eventually, attackers became more sophisticated and started emulating window dialogs from your operating system which made it more believable that this was an official product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSpiderpig86%2Fblog%2Fraw%2Fmaster%2Fimages%2FBrowser%2520in%2520the%2520Browser%2520%28BITB%29%2520Attack%2Ffakeavgif.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSpiderpig86%2Fblog%2Fraw%2Fmaster%2Fimages%2FBrowser%2520in%2520the%2520Browser%2520%28BITB%29%2520Attack%2Ffakeavgif.gif" alt="Fake AV Gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A panicked user would obediently follow the prompts to download the &lt;strong&gt;scareware&lt;/strong&gt; which launches the fake antivirus with a “trial” period. After that period ends, it will degrade the performance of the host machine or flood it with more malware to convince the user to purchase an official license to remove all the adware, spyware, and other crap it put on your machine. Once the victim purchases the software, the attacker now has your name, billing address, and credit card information to perform whatever transaction it wants.&lt;/p&gt;

&lt;p&gt;Fake antiviruses are a whole other topic that I can dive deep into, but the part of this whole phishing exercise that is still very effective and is presented in &lt;em&gt;mr.d0x’s&lt;/em&gt; blog post is the usage of a fake window inside of a webpage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Attack
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;mr.d0x’s&lt;/em&gt; proof of concept is an evolution from what we saw with the fake antivirus scan windows. Instead of displaying a scanning page with system-related details, this proof of concept phishes uses a malicious OAuth dialog akin to signing up for an online service using a third-party account. If this doesn’t sound familiar to you, then the GIF below may be more familiar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSpiderpig86%2Fblog%2Fraw%2Fmaster%2Fimages%2FBrowser%2520in%2520the%2520Browser%2520%28BITB%29%2520Attack%2Foauth.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSpiderpig86%2Fblog%2Fraw%2Fmaster%2Fimages%2FBrowser%2520in%2520the%2520Browser%2520%28BITB%29%2520Attack%2Foauth.gif" alt="OAuth Gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sample OAuth dialog from Facebook’s JavaScript SDK.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The first portion of this attack requires the victim to visit a compromised domain where the attacker set up the trap. This can either be done using &lt;a href="https://en.wikipedia.org/wiki/IDN_homograph_attack" rel="noopener noreferrer"&gt;IDN Homographs&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/DNS_spoofing" rel="noopener noreferrer"&gt;DNS poisoning/spoofing&lt;/a&gt;. This portion of the attack is probably the harder part to implement and convince users to visit the site for reasons explained below. The most convincing approach is to use DNS poisoning, but that in itself is harder to set up.&lt;/p&gt;

&lt;p&gt;Assuming that the attacker successfully lures the victims into the phishing site, by cleverly designing the page to look as legitimate as possible to an existing sign-in page today, the attacker should be able to convince users to sign in via some third party service. Once the user clicks the ‘Sign In’ or ‘Sign Up’ option, they will be presented with a fake popup window that loads the attacker’s fake login page. Once the user enters their credentials, the user has been completely compromised.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/mrd0x/BITB" rel="noopener noreferrer"&gt;demo&lt;/a&gt; &lt;em&gt;mr.d0x&lt;/em&gt; created provides templates for creating fake popup windows for several popular operating systems and their variants.  Each folder for each OS has these files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├───Windows-Chrome-LightMode
│       index.html
│       logo.svg
│       script.js
│       ssl.svg
│       style.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index.html&lt;/code&gt; is just a test page that contains the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logo.svg&lt;/code&gt; is the favicon used by the fake window.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;script.js&lt;/code&gt; contains the logic for dragging the window, animating the buttons, etc.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ssl.svg&lt;/code&gt; is the lock icon you would see in the address bar of the popup window.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;style.css&lt;/code&gt; contains styles used on the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code for the window is quite simple and setup instructions can be found in the &lt;a href="https://github.com/Spiderpig86/BITB" rel="noopener noreferrer"&gt;README&lt;/a&gt;. It should work like what is shown in the GIF below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmrd0x%2FBITB%2Fraw%2Fmain%2Fdemo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmrd0x%2FBITB%2Fraw%2Fmain%2Fdemo.gif" alt="mr.d0x BITB Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A gif from mr.d0x’s BITB repo demonstrating how it works.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhancements in the Fork
&lt;/h3&gt;

&lt;p&gt;After seeing the templates provided in the repository, I decided to play around with it to make things more convincing and created a fork &lt;a href="https://github.com/Spiderpig86/BITB" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Currently, the templates are very well designed for each OS which makes it look seamless, however, some interactions with the popup can lead more experienced users to easily tell that this is fake.&lt;/p&gt;

&lt;p&gt;Out of curiosity, I wanted to see what other changes we can do to make this more believable. To keep things simple, I mainly focused on enhancing the Windows dark theme in a directory called &lt;code&gt;Windows-Chrome&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I first changed the title and address bars to correspond to a login that existed, such as the Google sign-in page. I then fixed up some of the styling involving the fonts, alignment of text elements, and the overflow of the address bar so the text doesn’t look cut off.&lt;/p&gt;

&lt;p&gt;To make the window loading more realistic, I added some random delay when displaying the page and an opacity transition when showing and hiding the window.&lt;/p&gt;

&lt;p&gt;The next thing I did was made the window itself resizable. The user should be able to drag the window in all directions just like a real one would. This came with some challenges that were solved with some clever CSS trickery during dragging and not dragging.&lt;/p&gt;

&lt;p&gt;The final step was creating a sample phishing page with the SSO sign-in button and creating a modified version of the Google login page that looks similar to what we see today. The only thing I added was a new password field (which is shown when we hit next but that was pretty hard to replicate) and a custom function for when the user hits enter/clicks next. For any seasoned user, they would be able to tell that Google no longer places a password field under the email field in the latest version of the sign-in dialog. &lt;/p&gt;

&lt;p&gt;For the phishing page that launches the fake OAuth dialog, we can make things more convincing by setting the &lt;code&gt;href&lt;/code&gt; to a real-looking Google sign-in URL and having an &lt;code&gt;onclick&lt;/code&gt; function that returns false. By doing so, the browser will ignore the &lt;code&gt;href&lt;/code&gt; and run your JavaScript function instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://accounts.google.com/o/oauth2/auth/identifier?response_type=code&amp;amp;client_id=1083004"&lt;/span&gt;
    &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"return loadWindow()"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"clickme u-flex text-gray-800 font-normal"&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"clickme"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon-svg"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://upload.wikimedia.org/wikipedia/commons/5/53/Google_%22G%22_Logo.svg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn-text m-0 u-flex u-items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;b&amp;gt;&lt;/span&gt;Sign in with Google&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadWindow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#window&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toggleClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phish.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After these changes, the final result looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSpiderpig86%2Fblog%2Fraw%2Fmaster%2Fimages%2FBrowser%2520in%2520the%2520Browser%2520%28BITB%29%2520Attack%2Fdemo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSpiderpig86%2Fblog%2Fraw%2Fmaster%2Fimages%2FBrowser%2520in%2520the%2520Browser%2520%28BITB%29%2520Attack%2Fdemo.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Further Enhancements
&lt;/h3&gt;

&lt;p&gt;What I mentioned prior is not an exhaustive list of enhancements that can be made to the existing phishing template. Some other things that could be added are switching between dark and light themes depending on whether the user has a light-colored or dark-colored operating system. It would still require some refactoring of the existing template to bundle multiple variants of the window there, but it can be done relatively easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Dark theme styles go here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Light theme styles go here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another enhancement would be to add window states to simulate when the window is in focus and out of focus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this is effective
&lt;/h3&gt;

&lt;p&gt;This attack wouldn’t be as effective on observant users who are well aware of similar attacks such as this. However, where this phishing attack shines is how it can mimic a legitimate environment so closely that most users wouldn’t think twice about entering their credentials. To the average user, seeing a lock and a trusted domain in the “address bar” is enough verification to trust the login page they are currently seeing.&lt;/p&gt;

&lt;p&gt;Browsers won’t be able to effectively tag which popup iframes are malicious and which aren’t making it very difficult to solve. The HTML structure of the page would not be too different from another website that uses iframes and browsers would have to scan for different variations of class names to identify if the page is displaying a fake popup window or not. Therefore from a web browser standup, not much can be done.&lt;/p&gt;

&lt;p&gt;One project that aims to tackle this by alerting every iframe (including legitimate content) is &lt;a href="https://github.com/odacavo/enhanced-iframe-protection" rel="noopener noreferrer"&gt;odacavo/enhanced-iframe-protection&lt;/a&gt;. It’s a good first step towards addressing this issue but proves to be very cumbersome as you would need to allow-list all frames yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why it is not effective
&lt;/h3&gt;

&lt;p&gt;The major reason why this attack does not work is that it relies on the user landing on a phishing domain, to begin with. Browsers today are baked in with multiple measures to let users know whether a website is a phishing site or not such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Displaying &lt;a href="https://www.forbes.com/sites/leemathews/2019/12/11/google-chrome-adds-real-time-warnings-for-phishing-attacks/?sh=139fa5166068" rel="noopener noreferrer"&gt;phishing warnings&lt;/a&gt; for suspected phishing sites.&lt;/li&gt;
&lt;li&gt;Highlighting the domain in the address bar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These measures generally help users to be more cognizant of possible attacks, but not all will pay attention to these warnings.&lt;/p&gt;

&lt;p&gt;Assuming that the victim is not aware of the phishing page, the next stage of the attack still has plenty of holes that the user can poke to realize that they are being duped. For starters, some of the possible criticisms addressed with my enhanced fork include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing window behavior such as resizing.&lt;/li&gt;
&lt;li&gt;Weird clipping of the address in the address bar on the right side.&lt;/li&gt;
&lt;li&gt;Instant load of the window (added fake delays).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even with the enhancements, there are still lots of holes in the phishing attack. The first glaring issue is figuring out how to handle the window frames of all operating systems in existence? Sure, we can use the Windows and Mac OS window design to fool most users, but even these common operating systems also come with different variants and shades. Designing a bunch of Windows to match all these variations is a huge waste of time. Not to mention all the different combinations that come with different Linux distros. I mean, seeing a Windows border frame on Mac OS is just &lt;em&gt;sloppy&lt;/em&gt; and &lt;em&gt;embarrassing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.discordapp.net%2Fattachments%2F496678355123306508%2F783798110476435476%2Funknown.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.discordapp.net%2Fattachments%2F496678355123306508%2F783798110476435476%2Funknown.png" alt="Steam BitB"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second issue is that the fake OAuth dialog window cannot be moved outside the browser. You’ll notice that attempting to drag it outside the browser would just cause it to get stuck at the edge of the page. Minimizing the browser would also hide the window with it. There is no trace of it in your taskbar and it won’t be shown on Task View for Windows or Mission Control on Mac OS.&lt;/p&gt;

&lt;p&gt;The third big issue is that if you use a password manager (which you should be using, offline or not), your password manager will not fill out the login information because you’re not on a website your password manager trusts. This should be a very clear indication that something is not right and you should migrate away from the page immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigations
&lt;/h3&gt;

&lt;p&gt;To counter this phishing attack, below are a few things you can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the popup window and compare it to your OS’s window controls. If it looks different, then you are being phished.&lt;/li&gt;
&lt;li&gt;Try moving the fake popup window outside the browser, checking to see if it shows up as a window in your taskbar, maximize, minimize, etc. If it does not work as expected, then it is a phishing attack.&lt;/li&gt;
&lt;li&gt;If your password manager does not autofill the details, then you are on a phishing site.&lt;/li&gt;
&lt;li&gt;Setup 2FA/MFA on your online services to add extra protection from any compromised credentials.&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://noscript.net/" rel="noopener noreferrer"&gt;NoScript&lt;/a&gt;. An extreme way to mitigate this issue, but it will 100% work.&lt;/li&gt;
&lt;li&gt;Spoof your user agent. Any possible user-agent detection by the phishing site to show the correct window won’t work correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The new, or old I should say, &lt;em&gt;browser in the browser&lt;/em&gt; (BitB) attack may be making a comeback for the 2020s — this time in the form of fraudulent OAuth popups. Although this method of phishing is extremely convincing to most, modern browsers offer a large amount of protection against phishing sites already which stops users before they even land on the phishing site. A plethora of issues already makes this attack less effective than it could’ve been. Its lethality, however, comes from browsers failing to block the phishing site. The sign-in dialog’s appearance gives most users a false sense of security leading the user to input their credentials.&lt;/p&gt;

&lt;p&gt;It’s possible that in the future more of the things we are used to on the web, such as accepting cookies on sites, may also be weaponized against us by attackers.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mrd0x.com/browser-in-the-browser-phishing-attack/" rel="noopener noreferrer"&gt;https://mrd0x.com/browser-in-the-browser-phishing-attack/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.malwarebytes.com/cybercrime/2017/03/drafta-multi-purpose-fake-online-scanner/" rel="noopener noreferrer"&gt;https://blog.malwarebytes.com/cybercrime/2017/03/drafta-multi-purpose-fake-online-scanner/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Spiderpig86/BITB" rel="noopener noreferrer"&gt;https://github.com/Spiderpig86/BITB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>🚀 The fastest way to design and implement your app - Cirrus 0.7.0 is now out!</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Tue, 22 Mar 2022 16:33:27 +0000</pubDate>
      <link>https://dev.to/spiderpig86/the-fastest-way-to-design-and-implement-your-app-cirrus-070-is-now-out-2ndm</link>
      <guid>https://dev.to/spiderpig86/the-fastest-way-to-design-and-implement-your-app-cirrus-070-is-now-out-2ndm</guid>
      <description>&lt;p&gt;👋 Hey everyone!&lt;/p&gt;

&lt;p&gt;In the middle of the pandemic, I sought to rewrite Cirrus to fix many of the issues and inconsistencies that plagued its previous versions and launched on November of 2020. It was well received and definitely inspired me to keep on going.&lt;/p&gt;

&lt;p&gt;This year, I am releasing a new version of Cirrus that focuses on the framework's future. There are often debates on whether component-centric frameworks such as Bootstrap or atomic-classed frameworks such as Tailwind are better for its:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning curve&lt;/li&gt;
&lt;li&gt;Maintainability&lt;/li&gt;
&lt;li&gt;Flexibility&lt;/li&gt;
&lt;li&gt;Ease of use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cirrus is a framework that takes the best parts of both of these types of frameworks and provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many pre-built basic components to accelerate your development velocity (e.g. Avatars, Modals, Tabs, etc.). To keep your code clean, all component classes are built following the BEM convention.&lt;/li&gt;
&lt;li&gt;A suite of common utility classes to help tweak and polish your designs when needed. These utility classes are so powerful that you can construct components with them alone.&lt;/li&gt;
&lt;li&gt;Different CDN builds of the framework that can be dropped in at any browser (core, ext, all). Choose one that fits your needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the release of 0.7, much of the framework can now be customized. Since the framework is written entirely in Scss, it can take advantage of all the existing APIs for class generation. The new configuration system in Cirrus allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add/Edit/Delete component styles.&lt;/li&gt;
&lt;li&gt;Add/Edit/Delete utility classes.&lt;/li&gt;
&lt;li&gt;Specify which breakpoints are supported.&lt;/li&gt;
&lt;li&gt;Toggle which classes should have viewport variants (to help save on build size).&lt;/li&gt;
&lt;li&gt;Enable/Disable different parts of the framework.&lt;/li&gt;
&lt;li&gt;And more :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hVj01Qtu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vc16utejtkjpsia4b2g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hVj01Qtu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vc16utejtkjpsia4b2g.jpg" alt="Responsive Demo" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  😵 So what's actually new?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🌞 A new way to use Cirrus
&lt;/h3&gt;

&lt;p&gt;With previous versions, the only ways to use Cirrus was to either linking the CDN link at the top of your page or importing &lt;code&gt;cirrus-ui&lt;/code&gt; into your Node project.&lt;/p&gt;

&lt;p&gt;This is fine, but it lacked any sort of customization. The only way to change Cirrus was to clone the project and build your own version locally. Dart Sass has given me immense opportunity to improve Cirrus's usability and fix technical debt and hacks. One of the things it also enabled was directly importing Cirrus into your Sass/Scss project directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not only can you import different pre-configured versions of Cirrus, but also take advantage of all the functions, mixins, constants, etc. that Cirrus comes built with.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.scss&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"cirrus-ui/cirrus-core"&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Core build OR&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"cirrus-ui/src/cirrus-ext"&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Extended build&lt;/span&gt;

&lt;span class="k"&gt;@screen-above&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;md&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.my-class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nf"&gt;hex-to-rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'blue'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'600'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;.25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more having to build Cirrus separately and then copying and pasting the generated styles to your project. Read more on this &lt;a href="https://www.cirrus-ui.com/getting-started/configuration"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔨 Focus on configurability
&lt;/h3&gt;

&lt;p&gt;One of the things this rewrite aimed to accomplish was configurability. In the past, CSS frameworks were just things we plop into our project and we either accepted styles it gave us or we spent countless hours overriding them to fit our needs. Why should we continue to subject ourselves to that amount of torture?&lt;/p&gt;

&lt;p&gt;0.7.0 marks a big step in dynamic class generation. Whether you are building Cirrus locally or you're importing Cirrus directly into your Sass files, you can take advantage of defining a configuration object in both scenarios when you import the framework. Use the configuration object to specify viewports for classes, extend existing component and utility styles, toggle features, and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"cirrus-ui/src/cirrus-ext"&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;with&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nt"&gt;excludes&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nt"&gt;ABSOLUTES&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="nt"&gt;opacity&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;Disable&lt;/span&gt; &lt;span class="nt"&gt;default&lt;/span&gt; &lt;span class="nt"&gt;opacity&lt;/span&gt; &lt;span class="nt"&gt;classes&lt;/span&gt;
        &lt;span class="nt"&gt;extend&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;Add&lt;/span&gt; &lt;span class="nt"&gt;your&lt;/span&gt; &lt;span class="nt"&gt;own&lt;/span&gt;
            &lt;span class="nt"&gt;opacity&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nt"&gt;25&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;.25&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nt"&gt;50&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;.5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nt"&gt;75&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;.75&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently most utility classes and components can be customized through here in this release, but I hope to extend this capability a lot more in future updates. Read more on this &lt;a href="https://www.cirrus-ui.com/getting-started/configuration"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎨 Revamped colors
&lt;/h3&gt;

&lt;p&gt;Default colors have been tuned a bit for increased vibrancy and range. Of course, these colors can be customized via the configuration object as well.&lt;/p&gt;

&lt;p&gt;Check out all colors &lt;a href="https://www.cirrus-ui.com/fundamentals/colors"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ Utils, utils, and more utils
&lt;/h3&gt;

&lt;p&gt;Utility classes within Cirrus have grown to be quite popular. This update adds utility classes for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/fonts/letter-spacing"&gt;Letter Spacing&lt;/a&gt; — utilities to control letter spacing of an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/utils/flexbox#flex-wraps"&gt;Flex Wrap&lt;/a&gt; — utiltiies to control how flex items wrap.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/utils/flexbox#flex-grow"&gt;Flex Grow&lt;/a&gt; — utilties to control how flex items grow.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/utils/flexbox#flex-shrink"&gt;Flex Shrink&lt;/a&gt; — utilities to control how flex items shrink.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/fundamentals/colors#changing-opacity"&gt;Color Opacity&lt;/a&gt; — utilities to control color opacity (background and text/border).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/utils/gap"&gt;Flex/Grid Gap&lt;/a&gt; — utilities to control gutters between grid and flexbox items.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/fonts/size"&gt;Font Size&lt;/a&gt; — utilities for font sizes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/utils/box-shadow"&gt;Box Shadow&lt;/a&gt; — utilities to add box shadows to an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/utils/border-radius"&gt;Border Radius&lt;/a&gt; — utilities to control border radius of an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/layout/min-height"&gt;Min Height&lt;/a&gt; — utilities to set min height of an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/layout/max-height"&gt;Max Height&lt;/a&gt; — utilities to set max height of an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/layout/min-width"&gt;Min Width&lt;/a&gt; — utilities to set min width of an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/layout/max-width"&gt;Max Width&lt;/a&gt; — utilities to set max width of an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/fonts/font-weights"&gt;Font Weights&lt;/a&gt; — utiltiies to control font weight of an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/utils/opacity"&gt;Opacity&lt;/a&gt; — utilities to control the opacity of an element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/utils/zindex"&gt;Z-Index&lt;/a&gt; — utilities to control the z-index of an element.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of classes have also seen the introduction of viewport variants. Those can be found in the class specific documentation in the docs page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-50 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;50&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-40 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;40&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-30 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;30&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-20 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;20&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-10 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;10&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-1 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-0 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-n1 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;-1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-z-auto ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;auto&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🏗️ Components
&lt;/h3&gt;

&lt;p&gt;This update wasn't as focused on introducing new components, but it was not forgotten. Some of the new components styles are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/elements/breadcrumbs"&gt;Breadcrumbs&lt;/a&gt; — a component used to create a sense of hierarchy when navigating a website.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/elements/progress"&gt;Progress&lt;/a&gt; — Cirrus comes with styling right out of the box for the progress HTML element.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cirrus-ui.com/animations"&gt;New Animations&lt;/a&gt; — new animation classes include pulse (skeleton loaders) and ping (notification).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💎 But wait, there's more
&lt;/h3&gt;

&lt;p&gt;Check out all the other changes in the &lt;a href="https://www.cirrus-ui.com/getting-started/release-notes"&gt;release notes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  😊 Thank You
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/euMcV5dkIqL0GzycqG/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/euMcV5dkIqL0GzycqG/giphy.gif" alt="Thank you" width="278" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before I start blabbering on too much about the technical details, I will end by saying please check out the docs, give it a try on the playground, and let me know your thoughts!&lt;/p&gt;

&lt;p&gt;And as always, if you like my work, feel free to follow me on these platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.stanleylim.me/"&gt;Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Spiderpig86"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/serbis/"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>codenewbie</category>
      <category>webdev</category>
      <category>css</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why I Unit Test My Sass: Mixins 🧪</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Tue, 01 Feb 2022 17:05:15 +0000</pubDate>
      <link>https://dev.to/spiderpig86/why-i-unit-test-my-sass-mixins-2cbp</link>
      <guid>https://dev.to/spiderpig86/why-i-unit-test-my-sass-mixins-2cbp</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/spiderpig86/why-i-unit-test-my-sass-functions-37l8"&gt;previous post&lt;/a&gt;, I discussed why Sass unit testing is a thing and why you may want to incorporate it into your project. If you were shocked to find out that this is something that is done, please let me explain in my first post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IFYXG-2t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media0.giphy.com/media/Z5HVfEvnxr67u/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IFYXG-2t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media0.giphy.com/media/Z5HVfEvnxr67u/giphy.gif" alt="Mind blown" width="319" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unit testing Sass functions wasn’t too complicated. But what about mixins? Testing-wise, it is quite similar. The only difference here is the syntax.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that the steps outlined here assume you have set up your test project similar to how it is described in the first post. If you have an existing project, you can follow along and delete the test artifacts later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this tutorial, let’s create a simple viewport mixin similar to the &lt;code&gt;screen-above()&lt;/code&gt; mixin defined in &lt;a href="https://github.com/Spiderpig86/Cirrus"&gt;Cirrus&lt;/a&gt;. The mixin below will apply any styles passed into it if it is larger than the specified breakpoints. Copy the contents below into a new file within &lt;code&gt;/src/mixins.scss&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// mixins.scss&lt;/span&gt;

&lt;span class="nv"&gt;$breakpoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;$xs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;640px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$sm&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;768px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$md&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$lg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1280px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$xl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1536px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Applies styles for viewports greater than a certain breakpoint. An error is thrown if the breakpoint does not exist.&lt;/span&gt;
&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;screen-above&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$breakpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;map-has-key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$map&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$breakpoints&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$breakpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;@error&lt;/span&gt; &lt;span class="s1"&gt;'The given breakpoint &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$breakpoint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; for @screen-above does not exist.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="nf"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$breakpoints&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$breakpoint&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;@content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Before you write the test, make sure you have already done the prerequisites required to run Sass unit tests in the first place. You can read all about it in my first post &lt;a href="https://dev.to/spiderpig86/why-i-unit-test-my-sass-functions-37l8"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s start by creating our test file for mixins in &lt;code&gt;test/mixins.spec.scss&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'../src/mixins.scss'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s add our test block with the &lt;code&gt;@describe&lt;/code&gt; annotation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'screen-above()'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Next, we will add our test case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'screen-above()'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'should return screen-above 640px given $xs'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Then, we will use the &lt;code&gt;assert()&lt;/code&gt; mixin to take the actual &lt;code&gt;output&lt;/code&gt; and the value we &lt;code&gt;expect&lt;/code&gt;. Below we place the &lt;code&gt;.test&lt;/code&gt; class in the output that includes the &lt;code&gt;screen-above&lt;/code&gt; mixin. The &lt;code&gt;expect&lt;/code&gt; would contain the CSS output we expect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'should return screen-above 640px given $xs'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;assert&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;output&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;.test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;screen-above&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="nf"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;640px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;.test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To summarize this better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;expect&lt;/code&gt; - describes the expected results.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt; - the actual output from the mixin you are testing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assert&lt;/code&gt; - a clause that binds the &lt;code&gt;output&lt;/code&gt; and &lt;code&gt;expect&lt;/code&gt; mixins together for the test.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running Your Tests
&lt;/h3&gt;

&lt;p&gt;Like in the previous blog post, you can now run &lt;code&gt;npm test&lt;/code&gt; to execute Jest for us. To be honest, it’s probably faster to just type &lt;code&gt;jest&lt;/code&gt; instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the results of your first test similar to the output below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; PASS  tests/scss.spec.js
  Sass
    screen-above()
      √ should return screen-above 640px given $xs (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.143 s, estimated 4 s
Ran all test suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Well, that wraps up the tutorial on how to unit test mixins in Sass. In the next post, I will discuss how we can add error handling within our Sass that can be caught and tested using True.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tutorial</category>
      <category>testing</category>
    </item>
    <item>
      <title>Why I Unit Test My Sass: Functions 🧪</title>
      <dc:creator>Stanley Lim</dc:creator>
      <pubDate>Mon, 10 Jan 2022 16:30:39 +0000</pubDate>
      <link>https://dev.to/spiderpig86/why-i-unit-test-my-sass-functions-37l8</link>
      <guid>https://dev.to/spiderpig86/why-i-unit-test-my-sass-functions-37l8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Did you know you could unit test your Sass/Scss? No? Me neither.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you were to ask me years ago if there was such thing as unit testing Sass let alone stylesheets, I would tell you that it would probably be manual or automated with some framework like Selenium. Testing would be slow, arduous, and not very lightweight. &lt;/p&gt;

&lt;p&gt;This was the past and luckily, we now live during a time where we have choices. &lt;/p&gt;

&lt;p&gt;Today, there exists a nifty little testing framework called &lt;a href="https://github.com/oddbird/true"&gt;True&lt;/a&gt; that allows you to quickly write Sass unit tests without too much pain. It features a syntax that makes most front-end developers feel at home. If you have ever written unit tests using &lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt;, then using &lt;strong&gt;True&lt;/strong&gt; won’t actually be that much different.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Unit Test Sass?
&lt;/h3&gt;

&lt;p&gt;Before we dive into the tooling, let’s discuss why we should do it in the first place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/C2aPLNEvhtI6k/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/C2aPLNEvhtI6k/giphy.gif" alt="Deep in thought - Simpsons" width="299" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a basic example, let’s say you’re building a simple application that takes employees' orders for Pizza Friday. It’s a simple form that people in your team can select some radio box for which pizzas they want. You manually test, ship, and it becomes a big hit within the company.&lt;/p&gt;

&lt;p&gt;Now the people demand more features. Next, they ask you for dropdowns to allow for customization. You rinse and repeat the process above and you ship it. Now suddenly, people aren't too fond of radio boxes because it doesn’t allow them to select multiple types of pizzas. You go back and make some changes and test. Fearing that you may have broken other parts of the app, you manually test as well. Each successive update becomes more and more of a drag until you decide to give up maintaining it all together.&lt;/p&gt;

&lt;p&gt;The point is, unit testing solves the repetitive and arduous task of manually testing features over and over again. It is essentially:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A built-in sanity check for any new feature/integration into your existing application.&lt;/li&gt;
&lt;li&gt;A record/collection of all possible cases that may break your app. It is essentially free documentation for app behaviors given some input.&lt;/li&gt;
&lt;li&gt;Ensures that the code is maintainable even if someone else works in your codebase and makes a breaking change. Unit tests, if written properly, will catch those.&lt;/li&gt;
&lt;li&gt;Fewer fires in prod. &lt;em&gt;No one likes putting out fires on a Friday night.&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These principles apply to so many parts of software development, so maybe it wouldn’t be too farfetched to also unit test your Sass. If you have a large project with lots of generated styles, debugging issues won’t always be that straightforward. Unit testing Sass should help with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asserting that the inputs to your functions and mixins output what you expect.

&lt;ul&gt;
&lt;li&gt;This is especially important if you use any third-party libraries. You don’t want your stylesheets breaking because of some breaking change upstream.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Reduce the amount of manual testing required for checking styles.&lt;/li&gt;
&lt;li&gt;Checking Sass compilation errors that generate some of your stylesheets properly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Is this for me?
&lt;/h3&gt;

&lt;p&gt;I usually advocate for more testing regardless of the size of your project, but it might not be appropriate for what you are building.  You should be testing if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You use functions and mixins in your project, especially if they are on the more complicated side.&lt;/li&gt;
&lt;li&gt;Your Sass files are used in many other files.&lt;/li&gt;
&lt;li&gt;Your customer experience highly depends on how reliable the UI is rendered correctly. For example with an e-commerce site, you wouldn’t want your styles to accidentally hide the “Buy” button with some adjacent element.&lt;/li&gt;
&lt;li&gt;You’re building a library, like &lt;a href="https://github.com/Spiderpig86/Cirrus"&gt;Cirrus&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;p&gt;Although unit testing Sass sounds like it will solve all your design problems, it doesn’t. Using a framework like Sass True would help to make sure that the CSS generated is what you expect, however it is only limited to testing functions and mixins.&lt;/p&gt;

&lt;p&gt;The final CSS output is really up to you to double-check and verify by hand. The same applies to whether the Sass would compile properly or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  So how do I get started?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_cv3jgOf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://64.media.tumblr.com/tumblr_ljkn5yjPkO1qixleeo1_400.gifv" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_cv3jgOf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://64.media.tumblr.com/tumblr_ljkn5yjPkO1qixleeo1_400.gifv" alt="Spongebob excited to start" width="372" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tutorial below walks you through setting up unit testing for a random Sass project. If you have another project you would like to add this to, then it may require some modification to the steps to fit your needs. We will be using &lt;a href="https://github.com/sass/dart-sass"&gt;Dart Sass&lt;/a&gt; as our transpiler of choice and the &lt;a href="https://github.com/oddbird/true"&gt;Sass True&lt;/a&gt; framework backed by Jest to write and perform our tests.&lt;/p&gt;

&lt;p&gt;In your project, first install the necessary dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add dart-sass sass-true glob jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize your node project.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;In your &lt;code&gt;package.json&lt;/code&gt;, define a test script to trigger Jest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test, the first thing we need to have is source code. If you don’t have any, you can copy this &lt;code&gt;string-split&lt;/code&gt; function to use an example and add it to &lt;code&gt;src/functions.scss&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* functions.scss */&lt;/span&gt;
&lt;span class="k"&gt;@function&lt;/span&gt; &lt;span class="nf"&gt;string-split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$delimiter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nv"&gt;$index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$delimiter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;@while&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$substring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$substring&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$delimiter&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nv"&gt;$index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$delimiter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;@return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this tutorial, we want to write unit tests to ensure that our string split function works as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bind Sass True with Jest
&lt;/h3&gt;

&lt;p&gt;With that part done, let’s integrate Jest to work with Sass True. In essence, Sass True acts as the &lt;em&gt;glue&lt;/em&gt; or &lt;a href="https://en.wikipedia.org/wiki/Shim_(computing)"&gt;Shim&lt;/a&gt; that binds your unit tests written in Sass into something that Jest can interpret.&lt;/p&gt;

&lt;p&gt;For our tests, let’s create a &lt;code&gt;test&lt;/code&gt; directory. Here we can throw whatever unit tests we want along with our shim file. Copy the shim file contents below and paste it into &lt;code&gt;test/scss.spec.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// scss.spec.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sassTrue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sass-true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;glob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;glob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`tests/**/*.spec.scss`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;testPath&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Run on each file with describe() and it() functions&lt;/span&gt;
    &lt;span class="nx"&gt;testFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sassTrue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runSass&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this shim file does is search for all files ending with the &lt;code&gt;.spec.scss&lt;/code&gt; extension to be unit tested. We use &lt;code&gt;glob&lt;/code&gt; to resolve all paths given the &lt;code&gt;testPath&lt;/code&gt; regex.&lt;/p&gt;

&lt;p&gt;For each test file, we will use Sass True to run the tests defined. In the second parameter of &lt;code&gt;runSass&lt;/code&gt;, we pass in the functions that we would want to use such as &lt;code&gt;describe&lt;/code&gt; and &lt;code&gt;it&lt;/code&gt; within our Sass unit tests. If you are not sure what these functions do, luckily you can refer to the &lt;a href="https://jestjs.io/docs/api#describename-fn"&gt;Jest Documentation&lt;/a&gt; because they behave exactly the same as their JavaScript counterparts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing Unit Tests
&lt;/h3&gt;

&lt;p&gt;Now comes the fun/boring part. Now we have to write the actual tests for our &lt;code&gt;string-split()&lt;/code&gt; function. Let’s create our first test file under the path &lt;code&gt;test/functions.spec.scss&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first thing we need to do is import the Sass True library and the file we want to test itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* functions.spec.scss */&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'../src/functions.scss'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What comes after will look very familiar if you are used to unit testing with Jest. The syntax is almost identical to writing JS, except = with a few minor syntactical differences. Instead of function calls, each of Jests’s global functions is defined as a mixin. First, let’s &lt;strong&gt;&lt;a href="https://jestjs.io/docs/api#describename-fn"&gt;describe&lt;/a&gt;&lt;/strong&gt; the item we are testing. This helps to group multiple test cases that test the same thing together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'string-split()'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Next, we will define the test cases themselves. We use the &lt;strong&gt;&lt;a href="https://jestjs.io/docs/api#testname-fn-timeout"&gt;it&lt;/a&gt;&lt;/strong&gt; aka the &lt;code&gt;test&lt;/code&gt; keyword to specify each input we want to test our function with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'string-split()'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'should return input string split using delimiter ","'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;To wrap this test case, we need a way to check whether the output value is what we expect, commonly known as asserting our output. The &lt;code&gt;assert-equal($assert, $expected)&lt;/code&gt; mixin is perfect for checking the output of &lt;code&gt;string-split()&lt;/code&gt;. The first parameter is what’s output by the function under test and the second parameter is the expected output of that value. You can read more about it &lt;a href="https://www.oddbird.net/true/docs/api-assert-values#mixin--assert-equal"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s say we want to test our function with an input string of &lt;code&gt;apples,oranges,bananas&lt;/code&gt; with a delimiter of &lt;code&gt;,&lt;/code&gt;. Then, we should expect our output to be &lt;code&gt;(‘apples’ ‘oranges’ ‘bananas’)&lt;/code&gt;. We can write our test like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'string-split()'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'should return input string split using delimiter ","'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;assert-equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;string-split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'apples,bananas,oranges'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'apples'&lt;/span&gt; &lt;span class="s1"&gt;'oranges'&lt;/span&gt; &lt;span class="s1"&gt;'bananas'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you are ready to run your first test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Your Tests
&lt;/h3&gt;

&lt;p&gt;Remember that script we added in the &lt;code&gt;package.json&lt;/code&gt; file earlier? We can now run &lt;code&gt;npm test&lt;/code&gt; to execute Jest for us. To be honest, it’s probably faster to just type &lt;code&gt;jest&lt;/code&gt; instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the results of your first test similar to the output below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; PASS  tests/scss.spec.js
  Sass
    string-split&lt;span class="o"&gt;()&lt;/span&gt;
      √ should &lt;span class="k"&gt;return &lt;/span&gt;input string &lt;span class="nb"&gt;split &lt;/span&gt;using delimiter &lt;span class="s2"&gt;","&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.058 s, estimated 6 s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎉 You did it 🎉
&lt;/h3&gt;

&lt;p&gt;Well, congratulations! Give yourself a big pat on the back for writing your first unit test in Sass. &lt;/p&gt;

&lt;p&gt;Unit testing is the most glamorous topic in any aspect of software development. It is arguably the part that developers dread the most as it is repetitive, boring, and disappointing when you find out the code you wrote is broken. However, it is the most essential mechanism that keeps code maintainable and explicit. Edge cases are explicit and all it takes is a single command execution to confirm any assumptions you had on how the system behaved given certain inputs.&lt;/p&gt;

&lt;p&gt;I previously thought Sass unit testing would never be a thing and why would it be a thing that developers would try to figure out. Today, I cannot think of developing &lt;a href="https://github.com/Spiderpig86/Cirrus"&gt;Cirrus&lt;/a&gt; or any large projects with hundreds of styles without unit testing.&lt;/p&gt;

&lt;p&gt;Anyway, don’t forget to check out &lt;a href="https://www.oddbird.net/true/docs/"&gt;True&lt;/a&gt;’s amazing documentation to learn more about what else it can do. In the next post in this series, I will go over how you can easily test your mixins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual &lt;a href="https://blog.stanleylim.me/"&gt;blog&lt;/a&gt;. Feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/serbis/"&gt;LinkedIn&lt;/a&gt; and follow me on &lt;a href="https://github.com/Spiderpig86"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tutorial</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
