<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Surjith's Blog]]></title><description><![CDATA[I write about things going on my mind.]]></description><link>https://blog.surjithctly.in</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 07:05:08 GMT</lastBuildDate><atom:link href="https://blog.surjithctly.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Component-based Web Development for Beginners]]></title><description><![CDATA[Website, It all starts with a simple index.html page. We can write anything and publishing it to the whole world is mind-blowing. Nobody believed it was the future. But here we are, living in the future. Web design is not about index.html anymore. It...]]></description><link>https://blog.surjithctly.in/component-based-web-development-for-beginners</link><guid isPermaLink="true">https://blog.surjithctly.in/component-based-web-development-for-beginners</guid><category><![CDATA[React]]></category><category><![CDATA[Astro]]></category><category><![CDATA[components]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Tue, 13 Sep 2022 14:47:01 GMT</pubDate><content:encoded><![CDATA[<p>Website, It all starts with a simple <code>index.html</code> page. We can write anything and publishing it to the whole world is mind-blowing. Nobody believed it was the future. But here we are, living in the future. Web design is not about <code>index.html</code> anymore. It's a complex infrastructure with complicated servers, countless libraries, frameworks, and tools. </p>
<p>Everything I mentioned above was invented for a common goal: Save Time. In this article, I'm going to explain  how Component-based approach speed up your web development using DRY Principle (Don't repeat yourself)</p>
<h2 id="heading-the-problem">The Problem.</h2>
<p>Imagine you have developed a simple website with few pages: "Home, About, Contact". Each page contains a Navbar, information for each page, and a footer. </p>
<p>Now you want to add another page called "Services". Easy. You create another page called services.html and add a link from other pages. </p>
<p>Now Imagine if this was a big project with 100s of pages. You would need to open 100 pages to add a link. Then you want the link to be first, Now you open the 100 pages again to make these changes. Sounds fun? No more. It's now become a boring repeatable task. And it's not scalable as our simple website. </p>
<h2 id="heading-the-solution">The Solution</h2>
<p>What if we could have a single Navbar page where we could update in one place and it automatically updates on 100s of these pages? Mind-blowing right? That's where the component-based approach comes into play. </p>
<p>Every modern framework like React, Next, Vue, Nuxt, Svelte, Astro, etc. supports this approach. We will see this through a live example by converting a normal page to a  component.</p>
<h2 id="heading-live-example">Live Example</h2>
<p>Here is a live example of how you can convert the following section to a component-based approach. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663061413054/9VcSseDaM.png" alt="image.png" /></p>
<p>Look at the above example. Which is often seen on any website. Usually the section will be on your index page. Let's see how we can transform it using modern tools. </p>
<p>Existing Layout</p>
<pre><code><span class="hljs-operator">&lt;</span><span class="hljs-operator">!</span>DOCTYPE html<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>html lang<span class="hljs-operator">=</span><span class="hljs-string">"en"</span><span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>head<span class="hljs-operator">&gt;</span> ... <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>head<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>body<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span><span class="hljs-comment">// nav, hero etc goes here&lt;/div&gt;</span>
    <span class="hljs-operator">&lt;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"our-section"</span><span class="hljs-operator">&gt;</span>
          ...
          ...
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span><span class="hljs-comment">// cta, footer etc goes here. &lt;/div&gt;</span>
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>body<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>html<span class="hljs-operator">&gt;</span>
</code></pre><p>The first thing you should do is to move it to another page and import the same in your main page. </p>
<h3 id="heading-heres-how-you-would-do-it-in-react">Here's how you would do it in React.</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/features.js</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MySection</span>(<span class="hljs-params"></span>) </span>{
 <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span> My section content <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
)}
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-comment">// pages/index.js</span>

<span class="hljs-keyword">import</span> MySection <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/features"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
 <span class="hljs-keyword">return</span> ( 
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span> 
      // nav, hero etc goes here
     <span class="hljs-tag">&lt;<span class="hljs-name">MySection</span>&gt;</span>
      // cta, footer etc goes here.
    <span class="hljs-tag">&lt;/&gt;</span>
)}</span>
</code></pre>
<h3 id="heading-heres-the-same-example-using-astrohttpsastrobuild">Here's the same example using <a target="_blank" href="https://astro.build/">Astro</a></h3>
<p>Astro is a web framework used for static site generation focused on speed using less or no javascript.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- components/features.astro --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span> My section content <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- pages/index.astro --&gt;</span>
---
import MySection from "../components/features.astro"
---

<span class="hljs-comment">&lt;!-- Nav, Hero goes here --&gt;</span>

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

<span class="hljs-comment">&lt;!-- CTA, Footer goes here --&gt;</span>
</code></pre>
<h2 id="heading-we-are-not-done-there-is-more">We are not done. There is more</h2>
<p>The first step is done, we have made sure it can be imported into any page. But there is still work pending. If you see the above image again, you can see that the feature is a two-column grid and shares the same design. So instead of repeating those four grids, we can use the data-driven component method to reduce even further. </p>
<p>Here's how you can do that in React. </p>
<ol>
<li>First, extract the repeatable data in to an array. In real project this might be coming from a headless CMS or an API. for demo, I will just put those as static array. </li>
</ol>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/features.js</span>

<span class="hljs-keyword">import</span> { MyIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"lib/icons"</span>;

<span class="hljs-keyword">const</span> features = [
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Competitive exchange rates"</span>,
    <span class="hljs-attr">desc</span>: <span class="hljs-string">"Lorem ipsum, dolor sit amet consectetur adipisicing elit. "</span>,
    <span class="hljs-attr">icon</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyIcon</span>/&gt;</span></span>
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"No hidden fees"</span>,
    <span class="hljs-attr">desc</span>: <span class="hljs-string">"Lorem ipsum, dolor sit amet consectetur adipisicing elit. "</span>,
    <span class="hljs-attr">icon</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyIcon</span>/&gt;</span></span>
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Transfers are instant"</span>,
    <span class="hljs-attr">desc</span>: <span class="hljs-string">"Lorem ipsum, dolor sit amet consectetur adipisicing elit. "</span>,
    <span class="hljs-attr">icon</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyIcon</span>/&gt;</span></span>
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Mobile notifications"</span>,
    <span class="hljs-attr">desc</span>: <span class="hljs-string">"Lorem ipsum, dolor sit amet consectetur adipisicing elit. "</span>,
    <span class="hljs-attr">icon</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyIcon</span>/&gt;</span></span>
  },
]

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MySection</span>(<span class="hljs-params"></span>) </span>{
 <span class="hljs-keyword">return</span> (
 <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span> // Section Title<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>  
      {features.map((item, index) =&gt; (
             <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>
               <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{item.icon}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{item.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{item.desc}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
             <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        ))}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/&gt;</span></span>
)}
</code></pre>
<p>Same example for Astro Static Site Generator. </p>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/features.astro</span>
---
<span class="hljs-keyword">import</span> { MyIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"assets/Icons.astro"</span>;

<span class="hljs-keyword">const</span> features = [
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Competitive exchange rates"</span>,
    <span class="hljs-attr">desc</span>: <span class="hljs-string">"Lorem ipsum, dolor sit amet consectetur adipisicing elit. "</span>,
    <span class="hljs-attr">icon</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyIcon</span>/&gt;</span></span>
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"No hidden fees"</span>,
    <span class="hljs-attr">desc</span>: <span class="hljs-string">"Lorem ipsum, dolor sit amet consectetur adipisicing elit. "</span>,
    <span class="hljs-attr">icon</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyIcon</span>/&gt;</span></span>
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Transfers are instant"</span>,
    <span class="hljs-attr">desc</span>: <span class="hljs-string">"Lorem ipsum, dolor sit amet consectetur adipisicing elit. "</span>,
    <span class="hljs-attr">icon</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyIcon</span>/&gt;</span></span>
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Mobile notifications"</span>,
    <span class="hljs-attr">desc</span>: <span class="hljs-string">"Lorem ipsum, dolor sit amet consectetur adipisicing elit. "</span>,
    <span class="hljs-attr">icon</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyIcon</span>/&gt;</span></span>
  },
]
---

  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span> // Section Title<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>  
      {features.map((item) =&gt; (
             <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
               <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{item.icon}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{item.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{item.desc}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
             <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        ))}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<h2 id="heading-further-customizations">Further Customizations</h2>
<p>You can go one step and pass the data as props. You can pass any data to this component to render. Here's a quick example with the title as props. </p>
<p><strong> React Props Example </strong></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// pages/index.js</span>

<span class="hljs-keyword">import</span> MySection <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/features"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
 <span class="hljs-keyword">return</span> ( 
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>  
     <span class="hljs-tag">&lt;<span class="hljs-name">MySection</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"My Custom Section Title"</span>&gt;</span> 
    <span class="hljs-tag">&lt;/&gt;</span>
)}</span>
</code></pre>
<p>Then on the component page: </p>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/features.js</span>

<span class="hljs-keyword">const</span> features = [...]

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MySection</span>(<span class="hljs-params">props</span>) </span>{
 <span class="hljs-keyword">return</span> (
 <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{props.title}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>  
      {features.map((item, index) =&gt; (
             ...
        ))}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/&gt;</span></span>
)}
</code></pre>
<p>Same with Astro:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/features.astro</span>
---
<span class="hljs-keyword">const</span> { title } = Astro.props;
---

  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span> {title} <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>  
      {features.map((item) =&gt; (
             ...
        ))}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<h2 id="heading-rendering-as-child-content">Rendering as child content</h2>
<p>Both React &amp; Astro support rendering content as children. Here's an example:</p>
<p><strong> React Example </strong></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// pages/index.astro</span>

<span class="hljs-keyword">import</span> MySection <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/features"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
 <span class="hljs-keyword">return</span> ( 
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>  
     <span class="hljs-tag">&lt;<span class="hljs-name">MySection</span>&gt;</span> 
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>I can do anything here..<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">MySection</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
)}
</code></pre>
<p>Then you can render that content using <code>{props.children}</code></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/features.js</span>

<span class="hljs-keyword">const</span> features = [...]

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MySection</span>(<span class="hljs-params">props</span>) </span>{
 <span class="hljs-keyword">return</span> (
 <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>  
      {props.children}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/&gt;</span></span>
)}
</code></pre>
<p><strong>Astro Example</strong></p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- pages/index.astro --&gt;</span>
---
import MySection from "../components/features.astro"
---

<span class="hljs-tag">&lt;<span class="hljs-name">MySection</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>I can do anything here..<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">MySection</span>&gt;</span>
</code></pre>
<p>Then, using <code>&lt;slot/&gt;</code> we can render the content inside the component. </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- components/features.astro --&gt;</span>

 <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>   
      <span class="hljs-tag">&lt;<span class="hljs-name">slot</span>/&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<hr />
<p>Hope you have learned something new. This is the best way to approach modern web development which is scalable and easy to maintain.  Let me know your questions. </p>
]]></content:encoded></item><item><title><![CDATA[How to create cross-compatible website shortcut for Mac & Windows]]></title><description><![CDATA[Mac creates shortcuts with .webloc extension while Windows creates it using .url extension. Let's see how to create both and how to make it work on both operating system. 
Creating Website Shortcut in Mac
You can create shortcut by dragging the websi...]]></description><link>https://blog.surjithctly.in/cross-compatible-website-shortcut-mac-windows</link><guid isPermaLink="true">https://blog.surjithctly.in/cross-compatible-website-shortcut-mac-windows</guid><category><![CDATA[macOS]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Shortcuts]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[website]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Mon, 08 Aug 2022 08:15:03 GMT</pubDate><content:encoded><![CDATA[<p>Mac creates shortcuts with <code>.webloc</code> extension while Windows creates it using <code>.url</code> extension. Let's see how to create both and how to make it work on both operating system. </p>
<h2 id="heading-creating-website-shortcut-in-mac">Creating Website Shortcut in Mac</h2>
<p>You can create shortcut by dragging the website URL to your finder folder or desktop using a Mac device. But if you are not on mac, you can create it manually. </p>
<h3 id="heading-1-create-a-file-with-webloc-extension">1. Create a file with <code>.webloc</code> extension.</h3>
<p>And add the following contents inside. Make sure to change the URL inside <code>&lt;string&gt;</code></p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">plist</span> <span class="hljs-meta-keyword">PUBLIC</span> <span class="hljs-meta-string">"-//Apple//DTD PLIST 1.0//EN"</span> <span class="hljs-meta-string">"http://www.apple.com/DTDs/PropertyList-1.0.dtd"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">plist</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>URL<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>https://yourwebsite.com<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">plist</span>&gt;</span>
</code></pre>
<p>Note: This only works on Mac and on windows this won't work. To create a shortcut compatible for both operating system, follow this steps:</p>
<h2 id="heading-creating-website-shortcut-in-windows">Creating Website Shortcut in Windows</h2>
<p>You can create shortcut by dragging the website URL to your finder folder or desktop using a Windows device. But if you are not on Windows, you can create it manually. </p>
<h3 id="heading-1-create-a-file-with-url-extension">1. Create a file with <code>.url</code> extension.</h3>
<pre><code class="lang-xml">[InternetShortcut]
URL=https://yourwebsite.com
</code></pre>
<p>The above shortcut will work on both Windows &amp; Mac. </p>
]]></content:encoded></item><item><title><![CDATA[Problems I faced in a Mac as a Windows user (and how to solve them)]]></title><description><![CDATA[Hey everyone,
This is a super small collection of small issues you might face in Mac as a Windows user. I will also explain how I solved them so you don't have to spend time searching. I'm a designer & front-end developer so my workflow is related to...]]></description><link>https://blog.surjithctly.in/problems-i-faced-in-a-mac-as-a-windows-user-and-how-to-solve-them</link><guid isPermaLink="true">https://blog.surjithctly.in/problems-i-faced-in-a-mac-as-a-windows-user-and-how-to-solve-them</guid><category><![CDATA[macOS]]></category><category><![CDATA[mac]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[Bugs and Errors]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Sat, 04 Sep 2021 13:00:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1630759717457/IbaChZfPO.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey everyone,</p>
<p>This is a super small collection of small issues you might face in Mac as a Windows user. I will also explain how I solved them so you don't have to spend time searching. I'm a designer &amp; front-end developer so my workflow is related to that. </p>
<h2 id="mouse-issues">Mouse issues</h2>
<p>You might feel so many problems with Mouse when you came from Windows. </p>
<h3 id="slow-mouse-tracking-speed">Slow mouse tracking speed</h3>
<p>This is the first issue I faced while setting up my Mac. The mouse speed was too slow than what we used to. </p>
<h4 id="solution">Solution:</h4>
<p>First, we have to patiently wait until the setup is completed. Then press CMD + Space to open a search box. Type mouse and it will lead you to Mouse settings in the System Preferences. </p>
<p>Just move the slider all the way to the right. Yes. to the right. You need that. </p>
<h3 id="no-middle-click">No Middle click</h3>
<p>In Windows, I can easily click on the scroll button and each link would open in a new tab. In Mac, there is no scroll wheel or button, so we have to hold the CMD key while clicking a link and it will open in a new tab. </p>
<h3 id="no-right-click-in-mouse">No right-click in mouse</h3>
<p>Yeah, you need to enable right-click manually from the same settings panel. </p>
<h3 id="mouse-scroll-direction">Mouse scroll direction</h3>
<p>We have to disable the Scroll direction: Natural option to feel the normal scrolling behavior as Windows. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627648765733/BMOWBppwd.png" alt="image.png" /></p>
<h2 id="maximize-window-full-screen">Maximize Window (Full Screen)</h2>
<p>In Windows, we were able to maximize windows easily using the icon. In Mac, if we clicked the maximize icon (green) it will go to full screen and hide the top and bottom menu. </p>
<p>To mimic Windows Maximize, we can double click on the top-bar of the selected application. But in some applications like Safari, it won't work. I have not yet found the best solution for it.</p>
<h2 id="facetime-keeps-signing-in-loading">Facetime keeps Signing in (loading)</h2>
<p>My facetime was not worked the first time but I connected to Ethernet instead of Wifi and restarted and it worked fine. </p>
<h2 id="messages-from-iphones-are-not-showing-in-imessage">Messages from iPhones are not showing in iMessage.</h2>
<p>It was all blank for me in iMessage. For that, I had to enable the "Forward Messages" option in iPhone and "Enable" iCloud Messages in Mac iMessage settings. Then I relaunched the app and all messages were synced. </p>
<h2 id="sleep-vs-shutdown-no-hibernate">Sleep vs Shutdown (No Hibernate)</h2>
<p>In Mac, there is no Hibernate option. Only sleep and Shutdown. Sleep is the popular option but makes sure you keep the Power connected and it's ON (except Macbook). </p>
<h2 id="display-size">Display Size</h2>
<p>By default, iMac uses the second right-most Display size as default. But for me, it was too small for my liking esp when coming from Windows. So I decided to change the display to the Scaled version. I asked a  <a target="_blank" href="https://twitter.com/surjithctly/status/1420780909616267266">few Mac users</a>  about it and seems it's perfectly fine to do so. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627998004338/FzyVfx3K7.png" alt="image.png" /></p>
<h2 id="npm-install-g-package-not-working">npm install -g [package] not working</h2>
<p>NPM global install won't work by default. I had to type in 3 following commands in the Terminal to get that work. </p>
<pre><code>sudo <span class="hljs-keyword">chown</span> -R $USER /usr/<span class="hljs-keyword">local</span>/lib/node_modules/
sudo <span class="hljs-keyword">chown</span> -R $USER /usr/<span class="hljs-keyword">local</span>/bin/
sudo <span class="hljs-keyword">chown</span> -R $USER /usr/<span class="hljs-keyword">local</span>/share/
</code></pre><p>⚠️ I'm still unsure of the consequences of this command. So use with caution. </p>
<h2 id="external-monitor-issues">External Monitor issues</h2>
<p>Since I was used to dual monitor on my Windows, I decided to buy new monitor to match the iMac's 24" screen. I also made sure its USB-C compatible so I could easily connect it. Since the main use for that is to code, I didn't buy 4k. But that was a mistake. Apple forces you to buy 4k monitors otherwise, all text on the second monitor looks pixelated from the 90s. The monitor I bought was HD (1980 x 1280) but for Mac, seems it's not enough. </p>
<p>For older software versions, they had an option called "Smooth Font when available" but were removed on Mac OS Big Sur update. I tried to install some 3rd party apps like "Tinkertool" and "Font Smoothing Adjuster" but none of them worked as expected. </p>
<p>I still have this issue and never found any good fix unless buying a new 4k monitor. That's a bummer. </p>
<h2 id="focusing-other-windows">Focusing other windows</h2>
<p>This is another frustrating thing in Mac when focusing on other windows. Imagine you are coding on VSCode and then you open a new Chrome Tab by right-clicking the tab, the cursor will blink on the address bar. Now when you start typing the text, you realize it's still typing on VSCode. Even we right-clicked and opened a tab on Chrome, the keyboard focus is still on VSCode. So I have to delete that text again and then type again after left-clicking on Chrome. </p>
<p>Usually, this won't affect much when you use a single monitor since we need to left-click anyways, but for Dual monitor users, it's a pain. </p>
<h2 id="drag-and-drop-selected-text">Drag and Drop Selected Text</h2>
<p>In windows, its easy to drag &amp; drop selected text using mouse. To drag selected text in mac, you have to hold left click for a second to enable dragging. Otherwise, it will remove the selection. So here's how you can drag selected text.</p>
<ol>
<li>Select Text to Drag and release the mouse</li>
<li>Left click on Mouse and hold it for a second</li>
<li>Now Start dragging the text with your mouse</li>
</ol>
<h2 id="useful-shortcuts">Useful Shortcuts</h2>
<ul>
<li><code>CMD + Space</code> - To Search anything in Mac</li>
<li><code>CMD + Shift + 3</code> - Take instant full-screen screenshot (and save to desktop)</li>
<li><code>CMD + Shift + 4</code> -  Crop &amp; Take Screenshot an Area</li>
<li><code>CMD + Shift + 4</code> then press <code>SPACE</code> - Capture a window (as shown above). Thanks 
@niranjannitesh for the tip</li>
<li><code>CMD + Shift + 4</code> then hold <code>Control</code> - Take Screenshot but copy to Clipboard instead of desktop. Then you will be able to paste it on any app using CMD + V</li>
<li><code>CMD + Shift + 5</code> - Record Video of chosen Area (Screencast)</li>
<li><code>Double Tap</code> on a Magic Mouse - Zoom in to the content (works anywhere)</li>
<li><code>Double Tap with 2 Fingers</code> - Opens Mission Control</li>
</ul>
<h2 id="what-do-i-still-miss">What do I still miss</h2>
<p>I have still not found a great way to Show the Desktop option as in Windows + D. The closest is CMD + H or CMD + F3 but it won't show the Desktop as we needed. </p>
]]></content:encoded></item><item><title><![CDATA[Ultimate Email Forwarding Guide + Send Reply]]></title><description><![CDATA[Imagine you have 5-6 different emails for different purposes, like your personal, work, side projects, etc. Managing those will be a nightmare since you have to literally set up each account and open it every day to check and respond. 
But what if th...]]></description><link>https://blog.surjithctly.in/ultimate-email-forwarding-guide-send-reply</link><guid isPermaLink="true">https://blog.surjithctly.in/ultimate-email-forwarding-guide-send-reply</guid><category><![CDATA[email]]></category><category><![CDATA[gmail]]></category><category><![CDATA[domain]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Wed, 19 May 2021 12:14:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1621427677545/_qJO0MslL.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you have 5-6 different emails for different purposes, like your personal, work, side projects, etc. Managing those will be a nightmare since you have to literally set up each account and open it every day to check and respond. </p>
<p>But what if there is a way to manage them all in one place? In your Gmail? Yes, it's a bit hacky but you can do the following on your personal Gmail account. </p>
<ul>
<li>Use your Personal Gmail as usual</li>
<li>Forward other (<code>@domain.com</code>) emails to your Gmail</li>
<li>Send Replies from Multiple <code>@domain.com</code> automatically</li>
</ul>
<h2 id="heading-step-1-email-forwarding">Step 1 - Email Forwarding</h2>
<p>The first step is to forward your domain email address to your personal Gmail. Email Forwarding is supported by most domain registrars and it's free. Just check your registrar's help docs to find this option. </p>
<p>In my case, I'm using Cloudflare, which provides this option for free. So I forwarded my <code>@domain.com</code> email to my personal email address. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1654506207327/6AjwCuT-7.png" alt="Cloudflare Email Routing.png" /></p>
<p>Here are some links to popular registrars for email forwarding docs.</p>
<ul>
<li><a target="_blank" href="https://in.godaddy.com/help/set-up-my-forwarding-email-address-7598">Go Daddy</a></li>
<li><a target="_blank" href="https://www.namecheap.com/support/knowledgebase/article.aspx/308/2214/how-to-set-up-free-email-forwarding/">Namecheap</a></li>
<li><a target="_blank" href="https://support.google.com/domains/answer/3251241?hl=en">Google Domains</a></li>
</ul>
<p>Make sure the MX records in the DNS matches the Email forwarding service you use. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1654506393312/usUFqzapd.png" alt="Cloudflare DNS MX Records.png" /></p>
<h2 id="heading-step-2-send-email-andamp-reply-from-gmail">Step 2 - Send Email &amp; Reply from Gmail</h2>
<p>In this step, we will set up sending emails from your custom domain alias from Gmail. Gmail auto-selects the email alias based on which address you received the email.  When you receive an email to your personal email <code>you@gmail.com</code>, then you can reply to it from that email. Otherwise, If the email is sent to <code>you@domain.com</code> then you can reply to it from the same domain alias. </p>
<p>Follow the steps to set it up!</p>
<p><em><code>Note: You can skip this step if you want to use an external SMTP Server. Just go to step 3 and add your SMTP Details.</code></em></p>
<h3 id="heading-1-enable-2-step-verification">1. Enable 2-Step Verification</h3>
<p>2-Step Verification is mandatory for using email as an alias in Gmail. Click on the following link to enable it</p>
<p><a target="_blank" href="https://myaccount.google.com/signinoptions/two-step-verification">Enable Enable 2-Step Verification</a></p>
<h3 id="heading-2-generate-your-app-password">2. Generate your App Password</h3>
<p>Visit  “App passwords" from Manage Account -&gt; Security.</p>
<p>Direct Link: https://myaccount.google.com/apppasswords </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621418308378/zjEomK195.png" alt="image.png" /></p>
<ul>
<li>Under “App passwords" click Select app and then <code>Mail</code>. </li>
<li>Click Select device and then <code>Other</code>.</li>
<li>Enter the custom name (eg: your email address) and click Generate. </li>
<li>From the app password box, copy the 16 character password generated. </li>
</ul>
<p>You'll need this password for the next step. Paste it in a safe place. </p>
<blockquote>
<p><strong>Important:</strong> If you are using Google Workspace, visit your Workspace Settings for Gmail -&gt; Advanced settings and make sure to check "Allow users to send mail through an external SMTP server...". </p>
</blockquote>
<h3 id="heading-3-add-an-email-alias">3. Add an email alias</h3>
<p>Go to <a target="_blank" href="https://mail.google.com/">Gmail</a> and under Settings -&gt; Accounts and Import -&gt; Send mail as, click "<strong>Add another email address</strong>"</p>
<p>Then enter the Name that you want your email to be seen as "From". For "Email address", enter the email address with the custom domain you used above (e.g. <code>hello@web3templates.com</code>)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621420018055/pBUWb87GF.png" alt="image.png" /></p>
<p>Confirm that "Treat as an alias" is marked, and click "<strong>Next Step</strong>" to proceed.</p>
<p>On the next page, In the "SMTP Server" field, enter <code>smtp.gmail.com</code>. and leave the port as <code>587</code></p>
<p>In the "Username" field, enter the portion of your Gmail address without the gmail.com part (e.g. just "user" if my email is user@gmail.com)</p>
<p>Fill in the "Password" field with the password you generated above.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621420341964/tufZ8MSNK.png" alt="image.png" /></p>
<p>Now, you will receive an email from Gmail with a confirmation code. Since you have set up the forward already, you should see It in your Gmail itself. Open that email and copy the code and paste it to the following screen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621420373409/-g3G8HVNy.png" alt="image.png" /></p>
<h3 id="heading-final-step">Final Step</h3>
<p>Now, open your Gmail settings -&gt; Account &amp; Imports -&gt; "<strong>Send Mail as</strong>". Then check the radio button says "Reply from the same address the message was sent to" </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621424140162/KlG8UDicS.png" alt="image.png" /></p>
<h2 id="heading-done">Done 🎉</h2>
<p>Congratulations! You've successfully completed all steps. Now you will be able to receive &amp; send emails from your own custom email address inside your Gmail Account. Make sure to test it from a different email address (Don't test from the same email).</p>
<p>Let me know what you think in the comments.  </p>
]]></content:encoded></item><item><title><![CDATA[Sync GitHub Fork with Original Repo (not using command or bash)]]></title><description><![CDATA[If you are new to git, you might have a hard time syncing your fork with the original repo. It's not easy for a beginner. It requires some  long steps as explained in the official docs. But there is a better & sneaky way to do that using github.com i...]]></description><link>https://blog.surjithctly.in/sync-github-fork-with-original-repo-not-using-command-or-bash</link><guid isPermaLink="true">https://blog.surjithctly.in/sync-github-fork-with-original-repo-not-using-command-or-bash</guid><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[tricks]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Sat, 20 Mar 2021 08:35:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616229181033/HMqqhOcwg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you are <a target="_blank" href="https://blog.surjithctly.in/git-cheatsheat-for-beginners-with-vscode">new to git</a>, you might have a hard time syncing your fork with the original repo. It's not easy for a beginner. It requires some <a target="_blank" href="https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork"> long steps</a> as explained in the official docs. But there is a better &amp; sneaky way to do that using github.com itself. No need to touch command or bash. </p>
<p>If you prefer a video, You may watch it here or scroll below. </p>
<iframe width="800" height="450" src="https://www.youtube.com/embed/C7gHynUlSLk"></iframe>

<h2 id="how-to-sync-github-repo-with-original">How to sync Github repo with original?</h2>
<p>So, if I have forked a repo from GitHub and then made some changes, then whenever the original repo pushed an update, it's a pain in the <em>*</em> to sync with it. So let's see how I tackle this issue. </p>
<h3 id="step-1-your-forked-repo">Step 1: Your forked repo</h3>
<p>Once you open your forked repo, you can find a banner on top if there are any new updates available from your original repo. There would be an option to Pull Request. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616226993504/eKvPHMPLG.png" alt="image.png" /></p>
<h3 id="step-2-the-pull-request-problem">Step 2: The Pull Request Problem</h3>
<p>Click on the <strong>pull request</strong> button and it will take you to the original repo and GitHub will start a pull request on the original repo. This is <strong>exactly the opposite</strong> of what we want. </p>
<p>As you can see in the below screenshot, Gituhb took us to the original repo and asking us to open a pull request on the original repo. We want the original to sync with our forked repo. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616227394193/ePtIAY8aG.png" alt="image.png" /></p>
<p>This is currently not possible with the current UI because if we tried to change the base repository or head repository, it will change to compare branches only. See below GIF. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616227718112/mHNBNnrXB.gif" alt="git-ee.gif" /></p>
<h3 id="step-3-the-sneaky-way-to-tackle-the-issue">Step 3: The Sneaky way to tackle the issue</h3>
<p>As you see above we can't really open a pull request in our repo from the original. So we can do this little hack to do that. </p>
<p>Here are how the URLs look like right now. </p>
<p><strong>Original Repo URL</strong></p>
<pre><code class="lang-markup">https://github.com/mikecao/umami/
</code></pre>
<p><strong>Forked Repo URL</strong></p>
<pre><code class="lang-markup">https://github.com/surjithctly/web3analytics
</code></pre>
<p><strong>Github Compare &amp; Pull Request URL</strong></p>
<pre><code class="lang-markup">https://github.com/mikecao/umami/compare/main...surjithctly:main
</code></pre>
<p>Now, here's the trick. </p>
<p>Replace the first part of the Compare and Pull Request URL with your Forked repo URL. </p>
<p><strong>Example:</strong></p>
<pre><code class="lang-markup">// Before
https://github.com/mikecao/umami/compare/main...surjithctly:main

// After
https://github.com/surjithctly/web3analytics/compare/main...surjithctly:main
</code></pre>
<p>Now, change your username in the last part with the original repo Author. </p>
<p><strong>Example:</strong></p>
<pre><code class="lang-markup">// Before
https://github.com/surjithctly/web3analytics/compare/main...surjithctly:main

// After
https://github.com/surjithctly/web3analytics/compare/main...mikecao:main
</code></pre>
<p><strong>Now our Final URL looks like this:</strong></p>
<pre><code class="lang-markup">https://github.com/{username}/{forked_repo}/compare/main...{original_repo_author}:main
</code></pre>
<blockquote>
<p>Tip: use <code>master</code> instead of <code>main</code> if your repo is created before new github changes. </p>
</blockquote>
<h3 id="step-4-final-step">Step 4: Final Step</h3>
<p>Now, visit the URL and if everything is correctly followed, you will see a compare &amp;  pull request page on your forked repo. Like the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616228873591/FG0IOjX_k.png" alt="image.png" /></p>
<p>Now, you can click on the <strong>Pull Request</strong> button as usual.  It will open a new pull request on your fork. If there are no conflict, it can merge automatically. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616229006895/mkzbD-OtV.png" alt="image.png" /></p>
<h3 id="you-are-done">You are done!</h3>
<p>That's all folks. How do you sync your repos? Do you have any good alternative way to do this fast? Let me know in the comments. </p>
]]></content:encoded></item><item><title><![CDATA[Building TailwindCSS Components — Web3Templates]]></title><description><![CDATA[Web3Templates Components is an growing Library of Components built with TailwindCSS you can copy paste to your Tailwind Projects. Do whatever you want with it. 
I had this idea for a while. But I procrastinated. When I saw the Vercel Hackathon, I cou...]]></description><link>https://blog.surjithctly.in/building-tailwindcss-components-web3templates</link><guid isPermaLink="true">https://blog.surjithctly.in/building-tailwindcss-components-web3templates</guid><category><![CDATA[Vercel Hashnode Hackathon]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[components]]></category><category><![CDATA[free]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Sat, 06 Feb 2021 13:47:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1614265989410/76xZZPSD0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Web3Templates Components is an growing Library of Components built with TailwindCSS you can copy paste to your Tailwind Projects. Do whatever you want with it. </p>
<p>I had this idea for a while. But I procrastinated. When I saw the Vercel Hackathon, I couldn't resist myself. To I started working on it. Here is how!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612627742839/A9SJHwRS4.png" alt="image.png" /></p>
<h2 id="building-the-base">Building the Base</h2>
<p>I initially planned to do this with Eleventy, however I decided to start with Next.js instead. So the first thing was to setup Next.js &amp; TailwindCSS. Luckily I have created a starter template called  <a target="_blank" href="https://github.com/surjithctly/nest-starter">NEST Starter</a>  which supports both, So I used it as my starting point. </p>
<h2 id="setup-component-preview">Setup Component Preview</h2>
<p>The next complex thing was to setup the component preview. It took more time as expected. I also added an option to preview responsiveness which had so problems I have to solve. </p>
<p><strong>Component preview resizer</strong></p>
<p>Since I have to show its responsiveness, I have added a resize handle on the right side. Now I wanted a plugin  which helps me to resize the component. I have tried few plugins and those were too complex to implement. I finally settled on  <a target="_blank" href="https://github.com/bokuweb/re-resizable">re-resizable</a> </p>
<p><strong>Responsive View of a Component</strong></p>
<p>Just by resizing a container, the component inside will not show the responsive view because the screen size does not change. We have to resize the entire browser. That's how media query works now. This is one reason we should plea for Container Queries to W3C. </p>
<p>We can fix that problem by adding it inside an iframe. But here comes another problem, since the height of an iframe will change based on the content, the parent page cannot render it properly. Thus it have to be a fixed height. To solve that I used the react version of  <a target="_blank" href="https://www.npmjs.com/package/iframe-resizer-react">iframe-resizer</a> </p>
<p>After some trial and errors, both worked as expected. Now the next phase is to actually make some components. For the initial launch I planned to create components which are much-needed among developers. </p>
<h2 id="creating-components">Creating Components</h2>
<p>I have quickly created few components, but I couldn't do more for the hackathon as the deadline is reached. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612627781435/rmURphh5g.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612627807229/HR9cassZ0.png" alt="image.png" /></p>
<h2 id="website-link-and-source-code">Website Link &amp; Source Code</h2>
<p>Checkout: https://web3templates.com/</p>
<p>Source code: https://github.com/surjithctly/web3templates.com</p>
<h2 id="hosting">Hosting</h2>
<p>This is my favorite part. just push the code to github and the rest will take care by  <a target="_blank" href="https://vercel.com/">Vercel</a> . I liked this service very much. Their only drawback is they don't let you connect Organization Accounts in the free plan. Since Github made organizations free, small founders are using it, but Vercel thinks its a big company and should pay 😀</p>
<h2 id="conclusion">Conclusion</h2>
<p>That's it. I'll be adding more components to it each week. 
So stay tuned for something awesome. </p>
]]></content:encoded></item><item><title><![CDATA[How to setup Sanity CMS with Next.js & TailwindCSS]]></title><description><![CDATA[There are many Headless CMS out there, From that Sanity CMS is what I liked to pair with Next.js because of three main reasons. 
First, the schema & the UI is completely controlled by the code so that we can customize it in any way we like. There are...]]></description><link>https://blog.surjithctly.in/how-to-setup-sanity-cms-with-nextjs-and-tailwindcss</link><guid isPermaLink="true">https://blog.surjithctly.in/how-to-setup-sanity-cms-with-nextjs-and-tailwindcss</guid><category><![CDATA[headless cms]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[cms]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Fri, 29 Jan 2021 15:55:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1614268621744/xhB1REDxc.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are many Headless CMS out there, From that Sanity CMS is what I liked to pair with Next.js because of three main reasons. </p>
<p>First, the schema &amp; the UI is completely controlled by the code so that we can customize it in any way we like. There are no restrictions. They are using react for their UI, so we can customize it as we like. In case of other CMS, we have to stick with their UI &amp; only use whatever option they provides. </p>
<p>Second, The Admin UI can be completely hosted anywhere we like and it will communicate with the main server in real-time. While using another CMS like DatoCMS or Contentful, we have to open their website and login to access our website content. But in Sanity, we can host the Admin in our server itself. </p>
<p>See an example on how it might looks like: </p>
<p><strong>Without Sanity</strong></p>
<pre><code class="lang-bash">Website: https://web3forms.com/ 

Admin: https://username.someothercms.com/dashboard/manage
</code></pre>
<p><strong>With Sanity</strong></p>
<pre><code class="lang-bash">Website: https://web3forms.com/ 

Admin: https://web3forms.com/studio
</code></pre>
<p>How cool is that. When doing projects for clients, this will be a main turning point. In my knowledge this feature is only providing by Sanity CMS &amp; Netlify CMS. </p>
<p>Third reason I like sanity is because its beautiful design. I have played around with some other CMS, when the design is good, the pricing keeps us away. But I liked the UI  Design of Sanity along with a better Pricing, This is of course a personal opinion. </p>
<p>So, without wasting much time, let's dive in. </p>
<h2 id="setting-up-nextjs-and-tailwind">Setting up Next.js &amp; Tailwind</h2>
<p>This is pretty straight forward, also there are many tutorials available. So I won't get deep, but I have also made a starter template which you can use to save time. </p>
<p> <a target="_blank" href="https://github.com/surjithctly/nest-starter">Next.js &amp; TailwindCSS Starter Template</a> </p>
<p>First step is to install Next.js with their bootstrap template called "Create Next App". If you want an in depth tutorial, visit: <a target="_blank" href="https://nextjs.org/docs/getting-started">Next.js Docs</a> </p>
<pre><code class="lang-bash">npx create-next-app
<span class="hljs-comment"># or</span>
yarn create next-app
</code></pre>
<p>Now we can install TailwindCSS. This also easy. Follow the steps below or checkout the official docs here:  <a target="_blank" href="https://tailwindcss.com/docs/guides/nextjs">Install TailwindCSS with Next.js</a> </p>
<pre><code class="lang-bash">npm install tailwindcss postcss autoprefixer
<span class="hljs-comment"># or</span>
yarn add tailwindcss postcss autoprefixer
</code></pre>
<p>Now Generate your Configuration file. </p>
<pre><code class="lang-bash">npx tailwindcss init -p
</code></pre>
<p>This will create a minimal <code>tailwind.config.js</code> file and <code>postcss.config.js</code> at the root of your project. Make sure you add purge settings to remove unused classes from production build.</p>
<p>Now add TailwindCSS file eg: <code>/styles/tailwind.css</code></p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<p>Then you can include the CSS in <code>pages/_app.js</code></p>
<p>That's it! Done! now run the following command to see if everything working. You should be able to see it live on <code>http://localhost:3000</code></p>
<hr />
<h2 id="setting-up-sanity-cms">Setting up Sanity CMS</h2>
<p>The first step is to install Sanity CLI globally. use the following command to do that.</p>
<pre><code class="lang-bash">npm install -g @sanity/cli
</code></pre>
<p>Now, go to the root folder of your created next.js app, and run the following command.</p>
<pre><code class="lang-bash">sanity init
</code></pre>
<p>The above command will walk you through some steps to create / login to an account, creating a project, set up the dataset, generate the files, etc. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611925390563/5gSl2NzKO.png" alt="image.png" /></p>
<p>The only thing to consider is when it ask to choose a folder name, make sure its in the root folder of next.js and name it as something like <code>studio</code> or <code>admin</code></p>
<p>Now, the folder will create in the root of Next.js project. </p>
<h2 id="setup-admin-path">Setup Admin Path</h2>
<p>To setup the admin path as <code>/studio</code> or <code>/admin</code> as I mentioned in the intro, you have to configure some steps. Go to <code>next.config.js</code> (or create one) and add the following code. </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> STUDIO_REWRITE = {
  <span class="hljs-attr">source</span>: <span class="hljs-string">"/studio/:path*"</span>,
  <span class="hljs-attr">destination</span>:
    process.env.NODE_ENV === <span class="hljs-string">"development"</span>
      ? <span class="hljs-string">"http://localhost:3333/studio/:path*"</span>
      : <span class="hljs-string">"/studio/index.html"</span>,
};

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">rewrites</span>: <span class="hljs-function">() =&gt;</span> [STUDIO_REWRITE],
};
</code></pre>
<p>You may change the word <code>studio</code> to <code>admin</code> if you like. This uses Next.js rewrite function so that we don't need to browse separate URL. </p>
<p>Also, make sure you update the <strong>basepath</strong> in <code>studio/sanity.json</code> so that the dependencies resolves correctly.</p>
<pre><code class="lang-json">  <span class="hljs-string">"project"</span>: {
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Your Sanity Project"</span>,
    <span class="hljs-attr">"basePath"</span>: <span class="hljs-string">"/studio"</span>
  },
</code></pre>
<h2 id="setup-cors">Setup CORS</h2>
<p>If you are doing the step above, you must allow CORS origin from the Sanity Project Settings. Go to: <code>https://manage.sanity.io/projects/{project_id}/settings/api</code></p>
<p>Project ID can be found in <code>/studio/sanity.json</code></p>
<p>Now, click on ADD ORIGIN button on the page and add your URL &amp; Enable "Allow Credentials" checkbox. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611925869063/xGIZgcPUE.png" alt="image.png" /></p>
<p>Since I run Next.js on port 3000 on localhost, I'm using that URL, You can also add the Production URL in same way. </p>
<h2 id="configure-development-server">Configure Development Server</h2>
<p>The next step is to run both Sanity &amp; Next.js Together. For that, we have to do some steps. </p>
<ol>
<li>Install Concurrently Plugin</li>
</ol>
<pre><code class="lang-bash">npm install -g concurrently
<span class="hljs-comment"># or</span>
yarn add -g concurrently
</code></pre>
<p>Now, open your <code>package.json</code> and change your build script like this. </p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"concurrently \"next dev\" \"cd studio &amp;&amp; sanity start\""</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"echo 'Building Sanity to public/studio…' &amp;&amp; cd studio &amp;&amp; sanity build ../public/studio -y &amp;&amp; cd .. &amp;&amp; cross-env NODE_ENV=production next build"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"next start"</span>
  },
</code></pre>
<p>This will make sure Next.js &amp; Sanity runs &amp; build together. </p>
<h2 id="add-env-for-sanity">Add .env for Sanity</h2>
<p>You have to add a <code>.env</code> file to add the project ID. Use the following: </p>
<pre><code class="lang-env"># For Studio Locally
SANITY_STUDIO_API_PROJECT_ID = "PROJECT_ID"
SANITY_STUDIO_API_DATASET = "production"

# For Next Locally
NEXT_PUBLIC_SANITY_PROJECT_ID = "PROJECT_ID"
NEXT_PUBLIC_SANITY_DATASET = "production"
</code></pre>
<p>Make sure your env name is <code>.env</code> and not <code>.env.local</code> or you will spend countless hours debugging the issue like me 😀</p>
<h2 id="setup-next-sanity-plugin">Setup Next Sanity Plugin</h2>
<p>Now, we need to install one last plugin which is called <code>next-sanity</code>. This plugin is needed so that we can call the API easily. The plugin will handle the rest. </p>
<pre><code class="lang-bash">npm install next-sanity
<span class="hljs-comment"># or </span>
yarn add next-sanity
</code></pre>
<p>Now, create a file called <code>sanity.js</code> in <code>/utils</code> folder in the root of our project. This will be file communicating with the plugin. (Code taken from the <code>next-sanity</code> repo). No changes need in the below file, Just copy-paste and save. </p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> {
  groq,
  createClient,
  createImageUrlBuilder,
  createPortableTextComponent,
  createPreviewSubscriptionHook,
  createCurrentUserHook,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"next-sanity"</span>;

<span class="hljs-keyword">const</span> config = {
  <span class="hljs-comment">/**
   * Find your project ID and dataset in `sanity.json` in your studio project.
   * These are considered “public”, but you can use environment variables
   * if you want differ between local dev and production.
   *
   * https://nextjs.org/docs/basic-features/environment-variables
   **/</span>
  <span class="hljs-attr">dataset</span>: process.env.NEXT_PUBLIC_SANITY_DATASET || <span class="hljs-string">"production"</span>,
  <span class="hljs-attr">projectId</span>: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  <span class="hljs-attr">useCdn</span>: process.env.NODE_ENV === <span class="hljs-string">"production"</span>,
  <span class="hljs-comment">/**
   * Set useCdn to `false` if your application require the freshest possible
   * data always (potentially slightly slower and a bit more expensive).
   * Authenticated request (like preview) will always bypass the CDN
   **/</span>
};

<span class="hljs-comment">/**
 * Set up a helper function for generating Image URLs with only the asset reference data in your documents.
 * Read more: https://www.sanity.io/docs/image-url
 **/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> urlFor = <span class="hljs-function">(<span class="hljs-params">source</span>) =&gt;</span> createImageUrlBuilder(config).image(source);

<span class="hljs-comment">// Set up the live preview subsscription hook</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> usePreviewSubscription = createPreviewSubscriptionHook(config);

<span class="hljs-comment">// Set up Portable Text serialization</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> PortableText = createPortableTextComponent({
  ...config,
  <span class="hljs-comment">// Serializers passed to @sanity/block-content-to-react</span>
  <span class="hljs-comment">// (https://github.com/sanity-io/block-content-to-react)</span>
  <span class="hljs-attr">serializers</span>: {},
});

<span class="hljs-comment">// Set up the client for fetching data in the getProps page functions</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> sanityClient = createClient(config);
<span class="hljs-comment">// Set up a preview client with serverless authentication for drafts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> previewClient = createClient({
  ...config,
  <span class="hljs-attr">useCdn</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">token</span>: process.env.SANITY_API_TOKEN,
});

<span class="hljs-comment">// Helper function for easily switching between normal client and preview client</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getClient = <span class="hljs-function">(<span class="hljs-params">usePreview</span>) =&gt;</span>
  usePreview ? previewClient : sanityClient;

<span class="hljs-comment">// Helper function for using the current logged in user account</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useCurrentUser = createCurrentUserHook(config);
</code></pre>
<h2 id="creating-the-schema">Creating the Schema</h2>
<p>Now, open the <code>/studio/schemas/schema.js</code> file and add a sample schema. More details about this can be found on  <a target="_blank" href="https://www.sanity.io/docs/content-modelling">Sanity Docs</a> </p>
<p>Here I am adding a sample schema called "Settings"</p>
<pre><code class="lang-js"><span class="hljs-comment">// First, we must import the schema creator</span>
<span class="hljs-keyword">import</span> createSchema <span class="hljs-keyword">from</span> <span class="hljs-string">"part:@sanity/base/schema-creator"</span>;

<span class="hljs-comment">// Then import schema types from any plugins that might expose them</span>
<span class="hljs-keyword">import</span> schemaTypes <span class="hljs-keyword">from</span> <span class="hljs-string">"all:part:@sanity/base/schema-type"</span>;

<span class="hljs-comment">// Then we give our schema to the builder and provide the result to Sanity</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createSchema({
  <span class="hljs-comment">// We name our schema</span>
  <span class="hljs-attr">name</span>: <span class="hljs-string">"default"</span>,
  <span class="hljs-comment">// Then proceed to concatenate our document type</span>
  <span class="hljs-comment">// to the ones provided by any plugins that are installed</span>
  <span class="hljs-attr">types</span>: schemaTypes.concat([
    <span class="hljs-comment">/* Your types here! */</span>
    {
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Settings"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"settings"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"document"</span>,
      <span class="hljs-attr">fields</span>: [
        {
          <span class="hljs-attr">title</span>: <span class="hljs-string">"Website Name"</span>,
          <span class="hljs-attr">name</span>: <span class="hljs-string">"name"</span>,
          <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
        },
      ],
    },
  ]),
});
</code></pre>
<p>Don't fret, we only need to add schema inside the <code>types</code>, the rest of the code is already there for us. Pretty neat!</p>
<h2 id="making-it-singleton-one-off">Making it Singleton (one-off)</h2>
<p>Since the example I have shown above is for singleton document type, which means we only need to add this once, not repeatable like Blog Articles. We need some more configuration to do that. </p>
<p>Skip this step if you don't plan to use singleton.</p>
<p>This is the only thing I hate about Sanity right now, there is no easy way to make a singleton document. Its bit long route (As of Jan 2021). Hope they fix this in their future update. </p>
<p>So to make a singleton document, we have to create a file called "deskStructure.js". It will contain code something like this: </p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> S <span class="hljs-keyword">from</span> <span class="hljs-string">"@sanity/desk-tool/structure-builder"</span>;

<span class="hljs-keyword">const</span> hiddenDocTypes = <span class="hljs-function">(<span class="hljs-params">listItem</span>) =&gt;</span> ![<span class="hljs-string">"settings"</span>].includes(listItem.getId());

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () =&gt;
  S.list()
    .title(<span class="hljs-string">"Content"</span>)
    .items([
      S.listItem()
        .title(<span class="hljs-string">"Site settings"</span>)
        .child(S.document().schemaType(<span class="hljs-string">"settings"</span>).documentId(<span class="hljs-string">"settings"</span>)),
      <span class="hljs-comment">// Add a visual divider (optional)</span>
      S.divider(),
      <span class="hljs-comment">// List out the rest of the document types, but filter out the config type</span>
      ...S.documentTypeListItems().filter(hiddenDocTypes),
    ]);
</code></pre>
<p>Read more about  <a target="_blank" href="https://www.sanity.io/docs/structure-builder-typical-use-cases">Structure Builder on Sanity Docs</a>  </p>
<p>Now, we need to add the path in <code>sanity.json</code>. Open the file and add these lines.</p>
<pre><code class="lang-json"> <span class="hljs-string">"parts"</span>: [{
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"part:@sanity/base/schema"</span>,
      <span class="hljs-attr">"path"</span>: <span class="hljs-string">"./schemas/schema"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"part:@sanity/desk-tool/structure"</span>,
      <span class="hljs-attr">"path"</span>: <span class="hljs-string">"./deskStructure.js"</span>
    }
  ]
</code></pre>
<p>That's it, we are good to go now.</p>
<h2 id="adding-content-in-sanity-cms">Adding Content in Sanity CMS</h2>
<p>Its the time to add a sample content to our Database. Run the following command to start Next.js &amp; Sanity together: </p>
<pre><code class="lang-bash">npm run dev
<span class="hljs-comment"># or</span>
yarn dev
</code></pre>
<p>Then open <code>http://localhost:3000/studio</code></p>
<p>🥳 Our Sanity Studio is live (if followed the steps correctly), Now login to your sanity account (I prefer Github Login). Once logged it, click on our newly created type "Site settings" and add a  sample content by clicking it. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611933885860/gEEgFbER9.png" alt="image.png" /></p>
<h2 id="getting-the-data-in-nextjs">Getting the data in Next.js</h2>
<p>Now comes the final part, getting sanity content inside our next.js page. for that there are some steps. Please follow along. </p>
<p>First, you need to know the query language called <code>groq</code>. That's what sanity is using by default. Also they do provide an option for <code>graphql</code>. </p>
<p>Import Next Sanity &amp; <code>utils/sanity</code> in our next Page. In this case I'm using <code>index.js</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { groq } <span class="hljs-keyword">from</span> <span class="hljs-string">"next-sanity"</span>;
<span class="hljs-keyword">import</span> { getClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils/sanity"</span>;

<span class="hljs-keyword">const</span> query = groq<span class="hljs-string">`*[_type == "settings"]{
  name,
  _updatedAt,
}`</span>;
</code></pre>
<p>Now in the bottom of the page, also add the following code to get the data</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStaticProps</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> getClient().fetch(query);

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      <span class="hljs-attr">settings</span>: response || <span class="hljs-literal">null</span>,
    },
    <span class="hljs-attr">revalidate</span>: <span class="hljs-number">5</span>,
  };
}
</code></pre>
<p>Now we will be able to call this props in our function like this: </p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params">props</span>) </span>{
  <span class="hljs-keyword">const</span> settings = props.settings[<span class="hljs-number">0</span>];
  <span class="hljs-keyword">return</span> (
    Welcome to {settings.name}
  );
}
</code></pre>
<p>🥂🥳 Yaaaayyy!!! That's it. Now refresh your browser and see your data. </p>
<p>Hope this tutorial helps you get started. Don't forget to checkout the  <a target="_blank" href="https://www.sanity.io/docs">Sanity Docs</a>  for more information &amp; help. They also have some nice starter templates. </p>
<p>If you have any questions or feedback, comment below</p>
<p> <a target="_blank" href="https://twitter.com/surjithctly">Follow me on Twitter</a> </p>
]]></content:encoded></item><item><title><![CDATA[Git Cheatsheat for beginners with VSCode]]></title><description><![CDATA[I decided to start with Git & Github for one of my existing projects. 
I started with creating a Github Repo with my account. 
Mistake #1
I initiated the repo with Readme & .gitignore. Since I already have a project to publish to GitHub, you should k...]]></description><link>https://blog.surjithctly.in/git-cheatsheat-for-beginners-with-vscode</link><guid isPermaLink="true">https://blog.surjithctly.in/git-cheatsheat-for-beginners-with-vscode</guid><category><![CDATA[Git]]></category><category><![CDATA[cheatsheet]]></category><category><![CDATA[Visual Studio Code]]></category><category><![CDATA[tips]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Thu, 21 Jan 2021 09:24:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1614333227212/gP5orQzN9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I decided to start with Git &amp; Github for one of my existing projects. </p>
<p>I started with creating a Github Repo with my account. </p>
<h3 id="mistake-1">Mistake #1</h3>
<p>I initiated the repo with Readme &amp; .gitignore. Since I already have a project to publish to GitHub, you should keep it blank, or otherwise, you will get an error asking to pull first, and that pulling will also fail due to history unmatch error, same as me :)</p>
<h2 id="initialize-git">Initialize Git</h2>
<p>First, install git from <a target="_blank" href="https://git-scm.com/">https://git-scm.com/</a>, and it's better to install Powershell as well if not installed before. (Only for Windows users 😋)</p>
<p>Then open the project folder in VSCode and click the third icon from the left menu called "Source Control" and click "Initialize Repository"</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1603973415371/ms07jyQfc.jpeg" alt="Init Repo on VSCode" /></p>
<h3 id="terminal-way">Terminal way.</h3>
<p>Open VSCode and press <code>Ctrl + ` </code> to open integrated terminal. In my case it's Powershell. </p>
<p>Now type the following command. </p>
<pre><code>&gt; git <span class="hljs-keyword">init</span>
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1603973417131/_LKWivQfF.jpeg" alt="Alt Text" /></p>
<h2 id="adding-a-remote-server-connect-to-github">Adding a Remote Server (Connect to Github)</h2>
<p>Inside VSCode press <code>CTRL+SHIFT+P</code> and type "Git Origin", Select the same from the list and add your Origin URL (ie GitHub *.git URL)</p>
<p>The same can be done via a terminal. See below.</p>
<pre><code>$ git remote <span class="hljs-keyword">add</span> origin https://github.com/<span class="hljs-keyword">user</span>/repo.git
</code></pre><p>Once added you can commit your changes by clicking the "Tick Icon" in the Source control Tab. When you are ready to push to GitHub, click the 3 dots ... menu and choose "Push". </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1603973418899/JfcgKtIdH.jpeg" alt="Alt Text" /></p>
<p>This is where I did wrong. As I already have an existing project but Github is initiated with Readme, it asks to pull first. When doing that it pops another error that says the history is not matching. </p>
<p>If you got this error, please read Mistake #1 to avoid this later. For now, you can use the below code to overwrite it. </p>
<pre><code>$ git pull origin main <span class="hljs-comment">--allow-unrelated-histories</span>
</code></pre><h2 id="adding-multiple-origins">Adding multiple Origins</h2>
<p>Sometimes you want to add multiple origins when you have to use bitbucket or GitHub or even with two different accounts on GitHub. In that case, you can run the following command to add a new origin. Change <code>originname</code> with your origin name. It can be any text. </p>
<pre><code>$ git remote add originname <span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/github.com/another</span>_user/another_repo.git
</code></pre><p>Now when you need to push this to that particular origin, run the following: </p>
<pre><code>$ git <span class="hljs-keyword">push</span> originname main
</code></pre><h2 id="git-stash-and-pop">Git Stash &amp; POP</h2>
<p>This is useful when there are multiple people working on the same project or working on multiple branches. It won't work if you have existing commits. In that case, you can check the merge option below. </p>
<p>What Git Stash does is save all of our working files in temporary storage and revert to the original state. So that we don't lose our changes. From there you can pull the latest and commit using Git Stash Apply or Pop. </p>
<h3 id="when-do-you-need-this">When do you need this?</h3>
<ol>
<li>When any other developer modified the same files and you want to see/work on that updated files. </li>
<li>When you mistakenly worked on the X feature branch but should actually be the Y feature branch. </li>
</ol>
<p>Commands</p>
<pre><code>
$ git stash 
<span class="hljs-comment">// Stash current directory and revert to original</span>

$ git stash save <span class="hljs-string">"message"</span>
<span class="hljs-comment">// save in a separate stash (multiple stash)</span>

$ git stash <span class="hljs-keyword">list</span>
<span class="hljs-comment">// See current stash</span>

$ git stash apply
<span class="hljs-comment">// Apply saved stash but keep the stash</span>

$ git stash pop
<span class="hljs-comment">// apply saved stash and delete</span>
</code></pre><p>Then after these commands, you will be able to pull/push as usual. </p>
<hr />
<p>Some StackOverflow answers also mention the use of <code>--force</code> or <code>-f</code> tag but if it's in the case of the big project it might get messed up. </p>
<h2 id="git-checkout-branch">Git Checkout Branch</h2>
<p>A branch is a powerful feature in git where you can make edits to a project without affecting the main source code.</p>
<h3 id="checkout-existing-branch">Checkout existing branch</h3>
<p>To checkout existing branch, use the following command</p>
<pre><code>
$ git checkout existing-branch-<span class="hljs-type">name</span>
</code></pre><h3 id="creating-a-new-branch">Creating a new branch</h3>
<p>To create a new branch, use the following command</p>
<pre><code>
$ git checkout -b <span class="hljs-built_in">new</span>-branch-<span class="hljs-type">name</span>
</code></pre><p>These commands will change the current working directory to your newly selected branch.</p>
<h2 id="git-merge">Git Merge</h2>
<p>Suppose you are working on a feature branch, while in development, the master is getting new updates from other pull requests. Now if you want to get those latest features on your branch, you can use the following code. </p>
<pre><code>git <span class="hljs-keyword">merge</span> <span class="hljs-keyword">master</span>
</code></pre><p>If you want to keep track of the branch and don't want so shift HEAD, you can use the following no-fast-forward flag. </p>
<pre><code><span class="hljs-attribute">git</span> merge --<span class="hljs-literal">no</span>-ff master
</code></pre><p>Here's an image to understand it better:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1617628745954/EnV0lFUue.png" alt="image.png" /></p>
<blockquote>
<p>PS: Make sure your local master is up to date with the remote (<code>git pull</code>) as the merge will use the files from the local master only. </p>
</blockquote>
<hr />
<p>I will be adding more findings when I learn them, If you have any suggestions to improve my Git workflow, comment below. </p>
<p>Peace!</p>
]]></content:encoded></item><item><title><![CDATA[How to Setup a Working Contact Form in your Hashnode Blog?]]></title><description><![CDATA[If you are using a hashnode blog, chances are you don't have a contact form in your blog. However its now easy to add one with their Widget Option where you can embed on a separate page on inside a blog post. The options are limitless.  I'll explain ...]]></description><link>https://blog.surjithctly.in/how-to-setup-a-working-contact-form-in-your-hashnode-blog</link><guid isPermaLink="true">https://blog.surjithctly.in/how-to-setup-a-working-contact-form-in-your-hashnode-blog</guid><category><![CDATA[JAMstack]]></category><category><![CDATA[HTML]]></category><category><![CDATA[forms]]></category><category><![CDATA[Emails]]></category><category><![CDATA[Christmas Hackathon]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Mon, 21 Dec 2020 15:28:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1608564600379/nyxMvQ9XL.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you are using a hashnode blog, chances are you don't have a contact form in your blog. However its now easy to add one with their Widget Option where you can embed on a separate page on inside a blog post. The options are limitless.  I'll explain how we can do it. before that, here's a quick preview on how it looks like</p>
<h2 id="form-preview">Form Preview</h2>
<div class="hn-embed-widget" id="web3forms-demo"></div><p>Feel free to submit this form to test it 😊</p>
<h2 id="how-to-setup-this-contact-form">How to Setup this Contact Form</h2>
<p>This is possible with my another side project called <strong>Web3Forms</strong>. Its a simple way to create contact forms for any static / jamstack websites, No backend required. </p>
<h3 id="step-1">Step 1</h3>
<p>So, the first step is to create an Access key from Web3Forms to start receiving email submissions to your form. Please also note, there is No Signup / Login needed for Web3Forms, so it should be easy to setup.</p>
<ol>
<li>Go to https://web3forms.com/</li>
<li>Click "Create Access Key"</li>
<li>Enter Email and Submit</li>
<li>Check your Email for Access Key</li>
<li>There is no Signup / Login Process (yay! 🎉)</li>
<li>Done. </li>
</ol>
<h2 id="step-2">Step 2</h2>
<p>Once you've got the Access Key, Add the following code in your Hashnode Blog -&gt; Dashboard -&gt; Widgets -&gt; Add New Widget</p>
<p>Note: This code is a replica of the form preview you've seen above. Since its pure HTML &amp; CSS. You have full customization &amp; design options. </p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w3_contact_form"</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"https://api.web3forms.com/submit"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"form"</span>&gt;</span>

    <span class="hljs-comment">&lt;!--   Your Access Key. Generate it from https://web3forms.com/ --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"apikey"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"YOUR_ACCESS_KEY_HERE"</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"subject"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"New Contact Message from My Blog"</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Your Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-input"</span> <span class="hljs-attr">required</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Email Address<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-input"</span> <span class="hljs-attr">required</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Message<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-input"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"8"</span> <span class="hljs-attr">required</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-comment">&lt;!--   Success Redriect --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"redirect"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"https://web3forms.com/success"</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Optional: But Recommended: To Prevent SPAM Submission. 
       Make sure its hidden by default --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"botcheck"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"display: none;"</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"submit-btn"</span>&gt;</span>Send Message<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

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

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
*,
<span class="hljs-selector-pseudo">::before</span>,
<span class="hljs-selector-pseudo">::after</span> {
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: Inter var, ui-sans-serif, system-ui, -apple-system,
    BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans,
    sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol,
    Noto Color Emoji;
<span class="hljs-attribute">padding</span>:<span class="hljs-number">50px</span>;
}
<span class="hljs-selector-class">.w3_contact_form</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">500px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#eee</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">50px</span> <span class="hljs-number">#ececec</span>;
}

<span class="hljs-selector-class">.form-input</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">border</span>: solid <span class="hljs-number">2px</span> <span class="hljs-number">#dedede</span>;
  <span class="hljs-attribute">outline</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
}
<span class="hljs-selector-class">.input-group</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">15px</span>;
}

<span class="hljs-selector-tag">label</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#8e8e8e</span>;
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">7px</span>;
}

<span class="hljs-selector-class">.hidden</span> {
  <span class="hljs-attribute">display</span>: none;
}

<span class="hljs-selector-class">.submit-btn</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#2962ff</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
  <span class="hljs-attribute">font-weight</span>: medium;
  <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-class">.submit-btn</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#2962ff</span>;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">500px</span>) {
  <span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  }
  <span class="hljs-selector-class">.w3_contact_form</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  }
  <span class="hljs-selector-class">.form-input</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
  }
}


</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre><h2 id="step-3">Step 3</h2>
<p>Now its time to Add the Access Key and the form contents. </p>
<p>Replace <code>"YOUR_ACCESS_KEY_HERE"</code> with your actual access key received in your email. Then you can customize the input fields, design and more as your creativity. No limits here. Or you can just put defaults to look like the above demo. </p>
<p>Currently the form redirects to <code>https://web3forms.com/success</code> URL. You can also change this URL to your custom website or page by changing the <code>redirect</code> attribute. </p>
<p>You can also change the Subject field or let users fill it by changing the input type from <code>hidden</code> to <code>text</code>.</p>
<h2 id="step-4">Step 4</h2>
<p>Now you can save this widget by naming it as <code>web3forms</code> and you will be able to use the widget across Hashnode using the the short tag <code>%%[web3forms]</code></p>
<hr />
<p>That's it. You should have a Working Contact form inside your <a class="user-mention" href="https://hashnode.com/@hashnode">Hashnode</a> blog. </p>
<p>PS: Web3Forms is currently in Beta, let me know if you found any issues. </p>
<p>Let me know what you think in the comments. </p>
<p> <a target="_blank" href="https://twitter.com/surjithctly">Follow me on Twitter</a> </p>
]]></content:encoded></item><item><title><![CDATA[Get URL Query Params in Next.js using getInitialProps()]]></title><description><![CDATA[As the title says, this is a simple code snippet to get URL Query params in Next.js using getInitialProps() method. 
In Next.js, getInitialProps() have context property where URL query can be fetched using query. See example code below. 
URL: http://...]]></description><link>https://blog.surjithctly.in/get-url-query-params-in-nextjs-using-getinitialprops</link><guid isPermaLink="true">https://blog.surjithctly.in/get-url-query-params-in-nextjs-using-getinitialprops</guid><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Tue, 01 Dec 2020 09:22:13 GMT</pubDate><content:encoded><![CDATA[<p>As the title says, this is a simple code snippet to get URL Query params in Next.js using getInitialProps() method. </p>
<p>In Next.js, getInitialProps() have <code>context</code> property where URL query can be fetched using <code>query</code>. See example code below. </p>
<p>URL: <code>http://localhost:3000/?title=Awesome&amp;desc=Content</code></p>
<pre><code class="lang-js">Page.getInitialProps = <span class="hljs-keyword">async</span> (context) =&gt; {
  <span class="hljs-keyword">return</span> { 
    <span class="hljs-attr">title</span>: context.query.title, 
    <span class="hljs-attr">desc</span>: context.query.desc 
  };
};
</code></pre>
<p>Now we can get these values in our function like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Page</span>(<span class="hljs-params">{ title, desc }</span>) </span>{
 <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
       Title: {title}
       Desc: {desc}
    <span class="hljs-tag">&lt;/&gt;</span></span>
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Black Friday & Cyber Monday Deals for Developers & Designers]]></title><description><![CDATA[Black Friday (Nov 27) and Cyber Monday (30 Nov) of 2020 is here. There are many startups & SAAS offering great discounts on these days. Its the time to fulfill your Wishlist. Here are list of offers & discounts I have curated. I'll be keep adding new...]]></description><link>https://blog.surjithctly.in/black-friday-and-cyber-monday-deals-for-developers-and-designers</link><guid isPermaLink="true">https://blog.surjithctly.in/black-friday-and-cyber-monday-deals-for-developers-and-designers</guid><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Mon, 23 Nov 2020 09:56:43 GMT</pubDate><content:encoded><![CDATA[<p>Black Friday (Nov 27) and Cyber Monday (30 Nov) of 2020 is here. There are many startups &amp; SAAS offering great discounts on these days. Its the time to fulfill your Wishlist. Here are list of offers &amp; discounts I have curated. I'll be keep adding new resources as I find them. So please bookmark and come back again.</p>
<p><em>Last Updated: Monday, 23 Nov 2020</em></p>
<h2 id="carrd-50-off">Carrd (50% Off)</h2>
<p>50% off the first year of any Pro plan OR 50% off a one-year renewal of your current Pro plan. </p>
<p>Coupon Code: <code>BLK2020</code></p>
<p>👉  <a target="_blank" href="https://carrd.co/pro">Grab this deal --&gt; </a> </p>
<h2 id="envato-elements-dollar19m">Envato Elements ($19/m)</h2>
<p><strong>40%</strong> off on Monthly &amp;  <strong>25%</strong> off on Annual Subscription. Envato Elements Subscription offers unlimited downloads across 53+ million creative assets.</p>
<p><strong>Date:</strong> Nov 30 to Dec 2, 2020</p>
<p>👉  <a target="_blank" href="https://1.envato.market/ey4NZ">Grab this deal --&gt; </a> </p>
<h2 id="envato-market">Envato Market</h2>
<p> 50% off 500+ best-selling and trending items from ThemeForest, CodeCanyon, GraphicRiver, VideoHive &amp; AudioJungle! Hand-selected, premium quality items for you to drive big sales results.</p>
<p><strong>Date:</strong> Nov 24 to Dec 2, 2020</p>
<p>👉  <a target="_blank" href="https://1.envato.market/PJ0VM">Grab this deal --&gt; </a> </p>
<h2 id="ui8-90-off">UI8 (90% Off)</h2>
<p>Hundreds of premium UI Kits, Illustrations and more products for up to 90% off</p>
<p><strong>Date:</strong> till Nov 30, 2020</p>
<p>👉  <a target="_blank" href="https://ui8.net/sale?rel=surjithctly">Grab this deal --&gt; </a> </p>
<h2 id="elementor-25-off">Elementor (25% Off)</h2>
<p>25% discount on new ‘Expert’ plans and 10% off of 'Plus' plans!</p>
<p><strong>Date:</strong> Nov 24 to Dec 2, 2020</p>
<p>👉  <a target="_blank" href="https://elementor.com/promotions/black-friday/?ref=10571">Grab this deal --&gt; </a> </p>
<h2 id="creative-market-75-off">Creative Market (75% Off)</h2>
<p>Explore hundreds of essential creative elements up to 75% Off. Illustrations, social media templates, Procreate brushes and more are on sale now.</p>
<p><strong>Date:</strong> till Nov 30, 2020</p>
<p>👉  <a target="_blank" href="https://creativemarket.com/finds/black-friday-cyber-monday-sale?u=surjithctly">Grab this deal --&gt; </a> </p>
<h2 id="email-octopus-50-off">Email Octopus (50% Off)</h2>
<p>Upgrade to a paid EmailOctopus plan in November and save 50% on your email marketing for the next three months.</p>
<p>Coupon Code: <code>BF2020</code></p>
<p>👉  <a target="_blank" href="https://emailoctopus.com/?urli=8GD8J&amp;coupon=bf2020">Grab this deal --&gt; </a> </p>
<h2 id="creative-tim-90-off">Creative Tim (90% Off)</h2>
<p>Up to 90% off on most bundles</p>
<p>👉  <a target="_blank" href="http://www.creative-tim.com/bundles?affiliate_id=99588">Grab this deal --&gt; </a> </p>
<h2 id="namecheap-99-off">Namecheap (99% Off)</h2>
<p>Save up to 99% on Domains &amp; Hosting</p>
<p>👉  <a target="_blank" href="https://www.namecheap.com/domain-web-hosting-ssl-deals/black-friday/">Grab this deal --&gt; </a> </p>
<h2 id="adobe-creative-cloud-25-off">Adobe Creative Cloud (25% Off)</h2>
<p>It’s on. Get 25% off. Black Friday savings on the new release of Creative Cloud.</p>
<p><strong>Date:</strong> till Nov 27, 2020</p>
<p>👉  <a target="_blank" href="https://www.adobe.com/creativecloud.html">Grab this deal --&gt; </a> </p>
<h2 id="bluehost-60-off">Bluehost (60% Off)</h2>
<p>Get up to 60% off now. 60% Off Select Website Plans. 40% Off Select Online Store Plans.  30% Off Select Domains</p>
<p>👉  <a target="_blank" href="https://web3canvas.com/bluehost">Grab this deal --&gt; </a> </p>
<h2 id="designmodo-60-off">Designmodo (60% Off)</h2>
<p>For 3 days only, Designmodo have dropped the prices for all of our Apps. Buy Startup, Slides, or Postcards apps and start to create websites and email newsletter templates online!</p>
<p>Coupon Code: <code>BLACK</code></p>
<p>👉  <a target="_blank" href="https://designmodo.com/blackfriday/?u=99">Grab this deal --&gt; </a> </p>
<h2 id="getillustrations-25">Getillustrations (25%)</h2>
<p>25% Off on 4268 Vector illustrations stories that makes your designs standout.</p>
<p>Coupon Code: <code>EARLYBIRD</code></p>
<p>👉  <a target="_blank" href="https://www.getillustrations.com/">Grab this deal --&gt; </a> </p>
<h2 id="themesberg-90-off">Themesberg (90% Off)</h2>
<p>Black Friday has officially started at Themesberg and now you can get all of our themes for a special price reduction of up to 90%! There are only a limited amount of 150 orders.</p>
<p>👉  <a target="_blank" href="https://themesberg.com/black-friday">Grab this deal --&gt; </a> </p>
<h2 id="craftwork-design-50-off">Craftwork Design (50% Off)</h2>
<p>Up to 50% off for Interface assets for designers and startup creatives</p>
<p>👉  <a target="_blank" href="https://craftwork.design/black-friday/">Grab this deal --&gt; </a> </p>
<p>and more.. (check back later)</p>
<hr />
<p>I'll be adding more deals here as I find them, please check back later. Do you know any awesome deals I missed here? Please comment below. </p>
<p> <a target="_blank" href="https://twitter.com/surjithctly">Follow me on Twitter</a> </p>
]]></content:encoded></item><item><title><![CDATA[What's new in TailwindCSS v2.0 & How to Upgrade?]]></title><description><![CDATA[Few days ago, On Nov 18th, TailwindCSS released its newest version to the public. Which is v2. There are some exciting new features in v2 and I will share it in this post and will guide you on how you can upgrade to newer version without any issues. ...]]></description><link>https://blog.surjithctly.in/whats-new-in-tailwindcss-v20-and-how-to-upgrade</link><guid isPermaLink="true">https://blog.surjithctly.in/whats-new-in-tailwindcss-v20-and-how-to-upgrade</guid><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[features]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Sat, 21 Nov 2020 15:00:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605859603523/T9J4n7vAU.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Few days ago, On Nov 18th, TailwindCSS released its newest version to the public. Which is v2. There are some exciting new features in v2 and I will share it in this post and will guide you on how you can upgrade to newer version without any issues. </p>
<h6 id="jump-to-upgrade-guidetailwindcss-v2-upgrade-guide"><a class="post-section-overview" href="#tailwindcss-v2-upgrade-guide">Jump to Upgrade Guide</a>  👇</h6>
<h2 id="new-color-palette">New color palette</h2>
<p>The new version includes a total of 220 colors with 22 color groups along with an extra <code>-50</code> option (eg: <code>text-red-50</code>).  It also includes 5 types of gray from cool to warm.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605865185730/yqCLfWQ_u.png" alt="image.png" /></p>
<p>However, by default, tailwindcss ship with 8 base color groups only to control the filesize. However you can configure the color you use and remove the one not using the <code>tailwind.config.js</code> file. </p>
<pre><code class="lang-js"><span class="hljs-comment">// tailwind.config.js</span>
<span class="hljs-keyword">const</span> colors = <span class="hljs-built_in">require</span>(<span class="hljs-string">'tailwindcss/colors'</span>)

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">colors</span>: {
      <span class="hljs-attr">gray</span>: colors.coolGray, 
      <span class="hljs-attr">emerald</span>: colors.emerald,
      <span class="hljs-attr">fuchsia</span>: colors.fuchsia,
    },
  },
}
</code></pre>
<h2 id="dark-mode">Dark Mode</h2>
<p>This is one of the anticipated feature of v2. Now TailwindCSS supports out of the box support for dark modes. Because of the size constraints, dark mode is not enabled by default. You should manually enable it from <code>tailwind.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-comment">// tailwind.config.js</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">darkMode</span>: <span class="hljs-string">'media'</span>,
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>By using above function, tailwind will respect OS preference and apply dark classes. The HTML will look like this: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white dark:bg-black"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-900 dark:text-white"</span>&gt;</span>Dark mode<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 dark:text-gray-300"</span>&gt;</span>
    The feature you've all been waiting for.
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Tailwind also provides an option to control dark theme using a class name called <code>dark</code>,  so the config should look like this: </p>
<pre><code class="lang-js"><span class="hljs-comment">// tailwind.config.js</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">darkMode</span>: <span class="hljs-string">'class'</span>,
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Here's how the HTML will look like: </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Dark mode enabled --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dark"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white dark:bg-black"</span>&gt;</span>
      content
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>You may even add hover and focus styles to dark theme like <code>dark:hover:bg-gray-50</code> or for responsive <code>lg:dark:hover:bg-gray-50</code></p>
<h2 id="extra-2xl-breakpoint">Extra 2xl Breakpoint</h2>
<p>TailwindCSS v2 added an extra media query breakpoint for extra large screens above 1536px using the class <code>2xl:</code></p>
<h2 id="outline-ring-utilities">Outline Ring Utilities</h2>
<p>TailwindCSS v2 ships with new Outline Ring utilities. We all know the default <code>outline</code> is broken and we have a tendency to un-style it. Using v2, you can simply solve this problem by adding ring utilities. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"... focus:outline-none focus:ring-2 focus:ring-blue-300 focus:ring-opacity-50"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<h2 id="use-and8203apply-with-anything">Use @​apply with anything</h2>
<p>Previously, <code>@apply</code> was not able to use with hover, focus &amp; media query elements. However from v2, you can use this in one class and TailwindCSS will generate it properly for you. Pretty neat!</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn</span> {
  @apply bg-indigo-500 p-3 <span class="hljs-attribute">md</span>:p-<span class="hljs-number">5</span> hover:bg-indigo-<span class="hljs-number">600</span> focus:ring-<span class="hljs-number">2</span> focus:ring-indigo-<span class="hljs-number">200</span> focus:ring-opacity-<span class="hljs-number">50</span>;
}
</code></pre>
<h2 id="dropped-ie-11-support">Dropped IE 11 Support</h2>
<p>Along with Bootstrap 5, TailwindCSS also made the brave move to stop supporting IE 11. This resulted in lower file size than before. If you still need IE 11 support, you can use v1.9 which is still great. </p>
<h2 id="and-more">and more..</h2>
<p>There are still lot of new features such as Utility-friendly form styles, Default line-heights per font-size, Extended spacing, typography, and opacity scales, New text overflow utilities, Extend variants, Group-hover and focus-within by default, Default transition duration and easing curve etc.. Be sure to check them out in their  <a target="_blank" href="https://blog.tailwindcss.com/tailwindcss-v2">official blog</a>.</p>
<hr />
<h1 id="tailwindcss-v2-upgrade-guide">TailwindCSS v2 Upgrade Guide</h1>
<p>To make the TailwindCSS upgrade less painful, the creators made a cool new option to allow v1.x users to use the future features using a flag. So if you are one of the user who is using the future or experimental features, its really less complicated. Even if you don't use, I will guide you in this section. </p>
<h2 id="updating-packages">Updating packages</h2>
<p>TailwindCSS requires PostCSS 8 &amp; latest autoprefixer to run properly. So run the following command to update packages. </p>
<pre><code class="lang-bash">npm install tailwindcss@latest postcss@latest autoprefixer@latest
</code></pre>
<p>If your tool is not ready with postcss 8, you might get an error while using the latest build. To use Postcss 7 compatible version, you should uninstall TailwindCSS and run this command instead. </p>
<pre><code class="lang-bash">npm install tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
</code></pre>
<blockquote>
<p>Note: TailwindCSS v2 does not support IE 11 and it requires Node.js 12.13 or higher in both your local and CI environments.</p>
</blockquote>
<h2 id="typography-and-forms">Typography &amp; Forms</h2>
<p>If you were using these plugins, you should update it to the compatible versions for v2. </p>
<p><code>@tailwindcss/custom-forms</code> is no more supported and its replaced with new plaugin called <code>@tailwindcss/forms</code></p>
<h2 id="renamed-class-names">Renamed class names</h2>
<p>Some class names have been renamed. If you are using those class names, you should do a global text search and replace it with new class names. </p>
<table>
<thead>
<tr>
<td>Old Name</td><td>New name</td></tr>
</thead>
<tbody>
<tr>
<td>whitespace-no-wrap</td><td>whitespace-nowrap</td></tr>
<tr>
<td>flex-no-wrap</td><td>flex-nowrap</td></tr>
<tr>
<td>col-gap-</td><td>gap-x-</td></tr>
<tr>
<td>row-gap-</td><td>gap-y-</td></tr>
</tbody>
</table>
<h2 id="replace-clearfix">Replace clearfix</h2>
<p>If you were using <code>clearfix</code> classes, it has been now removed. New <code>flow-root</code> class does the same. So you should use this instead. </p>
<h2 id="replace-shadow-outline-and-shadow-xs">Replace shadow-outline and shadow-xs</h2>
<p>The <code>shadow-outline</code> and <code>shadow-xs</code> classes has been removed in favor of new ring utilites. Again do a global search and replace it with new classes if needed. </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Before --&gt;</span> 
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"... focus:shadow-outline"</span>&gt;</span>
<span class="hljs-comment">&lt;!-- After--&gt;</span> 
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"... focus:ring"</span>&gt;</span>
</code></pre>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Before --&gt;</span> 
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"... shadow-xs"</span>&gt;</span>
<span class="hljs-comment">&lt;!-- After --&gt;</span> 
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"... ring-1 ring-black ring-opacity-5"</span>&gt;</span>
</code></pre>
<h2 id="updating-colors">Updating Colors</h2>
<p>Colors like <code>Teal</code> and <code>Orange</code> is removed from the default color palette in the new version. So if your old project is using that, you should specifically include it from the 22 color groups they provide. If you wish, you can customize it as you would do in previous versions. </p>
<pre><code class="lang-js"><span class="hljs-comment">// tailwind.config.js</span>
<span class="hljs-keyword">const</span> colors = <span class="hljs-built_in">require</span>(<span class="hljs-string">'tailwindcss/colors'</span>)

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">colors</span>: {
      <span class="hljs-attr">gray</span>: colors.trueGray,
      <span class="hljs-attr">teal</span>: colors.teal,
      <span class="hljs-attr">red</span>: colors.rose,
      <span class="hljs-attr">orange</span>: colors.orange,
      <span class="hljs-attr">violet</span>: colors.violet,
    }
  }
}
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>These are the main points you should consider while updating to  TailwindCSS v2. However there are still many small factor you should consider. So make sure you read the official   <a target="_blank" href="https://tailwindcss.com/docs/upgrading-to-v2">Upgrade Guide</a>  as well. </p>
<p> <a target="_blank" href="https://twitter.com/surjithctly">Follow me on Twitter</a> </p>
]]></content:encoded></item><item><title><![CDATA[Creating re-usable Components with Eleventy using Shortcodes (Advanced)]]></title><description><![CDATA[If you are creating websites with Eleventy, sometimes, you will need to create a reusable component where you can re-use it on many places. Eleventy provides us a neat way to handle this using shortcodes. However, this requires registering the shortc...]]></description><link>https://blog.surjithctly.in/creating-re-usable-components-with-eleventy-using-shortcodes-advanced</link><guid isPermaLink="true">https://blog.surjithctly.in/creating-re-usable-components-with-eleventy-using-shortcodes-advanced</guid><category><![CDATA[components]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Fri, 20 Nov 2020 12:48:44 GMT</pubDate><content:encoded><![CDATA[<p>If you are creating websites with Eleventy, sometimes, you will need to create a reusable component where you can re-use it on many places. Eleventy provides us a neat way to handle this using shortcodes. However, this requires registering the shortcode inside <code>.eleventy.js</code>. However when you have a lot of components, it would be better if we could organize it better. </p>
<p>In this article, I'll be showing how you can create as many components without polluting <code>.eleventy.js</code>. If you only need simple integration, Please refer the  <a target="_blank" href="https://www.11ty.dev/docs/shortcodes/">Official Docs</a>.</p>
<blockquote>
<p>I have created an Eleventy Starter Template called "NEAT Starter"  <br />
 <a target="_blank" href="https://github.com/surjithctly/neat-starter">View on Github</a> </p>
</blockquote>
<h2 id="the-plan">The Plan</h2>
<p>When we have many components, its better to organize in files &amp; folder. So first we will create a <code>components</code> folder inside <code>_includes/</code> and then inside components, we create another folder called <code>shortcodes</code>. This is where we put all re-usable components.  We will also create a file inside the components folder called <code>shortcode.js</code> where we import all component shortcodes and register in Eleventy.</p>
<h2 id="writing-shortcode">Writing Shortcode</h2>
<p>First, let us write our first shortcode to return full name &amp; username of a user.  Create a <code>user.js</code> file inside <code>components/shortcodes</code> folder and write some code like this: </p>
<pre><code class="lang-js"><span class="hljs-comment">// components/shortcodes/user.js</span>

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;div class="user"&gt;
&lt;div class="user"&gt;<span class="hljs-subst">${data.name}</span>&lt;/div&gt;
&lt;div class="usernmae"&gt;@<span class="hljs-subst">${data.username}</span>&lt;/div&gt;
&lt;/div&gt;`</span>;
};
</code></pre>
<h2 id="registering-shortcode">Registering Shortcode</h2>
<p>Now Register this shortcode inside <code>components/shortcode.js</code> file by importing the above shortcode and pass it as an argument. </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> user= <span class="hljs-built_in">require</span>(<span class="hljs-string">"./shortcodes/user.js"</span>);

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">eleventyConfig</span>) </span>{
  eleventyConfig.addShortcode(<span class="hljs-string">"user"</span>, user);
};
</code></pre>
<h2 id="adding-to-eleventyjs">Adding to .eleventy.js</h2>
<p>Now we need to integrate it to <code>.eleventy.js</code> to make it working. </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> addShortcodes = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./_includes/components/shortcode.js"</span>);

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">eleventyConfig</span>) </span>{
  ....
  addShortcodes(eleventyConfig);
  ....
}
</code></pre>
<h2 id="calling-the-component">Calling the Component</h2>
<p>Now call the component anywhere you like (eg: any html or njk files) like the following. </p>
<pre><code class="lang-html">{% user name="Surjith", username="surjithctly" %}
</code></pre>
<p>That's it. Now just refresh the server using <code>npm run start</code> to see the changes. </p>
<blockquote>
<p>Note: If you make some changes later to the js files, you might need to restart the server each time to see the new changes. </p>
</blockquote>
<hr />
<h2 id="adding-more-components">Adding More Components</h2>
<p>Our main objective is to create many components. So let's create another one and see how we can do that. This time, we will create a Paired Shortcode, so that you can put content inside the shortcode (like a wrapper).  <a target="_blank" href="https://www.11ty.dev/docs/shortcodes/#paired-shortcodes">Learn more</a> . Create a <code>card.js</code> file inside <code>components/shortcodes</code> folder and write some code like this: </p>
<pre><code class="lang-js"><span class="hljs-comment">// components/shortcodes/card.js</span>

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">content, options</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;div class="card"&gt;
&lt;div class="title"&gt;<span class="hljs-subst">${options.title}</span>&lt;/div&gt;
&lt;div class="content"&gt;<span class="hljs-subst">${content}</span>&lt;/div&gt;
&lt;/div&gt;`</span>;
};
</code></pre>
<h2 id="registering-paired-shortcode">Registering Paired Shortcode</h2>
<p>Now Register this as paired shortcode inside <code>components/shortcode.js</code>. We will add this along with our previous shortcode.  </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> user= <span class="hljs-built_in">require</span>(<span class="hljs-string">"./shortcodes/user.js"</span>);
<span class="hljs-keyword">const</span> card= <span class="hljs-built_in">require</span>(<span class="hljs-string">"./shortcodes/card.js"</span>);

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">eleventyConfig</span>) </span>{
  eleventyConfig.addShortcode(<span class="hljs-string">"user"</span>, user);
  eleventyConfig.addPairedShortcode(<span class="hljs-string">"card"</span>, card);
};
</code></pre>
<p>This time, we don't need to add config to <code>.eleventy.js</code> because its already handled by our previous import. That's the benefit of using this method instead of adding every config inside the <code>.eleventy.js</code>. </p>
<h2 id="calling-the-component">Calling the Component</h2>
<p>Now we call the component anywhere same as the other one. However note the <code>endcard</code> since we are using paired shortcode. </p>
<pre><code class="lang-html">{% card title="Awesome Title"  %}
  Hey, This should render as card content.
{% endcard %}
</code></pre>
<p>However, you will need to restart the server to view the changes. Run <code>npm run start</code> to view the changes. </p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Same way, you could create as many re-usable components you want. You may also create subfolders to arrange it even better. </p>
<p>Let me know your thoughts. </p>
<p> <a target="_blank" href="https://twitter.com/surjithctly">Follow me on Twitter</a></p>
]]></content:encoded></item><item><title><![CDATA[Creating a Floating Label Input form using TailwindCSS (No Javascript)]]></title><description><![CDATA[Here's how you can create a floating label input form using TailwindCSS. We will be doing it with TailwindCSS & some good ol' custom CSS. No need to use javascript for this technique. 
The HTML
First create an input with a wrapping div with class nam...]]></description><link>https://blog.surjithctly.in/creating-a-floating-label-input-form-using-tailwindcss-no-javascript</link><guid isPermaLink="true">https://blog.surjithctly.in/creating-a-floating-label-input-form-using-tailwindcss-no-javascript</guid><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[CSS]]></category><category><![CDATA[forms]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Thu, 12 Nov 2020 12:37:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605182671360/vFg8aQJrO.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here's how you can create a floating label input form using TailwindCSS. We will be doing it with TailwindCSS &amp; some good ol' custom CSS. No need to use javascript for this technique. </p>
<h3 id="the-html"><strong>The HTML</strong></h3>
<p>First create an input with a wrapping div with class name <code>floating-input</code>. Make sure the input have a <code>placeholder attribute</code> because we will be using <code>:placeholder-shown</code> for the floating logic. Then add your <code>label</code> below the <code>input</code> tag. Label should come last because so that we can use the <code>~</code> selector to select the sibling using CSS. </p>
<p>Now add the classes for transform, easing &amp; duration to the <code>label</code>. Your final code  will look something like the following. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"floating-input mb-5 relative"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"border border-gray-200 focus:outline-none rounded-md focus:border-gray-500 focus:shadow-sm w-full p-3 h-16"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"name@example.com"</span> <span class="hljs-attr">autocomplete</span>=<span class="hljs-string">"off"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute top-0 left-0 px-3 py-5 h-full pointer-events-none transform origin-left transition-all duration-100 ease-in-out "</span>&gt;</span>Email address<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"floating-input mb-5 relative"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"border border-gray-200 focus:outline-none rounded-md focus:border-gray-500 focus:shadow-sm w-full p-3 h-16"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">autocomplete</span>=<span class="hljs-string">"off"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute top-0 left-0 px-3 py-5 h-full pointer-events-none transform origin-left transition-all duration-100 ease-in-out "</span>&gt;</span>Password<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full bg-indigo-600 text-white p-3 rounded-md"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<h3 id="the-css"><strong>The CSS</strong></h3>
<p>First set the color of the <code>placeholder</code> to transparent, so that we can still use the property for our logic.  The next step is to make transition to the label when the input is focused. We will also make sure no text is typed by the user using <code>placeholder-shown</code>. Then we can also adjust the padding of input to type in the right location. The final custom CSS will look something like this:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.floating-input</span>&gt;<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">::placeholder</span> {
    <span class="hljs-attribute">color</span>: transparent;
}

<span class="hljs-selector-class">.floating-input</span>&gt;<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span>,
<span class="hljs-selector-class">.floating-input</span>&gt;<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:placeholder-shown)</span> { 
@apply pt-8
}

<span class="hljs-selector-class">.floating-input</span>&gt;<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span>~<span class="hljs-selector-tag">label</span>, 
<span class="hljs-selector-class">.floating-input</span>&gt;<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:placeholder-shown)</span>~<span class="hljs-selector-tag">label</span> {
  @apply opacity-75 scale-75 -translate-y-3 translate-x-1; 
}
</code></pre>
<p>That's it, our CSS only Floating label is done. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605182829357/Meykv6Pg6.gif" alt="floating-label.gif" /></p>
<h4 id="live-demo-httpsplaytailwindcsscom2purx7ojqj">Live Demo: https://play.tailwindcss.com/2Purx7OjqJ</h4>
<hr />
<h3 id="wrap-up">Wrap up</h3>
<p>Do you have any other cool ideas to make this work? or did I miss something? Let me know in the comments. </p>
<p> <a target="_blank" href="https://twitter.com/surjithctly"> Follow me on Twitter</a> </p>
]]></content:encoded></item><item><title><![CDATA[NEAT Stack: Create a Static Website with Netlify CMS, Eleventy, AlpineJS & TailwindCSS]]></title><description><![CDATA[In this article, I will explain how you can create a static website with NEAT Stack and setup CMS to edit the content.
What is NEAT Stack?
A NEAT Stack is a way of creating static websites with following tools.
Netlify CMS
Netlify CMS is used to hand...]]></description><link>https://blog.surjithctly.in/neat-stack-create-a-static-website-with-netlify-cms-eleventy-alpinejs-and-tailwindcss</link><guid isPermaLink="true">https://blog.surjithctly.in/neat-stack-create-a-static-website-with-netlify-cms-eleventy-alpinejs-and-tailwindcss</guid><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Thu, 05 Nov 2020 12:11:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1604578227252/8lXOiVC1L.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, I will explain how you can create a static website with NEAT Stack and setup CMS to edit the content.</p>
<h2 id="what-is-neat-stack">What is NEAT Stack?</h2>
<p>A NEAT Stack is a way of creating static websites with following tools.</p>
<h4 id="netlify-cmshttpswwwnetlifycmsorg"><a target="_blank" href="https://www.netlifycms.org/">Netlify CMS</a></h4>
<p>Netlify CMS is used to handle the CMS part of this static website. It uses <code>git</code> as backend so there is no need to maintain a different server. Maintained by the awesome folks at Netlify. </p>
<h4 id="eleventyhttpswww11tydev"><a target="_blank" href="https://www.11ty.dev/">Eleventy</a></h4>
<p>Eleventy is a simpler static site generator using javascript. Eleventy will compile all of our files and generate a static output where we can host it anywhere. </p>
<h4 id="alpinejshttpsgithubcomalpinejsalpine"><a target="_blank" href="https://github.com/alpinejs/alpine">Alpine.js</a></h4>
<p>Alpine JS is a minimal framework to provide JavaScript behavior in our markup. Think it as a lite version of React &amp; Vue. We use Alpine JS to add JavaScript components like Dropdown, Toggle etc. </p>
<h4 id="tailwind-csshttpstailwindcsscom"><a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a></h4>
<p>Tailwind CSS is a utility first CSS Framework. It has a set of preset utility classes to style anything without touching CSS file. I have wrote an in-depth  <a target="_blank" href="https://blog.surjithctly.in/bootstrap-or-tailwindcss-how-to-decide-a-framework-for-your-next-project">article on TailwindCSS</a> , Be sure to read it for more information.</p>
<h2 id="installation">Installation</h2>
<p>Setting up a project is the most time-consuming part. However getting started with NEAT Stack is now easy because of the following Starter Template. (its written by me 😊) </p>
<p>Github: https://github.com/surjithctly/neat-starter</p>
<h3 id="step-1">Step 1</h3>
<p>Open your favorite terminal in your Work folder and run the following command. </p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/surjithctly/neat-starter.git your-project-name
</code></pre>
<h3 id="step-2">Step 2</h3>
<p>Now navigate to the directory</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> your-project-name
</code></pre>
<h3 id="step-3">Step 3</h3>
<p>Now install the dependencies using the following command</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>You may also use <code>yarn install</code> if you are using Yarn as a package manager.</p>
<h3 id="step-4">Step 4</h3>
<p>Now the next step is to build the project for the first time so that it copies all required files.</p>
<pre><code class="lang-bash">npm run build
</code></pre>
<h3 id="step-5">Step 5</h3>
<p>Now Run Eleventy to create a server and to start local development. </p>
<pre><code class="lang-bash">npm run start
</code></pre>
<p>If you have done everything as instructed above, you'll be able to see the following page in your <code>localhost</code> server</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1604568222533/WVcJfvuAs.png" alt="image.png" /></p>
<p><a target="_blank" href="http://neat-starter.netlify.app/">Live Demo</a></p>
<h2 id="folder-structure">Folder Structure</h2>
<p>Root <code>/</code> folder of our project contains all configuration files like <code>tailwind.config.js</code>, <code>.eleventy.js</code> etc..</p>
<p><code>/src</code> folder contains all of our site contents and data. Before configuring anything, lets go to the <code>/src</code> folder and make some changes to see if its working. </p>
<h2 id="make-changes">Make Changes</h2>
<p>Go to the <code>/src</code> folder and open <code>index.html</code> file in your favorite code editor (My favorite is VSCode).   Now make any changes and save it to see if the changes are reloading or not.  If its reloading with your changes, then it means its working perfectly. If you have any issues, comment here or raise an issue in the github. </p>
<h2 id="nunjucks">Nunjucks</h2>
<p>Writing code with NEAT Stack is fun because of  <a target="_blank" href="https://mozilla.github.io/nunjucks/">Nunjucks</a> . Eleventy supports Nunjucks Templating language by default. So that you can split HTML Code and avoid writing repetitive code like a boss. </p>
<p>Including a part of HTML is easy.  Go to <code>src/_includes/partials/navbar.html</code> and make some changes there. Now we can include the navbar anywhere we wanted using <code>{% include ..}</code> </p>
<p>Another great feature of Nunjucks is the Loops. Suppose you have a card block repeating over 10 times, using Nunjucks, just write it once and loop it. means you will be able to modify easily by changing in one place. The data should be seperated here and It will be handled seamlessly by our Eleventy. </p>
<p>Example Nunjucks loop</p>
<p><code>_data/navigation.yml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">items:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">text:</span> <span class="hljs-string">Menu</span> <span class="hljs-string">One</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"#"</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">text:</span> <span class="hljs-string">Menu</span> <span class="hljs-string">Two</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"#"</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">text:</span> <span class="hljs-string">Menu</span> <span class="hljs-string">Three</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"#"</span>
</code></pre>
<p><code>navbar.html</code></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    {% for item in navigation.items %}
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav__item"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>  <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ item.url }}"</span>&gt;</span>{{ item.text }}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    {% endfor %}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<p>That's it. Adding classes or modifying layout is now easier since Eleventy &amp; Nunjucks combined do all the magic for us. </p>
<h2 id="eleventy-data">Eleventy Data</h2>
<p>Since we talked about the _data above, Its important to know that splitting our code from data helps us to easily modify it using our Netlify CMS. If you enter the data as raw text inside a page, it means you cannot edit that text using CMS. So its important to plan initially according to your needs. Make an list on which data you need to change later and which is not. </p>
<p>By default, Eleventy looks for a folder called <code>_data/</code> and get the data from the files inside. They accept <code>json</code>, <code>yaml</code>, <code>js</code> etc. So you can write data in any format you like. If you have to fetch a data from a different server, you can create <code>.js</code> file which returns the data. </p>
<blockquote>
<p>The <code>_data</code> folder name can be changed using Eleventy configuration</p>
</blockquote>
<h2 id="writing-html">Writing HTML</h2>
<p>Its really simple to create pages or sections in Eleventy. Just create an HTML file in the /src folder and it will automatically build it as a page. If you are using Nunjucks, you can also create filename with <code>.njk</code> extension. If you are writing a blog, you can even use markdown <code>.md</code> as well. </p>
<h2 id="tailwind-css">Tailwind CSS</h2>
<p>Nothing much to talk about TailwindCSS, just add classes as you normally would with any TailwindCSS Projects. If you are new to TailwindCSS, Checkout their website &amp; docs to know more details. (Spoiler Alert: Its awesome!)</p>
<h2 id="alpine-js">Alpine JS</h2>
<p>Adding interactivity is easy using Alpine JS. You can learn more about from the docs. Here's how a simple Toggle works with Alpine JS. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">x-data</span>=<span class="hljs-string">"{ isOpen: false }"</span> @<span class="hljs-attr">keydown.escape</span>=<span class="hljs-string">"isOpen = false"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"isOpen = !isOpen"</span>"&gt;</span>Menu <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"{ 'block': isOpen, 'hidden': !isOpen }"</span> @<span class="hljs-attr">click.away</span>=<span class="hljs-string">"isOpen = false"</span> <span class="hljs-attr">x-show.transition</span>=<span class="hljs-string">"true"</span>&gt;</span>
     ... menu
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</code></pre>
<p>Here you can see the initial data is created for <code>isOpen</code> and its set to <code>false</code> and it changes the data on button <code>click</code>. Based on the Value, we just toggle the <code>div</code> visibility. Wasn't it easy compared to jQuery? We didn't even created a <code>js</code> file.  </p>
<h2 id="netlify-cms">Netlify CMS</h2>
<p>To setup Netlify CMS, first you need to connect the project to git. If you haven't already, <code>git init</code> to setup Git and Push to your favorite repository like Github or Gitlab.</p>
<p>By default, Netlify CMS looks for <code>/admin</code> folder and it should contain a Page (eg: <code>index.html</code>) and <code>config.yml</code> file. </p>
<p>Index page should import the Netlify CMS script from their repo. Our Starter template already includes everything, so you don't need to do this manually. </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Include the script that builds the page and powers Netlify CMS --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Now, coming to the <code>config.yml</code>, we need to setup all configuration options here. </p>
<p>First of all, we need to choose which Authentication we are using for the CMS. If you are using Netlify to host website, you can use it otherwise just use the <code>github</code> Auth. </p>
<pre><code class="lang-yml"><span class="hljs-comment"># Netlify Auth</span>
<span class="hljs-attr">backend:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">git-gateway</span>

<span class="hljs-comment"># Github Auth</span>
<span class="hljs-attr">backend:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">github</span>
</code></pre>
<p>If you want to test the CMS locally, install Proxy Server using the following command. </p>
<pre><code class="lang-bash">npx netlify-cms-proxy-server
</code></pre>
<p>Now in <code>config.yml</code>, make sure local backend is enabled.</p>
<pre><code class="lang-yml"><span class="hljs-attr">local_backend:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>Now <code>admin</code> can be accessed from <code>localhost:PORT/admin</code></p>
<blockquote>
<p>make sure to run both Eleventy Server &amp; Proxy Server parallelly (using multiple terminal windows)</p>
</blockquote>
<p>The next step is to create collections based on our data. There is a  <a target="_blank" href="https://www.netlifycms.org/docs/intro/">well explained docs</a>  in their website. So I'm not repeating it here. </p>
<h2 id="hosting">Hosting</h2>
<p>Once you finished your project, run <code>npm run build</code> and it will create a folder called <code>_site</code> containing all static generated files. Just upload this anywhere you like. </p>
<p>However, to make use of CMS, Try to upload in a server with Continuous integration with Git. The obvious option is  <a target="_blank" href="https://www.netlify.com/">Netlify</a>  or  <a target="_blank" href="https://vercel.com/">Vercel</a>. You can also host it on Github Pages or any other places you wish. </p>
<h2 id="conclusion">Conclusion</h2>
<p>That's all folks. This is one of the modern way to create a static website. One thing to mention here is you are free to change the tools used as per your requirements. There is no requirement to use both four tools. Don't like Tailwind? use any other. Don't like AlpineJS? That's fine too. Don't plan to add CMS? Skip that. Its all based on your needs and requirements. </p>
<p>Questions? Suggestions? Comment below. </p>
<p>If you haven't already, <a target="_blank" href="https://twitter.com/surjithctly">Follow me on Twitter</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Bootstrap or TailwindCSS: How to decide a framework for your next project?]]></title><description><![CDATA[If you are like me, you should be creating websites with the world's popular CSS framework called Bootstrap. But I recently switched to TailwindCSS for a few projects. If you have also seen the hype and wanted to jump in to TailwindCSS yet not comple...]]></description><link>https://blog.surjithctly.in/bootstrap-or-tailwindcss-how-to-decide-a-framework-for-your-next-project</link><guid isPermaLink="true">https://blog.surjithctly.in/bootstrap-or-tailwindcss-how-to-decide-a-framework-for-your-next-project</guid><category><![CDATA[Bootstrap]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[framework]]></category><category><![CDATA[CSS Frameworks]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Wed, 04 Nov 2020 14:59:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1604501668804/AjtRnu88X.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you are like me, you should be creating websites with the world's popular CSS framework called Bootstrap. But I recently switched to TailwindCSS for a few projects. If you have also seen the hype and wanted to jump in to TailwindCSS yet not completely ready? I'll help you to decide whether you should keep using Bootstrap or switch to TailwindCSS.</p>
<h2 id="a-framework-is-a-framework">A Framework is a framework</h2>
<p>First of all, There is no right or wrong way here. Since you are already using Bootstrap, you know the importance and how much time it can save as a developer. Before Bootstrap, we struggled for browser compatibility and make it responsive across devices.  But Bootstrap made it easy and we never worried about these since they handled it for us so that we can focus on our design. </p>
<h2 id="what-is-tailwindcss">What is TailwindCSS</h2>
<p>TailwindCSS is also a framework and I would say its also similar to Bootstrap in some ways. Bootstrap is built with two core features: The pre-built Components and Utility Classes. In the other hand, Using TailwindCSS, you will be missing the pre-built components because TailwindCSS is only about Utility Classes</p>
<h2 id="about-the-hype">About the Hype</h2>
<p>If you watch the developer community closely, you must have encountered developers praising TailwindCSS and using it for their project. Developers with little or no CSS knowledge is loving TailwindCSS because by just remembering few utility classes, they can do many awesome things without even touching CSS file (which is their worst nightmare). </p>
<p>This is the one main reason people love TailwindCSS which you can't do with Bootstrap. Because Bootstrap provides few components but when you want to modify it or create new one, you will have to write custom CSS or SASS. In case of Tailwind, since there is a utility class for everything, its easy in some way. </p>
<h2 id="the-haters">The Haters</h2>
<p>You also might have noticed while some people literally loved TailwindCSS, Many others really hate this framework and they got many reasons for them. My finding is that they are the people knows how to write actual CSS. </p>
<p>Yes. If you are someone who knows how to write proper CSS, you might feel weird to write the same thing as class names. You'll think why don't we write normal CSS instead. </p>
<h2 id="enter-purge-css">Enter Purge CSS</h2>
<p>When using bootstrap, the easier way was to include their CSS using a CDN and do all of our modifications in a custom CSS file. This resulted in a large CSS files and the later version of Bootstrap Promoted SASS to include only the components we actually use. This really helped to reduce the file size. </p>
<p>Similarly, TailwindCSS is also provide a way to remove unused CSS  from our project. Since its powered by PostCSS,  It scans through all of our files and removes un-used class names from the output. If you compared it with Bootstrap it will be way smaller because in Bootstrap we control the file size by omitting components. Even if you use only one class from one component, you should include the whole component. Thus it increases the file size. </p>
<blockquote>
<p>Note: There might be similar tools to purge CSS like TailwindCSS in Bootstrap as well.  </p>
</blockquote>
<h2 id="lengthy-class-names">Lengthy Class Names</h2>
<p>Another main issue people raise with TailwindCSS is their long class names which makes it hard for repetitive elements. While this is true, they provide a nice option to write a single class name if you wish using their <code>@apply</code> directive. </p>
<h4 id="example">Example</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn-primary</span> {
@apply bg-blue-500 text-lg py-5 px-3 text-white;
}
</code></pre>
<p>Like this, you will be able to use just one class but still use TailwindCSS Utilities. </p>
<h2 id="the-benefits">The Benefits</h2>
<p>Now you might be thinking, then what's the difference between writing <code>@apply</code> and normal CSS. The main benefit I see with this method is code organization &amp; maintainability. No matter how many people wrote the CSS, or in how many pages you wrote the CSS, You can assure that there is a standard not just random magic numbers like <code>padding: 17px;</code> </p>
<p>It will be easier to create and maintain a Design System using the <code>tailwind.config.js</code> and you can make sure no extra un-documented classes or property is written to the project. </p>
<h2 id="coming-to-the-point">Coming to the Point</h2>
<p>Bootstrap or TailwindCSS? That's the main question and here is a simple way to figure out! Remember, there is no right or wrong way, it will be based on your project and its requirement. </p>
<h4 id="q-are-you-happy-with-bootstrap">Q) Are you happy with Bootstrap?</h4>
<p>A) If yes, keep using it, but try TailwindCSS is any other projects to get familiar with. </p>
<h4 id="q-do-you-feel-its-hard-to-maintain-bootstrap-css-overtime">Q) Do you feel its hard to maintain Bootstrap CSS overtime?</h4>
<p>A) If yes, Use TailwindCSS</p>
<h4 id="q-do-you-have-deep-understanding-in-writing-pure-css">Q) Do you have deep understanding in writing pure CSS?</h4>
<p>A) Use No framework at all, Just make one for your need. Although its better to try both to get familiar with.</p>
<h4 id="q-do-you-think-writing-a-bunch-of-css-class-names-is-funny">Q) Do you think writing a bunch of CSS class names is funny?</h4>
<p>A) You can still use TailwindCSS with <code>@apply</code> directive. </p>
<h4 id="q-is-multiple-people-writing-css-in-your-project">Q) Is multiple people writing CSS in your Project?</h4>
<p>A) Go with TailwindCSS as it will reduce your headache while maintaining large project. </p>
<h4 id="q-do-you-think-you-are-too-bad-at-css-and-struggling-to-make-layouts-or-ui-even-with-utility-classes">Q) Do you think you are too bad at CSS and struggling to make layouts or UI even with Utility classes?</h4>
<p>A) Go with Bootstrap as they provide many useful components you can copy paste. </p>
<h2 id="thats-it">That's it</h2>
<p>Hope you have an idea of both frameworks now and how it works, Now its your choice to choose your framework or no framework at all. Remember its all based on our needs and our expertise. I repeat there is no right or bad way. </p>
<p>Let me know what's your favorite and why! </p>
<p>Did I miss something, also let me know in the comments. </p>
<p> <a target="_blank" href="https://twitter.com/surjithctly">Follow me on Twitter</a> </p>
]]></content:encoded></item><item><title><![CDATA[The easiest way to create a responsive navbar menu in React JS using TailwindCSS]]></title><description><![CDATA[It took me an hour of googling and finding articles, just to create a responsive navigation bar in react JS. I thought why a simple function like this is complicated in react. I was using Next JS & React JS for the first time ever. So I searched for ...]]></description><link>https://blog.surjithctly.in/the-easiest-way-to-create-a-responsive-navbar-menu-in-react-js-using-tailwindcss</link><guid isPermaLink="true">https://blog.surjithctly.in/the-easiest-way-to-create-a-responsive-navbar-menu-in-react-js-using-tailwindcss</guid><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[navigation]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Sat, 31 Oct 2020 09:47:33 GMT</pubDate><content:encoded><![CDATA[<p>It took me an hour of googling and finding articles, just to create a responsive navigation bar in react JS. I thought why a simple function like this is complicated in react. I was using Next JS &amp; React JS for the first time ever. So I searched for a simple solution. </p>
<p>Every blogs or articles explained the complex way of adding navigation bar. Using some extra plugins or complex setup. </p>
<p>Earlier I did a project with Alpine JS and it was super easy to create a similar responsive menu with hamburger icon. I referred the code once again and found that Alpine JS doing all this magic with one "data". I knew about React Hooks which is similar to the data state in Alpine JS. I ported the data with hooks and viola.. it works without much fuss. </p>
<p>Here's how I did it. </p>
<h2 id="create-a-react-hook">Create a React Hook</h2>
<p>First, create a react hook inside your navigation component.</p>
<pre><code class="lang-jsx"> <span class="hljs-keyword">const</span> [isOpen, setisOpen] = React.useState(<span class="hljs-literal">false</span>);
</code></pre>
<h2 id="menu-icon">Menu Icon</h2>
<p>Now create an SVG hamburger Menu button that is visible only on mobile (using TailwindCSS Classes)</p>
<p>Then make sure the Menu &amp; Close icon path visible based on the State <code>isOpen</code>. </p>
<pre><code class="lang-jsx">&lt;button type=<span class="hljs-string">"button"</span> className=<span class="hljs-string">"block lg:hidden"</span>&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-6 w-6 fill-current"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span>&gt;</span>
        {isOpen &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fillRule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">clipRule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z"</span> /&gt;</span>
        )}
        {!isOpen &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fillRule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z"</span> /&gt;</span>
        )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span></span>
&lt;/button&gt;
</code></pre>
<h2 id="click-handler">Click Handler</h2>
<p>Now create an <code>onClick</code> handler to handle the click toggle to show / hide menu on mobile. </p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [isOpen, setisOpen] = React.useState(<span class="hljs-literal">false</span>);

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
    setisOpen(!isOpen);
  }

  <span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>  <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block lg:hidden"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>&gt;</span>
...
...</span>
</code></pre>
<h2 id="final-part">Final Part</h2>
<p>And the final part. Make sure to show / hide our main menu based on the state using TailwindCSS Classes.</p>
<pre><code class="lang-jsx">&lt;ul className={<span class="hljs-string">`lg:flex <span class="hljs-subst">${  isOpen ? <span class="hljs-string">"block"</span> : <span class="hljs-string">"hidden"</span> }</span> `</span>}&gt;
...
&lt;/ul&gt;
</code></pre>
<p>That's it. We now have a working Responsive Navbar using React Hooks. </p>
<blockquote>
<p>One thing to note here is the <code>hidden</code> class is always added to the <code>&lt;ul&gt;</code> regardless of browser size. But adding our magic <code>lg:flex</code> class overwrites the <code>hidden</code> class in large sreens. So no extra configiration is needed. </p>
</blockquote>
<p>Pretty neat!</p>
<h2 id="live-demo">Live Demo</h2>
<p>I can add a live demo on Codepen if you wish, just let me know in the comments. </p>
<h3 id="conclusion">Conclusion</h3>
<p>I found this solution is easier to do a responsive navigation menu in react. If you are not using tailwind, you can still do the same using pure css or scoped css. If you have a better &amp; easy version than this, please let me know in the comments as I'm a beginner in this react world. </p>
]]></content:encoded></item><item><title><![CDATA[Pure Vanilla JavaScript Smooth Scroll to Element on <a> anchor tag click #id]]></title><description><![CDATA[It took me half an hour and countless of stackoverflow pages to find a perfect solution for a smooth scroll for <a> anchor links. 
So I'm adding here it as a snippet for future googlers. 
document
    .querySelectorAll('.nav__item a[href^="#"]')
    ...]]></description><link>https://blog.surjithctly.in/pure-vanilla-javascript-smooth-scroll-to-element-on-lessagreater-anchor-tag-click-id</link><guid isPermaLink="true">https://blog.surjithctly.in/pure-vanilla-javascript-smooth-scroll-to-element-on-lessagreater-anchor-tag-click-id</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[vanilla-js]]></category><category><![CDATA[js]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Thu, 29 Oct 2020 11:59:38 GMT</pubDate><content:encoded><![CDATA[<p>It took me half an hour and countless of stackoverflow pages to find a perfect solution for a smooth scroll for <code>&lt;a&gt;</code> anchor links. </p>
<p>So I'm adding here it as a snippet for future googlers. </p>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>
    .querySelectorAll(<span class="hljs-string">'.nav__item a[href^="#"]'</span>)
    .forEach(<span class="hljs-function"><span class="hljs-params">trigger</span> =&gt;</span> {
        trigger.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
            e.preventDefault();
            <span class="hljs-keyword">let</span> hash = <span class="hljs-built_in">this</span>.getAttribute(<span class="hljs-string">'href'</span>);
            <span class="hljs-keyword">let</span> target = <span class="hljs-built_in">document</span>.querySelector(hash);
            <span class="hljs-keyword">let</span> headerOffset = <span class="hljs-number">100</span>;
            <span class="hljs-keyword">let</span> elementPosition = target.offsetTop;
            <span class="hljs-keyword">let</span> offsetPosition = elementPosition - headerOffset;

            <span class="hljs-built_in">window</span>.scrollTo({
                <span class="hljs-attr">top</span>: offsetPosition,
                <span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span>
            });
        };
    });
</code></pre>
<h3 id="note">Note</h3>
<p>Please note there is also a new CSS property available to do the same trick, however you won't be able to offset it properly. This will cause issue when you are using a fixed navbar. In that case, JS Solution will be better. </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/surjithctly/status/1293902210753916936?s=20">https://twitter.com/surjithctly/status/1293902210753916936?s=20</a></div>
]]></content:encoded></item><item><title><![CDATA[Migrating from Bootstrap 4 to 5 - Read this before you upgrade!]]></title><description><![CDATA[Bootstrap 5 is in Alpha Stage and we are all excited about this news as this new update from Bootstrap ships a lot of new features as well as breaking changes from the old version. So we should be very careful when updating your Website or Applicatio...]]></description><link>https://blog.surjithctly.in/migrating-from-bootstrap-4-to-5-read-this-before-you-upgrade</link><guid isPermaLink="true">https://blog.surjithctly.in/migrating-from-bootstrap-4-to-5-read-this-before-you-upgrade</guid><category><![CDATA[Bootstrap]]></category><category><![CDATA[bootstrap 4]]></category><category><![CDATA[migration]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Thu, 29 Oct 2020 11:56:31 GMT</pubDate><content:encoded><![CDATA[<p>Bootstrap 5 is in Alpha Stage and we are all excited about this news as this new update from Bootstrap ships a lot of new features as well as breaking changes from the old version. So we should be very careful when updating your Website or Application to the new version.</p>
<h4 id="table-of-contents">Table of Contents</h4>
<ol>
<li><a class="post-section-overview" href="#whats-new-in-bootstrap-5">What’s new in Bootstrap 5</a></li>
<li><a class="post-section-overview" href="#upgrade-bootstrap-4-to-5">Upgrade Bootstrap 4 to 5</a></li>
</ol>
<h2 id="whats-new-in-bootstrap-5">What’s new in Bootstrap 5</h2>
<p>Before we talk about how we can upgrade our website to new version, first let’s checkout the new features of Bootstrap v5.</p>
<h3 id="1-rip-jquery">1. RIP jQuery</h3>
<p>Yes. This is the first highlight of Bootstrap 5. It does not ship with <a target="_blank" href="https://web3canvas.com/best-premium-responsive-jquery-slider-plugins/">jQuery</a> by default. However they are not making the mistake by completely removing it. Instead Bootstrap now provides an option for users to include jQuery if they wish.</p>
<p>All of their components are now powered with Pure Vanilla Javascript. Thanks to the modern front-end development tools and browser support. Here’s what their creators said about dropping jQuery as a dependency.</p>
<blockquote>
<p><em>Personally, I’m forever grateful for the empowerment and support it gave me to continue writing front-end code.</em> <em>it’s forever changed JavaScript itself. Thank you to every jQuery contributor who made that possible for folks like me.</em></p>
<p><a target="_blank" href="https://github.com/mdo">@mdo</a> &amp; <a target="_blank" href="https://github.com/twbs">team</a></p>
</blockquote>
<h3 id="2-css-variables">2. CSS Variables</h3>
<p>Since they dropped the support for Internet Explorer, It was easy for them to implement the new CSS Custom Properties that everyone wished for. The distance between SASS and <a target="_blank" href="https://web3canvas.com/expert-tips-to-level-up-your-website-with-css-animations/">CSS</a> are now reducing day by day.</p>
<p>No more complex installation and compilation just to store some color variables. Yay!!! Here’s how it looks like:</p>
<pre><code class="lang-css">    <span class="hljs-selector-pseudo">:root</span> {
      <span class="hljs-attribute">--bs-blue</span>: <span class="hljs-number">#0d6efd</span>;
      <span class="hljs-attribute">--bs-indigo</span>: <span class="hljs-number">#6610f2</span>;
      <span class="hljs-attribute">--bs-purple</span>: <span class="hljs-number">#6f42c1</span>;
      <span class="hljs-attribute">--bs-pink</span>: <span class="hljs-number">#d63384</span>;
      <span class="hljs-attribute">--bs-red</span>: <span class="hljs-number">#dc3545</span>;
      <span class="hljs-attribute">--bs-orange</span>: <span class="hljs-number">#fd7e14</span>;
      <span class="hljs-attribute">--bs-yellow</span>: <span class="hljs-number">#ffc107</span>;
      ...
</code></pre>
<h3 id="3-improved-docs">3. Improved Docs</h3>
<p>That’s where we spend most of our time. And new Bootstrap 5 has an improved docs. More explanation and new Customize section.</p>
<p><img src="https://i0.wp.com/web3canvas.com/wp-content/uploads/2020/07/image-18.png?resize=1024%2C472&amp;ssl=1" alt /></p>
<h3 id="4-color-palettes">4. Color Palettes</h3>
<p>v4 shipped with limited colors. We had to manually create colors for our project because the stock one didn’t fit most of our use cases. But Bootstrap v5 comes with a lot of colors. Really a lot<strong>. 100+</strong> color variations are now available to choose from in the new Boostrap 5.</p>
<p><img src="https://i0.wp.com/user-images.githubusercontent.com/98681/84801339-e5585680-afb3-11ea-8743-29647ff3f3a9.png?w=1170&amp;ssl=1" alt="Bootstrap 5 color palette" /></p>
<h3 id="5-updated-forms">5. Updated Forms</h3>
<p>Another revamped area is the form section. It has now new styled components and form controls. Checkboxes are radio boxes are now custom by default. No more <code>.custom-</code> classes needed.</p>
<p><img src="https://i1.wp.com/blog.getbootstrap.com/assets/img/2020/06/v5-checks.png?w=1170&amp;ssl=1" alt="New Bootstrap 5 checks" /></p>
<h3 id="6-utilities-api">6. Utilities API</h3>
<p>Since the popularity of Tailwind CSS, Bootstrap 4 is also giving more options to create utilities. They have built a brand new Utility API to the new version.</p>
<h3 id="7-grid-system">7. Grid System</h3>
<p>Yes. its still flex based and they have not implemented the CSS Grid yet, but as per their announcement they plan to do so in the future. Since it will be a breaking change. They are very careful about this.</p>
<p>CSS Grid Lovers have to wait longer 😊</p>
<h3 id="8-bootstrap-icons">8. Bootstrap Icons</h3>
<p>Even its not related to the new version, but Bootstap has now their own open sourceIcons library called “<a target="_blank" href="https://icons.getbootstrap.com/">Bootstrap Icons</a>“. Be sure to check them out .</p>
<p><img src="https://i2.wp.com/web3canvas.com/wp-content/uploads/2020/07/image.png?resize=1024%2C638&amp;ssl=1" alt /></p>
<h3 id="9-coming-soon">9. Coming Soon</h3>
<p>At the time of the writing v5 is still in the Alpha Stage. As per their announcement, there are more to come. Features like Official RTL Support (finally!!!), Offcanvas and more are in the pipeline. Be sure to checkout their <a target="_blank" href="https://github.com/twbs/bootstrap/">Github</a> page for more info.</p>
<hr />
<h2 id="upgrade-bootstrap-4-to-5">Upgrade Bootstrap 4 to 5</h2>
<p>We will list main changes in the bootstrap v5 and then we will show you how you can modify &amp; update it to make it compatible with new Bootstrap 5 version.</p>
<h3 id="dropped-media-object">Dropped .media Object</h3>
<p>This is a breaking change as Bootstrap 5 does not ship with <code>.media</code> object. However it can be easily converted to bootstrap 5 using the utility classes. See the code below.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- // Bootstrap 4 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"media"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"..."</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mr-3"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"..."</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"media-body"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-0"</span>&gt;</span>Media heading<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
        Media Content ...
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- // Bootstrap 5 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"d-flex"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"..."</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"144"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"144"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-shrink-0 mr-3"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"..."</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-0"</span>&gt;</span>Media heading<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
        Media Content ...
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="custom-checkboxes-and-radio">Custom Checkboxes and Radio</h3>
<p>Previously in Boostrap 4, there were two options for checkboxes. Default and Custom. In v5, they merged in to one.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- // Bootstrap 4 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"custom-control custom-checkbox"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"custom-control-input"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"customCheck1"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"custom-control-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"customCheck1"</span>&gt;</span>Custom checkbox<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- // Bootstrap 5 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-check"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-check-input"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"flexCheckDefault"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-check-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"flexCheckDefault"</span>&gt;</span>
        Default checkbox
      <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="file-browser">File Browser</h3>
<p>Updated file input component with the same overall design, but improved HTML. Refactored <code>.form-file</code> markup to resolve some visual bugs while allowing translation and button text changes via HTML instead of CSS.<br />Dropped native <code>.form-control-file</code> and <code>.form-control-range</code> components entirely. Renamed <code>.custom-file</code> to <code>.form-file</code> (including variables).</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- // Bootstrap 4 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"custom-file"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"custom-file-input"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"customFile"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"custom-file-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"customFile"</span>&gt;</span>Choose file<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- // Bootstrap 5 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-file"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-file-input"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"customFile"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-file-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"customFile"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-file-text"</span>&gt;</span>Choose file...<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-file-button"</span>&gt;</span>Browse<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="dropped-form-group">Dropped .form-group</h3>
<p>Form group class <code>.form-group</code> has been removed in bootstrap 5 in favor of margin utilities. This is one of the the most used bootstrap class. So make sure to change it as per the code below.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- // Bootstrap 4 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email address<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"..."</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- // Bootstrap 5 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-3"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-label"</span>&gt;</span>Email address<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"..."</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="dropped-input-group-append-and-prepend">Dropped .input-group-append &amp; prepend</h3>
<p>Dropped <code>.input-group-append</code> and <code>.input-group-prepend</code>. You can now just add buttons and <code>.input-group-text</code> as direct children of the input groups.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- // Bootstrap 4 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group mb-3"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group-prepend"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group-text"</span>&gt;</span>$<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Amount (to the nearest dollar)"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group-append"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group-text"</span>&gt;</span>.00<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- // Bootstrap 5 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group mb-3"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group-text"</span>&gt;</span>$<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Amount (to the nearest dollar)"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group-text"</span>&gt;</span>.00<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="dropped-card-columns-and-deck">Dropped Card Columns &amp; Deck</h3>
<p>Removed the card columns in favor of a Masonry grid. Removed card decks in favor of the grid which adds more flexibility over responsive behavior.</p>
<p>👉 Example of new <a target="_blank" href="https://v5.getbootstrap.com/docs/5.0/examples/masonry/">Card Columns with Masonry</a>.</p>
<p><strong>See Below</strong>: Example Code for Card Deck v4 / Card Group v5</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- // Bootstrap 4 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-deck"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"..."</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-img-top"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"..."</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-body"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>Card title<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span>This is a longer card...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">small</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-muted"</span>&gt;</span>3 mins ago<span class="hljs-tag">&lt;/<span class="hljs-name">small</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        ...
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
       ...
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- // -------------------------------------------------------- // --&gt;</span>
    <span class="hljs-comment">&lt;!-- // Bootstrap 5 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-group"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"..."</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-img-top"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"..."</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-body"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>Card title<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span>This is a wider card...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">small</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-muted"</span>&gt;</span>3 mins ago<span class="hljs-tag">&lt;/<span class="hljs-name">small</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        ...
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        ...
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="dropped-jumbotron">Dropped .jumbotron</h3>
<p>The jumbotron component is removed in favor of utility classes like <code>.bg-light</code> for the background color and <code>.p-*</code> classes to control padding.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- // Bootstrap 4 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"jumbotron"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"display-4"</span>&gt;</span>Jumbotron<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"lead"</span>&gt;</span>This is a modified jumbotron...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- // Bootstrap 5 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-light p-3"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"display-4"</span>&gt;</span>Jumbotron<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"lead"</span>&gt;</span>This is a modified jumbotron...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="navbar-needs-container">Navbar needs .container</h3>
<p>From Bootstrap 5, All navbars now require a container within. This drastically simplifies spacing requirements and removes the need for extensive CSS overrides we added for responsive containers in v4.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- // Bootstrap 4 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar navbar-light bg-light"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-brand"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Navbar<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- // Bootstrap 5 --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar navbar-light bg-light"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container-fluid"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-brand"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Navbar<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</code></pre>
<h3 id="there-are-more-changes">There are more Changes</h3>
<p>We have only highlighted Main Changes which will break your layout. However there are lot of other changes you need to checkout before Upgrading or Migrating to Bootstrap 5.</p>
<p>👉 Check out the <a target="_blank" href="https://v5.getbootstrap.com/docs/5.0/migration/">Official Migration Docs</a> for More Info</p>
<h3 id="conclusion">Conclusion</h3>
<p>I hope you are able now to easily convert your old Bootstrap 4 project to Bootstrap 5 with this help. If yes. Please share it to your peers who might also need some help. If you have any suggestions, comments or any errors, please let me know in the comments.</p>
<p>Peace ✌</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[How to Bypass Medium.com Paywall (Upgrade) and Read Full Article?]]></title><description><![CDATA[We loved medium, for its simplicity. But no more. They have added a paywall to read their articles. Once you read few articles, they will ask you to upgrade.
We Only able to see the first paragraph, like this:

The Solution?
1. Log out
Log out from M...]]></description><link>https://blog.surjithctly.in/how-to-bypass-mediumcom-paywall-upgrade-and-read-full-article</link><guid isPermaLink="true">https://blog.surjithctly.in/how-to-bypass-mediumcom-paywall-upgrade-and-read-full-article</guid><category><![CDATA[medium]]></category><category><![CDATA[free]]></category><dc:creator><![CDATA[Surjith S M]]></dc:creator><pubDate>Thu, 29 Oct 2020 11:46:32 GMT</pubDate><content:encoded><![CDATA[<p>We loved medium, for its simplicity. But no more. They have added a paywall to read their articles. Once you read few articles, they will ask you to upgrade.</p>
<p>We Only able to see the first paragraph, like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1603971859946/xsQaEkBRm.png" alt="image.png" /></p>
<h2 id="the-solution">The Solution?</h2>
<p><strong>1. Log out</strong>
Log out from Medium.com and refresh the page.
Yes. Just logout from your medium account.</p>
<p><strong>2. Delete Cache</strong>
Sometimes, it won't still work because they store the data. For that you can just delete their site cache by opening Inspect Element =&gt; Application =&gt; Clear Site Data</p>
<p><strong>3. Incognito Mode</strong>
Sometimes its easier to open the URL in an incognito mode which makes sure there is no cache involved. Also you don't need to sign-out.</p>
<p><strong>3. Outline to the Rescue</strong>
Outline is a simple service which converts normal webpages to a readable format. Since this website caches content before reaching browser, Medium doesn't know if it came from human or search engine. Hence they allow full article.</p>
<p><strong>Example</strong></p>
<p>Medium Article: https://medium.com/..../78a0797259a6</p>
<p>Outline Link: https://outline.com/Ce4fv6</p>
]]></content:encoded></item></channel></rss>