<?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>Forem: Gleb Gorokhov</title>
    <description>The latest articles on Forem by Gleb Gorokhov (@glebgorokhov).</description>
    <link>https://forem.com/glebgorokhov</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%2F1330494%2Fb8e330e3-87bb-4fe5-a20c-f8b7afcb719c.jpg</url>
      <title>Forem: Gleb Gorokhov</title>
      <link>https://forem.com/glebgorokhov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/glebgorokhov"/>
    <language>en</language>
    <item>
      <title>How I reduced Tailwind CSS Home Page's HTML size by 28.5%, CSS by 23.7% and JS by 8.4%</title>
      <dc:creator>Gleb Gorokhov</dc:creator>
      <pubDate>Mon, 11 Mar 2024 14:32:56 +0000</pubDate>
      <link>https://forem.com/glebgorokhov/testing-the-breezify-reducing-tailwind-css-home-pages-html-by-30-css-by-20-and-js-by-10-5g05</link>
      <guid>https://forem.com/glebgorokhov/testing-the-breezify-reducing-tailwind-css-home-pages-html-by-30-css-by-20-and-js-by-10-5g05</guid>
      <description>&lt;p&gt;I really love Tailwind CSS! It's impressive to see how easily Tailwind has made it possible to integrate any project's design system into the code while optimizing the CSS simultaneously.&lt;/p&gt;

&lt;p&gt;Tailwind's approach to styling has resulted in very small CSS files. However, as a consequence, the HTML and JS files have become larger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/glebgorokhov/breezify"&gt;Breezify&lt;/a&gt; is my attempt to address this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;Breezify is a Node.js library that post-processes your CSS, HTML, and JS files in your build folder, updating them through AST (Abstract Syntax Tree) transformations in a few steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It identifies class names in your CSS files and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags in your HTML files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Breezify then generates a list of ultra-short class names, such as &lt;code&gt;.a&lt;/code&gt;, &lt;code&gt;.b&lt;/code&gt;, &lt;code&gt;.ab&lt;/code&gt;, etc., and creates a mapping that links your existing class names to these new ones. &lt;em&gt;Did you know that you only need 2 symbols to generate &lt;strong&gt;3328&lt;/strong&gt; unique class names?&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Afterwards, it updates the class names found in your CSS, HTML, and JS files to these new ones.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Breezify offers a simple mode that replaces everything using regex, but its default mode employs AST transformations, which are smarter and offer more control over what gets updated and what doesn't.&lt;/p&gt;

&lt;p&gt;So, in essence, Breezify converts this:&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;"w-full max-w-content px-6 md:px-9 grid mx-auto z-[1] relative gap-x-6 md:gap-x-10 gap-y-6 md:gap-y-10 grid-cols-1 md:grid-cols-2 justify-items-start items-start"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"gap-2 items-center mb-10 transition-all text-theme-text-interactive dark:text-dark-text-interactive group-hover:text-theme-text-interactive-hover hover:text-theme-text-interactive-hover dark:hover:text-dark-text-interactive-hover dark:group-hover:text-dark-text-interactive-hover"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Carts
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into this:&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;"a b c d e f g h i j k l m n o p"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"q r s t u v y z A B"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Carts
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySpecialClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary-color&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;decorated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decorated&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;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`.header[role='decorated']:not(.decorated) .aside, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decorated&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, aside &amp;gt; .aside, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mySpecialClass&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySpecialClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&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;decorated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&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;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`.c[role='decorated']:not(.b) .d, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decorated&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, aside &amp;gt; .d, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mySpecialClass&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how the word "decorated" is updated when it appears as a string, but remains unchanged when it is used as the name of a constant or as a value of the "role" attribute.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use Breezify
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;npm i breezify&lt;/code&gt; to install the module.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;breezify init&lt;/code&gt; to create a configuration file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run build&lt;/code&gt; (or your preferred build command) to build your project.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;breezify do&lt;/code&gt; to run Breezify.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more information, visit the &lt;a href="https://github.com/glebgorokhov/breezify"&gt;project's README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Breezify with the Tailwind CSS Homepage
&lt;/h2&gt;

&lt;p&gt;The testing process is straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Clone Breezify
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/glebgorokhov/breezify"&gt;Clone the repository&lt;/a&gt; and install dependencies with &lt;code&gt;pnpm install&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add Files for Testing
&lt;/h3&gt;

&lt;p&gt;Create a folder in &lt;code&gt;breezify/tests/data/build-files&lt;/code&gt; for your build files and place your files there.&lt;/p&gt;

&lt;p&gt;I will create a folder named &lt;code&gt;tailwind-complete&lt;/code&gt; and save the Tailwind homepage by simply saving the page from the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdiufc2y0x7s4de23wlq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdiufc2y0x7s4de23wlq8.png" alt="Tailwind CSS Homepage" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To check how it looks after such a straightforward saving, I can serve this folder using &lt;code&gt;npx serve tailwind-complete&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi648qimxxknym9aihb5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi648qimxxknym9aihb5s.png" alt="That's not bad" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwgri19of6jxsb3ozuq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwgri19of6jxsb3ozuq1.png" alt="Some pictures are missing" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Some pictures are missing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it works; however, some pictures are missing, but that's not an issue for testing purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Run the Test
&lt;/h3&gt;

&lt;p&gt;Then, I run &lt;code&gt;pnpm test&lt;/code&gt;, which creates a copy of this folder in &lt;code&gt;tests/data/output-files/%BUILD_FOLDER_NAME%&lt;/code&gt; and logs all the file size reductions in your terminal:&lt;/p&gt;

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

&lt;p&gt;Breezify reduced the size of HTML files by 28.55%, CSS files by 23.70%, and JS files by 8.44%. Not bad for already minified and obfuscated files, isn't it?&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Preview the Results
&lt;/h3&gt;

&lt;p&gt;Finally, we can preview the results by using &lt;code&gt;npx serve tailwind-complete&lt;/code&gt; in the &lt;code&gt;tests/data/output-files&lt;/code&gt; folder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvs2na0oznj3xl93yyvdk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvs2na0oznj3xl93yyvdk.png" alt="Preview Screenshot 1" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Interestingly, all images in the "breezified" version are functioning! Aside from the HTML, everything else appears the same:&lt;/p&gt;

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

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

&lt;p&gt;Breezify appears to be a valuable addition to the suite of post-processing libraries designed to minimize project file sizes as much as possible.&lt;/p&gt;

&lt;p&gt;However, it does have a few drawbacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It is written in JavaScript, meaning it may not be as fast as if it were developed in, for example, Rust.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a risk of inadvertently replacing the wrong value, but this is &lt;a href="https://github.com/glebgorokhov/breezify?tab=readme-ov-file#debugging"&gt;preventable&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a possibility of AST (Abstract Syntax Tree) parsing errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not all frameworks solely output CSS, JS, and HTML files. I am currently experiencing some difficulties making it work with Next.js, as it produces gzipped files that are challenging to process. However, I am confident this issue can be resolved. Feel free to &lt;a href="https://github.com/glebgorokhov/breezify?tab=readme-ov-file#contributing"&gt;contribute&lt;/a&gt; if you have a solution!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thank you for reading! I can't wait to hear your thoughts on Breezify. Please feel free to share your experiences and results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://t.me/breezifychat"&gt;Breezify Chat on Telegram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.me/ververy"&gt;Contact me on Telegram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/glebgorokhov"&gt;Follow me on X&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/breezify/"&gt;Join the r/breezify subreddit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>frontend</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
