<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ tailwind - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ tailwind - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 02 Jun 2026 21:42:53 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/tailwind/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use Flex in Tailwind CSS and Justify Flex Items ]]>
                </title>
                <description>
                    <![CDATA[ Hey there! If you're building modern web interfaces, chances are you've already fallen in love with Tailwind CSS for its speed and flexibility. One of the most powerful tools in Tailwind's arsenal is  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-flex-in-tailwind-css-and-justify-flex-items/</link>
                <guid isPermaLink="false">69b1975b6c896b0519a78b21</guid>
                
                    <category>
                        <![CDATA[ flexbox ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tailwind CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tailwind CSS Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flex ]]>
                    </category>
                
                    <category>
                        <![CDATA[ flex css ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ajay Patel ]]>
                </dc:creator>
                <pubDate>Wed, 11 Mar 2026 16:24:59 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/3522007b-55f0-44bf-9b6f-b0489d1a8774.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hey there! If you're building modern web interfaces, chances are you've already fallen in love with Tailwind CSS for its speed and flexibility.</p>
<p>One of the most powerful tools in Tailwind's arsenal is its set of Flexbox utilities. Flexbox lets you create dynamic, responsive layouts without writing custom CSS, and Tailwind makes it incredibly intuitive with simple class names.</p>
<p>In this tutorial, we'll walk through everything you need to know about using Flexbox in Tailwind, from the basics to advanced patterns. Whether you're a beginner or looking to level up your layouts, by the end, you'll feel confident building anything from card grids to complex dashboards.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="https://preview.freecodecamp.org/69857b45ac030cc5d597ac21#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-what-is-flexbox">What is Flexbox?</a></p>
</li>
<li><p><a href="#heading-how-tailwind-css-makes-flexbox-easy-to-use">How Tailwind CSS Makes Flexbox Easy to Use</a></p>
</li>
<li><p><a href="#heading-how-to-use-flex-in-tailwind">How to use flex in Tailwind</a></p>
</li>
<li><p><a href="#heading-flex-item-sizing-basis-grow-amp-shrink">Flex Item Sizing: Basis, Grow &amp; Shrink</a></p>
</li>
<li><p><a href="#heading-controlling-flex-direction">Controlling Flex Direction</a></p>
</li>
<li><p><a href="#heading-fine-tuning-flexbox-layout">Fine-Tuning Flexbox Layout</a></p>
</li>
<li><p><a href="#heading-how-to-justify-and-align-flex-items-in-tailwind">How to Justify and Align Flex Items in Tailwind</a></p>
</li>
<li><p><a href="#heading-practice-flexbox-with-interactive-games">Practice Flexbox with Interactive Games</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion:</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before diving into Flexbox with Tailwind CSS, it helps to have a few basics in place so you can follow along comfortably.</p>
<p><strong>Basic Knowledge:</strong></p>
<ul>
<li><p>A foundational understanding of <strong>HTML</strong> (how elements and containers work).</p>
</li>
<li><p>Basic familiarity with <strong>CSS</strong> (especially properties like <code>display</code>, <code>width</code>, and <code>height</code>).</p>
</li>
<li><p>A general idea of how responsive design works (helpful but not required).</p>
</li>
</ul>
<p>You do <em>not</em> need to be a Flexbox expert, as we’ll cover the important concepts as we go.</p>
<p><strong>Tools You’ll Need:</strong></p>
<ul>
<li><p>A code editor like VS Code (or any editor you prefer)</p>
</li>
<li><p>A browser for testing layouts</p>
</li>
<li><p>A project with Tailwind CSS installed</p>
</li>
</ul>
<h2 id="heading-what-is-flexbox">What is Flexbox?</h2>
<p><strong>Flexbox (Flexible Box Layout)</strong> is a CSS layout model designed to make it easier to design flexible, responsive layouts without using floats or complicated positioning tricks.</p>
<p>Before Flexbox, aligning elements vertically, spacing items evenly, or making layouts adapt to different screen sizes was often frustrating and required hacks. Flexbox solves these problems by providing a simple and predictable way to control alignment, spacing, and ordering of elements inside a container.</p>
<p>The main concept is simple:</p>
<ul>
<li><p>You have a flex container</p>
</li>
<li><p>Inside it are flex items</p>
</li>
<li><p>The container controls how its items are laid out</p>
</li>
</ul>
<p>Once an element is set to <code>display: flex</code>, its children automatically become flex items.</p>
<pre><code class="language-css">.container {
  display: flex;
}
</code></pre>
<h2 id="heading-how-tailwind-css-makes-flexbox-easy-to-use">How Tailwind CSS Makes Flexbox Easy to Use</h2>
<p>Flexbox is powerful, but writing custom CSS for every layout can become repetitive and time-consuming. Tailwind CSS simplifies this by providing utility classes that map directly to Flexbox properties, allowing developers to build layouts quickly without writing custom CSS.</p>
<p>Instead of switching between HTML and CSS files, Tailwind lets you apply Flexbox behavior directly in your markup, making layouts more readable and faster to develop. It turns Flexbox's sometimes verbose properties into short, memorable utilities. No more remembering <code>justify-content: space-between;</code> just write <code>justify-between</code>.</p>
<p>Benefits:</p>
<ul>
<li><p>Responsive by default (add <code>md:</code>, <code>lg:</code>, and so on)</p>
</li>
<li><p>Composable (combine classes freely).</p>
</li>
<li><p>No custom CSS needed for most cases.</p>
</li>
</ul>
<h2 id="heading-how-to-use-flex-in-tailwind">How to Use Flex in Tailwind</h2>
<p>Flexbox is one of the most powerful layout systems in modern CSS, and Tailwind CSS makes it extremely approachable by exposing Flexbox behavior through simple utility classes. Instead of writing custom CSS, you compose layouts directly in your HTML using predefined classes.</p>
<ol>
<li><p><code>flex</code>: The <code>flex</code> Class is the foundation of Flexbox in Tailwind.</p>
</li>
<li><p><code>flex-1</code>: Allows the element to grow and shrink and forces it to take up the remaining available space.</p>
</li>
<li><p><code>flex-auto</code>: It makes an item flexible while respecting its content size – that is, it only grows and shrinks as needed.</p>
</li>
<li><p><code>flex-none</code>: Disables growing and shrinking for an item.</p>
</li>
</ol>
<p>Here is a basic example that shows where to place these classes:</p>
<pre><code class="language-xml">&lt;!-- this is the container --&gt;
&lt;div class="flex"&gt;
  &lt;!-- these are the items inside the container --&gt;
  &lt;div class="flex-1"&gt;Item 1&lt;/div&gt;
  &lt;div class="flex-auto"&gt;Item 2&lt;/div&gt;
  &lt;div class="flex-none"&gt;Item 3&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<h2 id="heading-flex-item-sizing-basis-grow-amp-shrink">Flex Item Sizing: Basis, Grow, &amp; Shrink</h2>
<p>Three fundamental properties control how elements size themselves inside a flex container:</p>
<ul>
<li><p>flex-basis</p>
</li>
<li><p>flex-grow</p>
</li>
<li><p>flex-shrink</p>
</li>
</ul>
<p>Rather than thinking in fixed widths and heights, Flexbox uses a dynamic space-distribution model. Each flex item starts with an initial size, then grows or shrinks depending on the available space and the rules defined by these three properties.</p>
<h3 id="heading-flex-basis">flex-basis</h3>
<p><a href="https://tailwindcss.com/docs/flex-basis">flex-basis</a> controls the initial size of a flex item before <code>flex-grow</code> or <code>flex-shrink</code> kick in. Think of it as the item’s starting width or height (depending on flex direction).</p>
<pre><code class="language-xml">&lt;div class="flex ..."&gt;
	&lt;div class="... basis-1/5"&gt;01&lt;/div&gt;
  &lt;div class="...basis-4/5"&gt;02&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Here's what this makes:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770355749158/19071bac-8426-4602-a7ab-fcc0ecd62c95.png" alt="flex basis" style="display:block;margin:0 auto" width="553" height="94" loading="lazy">

<p>flex-basis's most used utility classes are:</p>
<ul>
<li><p><code>basis-auto</code>: This means the item’s initial size is based on its <strong>content size or any explicitly defined width/height</strong>. It doesn't force a specific starting size. Instead, it respects intrinsic sizing.</p>
</li>
<li><p><code>basis-0</code>: This makes the item start at <strong>0 width (or height in column layouts)</strong> before space is distributed. It’s commonly used with <code>grow</code> to evenly distribute space regardless of content size.</p>
</li>
<li><p><code>basis-full</code>: The item initially takes up the <strong>entire width (or height)</strong> of the container before shrinking or wrapping.</p>
</li>
<li><p><code>basis-xs/md/lg/xl..</code>: Built-in values.</p>
</li>
<li><p><code>basis-&lt;fraction&gt;</code>: Giving a value with a dynamic such as 1/2, 4/5, and so on.</p>
</li>
<li><p><code>basis-&lt;number&gt;</code>: Uses Tailwind’s spacing scale (in <code>rem</code> units).</p>
</li>
<li><p><code>basis-[&lt;value&gt;]</code>: Syntax to set the basis based on a completely custom value.</p>
</li>
</ul>
<h3 id="heading-flex-grow">flex-grow</h3>
<p><a href="https://tailwindcss.com/docs/flex-grow">flex-grow</a> controls how much a flex item expands to fill extra space in the flex container. It determines how leftover space is distributed among flex items after their initial sizes.</p>
<pre><code class="language-xml">&lt;div class="flex ..."&gt;
	&lt;div class="... grow"&gt;01&lt;/div&gt;
  &lt;div class="...grow-0"&gt;02&lt;/div&gt;
  &lt;div class="...grow"&gt;03&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Here's what this creates:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770355790996/6c4f3c72-0ff9-4f5c-bab7-6e5bc1e9df2e.png" alt="flex grow" style="display:block;margin:0 auto" width="471" height="92" loading="lazy">

<p>flex-grow's most used utility classes are:</p>
<ul>
<li><p><code>grow</code>: The item will grow to take up available extra space. If multiple items use <code>grow</code>, they share space equally (unless different grow values are specified)..</p>
</li>
<li><p><code>grow-0</code>: The item will <strong>not expand</strong> beyond its initial size, even if extra space is available.</p>
</li>
<li><p><code>grow-&lt;number&gt;</code>: If one item has <code>grow-2</code> and another has <code>grow-1</code>, the first item gets <strong>twice as much extra space</strong> as the second.</p>
</li>
<li><p><code>grow-[&lt;value&gt;]</code>: Allows a custom grow value (e.g. grow-[3]).</p>
</li>
</ul>
<h3 id="heading-flex-shrink">flex-shrink</h3>
<p><a href="https://tailwindcss.com/docs/flex-shrink">flex-shrink</a> controls how much a flex item shrinks when there isn’t enough space in the flex container. It determines how items reduce their size relative to each other when the container overflows.</p>
<pre><code class="language-xml">&lt;div class="flex ..."&gt;
	&lt;div class="... grow shrink-0"&gt;01&lt;/div&gt;
  &lt;div class="...shrink"&gt;02&lt;/div&gt;
  &lt;div class="...grow shrink-0"&gt;03&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Here's what this creates:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770355844387/bd1b9e5c-0ae5-4e61-97f7-2cbfbc060ecf.png" alt="flex shrink" style="display:block;margin:0 auto" width="630" height="90" loading="lazy">

<p>flex-shrink's most used utility classes are:</p>
<ul>
<li><p><code>shrink</code>: The item is allowed to shrink when necessary to prevent overflow.</p>
</li>
<li><p><code>shrink-0</code>: The item will <strong>not shrink</strong>, even if space becomes limited. This may cause overflow if other items cannot compensate.</p>
</li>
<li><p><code>shrink-&lt;number&gt;</code>: Sets proportional shrinking behavior. An item with <code>shrink-2</code> will shrink <strong>twice as much</strong> as one with <code>shrink-1</code>.</p>
</li>
<li><p><code>shrink-[&lt;value&gt;]</code>: Syntax to set a completely custom shrink value.</p>
</li>
</ul>
<h2 id="heading-controlling-flex-direction">Controlling Flex Direction</h2>
<p>In Tailwind CSS, the direction in which flex items are laid out is controlled using <a href="https://tailwindcss.com/docs/flex-direction">flex-direction utilities</a>. These utilities define whether items are placed horizontally or vertically, and in which order.</p>
<h3 id="heading-flex-row">flex-row</h3>
<p><code>flex-row</code> is the default flex direction in both CSS Flexbox and Tailwind. When it is applied, flex items are laid out <strong>horizontally</strong> along the main axis, starting from left to right (in left-to-right languages).</p>
<pre><code class="language-xml">&lt;div class="flex flex-row"&gt;
  &lt;div&gt;01&lt;/div&gt;
  &lt;div&gt;02&lt;/div&gt;
  &lt;div&gt;03&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>This outputs:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770356616341/4b3dbd4c-e437-49d0-a9b5-0576781ccfc3.png" alt="flex row default" style="display:block;margin:0 auto" width="285" height="141" loading="lazy">

<p><strong>Use case:</strong> Navigation bars, horizontal button groups, toolbars.</p>
<h3 id="heading-flex-row-reverse">flex-row-reverse</h3>
<p>The <code>flex-row-reverse</code> utility lays out flex items horizontally, but in the opposite direction from right to left. While the visual order of items is reversed, the HTML source order remains unchanged, which is important for accessibility and screen readers.</p>
<pre><code class="language-xml">&lt;div class="flex flex-row-reverse"&gt;
  &lt;div&gt;01&lt;/div&gt;
  &lt;div&gt;02&lt;/div&gt;
  &lt;div&gt;03&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Output:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770356916275/1fad634a-c4a0-4417-a63e-0ed51d9f76f7.png" alt="flex row reverse" style="display:block;margin:0 auto" width="272" height="146" loading="lazy">

<p><strong>Use case:</strong> Forms, cards, sidebars, vertical menus.</p>
<h3 id="heading-flex-col">flex-col</h3>
<p>The <code>flex-col</code> utility changes the flex direction to <strong>vertical</strong>, stacking items from top to bottom. In this case, the main axis runs vertically.</p>
<pre><code class="language-html">&lt;div class="flex flex-col"&gt;
  &lt;div&gt;01&lt;/div&gt;
  &lt;div&gt;02&lt;/div&gt;
  &lt;div&gt;03&lt;/div&gt;
&lt;/div&gt; 
</code></pre>
<p>Output:</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e0f2c2e490269cb30227a2b/cea4e846-a234-42e8-9509-2b166a5cf5c4.png" alt="cea4e846-a234-42e8-9509-2b166a5cf5c4" style="display:block;margin:0 auto" width="282" height="312" loading="lazy">

<p><strong>Use case:</strong> Forms, cards, sidebars, vertical menus.</p>
<h3 id="heading-flex-col-reverse">flex-col-reverse</h3>
<p>In <code>flex-col-reverse</code>, items are stacked vertically, but in <strong>reverse order</strong>, starting from bottom to top (that is, vertically reverse order).</p>
<pre><code class="language-xml">&lt;div class="flex flex-col-reverse"&gt;
  &lt;div&gt;01&lt;/div&gt;
  &lt;div&gt;02&lt;/div&gt;
  &lt;div&gt;03&lt;/div&gt;
&lt;/div&gt; 
</code></pre>
<p>Here's the output:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770357355380/9d2d03b9-0c23-4360-ab82-2ccf8c1f32d9.png" alt="flex col reverse" style="display:block;margin:0 auto" width="291" height="313" loading="lazy">

<p><strong>Use case:</strong> Chat messages, timelines, or when newer content should appear at the bottom.</p>
<p>Now you know a bit about managing flex-directions using tailwind utility classes. You can mange the stack depending on your needs.</p>
<h3 id="heading-responsive-control">Responsive Control</h3>
<p>Tailwind also allows you to change flex direction at different breakpoints so you’ll get a clean layout across different devices.</p>
<pre><code class="language-html">&lt;div class="flex flex-col md:flex-row"&gt;
	.....
&lt;/div&gt;
</code></pre>
<p>This stacks items vertically on small screens and switches to a horizontal layout on medium screens and above.</p>
<h2 id="heading-fine-tuning-flexbox-layout">Fine-Tuning Flexbox Layout</h2>
<p>Once a container is set to use Flexbox, the real power comes from controlling how flex items behave inside it. This is where properties like wrap, order, and gap become essential. These features determine how items flow, how they're visually arranged, and how much space exists between them.</p>
<p>In real-world layouts such as card grids, navigation menus, and dashboards, elements rarely fit perfectly in a single row or follow a fixed order across all screen sizes. Flexbox provides solutions to these challenges, and Tailwind CSS exposes them through simple, intuitive utility classes.</p>
<ul>
<li><p><strong>Wrap</strong> helps manage what happens when items exceed the available space</p>
</li>
<li><p><strong>Order</strong> allows you to rearrange elements visually without changing the HTML structure</p>
</li>
<li><p><strong>Gap</strong> controls spacing between items in a clean and predictable way</p>
</li>
</ul>
<p>Let’s dive into the depths of these fine-tuning flexbox properties.</p>
<h3 id="heading-flex-wrap"><a href="https://tailwindcss.com/docs/flex-wrap">flex-wrap</a></h3>
<p>By default, Flexbox tries to fit all items into one line. If there isn’t enough space, items will shrink to squeeze in. <code>flex-wrap</code> allows flex-items to move onto the next line instead of shrinking.</p>
<pre><code class="language-xml">&lt;div class="flex flex-wrap"&gt;
	&lt;div&gt;01&lt;div&gt;
	&lt;div&gt;02&lt;div&gt;
	&lt;div&gt;03&lt;div&gt;
&lt;/div&gt;
</code></pre>
<p><strong>Utility classes</strong>:</p>
<ul>
<li><p><code>flex-nowrap</code>: (default) All items stay on one line.</p>
</li>
<li><p><code>flex-wrap</code>: Items wrap onto multiple lines.</p>
</li>
<li><p><code>flex-wrap-reverse</code>: Items wrap, but in reverse order.</p>
</li>
</ul>
<h3 id="heading-flex-order">flex-order</h3>
<p><a href="https://tailwindcss.com/docs/order">flex-order</a> controls the visual order of the stack/flex-items without changing the HTML structure. Each item has an order value. Items with lower order values appear first.</p>
<pre><code class="language-xml">&lt;div class="flex"&gt;
	&lt;div class="order-3 ..."&gt;01&lt;div&gt;
	&lt;div class="order-1 ..."&gt;02&lt;div&gt;
	&lt;div class="order-2 ..."&gt;03&lt;div&gt;
&lt;/div&gt;
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770357768674/b70b52ca-712e-409a-a0af-f73be8b2c649.png" alt="default order custom order" style="display:block;margin:0 auto" width="564" height="148" loading="lazy">

<p><strong>Utility classes:</strong></p>
<ul>
<li><p><code>order-1</code> to <code>order-12</code>: Sets order value.</p>
</li>
<li><p><code>order-first</code>: Moves item to the start.</p>
</li>
<li><p><code>order-last</code>: Moves item to the end.</p>
</li>
<li><p><code>order-none</code>: Default order (0).</p>
</li>
</ul>
<h3 id="heading-gap">gap</h3>
<p><a href="https://tailwindcss.com/docs/gap">gap</a> controls the space between flex items, both rows and columns, without using margins. You can also apply for axis, which will help in giving space in both horizontal and vertical directions.</p>
<p>Here's an example of using it with a horizontal layout:</p>
<pre><code class="language-xml">&lt;div class="flex gap-8"&gt;
	&lt;div&gt;01&lt;div&gt;
	&lt;div&gt;02&lt;div&gt;
	&lt;div&gt;03&lt;div&gt;
&lt;/div&gt;
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770357860338/520e4c08-e334-4bee-9003-d6b22e857a18.png" alt="gap" style="display:block;margin:0 auto" width="551" height="139" loading="lazy">

<p>And here's an example showing a vertical layout:</p>
<pre><code class="language-xml">&lt;div class="flex flex-col gap-y-5"&gt;
	&lt;div&gt;01&lt;div&gt;
	&lt;div&gt;02&lt;div&gt;
	&lt;div&gt;03&lt;div&gt;
&lt;/div&gt;
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770357897235/488b63a8-b520-4b3e-b83b-1b18a316f23d.png" alt="gap y 5" style="display:block;margin:0 auto" width="120" height="305" loading="lazy">

<p><strong>Utility classes</strong>:</p>
<ul>
<li><p><code>gap-&lt;number&gt;</code>: Applies space (x-axis by default) between items according to the number, as shown in the example.</p>
</li>
<li><p><code>gap-[&lt;custom-property&gt;]</code>: You can apply a custom gap inside square brackets, such as <code>gap-[10px]</code>.</p>
</li>
<li><p><code>gap-x-&lt;number&gt;</code>: Provides horizontal spacing only.</p>
</li>
<li><p><code>gap-y-&lt;number&gt;</code>: Provides vertical spacing only.</p>
</li>
</ul>
<h3 id="heading-responsive-design">Responsive Design</h3>
<p>You can prefix&nbsp;the <code>gap</code>, <code>column-gap</code>,&nbsp;and&nbsp;<code>row-gap</code>&nbsp;utilities&nbsp;with a breakpoint variant, like&nbsp;<code>lg:</code>&nbsp;to only apply the utility at&nbsp;larger&nbsp;screen sizes and above. Here's an example:</p>
<pre><code class="language-xml">&lt;div class="flex gap-4 lg:gap-8 ..."&gt;
  &lt;!-- ... --&gt;
&lt;/div&gt;
</code></pre>
<h2 id="heading-how-to-justify-and-align-flex-items-in-tailwind">How to Justify and Align Flex Items in Tailwind</h2>
<p>If you're building interactive components like dropdowns, menus, or toolbars using Flexbox, alignment becomes even more important when handling keyboard navigation.</p>
<p>For example, when using arrow keys to navigate horizontally aligned items (<code>justify-between</code>, <code>justify-center</code>, and so on), proper spacing ensures a better user experience. You can explore how <a href="https://flyonui.com/docs/content/keyboard">Tailwind CSS keyboard</a> navigation works with <a href="https://flyonui.com/docs/content/keyboard/#arrow-keys">arrow keys</a> in FlyonUI.</p>
<p>FlyonUI provides accessible <a href="https://flyonui.com/docs/component/">Tailwind components</a> that integrate smoothly with Tailwind CSS, especially helpful when building flex-based navigation layouts.</p>
<p>Tailwind CSS offers a wide range of utility classes for aligning and justifying flex items, which can sometimes be confusing to differentiate. Below is a concise overview of these classes, along with practical examples.</p>
<h3 id="heading-justify-content">justify-content</h3>
<p><a href="https://tailwindcss.com/docs/justify-content">justify-content</a> is a flexbox property that controls how flex items are aligned along the main axis of a flex container. It decides how items are spaced inside a flex container. It is applied along with class <code>flex</code>.</p>
<p>Here's an example:</p>
<pre><code class="language-xml">&lt;div class="flex justify-start gap-2"&gt;
	&lt;div&gt;01&lt;div&gt;
	&lt;div&gt;02&lt;div&gt;
	&lt;div&gt;03&lt;div&gt;
&lt;/div&gt;
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770358388754/3fc9155b-067b-4ebd-9b75-037da1b34a00.png" alt="Justify Content" style="display:block;margin:0 auto" width="957" height="546" loading="lazy">

<p><strong>Utility classes</strong>:</p>
<ul>
<li><p><code>justify-start</code>: Aligns items at the start of the container.</p>
</li>
<li><p><code>justify-center</code>: Centers items along the main axis.</p>
</li>
<li><p><code>justify-end</code>: Aligns items at the end of the container.</p>
</li>
<li><p><code>justify-between</code>: Adds space between items, pushing first and last items to the edges.</p>
</li>
<li><p><code>justify-around</code>: Adds equal space around each item.</p>
</li>
<li><p><code>justify-evenly</code>: Distributes items with equal spacing everywhere, including edges.</p>
</li>
</ul>
<h3 id="heading-align-items">Align Items</h3>
<p><a href="https://tailwindcss.com/docs/align-items">align-items</a> is a flexbox property that controls how flex items are aligned along the <strong>cross-axis</strong> of a flex container.</p>
<p>In a row-based flex container (<code>flex-row</code>, which is the default direction), the cross axis is <strong>vertical</strong>. That means <code>align-items</code> controls vertical alignment.</p>
<p>In a column-based container (<code>flex-col</code>), the cross axis becomes <strong>horizontal</strong>, so <code>align-items</code> controls horizontal alignment instead.</p>
<p>This property is applied alongside the <code>flex</code> class and is commonly used to align items consistently inside navigation bars, toolbars, cards, and forms.</p>
<pre><code class="language-xml">&lt;div class="flex items-start gap-2"&gt;
	&lt;div&gt;01&lt;/div&gt;
	&lt;div&gt;02&lt;/div&gt;
	&lt;div&gt;03&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770358590807/6440854e-822a-449d-ab22-fc417916bc45.png" alt="align text flexbox" style="display:block;margin:0 auto" width="917" height="618" loading="lazy">

<p><strong>Utility classes</strong>:</p>
<ul>
<li><p><code>items-start</code>: Aligns items to the start of the cross-axis (top in a row).</p>
</li>
<li><p><code>items-center</code>: Centers items vertically along the cross-axis.</p>
</li>
<li><p><code>items-end</code>: Aligns items to the end of the cross axis (bottom in a row).</p>
</li>
<li><p><code>items-baseline</code>: Aligns items based on their text baseline.</p>
</li>
<li><p><code>items-stretch</code>: Stretches items to fill the container (default behavior).</p>
</li>
</ul>
<h2 id="heading-practice-flexbox-with-interactive-games">Practice Flexbox with Interactive Games</h2>
<p>Reading documentation is important, but Flexbox really <em>clicks</em> when you practice it visually. One of the best ways to build strong Flexbox intuition is through interactive games that let you experiment with alignment, spacing, and direction in real time.</p>
<p>Here are two excellent games that will strengthen your Flexbox fundamentals and make Tailwind’s flex utilities feel second nature:</p>
<h3 id="heading-flexbox-froggy">Flexbox Froggy</h3>
<p>🔗 <a href="https://flexboxfroggy.com/">https://flexboxfroggy.com/</a></p>
<p>Flexbox Froggy is a fun and beginner-friendly game where you help frogs reach their lily pads using Flexbox properties. Each level introduces a new concept, like <code>justify-content</code>, <code>align-items</code>, <code>flex-direction</code>, and <code>flex-wrap</code>.</p>
<p><strong>Why it’s great:</strong></p>
<ul>
<li><p>Perfect for beginners</p>
</li>
<li><p>Visual feedback makes concepts easy to grasp</p>
</li>
<li><p>Covers core Flexbox properties step by step</p>
</li>
</ul>
<p>If you’re new to Flexbox, this is one of the best places to start.</p>
<h3 id="heading-flexbox-adventure-coding-fantasy">Flexbox Adventure (Coding Fantasy)</h3>
<p>🔗 <a href="https://codingfantasy.com/games/flexboxadventure">https://codingfantasy.com/games/flexboxadventure</a></p>
<p>Flexbox Adventure turns Flexbox learning into a role-playing game where you move your character by writing Flexbox rules. It focuses more on real-world layout thinking and helps reinforce how different properties work together.</p>
<p><strong>Why it’s great:</strong></p>
<ul>
<li><p>More challenging than Flexbox Froggy</p>
</li>
<li><p>Helps solidify intermediate concepts</p>
</li>
<li><p>Encourages problem-solving with Flexbox logic</p>
</li>
</ul>
<p>This is a great follow-up once you’re comfortable with the basics.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Flexbox is a core part of modern CSS layouts, and Tailwind CSS makes it even more powerful by turning Flexbox properties into simple, readable utility classes. By understanding utilities for direction, sizing, wrapping, spacing, ordering, and alignment, you can build responsive and flexible layouts without writing custom CSS.</p>
<p>And by understanding how properties like <code>flex</code>, <code>grow</code>, <code>shrink</code>, <code>basis</code>, <code>justify-*</code>, <code>items-*</code>, <code>gap</code>, when responsive variants work together, you can build layouts that are not only flexible and responsive but also maintainable and scalable.</p>
<p>Instead of wrestling with custom CSS, Tailwind allows you to express layout intent directly in your markup, keeping your workflow fast and predictable.</p>
<p>With these Flexbox fundamentals in your toolkit, you’re well-equipped to design clean, responsive interfaces using Tailwind CSS with confidence. 🚀</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Integrate Tailwind with Django – With Code Examples ]]>
                </title>
                <description>
                    <![CDATA[ In modern web development, choosing the right technology is crucial because it impacts both the process and outcome of your projects. Using Django as a backend framework and Tailwind CSS as a utility-first CSS framework offers an efficient way to cre... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-integrate-tailwind-with-django/</link>
                <guid isPermaLink="false">672a2317dd9236b285ebb527</guid>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Abhijeet Dave ]]>
                </dc:creator>
                <pubDate>Tue, 05 Nov 2024 13:52:23 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730270888412/a440ff74-6e8b-4879-8b47-15aedca45bc4.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In modern web development, choosing the right technology is crucial because it impacts both the process and outcome of your projects. Using Django as a backend framework and Tailwind CSS as a utility-first CSS framework offers an efficient way to create responsive and visually appealing web applications.</p>
<p>This article will explain why Django and Tailwind CSS work well together, how to start a Django project, how to easily add Tailwind CSS, and how to speed up your development with Prettier for better class formatting.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-a-quick-overview-of-django">A Quick Overview of Django</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-tailwind-css">What is Tailwind CSS?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-django-and-tailwind-work-so-well-together">Why Django and Tailwind Work So Well Together?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-initialize-a-django-project">How to Initialize a Django Project?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-integrate-tailwind-css-with-django">How to Integrate Tailwind CSS with Django?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-create-a-chat-bubble-app">Let’s Create a Chat Bubble app.</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-prettier-for-class-formatting">How to Use Prettier for Class Formatting?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-chat-bubble-using-flyonui-tailwind-components-and-django">How to Create a Chat Bubble Using FlyonUI Tailwind Components and Django</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-a-quick-overview-of-django">A Quick Overview of Django</h2>
<p><a target="_blank" href="https://www.djangoproject.com/">Django</a> is an open-source, full-featured Python web framework that follows the batteries-included approach. Django aims at making the development of complex, database-driven websites as fast and easy as possible by providing a lot of built-in functionalities like ORM, authentication system, admin panel, and many more. Django enables rapid development by focusing on writing the app's unique parts rather than wasting time writing a lot of boilerplate code.</p>
<p>The reason for its popularity is that it follows the MVT design pattern which keeps data models, views, and templates well separated. In Django, security is paramount: it guards against SQL injection, cross-site scripting, and cross-site request forgery out of the box. Django scales well and is flexible – it is fit for both small projects and large, complex web applications, and that is why it is used by major sites such as Instagram and Pinterest.</p>
<h2 id="heading-what-is-tailwind-css">What is Tailwind CSS?</h2>
<p>It is a well-known fact that <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a> is a utility-first CSS framework. It lets you style elements directly within your HTML, thanks to pre-defined classes. Unlike other CSS frameworks that offer pre-built components, Tailwind offers these low-level utility classes that let you create your own design system. Thus, this makes crafting unique responsive designs effortless as there is not much to do with custom CSS.</p>
<h2 id="heading-why-django-and-tailwind-work-so-well-together">Why Django and Tailwind Work So Well Together?</h2>
<p>The combination of Django and Tailwind CSS offers a seamless way to build robust, full-featured applications. Here’s why:</p>
<ul>
<li><p><strong>Rapid Development</strong>: Django’s backend capabilities allow developers to create powerful applications quickly, while Tailwind CSS helps streamline the styling process with its utility-first approach.</p>
</li>
<li><p><strong>Customizable Design</strong>: With Tailwind, you’re not confined to predefined styles. You can craft a unique, consistent design that scales easily as your project grows.</p>
</li>
<li><p><strong>Separation of Concerns</strong>: Django’s templating system works hand-in-hand with Tailwind CSS, ensuring a clear separation between the backend logic and frontend styling.</p>
</li>
</ul>
<h2 id="heading-how-to-initialize-a-django-project">How to Initialize a Django Project?</h2>
<ol>
<li><p><strong>Install Django</strong>: Install Django using pip:</p>
<pre><code class="lang-bash"> pip install django
</code></pre>
</li>
<li><p><strong>Create a Django Project</strong>: Use the Django admin command to create a new project:</p>
<pre><code class="lang-bash"> django-admin startproject myproject
</code></pre>
</li>
<li><p><strong>Navigate to Your Project Directory</strong>:</p>
<pre><code class="lang-bash"> <span class="hljs-built_in">cd</span> myproject
</code></pre>
</li>
<li><p><strong>Modify</strong> <code>settings.py</code>:</p>
<ul>
<li><p>In the <code>TEMPLATES</code> setting, add a <code>templates</code> directory:</p>
<pre><code class="lang-python">  <span class="hljs-string">"DIRS"</span>: [BASE_DIR / <span class="hljs-string">"templates"</span>],
</code></pre>
</li>
<li><p>Add a <code>static</code> directory for your static files:</p>
<pre><code class="lang-python">  STATICFILES_DIRS = [BASE_DIR / <span class="hljs-string">"static"</span>]
</code></pre>
</li>
</ul>
</li>
</ol>
<h2 id="heading-how-to-integrate-tailwind-css-with-django">How to Integrate Tailwind CSS with Django?</h2>
<ol>
<li><p><strong>Install Tailwind CSS</strong>: Make sure Node.js is installed, then run:</p>
<pre><code class="lang-bash"> npm install -D tailwindcss
 npx tailwindcss init
</code></pre>
</li>
<li><p><strong>Set Up Tailwind CSS</strong>: In your <code>static/css</code> directory, create a <code>main.css</code> file with the following content:</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>
</li>
<li><p><strong>Modify tailwind.config.js</strong>: Adjust the content section to include <code>templates/*.html</code> files, ensuring Tailwind CSS generates the necessary styles.</p>
<pre><code class="lang-jsx"> <span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
 <span class="hljs-built_in">module</span>.exports = {
     <span class="hljs-attr">content</span>: [<span class="hljs-string">"./templates/**/*.html"</span>, <span class="hljs-string">"./**/templates/**/*.html"</span>],
     <span class="hljs-attr">darkMode</span>: <span class="hljs-string">"media"</span>,
     <span class="hljs-attr">theme</span>: {
         <span class="hljs-attr">extend</span>: {},
     },
     <span class="hljs-attr">plugins</span>: [],
 };
</code></pre>
</li>
<li><p><strong>Add a Build Script</strong>: Update your <code>package.json</code> to include a build script:</p>
<pre><code class="lang-json"> <span class="hljs-string">"scripts"</span>: {  <span class="hljs-attr">"watch:css"</span>: <span class="hljs-string">"tailwindcss build static/css/main.css -o static/output.css -w"</span>}
</code></pre>
</li>
<li><p><strong>Compile Tailwind CSS</strong>:</p>
<pre><code class="lang-bash"> npm run watch:css
</code></pre>
</li>
</ol>
<h2 id="heading-lets-create-a-chat-bubble-app">Let’s Create a Chat Bubble app.</h2>
<ol>
<li><p><strong>Create a Django App</strong>:</p>
<pre><code class="lang-bash"> django-admin startapp chat
</code></pre>
</li>
<li><p><strong>Set Up the Views</strong>:</p>
<ul>
<li><p>In <code>chat/views.py</code>, create a simple view:</p>
<pre><code class="lang-python">  <span class="hljs-keyword">from</span> django.shortcuts <span class="hljs-keyword">import</span> render
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">chat</span>(<span class="hljs-params">request</span>):</span>
      <span class="hljs-keyword">return</span> render(request, <span class="hljs-string">"chat.html"</span>)
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Configure URLs</strong>:</p>
<ul>
<li><p>In <code>chat/urls.py</code>, define the URL pattern for your view:</p>
<pre><code class="lang-python">  <span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path
  <span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> views
  urlpatterns = [
      path(<span class="hljs-string">""</span>, views.chat, name=<span class="hljs-string">"chat"</span>),
  ]
</code></pre>
</li>
<li><p>In the project’s <code>urls.py</code>, include the app URLs:</p>
<pre><code class="lang-python">  <span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> include, path
  urlpatterns = [
      path(<span class="hljs-string">""</span>, include(<span class="hljs-string">"chat.urls"</span>)),
  ]
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Set Up the Base HTML Template</strong>:</p>
<ul>
<li><p>Create a <code>templates/base.html</code> file to serve as the foundation of your application:</p>
<pre><code class="lang-html">  {% load static %}

  <span class="hljs-meta">&lt;!doctype <span class="hljs-meta-keyword">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Django App with Tailwind<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{% static 'css/output.css' %}"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-screen bg-slate-300 dark:bg-slate-400"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mx-auto flex flex-col items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4 mt-10 text-6xl font-bold text-blue-500 dark:text-blue-200"</span> &gt;</span>
          Chat Bubble
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        {% block content %} {% endblock %}
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</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>
</li>
</ul>
</li>
<li><p><strong>Create the Chat Template</strong>:</p>
<ul>
<li>In <code>templates/chat.html</code>, extend the base template:</li>
</ul>
</li>
</ol>
<pre><code class="lang-html">    {% extends "base.html" %}

    {% block content %}
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-start gap-2.5"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-8 h-8 rounded-full"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"&lt;https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png&gt;"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Jhon  image"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-col gap-1 w-full max-w-[320px]"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center space-x-2 rtl:space-x-reverse"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm font-semibold text-gray-900 dark:text-white"</span>&gt;</span>Jhon Doe<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">"text-sm font-normal text-gray-500 dark:text-gray-400"</span>&gt;</span>11:46<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>
                <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-col leading-1.5 p-4 border-gray-200 bg-gray-100 rounded-e-xl rounded-es-xl dark:bg-gray-700"</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-sm font-normal text-gray-900 dark:text-white"</span>&gt;</span> That's awesome. I think our users will really
                    appreciate the improvements.<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">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm font-normal text-gray-500 dark:text-gray-400"</span>&gt;</span>Delivered<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>
    {% endblock %}
</code></pre>
<ol start="6">
<li><strong>Run the Development Server</strong>: <code>bash python manage.py runserver</code></li>
</ol>
<h2 id="heading-how-to-use-prettier-for-class-formatting">How to Use Prettier for Class Formatting?</h2>
<p>To keep your Tailwind CSS classes clean and organized, you can integrate Prettier into your workflow.</p>
<ol>
<li><p><strong>Install Prettier and the Tailwind Plugin</strong>:</p>
<pre><code class="lang-bash"> npm install --save-dev prettier prettier-plugin-tailwindcss
</code></pre>
</li>
<li><p><strong>Configure Prettier</strong>: Create a <code>.prettierrc</code> file in your project root:</p>
<pre><code class="lang-json"> {  <span class="hljs-attr">"plugins"</span>: [<span class="hljs-string">"prettier-plugin-tailwindcss"</span>]}
</code></pre>
</li>
<li><p><strong>Format on Save</strong>: Set up your code editor to format files automatically with Prettier on save.</p>
</li>
</ol>
<h3 id="heading-result">Result:</h3>
<p>GitHub Repo: <a target="_blank" href="https://github.com/themeselection/ts-django-tailwind">ts-django-tailwindcss</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730793420930/6fbdbfbb-2476-4454-9b90-89bcfd405abf.png" alt="chat bubble ts" class="image--center mx-auto" width="503" height="278" loading="lazy"></p>
<h2 id="heading-how-to-create-a-chat-bubble-using-flyonui-tailwind-components-and-django">How to Create a Chat Bubble Using FlyonUI Tailwind Components and Django</h2>
<p>Here, we’ll use FlyonUI, an open-source <a target="_blank" href="https://flyonui.com/">Tailwind CSS Components Library</a>. It offers a wide range of customizable, accessible, and ready-to-use components.</p>
<p>Let’s integrate Django with FlyonUI components and create a chat bubble.</p>
<p><strong>Step 1: Install flyonui</strong></p>
<p>Install <code>flyonui</code> via npm.</p>
<pre><code class="lang-bash">npm install -D flyonui@latest
</code></pre>
<p><strong>Step 2: Configure Tailwind</strong></p>
<p>Add the path to FlyonUI JavaScript files in your <code>tailwind.config.js</code> file.</p>
<pre><code class="lang-plaintext">module.exports = {
  content: ["./node_modules/flyonui/dist/js/*.js"], // Require only if you want to use FlyonUI JS component

  plugins: [
    require("flyonui"),
    require("flyonui/plugin") // Require only if you want to use FlyonUI JS component
  ]
}
</code></pre>
<p><strong>Step 3: Copy the FlyonUI JavaScript</strong></p>
<p>Copy FlyonUI's JavaScript (node_modules/flyonui/flyonui.js) files to the <code>static/</code>  folder.</p>
<p><strong>Step 4: Add Js to your base.html</strong></p>
<p>Once you copied the <code>js file</code> to your static folder include it in base.html.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
 ...
 <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  ...
  <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"{% static 'js/flyonui.js' %}"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>Let's Update the Chat bubble code block:</p>
<pre><code class="lang-html">{% extends "base.html" %}
{% block 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> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat chat-receiver"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"avatar chat-avatar"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"size-10 rounded-full"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
          <span class="hljs-attr">src</span>=<span class="hljs-string">"&lt;https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png&gt;"</span>
          <span class="hljs-attr">alt</span>=<span class="hljs-string">"avatar"</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">"chat-header text-base-content/90"</span>&gt;</span>
      Obi-Wan Kenobi
      <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-base-content/50"</span>&gt;</span>12:45<span class="hljs-tag">&lt;/<span class="hljs-name">time</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">"chat-bubble"</span>&gt;</span>I started learning guitar today!<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">"chat-footer text-base-content/50"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Delivered<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>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat chat-sender"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"avatar chat-avatar"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"size-10 rounded-full"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
          <span class="hljs-attr">src</span>=<span class="hljs-string">"&lt;https://cdn.flyonui.com/fy-assets/avatar/avatar-2.png&gt;"</span>
          <span class="hljs-attr">alt</span>=<span class="hljs-string">"avatar"</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">"chat-header text-base-content/90"</span>&gt;</span>
      Anakin
      <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-base-content/50"</span>&gt;</span>12:46<span class="hljs-tag">&lt;/<span class="hljs-name">time</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">"chat-bubble"</span>&gt;</span>
      That's awesome! You're going to be great at it!
    <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">"chat-footer text-base-content/50"</span>&gt;</span>
      Seen
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"icon-[tabler--checks] align-bottom text-success"</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">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
{% endblock %}
</code></pre>
<h3 id="heading-result-1"><strong>Result:</strong></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730793343310/63ca723e-ef67-4cee-a112-bed110ce8ea6.png" alt="chat bubble example" class="image--center mx-auto" width="700" height="334" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Using Tailwind CSS with Django is a great way to make your web applications look good and work well on different devices, while you take advantage of Django's many features. This setup not only boosts productivity but also helps you follow good styling and design practices.</p>
<p>Here's the repository where you can find more details or see the complete code: <a target="_blank" href="https://github.com/themeselection/ts-django-tailwind">ts-django-tailwindcss</a>. I hope this tutorial helps you with the Django integration with Tailwind CSS. I have prepared this article with the help of <a target="_blank" href="https://github.com/PruthviPraj00">Pruthvi Prajapati</a>, a front-end developer with 2 years of experience.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Counter Button with React, TailwindCSS, and TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ How can you keep track of the number of times a user clicks a button? How are the hearts on Instagram or the likes on Facebook counted?  In this tutorial, we will build a button that tracks the number of times a button has been clicked. Along the way... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-counter-button-with-react/</link>
                <guid isPermaLink="false">66bce123d84f19f03de63fd1</guid>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Devin Lane ]]>
                </dc:creator>
                <pubDate>Wed, 10 Jul 2024 14:41:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/Build-a-counter-button-with-React-6-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>How can you keep track of the number of times a user clicks a button? How are the hearts on Instagram or the likes on Facebook counted? </p>
<p>In this tutorial, we will build a button that tracks the number of times a button has been clicked. Along the way, you will learn some fundamental concepts in React such as components, JSX, passing props between components, and managing state with hooks. You will also get small introductions to Tailwind and TypeScript.</p>
<p>This tutorial builds upon examples and concepts outlined in the "Learn" section of the React documentation, which you can find <a target="_blank" href="https://react.dev/learn">here</a>.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li>Basic familiarity with JavaScript, such as working with variables, functions, arrays, and objects. </li>
<li>Basic familiarity with CSS and HTML.</li>
<li>Basic familiarity with the command line.</li>
<li><a target="_blank" href="https://nodejs.org/en">Node</a> installed.</li>
<li>A code editor of your choice (I'll be using <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> here)</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-chapter-1-how-to-build-the-counter-button">How to Build the Counter Button</a></li>
<li><a class="post-section-overview" href="#heading-chapter-2-how-to-refactor-the-project">How to Refactor the Project</a></li>
<li><a class="post-section-overview" href="#heading-chapter-3-two-components-with-independent-and-shared-state">Two Components with Independent and Shared State</a></li>
<li><a class="post-section-overview" href="#heading-chapter-4-how-to-add-both-pairs-of-buttons-to-our-site">How to Add Both Pairs of Buttons to our Website</a></li>
<li><a class="post-section-overview" href="#heading-chapter-5-how-to-deploy-the-site-to-netlify">How to Deploy the Site to Netlify</a></li>
</ol>
<h2 id="heading-chapter-1-how-to-build-the-counter-button">Chapter 1: How to Build the Counter Button</h2>
<h3 id="heading-what-is-react">What is React?</h3>
<p>Before we dive in, let's define React. <a target="_blank" href="https://react.dev/">React</a> is a JavaScript library for creating user interfaces out of pieces called <em>components</em>. Components are JavaScript functions that can receive and display data interactively to your users.</p>
<h3 id="heading-project-setup">Project setup</h3>
<p>We're going to use <a target="_blank" href="https://nextjs.org/">Next.js</a> for our local React setup.</p>
<p>Within the directory you'd like to store this project, open your terminal and execute the following command:</p>
<pre><code class="lang-zsh">npx create-next-app@latest
</code></pre>
<p>Name your project however you like, and answer the commands as follows:</p>
<pre><code class="lang-zsh">What is your project named? react-counter-button
Would you like to use TypeScript? Yes
Would you like to use ESLint? Yes
Would you like to use Tailwind CSS? Yes
Would you like to use `src/` directory? No
Would you like to use App Router? (recommended) Yes
Would you like to customize the default import <span class="hljs-built_in">alias</span> (@/*)? No
</code></pre>
<p>Now let's <code>cd</code> into our project directory</p>
<pre><code class="lang-zsh"><span class="hljs-built_in">cd</span> react-counter-button
</code></pre>
<p>And run the project in Visual Studio Code:</p>
<pre><code class="lang-zsh">code .
</code></pre>
<p>Note: if you don't have the <code>code</code> command in your PATH, you can press ⇧⌘P (Ctrl+Shift+P on Windows/Linux) and type in 'Shell Command: Install 'code' command in PATH'. Alternatively, you can drag the folder onto the Visual Studio Code icon in MacOS. Or, within Visual Studio Code, you can select File -&gt; Open, and find "react-counter-button", or the name of your project. </p>
<p>In your terminal run:</p>
<pre><code class="lang-zsh">npm run dev
</code></pre>
<p>Open your browser to <code>localhost:3000</code> and you should see the following page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-14-at-7.10.35-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next.js boilerplate</em></p>
<p>We now have the project up and running. Back over in our code editor, we can begin the work.</p>
<h3 id="heading-remove-boilerplate">Remove boilerplate</h3>
<p>In <code>app/page.tsx</code>, let's delete most of the boilerplate code except the two <code>main</code> tags. Then let's add a title for our project in an <code>h1</code> tag in between the <code>main</code> tags. Our code should look 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"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center justify-between p-24"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>Here's what we should now see:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-16-at-8.22.30-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Initial state of our project</em></p>
<h3 id="heading-writing-our-first-component">Writing our first component</h3>
<p>Let's create our first component. A React component is a function that returns markup. Below and outside of the scope of our <code>Home</code> function, let's write the following:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</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">button</span>&gt;</span>I have been clicked X times<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>;
}
</code></pre>
<p>Here we have a function <code>Button</code> that returns some markup in JSX. JSX looks a lot like HTML, but it can display dynamic content, and has stricter rules than HTML. You can learn more about JSX in the React docs <a target="_blank" href="https://react.dev/learn/writing-markup-with-jsx">here</a>.</p>
<p>The <code>Button</code> function must be uppercase to be recognized as a valid React component. This contrasts it with an HTML tag, which is lower case.</p>
<p>You'll notice that we still see no change on our webpage – we need to render this component in order to see it on the screen. </p>
<p>We can use our <code>Button</code> component as if it were an HTML tag we created. If we nest the <code>Button</code> component within the <code>Home</code> component, we should see it on the screen:</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"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center justify-between p-24"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</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">button</span>&gt;</span>I have been clicked X times<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-20-at-6.37.57-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Rendering the <code>Button</code> component (with less than ideal CSS)</em></p>
<h3 id="heading-styling-our-first-component-with-tailwind">Styling our first component with Tailwind</h3>
<p>You'll notice the button is on the bottom of the screen. This is because the styles on <code>main</code> include <code>justify-between</code> in the <code>flex-col</code> direction. If we remove <code>justify-between</code> we should see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-20-at-6.36.57-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Improving the CSS of the initial state of our application</em></p>
<p>You can read more about aligning items in a flexbox from MDN <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Aligning_Items_in_a_Flex_Container">here</a>.</p>
<p>You'll also notice that the button is un-styled. This is because <a target="_blank" href="https://tailwindcss.com/">Tailwind</a> removes default styling on buttons as a part of their "preflight" styles. If you're curious to see where these styles come from, you can open <code>node_modules/tailwindcss/src/css/preflight.css</code> and check out ~line 193 (permalink on GitHub <a target="_blank" href="https://github.com/tailwindlabs/tailwindcss/blob/332347ed834a3078547923ccfddc1c22035011b6/packages/tailwindcss/preflight.css#L182">here</a>):</p>
<pre><code class="lang-css"><span class="hljs-comment">/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/</span>

<span class="hljs-selector-tag">button</span>,
<span class="hljs-selector-attr">[type=<span class="hljs-string">'button'</span>]</span>,
<span class="hljs-selector-attr">[type=<span class="hljs-string">'reset'</span>]</span>,
<span class="hljs-selector-attr">[type=<span class="hljs-string">'submit'</span>]</span> {
  <span class="hljs-attribute">-webkit-appearance</span>: button; <span class="hljs-comment">/* 1 */</span>
  <span class="hljs-attribute">background-color</span>: transparent; <span class="hljs-comment">/* 2 */</span>
  <span class="hljs-attribute">background-image</span>: none; <span class="hljs-comment">/* 2 */</span>
}
</code></pre>
<p>We're not going to change the styles within <code>node_modules</code> – instead we'll add our own styling to the Button component. One of the benefits of Tailwind is that our CSS is co-located with our JavaScript, making quick changes to styles easier than opening a separate stylesheet file. </p>
<p>Let's make the following changes:</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"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</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">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>&gt;</span>
            I have been clicked X times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>We've added styles to our button, and we've also added a <code>gap-4</code> to our <code>main</code> parent flex box to provide a space between the <code>h1</code> and the <code>button</code>. (You can read more about the CSS property "gap" in the MDN <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/gap">here</a>.) We should now see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-20-at-6.49.22-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Viewing our styled <code>Button</code> component</em></p>
<h3 id="heading-wait-but-what-is-tailwind">Wait, but what is Tailwind?</h3>
<p>Now that we've styled our button component and spaced the items out, let's reflect on what Tailwind is, and what it provided for us. <a target="_blank" href="https://tailwindcss.com/">Tailwind</a> is a CSS framework that provides a set of "utility" classes that we can use to style each element. </p>
<p>But what is a utility class? You'll see that to style our button, we added classes such as <code>bg-blue-500</code> – which corresponds to setting the CSS <code>background-color</code> property to blue, and <code>rounded</code> – which corresponds to <code>border-radius: 0.25rem</code>. </p>
<p>Each class is defined according to its <em>utility:</em> changing the background color, the border radius, and so on. Through adding these utility classes to our elements, we arrive at our desired styles. </p>
<p>Tailwind sits in contrast to other frameworks, such as Bootstrap, that provide predefined classes for elements such as buttons. In Bootstrap, we would add a class of <code>btn</code> to achieve a styled button. And of course, with standard CSS we would likely add a custom class (perhaps called <code>button</code>) to our element and create CSS rulesets in a separate stylesheet. </p>
<p>Returning to our project, so far we've set up a React project using Next.js, created our first React component, and styled our button using Tailwind. How do we introduce the counter functionality?</p>
<h3 id="heading-how-to-add-state">How to add state</h3>
<p>In order to display the number of times a button has been clicked, we need to use an event handler, and we need a way to manage <em>state.</em> </p>
<p><a target="_blank" href="https://react.dev/learn/state-a-components-memory">State</a> is component-specific memory. In our example, this is how the button will remember how many times it has been clicked. Using a special React function "<a target="_blank" href="https://react.dev/reference/react/hooks">hook</a>", we trigger a re-render and retain the data across renders – the <code>[useState](https://react.dev/reference/react/useState)</code> hook is provided by React for this purpose. </p>
<p>At the top of our <code>page.tsx</code>, let's import <code>useState</code>: </p>
<p><code>import { useState } from "react"</code></p>
<p>and within our <code>Button</code> component, let's add the following:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>)
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded py-2 px-4 text-white font-bold"</span>&gt;</span>
            I have been clicked X times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>Let's unpack what we have here: </p>
<ul>
<li>We're using the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">destructuring assignment</a> to get the values of <code>count</code> and the function <code>setCount</code> from <code>useState</code>. The convention is to name these two values <code>something</code> and <code>setSomething</code>, though we could name them anything. </li>
<li>The argument to <code>useState</code> is the initial value of our state variable. Here we've set it to 0.</li>
<li><code>count</code> is our current state.</li>
<li><code>setCount</code> is the function that updates our state and triggers a re-render. </li>
</ul>
<p>However, if you click save you'll see the following error in your terminal and in your browser:</p>
<pre><code class="lang-zsh">You<span class="hljs-string">'re importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they'</span>re Server Components by default.
Learn more: https://nextjs.org/docs/getting-started/react-essentials

   ╭─[/[...your project path]/src/app/page.tsx:1:1]
 1 │ import { useState } from <span class="hljs-string">"react"</span>;
   ·          ────────
 2 │ 
 3 │ <span class="hljs-built_in">export</span> default <span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">Home</span></span>() {
 4 │     <span class="hljs-built_in">return</span> (
   ╰────

Maybe one of these should be marked as a client entry with <span class="hljs-string">"use client"</span>:
  ./src/app/page.tsx
</code></pre>
<p>This is due to Next.js's use of <a target="_blank" href="https://www.joshwcomeau.com/react/server-components/">React Server Components</a>, which you can learn more about <a target="_blank" href="https://nextjs.org/docs/app/building-your-application/rendering">here</a>. React Server Components is a large topic, but the bottom line is that, by default, components are Server Components in Next.js and <code>useState</code> only works in a Client Component. If we write the  <code>"use client"</code> directive at the top of our <code>page.tsx</code>, we resolve the error.</p>
<h3 id="heading-how-to-evaluate-javascript-within-jsx">How to Evaluate JavaScript within JSX</h3>
<p>If we click the button, we still don't see the numbers update. This is because we need a way to <em><a target="_blank" href="https://react.dev/learn/javascript-in-jsx-with-curly-braces">interpolate</a></em> (or evaluate) JavaScript within our JSX markup. Enter the curly braces: <code>{}</code>.</p>
<p>We can use curly braces to "escape" into JavaScript from within JSX markup. This way we can evaluate JavaScript expressions (such as adding to a counter) and dynamically display data in our components. Here's what we'll do:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyButton</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded py-2 px-4 text-white font-bold"</span>&gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>We have added <code>{count}</code> to evaluate the value of <code>count</code> from <code>useState</code> within our button. We should see the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Screenshot-2023-11-26-at-4.46.56-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Displaying data in JSX with curly braces {}</em></p>
<p>We see a 0 – this comes from the <code>count</code> variable that we destructured from our <code>useState</code> hook, which we initially set to 0. We've successfully interpolated the JavaScript within our JSX markup!</p>
<h3 id="heading-event-handling">Event handling</h3>
<p>You'll notice that if we click the button, still nothing happens. How do we get the number to increment when we click it? </p>
<p>For this, we'll make use of an <a target="_blank" href="https://react.dev/learn/responding-to-events#adding-event-handlers">event handler function</a> as well as the setter function (which we named <code>setCount</code>) that we get from <code>useState</code>:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyButton</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded py-2 px-4 text-white font-bold"</span>&gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>What we've done here is add a function <code>handleClick</code> to update the state of the <code>count</code> variable. The convention is to name event handler functions <code>handle</code> followed by the name of your event (for example, <code>handleClick</code>). </p>
<p><code>[setCount](https://react.dev/reference/react/useState#setstate)</code> is a special <code>set</code> function returned by <code>useState</code> that will update the state of the <code>count</code> variable to whatever we pass in as an argument. For example, we could call <code>setCount(2)</code>, and it would update <code>count</code> to 2. <code>setCount(7)</code> would set it to 7, and so on. </p>
<p>We are calling <code>setCount(count + 1)</code>, which evaluates to <code>setCount(0 + 1)</code>, because the initial value of <code>count</code> is 0. Upon the next click, <code>count</code> will be 1, so we'd be calling <code>setCount(1 + 1)</code>, and the next click would call <code>setCount(2 + 1)</code> and so on. </p>
<p>This allows us to update the counter with every click. But, if you click, you'll notice that <em>still</em> nothing happens – why? Perhaps take a moment to try to figure this out for yourself before reading on to help the concept stick even better.</p>
<h3 id="heading-how-to-pass-an-event-handler-as-a-prop-to-your-jsx">How to Pass an Event Handler as a Prop to Your JSX</h3>
<p>Looking at our code, there is no relationship between the user clicking the button, and the <code>handleClick</code> function. We need to pass the <code>handleClick</code> event handler to the <code>onClick</code> property on the button! Let's add that here:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyButton</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded py-2 px-4 text-white font-bold"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>Notice how we haven't said <code>onClick={handleClick()}</code>. We aren't calling the function ourselves here – we are instead passing it down. This is an important distinction, as React calls the function for us when the user clicks the button, instead of it firing immediately. </p>
<p>You can learn more about passing props to components in the React docs <a target="_blank" href="https://react.dev/learn/passing-props-to-a-component">here</a>.</p>
<h3 id="heading-our-working-project">Our working project</h3>
<p>Try it out now, the button works! </p>
<p>You now have a button that updates its count when you click it. This shows usage of interpolating JavaScript within JSX using curly braces, creating your own component and nesting it within other components, using state and hooks within React, as well as working with Next.js and Tailwind. Congratulations! </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/react-counter-button.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Our working project</em></p>
<p>Here would be a good point to <em>commit</em> our changes using Git. You can close the current terminal process by pressing <code>ctrl + c</code>, and then type in <code>git add .</code>, followed by <code>git commit -m "counter button"</code> or some other message that is meaningful.</p>
<h2 id="heading-chapter-2-how-to-refactor-the-project">Chapter 2: How to Refactor the Project</h2>
<h3 id="heading-moving-our-component-to-another-file">Moving our component to another file</h3>
<p>As our project sits, all the code is within <code>app/page.tsx</code>. What if we wanted to add another component, or several? Over time, our <code>page.tsx</code> would get large and difficult to read. </p>
<p>Instead, we can break our components up into their own files to help with readability as well as modularity (reusing the component in multiple different places).</p>
<p>Let's start by creating a folder <code>components</code> at the root of our project to store our components. Inside <code>components</code>, create a file called <code>button.tsx</code>. Then, within <code>app/page.tsx</code> cut (copy and then delete) the entire <code>Button</code> function component and paste it within <code>components/button.tsx</code>. </p>
<p><code>components/button.tsx</code> should look like this:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<h3 id="heading-fix-the-usestate-import-error">Fix the <code>useState</code> import error</h3>
<p>You'll likely notice in your code editor that <code>useState(0)</code> has red squiggly lines underneath it. In Visual Studio Code, if you hover over it, you will see an error that says:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-2024-06-07-at-6.10.44-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Error: cannot find name 'useState'</em></p>
<p>Why is this? We are using <code>useState</code> but we have not imported the module from React. Adding <code>import { useState } from "react";</code> to the top of our <code>button.tsx</code> file will fix this error.</p>
<p>If you look at the beginning of the function, you'll see that <code>Button()</code> is underlined with white lines in Visual Studio Code. Hovering over it will show this error. Reflect on why this might be the case – we'll address this later. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-2024-06-07-at-6.13.45-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Error: 'Button' is declared but its value is never read</em></p>
<h3 id="heading-importing-and-exporting-components">Importing and Exporting Components</h3>
<p>Let's return to <code>app/page.tsx</code> . You'll see two errors here: one on <code>import { useState } from "react";</code> and another on <code>&lt;Button /&gt;</code>. </p>
<p>Let's address the <code>useState</code> error first. We used <code>useState</code> within our <code>Button</code> component, but now that we've moved that component to its own file, we no longer it. Deleting it will solve our error. You can use <code>cmd (ctrl on Windows) + shift + k</code> to delete the entire line in Visual Studio Code.</p>
<p>If you've saved your <code>app/page.tsx</code> you will see this error in the console:</p>
<pre><code class="lang-zsh"> ⨯ app/page.tsx (7:14) @ Button
 ⨯ ReferenceError: Button is not defined
    at Home (./app/page.tsx:19:89)
digest: <span class="hljs-string">"2129895745"</span>
   5 |         &lt;main className=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;
   6 |             &lt;h1&gt;React Counter Button&lt;/h1&gt;
&gt;  7 |             &lt;Button /&gt;
     |              ^
   8 |         &lt;/main&gt;
   9 |     );
  10 | }
 GET / 500 <span class="hljs-keyword">in</span> 87ms
</code></pre>
<p>Why wouldn't <code>Button</code> be defined? The issue is that within our <code>app/page.tsx</code> we have no way to access the <code>Button</code> component over in <code>components/button.tsx</code>. We solve this by exporting and importing the appropriate module.</p>
<p>Within <code>components/button.tsx</code>, at the beginning of our function declaration, let's add the keywords <code>export default</code>. The file will look like this now:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">Button</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>You'll notice that our earlier error of <code>'Button' is declared but its value is never read</code> has gone away, because now the value is being read as a default export. </p>
<p>But what have we done here? What is an export, or a default export? <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export">Exporting</a> and importing allows us to modularize JavaScript components into their own sections and use them in others. </p>
<p>There are two types: <em>default</em> exports<em>,</em> and <em>named</em> exports. Each file can have multiple <em>named</em> exports but only one <em>default</em> export. You can read more about importing and exporting components the React documentation <a target="_blank" href="https://react.dev/learn/importing-and-exporting-components">here</a>.</p>
<p>Now that we have exported the component from <code>components/button.tsx</code>, we have to import it within <code>app/page.tsx</code>. Visual Studio Code can help with "<a target="_blank" href="https://code.visualstudio.com/docs/editor/intellisense">intellisense</a>" suggestions: at the top of your file if you start typing "Button", it will suggest the correct import with the correct filepath:</p>
<p><code>import Button from "@/components/button";</code></p>
<p>Within Next.js we can use this <code>@/</code> syntax to reference the root of the project. This is a convenience added in case our import is several file layers deep. You can read the examples of the <code>@/</code> syntax in the Next.js documentation <a target="_blank" href="https://nextjs.org/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases">here</a>. </p>
<p>You'll see that our errors have disappeared and the project still works! We haven't added any new features but we have successfully refactored our code to make it more modular, readable, and maintainable.</p>
<p>Here let's follow the same steps to commit our changes, adding a message such as <code>refactor: move button to its own file</code>.</p>
<h2 id="heading-chapter-3-two-components-with-independent-and-shared-state">Chapter 3: Two Components with Independent and Shared State</h2>
<h3 id="heading-two-components-with-independent-state">Two Components with Independent State</h3>
<p>What if we wanted to have two buttons that can count independently of each other? This will showcase the beauty of React and component-based development implementation will be simpler than building the button from scratch entirely again.</p>
<p>Within <code>app/page.tsx</code> we can simply add another <code>&lt;Button /&gt;</code>. You can focus your cursor on <code>&lt;Button /&gt;</code> and press <code>option + shift + ↓</code> to create another <code>&lt;Button /&gt;</code>:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button"</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;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>You should now see two button counters with their own independent state:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-2024-06-07-at-6.40.47-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Two buttons with independent state</em></p>
<p>Component-based design with React makes re-using parts of your application easy. Easy win.</p>
<h3 id="heading-two-components-with-shared-state">Two Components with Shared State</h3>
<p>What if we wanted the buttons to share their state and update together? You'll notice that as we click each button, they separately increment. </p>
<p>In order for the buttons to share their state, we will need to move their state from each individual components "upward" to their common parent component (in this case, the <code>Home</code> function in <code>app/page.tsx</code>). You'll also hear this referred to as "<a target="_blank" href="https://react.dev/learn#sharing-data-between-components">lifting state up</a><em>".</em></p>
<p>Cut the counting logic from <code>components/button.tsx</code> and paste it into <code>app/page.tsx</code> within the body of the <code>Home</code> function. We will also need our <code>useState</code> import at the top of the file:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button"</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">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<h3 id="heading-passing-props-down-to-a-component">Passing Props Down to a Component</h3>
<p>Now that we have our state in the parent component of each button (<code>Home</code>), we can pass this state down via <em><a target="_blank" href="https://react.dev/learn/passing-props-to-a-component">props</a></em> to the <code>Button</code> component. We will want to pass down both the event handler <code>handleClick</code> as well as the <code>count</code> variable we wish to display:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button"</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">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>The <code>count</code> from <code>useState</code> is passed to the <code>count</code> prop, and the function <code>handleClick</code> is passed to the <code>onClick</code> prop, both on the <code>Button</code> component. In JSX, we can define our own props (which might remind you of HTML attributes) so that we can pass data from one component to another. </p>
<p>You might see some errors related to TypeScript at this point – we will come back to these later.</p>
<h3 id="heading-read-props-in-your-child-component">Read Props in Your Child Component</h3>
<p>Now that we have passed the data as props to our component, we need to adjust our <code>Button</code> component to <em>read</em> the props from its parent component. Within <code>components/button.tsx</code>:</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">Button</span>(<span class="hljs-params">{ count, onClick }</span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>React function components accept a single <code>props</code> object as an argument. Here we destructure the props that we want to pass into our <code>Button</code> component. In other words, we are taking <code>count</code>, and <code>onClick</code> from the <code>props</code> object directly, as an argument to <code>Button</code>.</p>
<p>If you save your file you'll see that this now works: you have two buttons with shared state:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screen-Recording-2024-06-14-at-12.07.37-PM.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Two buttons with shared state</em></p>
<p>But why did we pass <code>onClick</code> and not <code>handleClick</code> to the <code>Button</code> component? Isn't <code>handleClick</code> what we want to run when we click the button?</p>
<p>Within the <code>Home</code> component in <code>app/page.tsx</code>, we define <code>handleClick</code> and pass it down as a prop to the <code>Button</code> component. Within the <code>Button</code> component's body in <code>components/button.tsx</code> we read the <em>prop</em> <code>onClick</code>, not the event handler <code>handleClick</code> itself. So when the <code>Button</code> component fires, it calls the <code>onClick</code> prop, which sits "up" the component tree inside <code>Home</code>, where it then calls <code>handleClick</code>, updates the count, and then passes that state back down to both <code>Button</code> components.</p>
<h3 id="heading-tiny-crash-course-in-typescript">Tiny Crash Course in TypeScript</h3>
<p>If you check <code>components/button.tsx</code> you will see the following errors for both the <code>count</code> and <code>onClick</code> props you are reading into <code>Button</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-2024-06-14-at-2.49.55-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Binding element 'count' implicitly has an 'any' type.</em></p>
<p>(You can get these "pretty" TypeScript syntax-highlighted errors with the Visual Studio Code extension <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors">here</a>).</p>
<p>What do these errors mean, and what is TypeScript? TypeScript is a superset of JavaScript that adds <em>types</em> to JavaScript. These can help ensure that our program works as we intend. Examples of types are <code>number</code>, <code>boolean</code>, and <code>string</code>. This error is telling us that we haven't defined a type for the props <code>count</code> or <code>onClick</code>. </p>
<p>So what will the type of <code>count</code> be? If we consider the result of count, the answers could be <code>1</code>, <code>2</code>, <code>3</code>, and so on. These are all numbers, so we will assign the type <code>number</code> to <code>count</code>.</p>
<p>The <code>onClick</code> prop is a function that doesn't take any arguments or return any value – we use it for its side effect of updating <code>setCount</code>. So we assign it the type <code>() =&gt; void</code>. </p>
<p>We create an <em><a target="_blank" href="https://www.typescriptlang.org/docs/handbook/interfaces.html">interface</a></em> where we define the types for our <code>ButtonProps</code>, and then read this interface into our component:</p>
<pre><code class="lang-js">interface ButtonProps {
    <span class="hljs-attr">count</span>: number;
    onClick: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">void</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">Button</span>(<span class="hljs-params">{ count, onClick }: ButtonProps</span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>The errors are gone and there you have it: a tiny intro to TypeScript!</p>
<p>Here let's make another commit with a message such as <code>buttons with shared state</code>.</p>
<h2 id="heading-chapter-4-how-to-add-both-pairs-of-buttons-to-our-site">Chapter 4: How to Add Both Pairs of Buttons to Our Site</h2>
<p>Let's showcase both our buttons with shared and independent state, and deploy the application.</p>
<p>Let's rename our <code>button.tsx</code> to <code>button-shared-state.tsx</code>. Let's also rename the function, the interface, the import within <code>app/page.tsx</code>, as well as the component in <code>app/page.tsx</code>. And let's switch these to <em>named</em> exports using a function expression using <code>const</code> instead of a function declaration:</p>
<pre><code class="lang-js">interface ButtonSharedStateProps {
    <span class="hljs-attr">count</span>: number;
    onClick: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">void</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ButtonSharedState = <span class="hljs-function">(<span class="hljs-params">{
    count,
    onClick,
}: ButtonSharedStateProps</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
};
</code></pre>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">import</span> { ButtonSharedState } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button-shared-state"</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">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSharedState</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSharedState</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>Now let's create a file <code>components/button-independent-state.tsx</code>:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ButtonIndependentState = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold py-2 px-4"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
};
</code></pre>
<p>What we've done here is similar to our logic in the beginning of this guide: we've located the state within the button component itself, so that each implementation of the button component creates and tracks its own independent state.</p>
<p>Let's import <code>ButtonIndependentState</code> into <code>app/page.tsx</code>:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">import</span> { ButtonSharedState } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button-shared-state"</span>;
<span class="hljs-keyword">import</span> { ButtonIndependentState } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button-independent-state"</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">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-bold"</span>&gt;</span>React Counter Buttons<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl"</span>&gt;</span>Buttons with shared state<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSharedState</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSharedState</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl"</span>&gt;</span>Buttons with independent state<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonIndependentState</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonIndependentState</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>We have now showcased a set of buttons that have independent state, as well as buttons that have shared state. We added a tiny bit of CSS to make things look nicer as well.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screen-Recording-2024-06-21-at-12.25.22-PM.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Our finished project</em></p>
<p>Here let's add another commit with a message such as <code>buttons with both shared and independent state</code>.</p>
<h2 id="heading-chapter-5-how-to-deploy-the-site-to-netlify">Chapter 5: How to Deploy the Site to Netlify</h2>
<h3 id="heading-publish-to-github">Publish to GitHub</h3>
<p>Let's deploy our application to the world to show it off. We are going to push our code to GitHub, and then deploy it to Netlify.</p>
<p>The first step is push our code to GitHub. If you don't have a GitHub account, create one first. In Visual Studio Code, you can push to GitHub from the command palette: open up the command palette by pressing <code>⇧⌘P</code> , then type in <code>publish to GitHub</code>, and select <code>publish to public GitHub repository</code>. </p>
<p>Open your GitHub account and verify that the project has successfully been uploaded.</p>
<h3 id="heading-deploy-to-netlify">Deploy to Netlify</h3>
<p>Once you've uploaded your project to GitHub, you can now deploy it to Netlify. Open up the <a target="_blank" href="https://www.netlify.com/">Netlify website</a>, and log in (or create an account if you don't have one).</p>
<p>Click <code>add new site</code>, and then <code>import an existing project</code>. When asked <code>Let’s deploy your project with…</code>, select <code>GitHub</code>. </p>
<p>Select the name of your repository from the list, then give the site a name under <code>site name</code>. You can leave the rest of the settings at their defaults and then click <code>deploy [your site name]</code>.</p>
<p>If the project builds successfully, you will have a live link of your work!</p>
<h2 id="heading-concluding-thoughts-and-next-steps">Concluding Thoughts and Next Steps</h2>
<p>In this project, you have learned fundamental concepts in React such as creating a functional component, importing and exporting modules, interpolating JavaScript within JSX using curly braces, working with state, and using React hooks. </p>
<p>You've also seen an introduction to using utility-based CSS techniques with Tailwind CSS, and you've gotten a tiny introduction into adding types to your JavaScript with TypeScript. Finally, you learned how to deploy your project to Netlify via GitHub.</p>
<p>Where can you go from here? One idea for expanding the project could be to create a "ticker": a counter that could be incremented and decremented (you would have one button that increases the number of the counter, and one that decreases it). </p>
<p>In the name of learning, one effective method for solidifying the concepts you've learned here would be to start a project completely fresh, and see if you can build everything in this tutorial without checking the tutorial. As you need to check in, you will identify which concepts benefit from further study and practice.</p>
<p>If you'd like to stay in touch, you can:</p>
<ul>
<li>Follow me on <a target="_blank" href="https://twitter.com/DevinCLane">Twitter</a> </li>
<li>Follow me on <a target="_blank" href="https://www.linkedin.com/in/devinlane/">LinkedIn</a></li>
</ul>
<p>Please post about what you've made along with any questions or feedback you might have.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Login Page with the Material Tailwind Framework – Step by Step Guide ]]>
                </title>
                <description>
                    <![CDATA[ Login pages are like the front doors to our web apps. They should be inviting, easy to use, and safe. If you're looking to create one that combines both style and function, you're in the right place. In this guide, we'll explore how to build a login ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-login-page-with-material-tailwind-framework/</link>
                <guid isPermaLink="false">66c375a9b737bb2ce7073268</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alexandru Paduraru ]]>
                </dc:creator>
                <pubDate>Mon, 29 Apr 2024 14:21:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/how-to-build-a-login-page-with-the-material-tailwind-framework-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Login pages are like the front doors to our web apps. They should be inviting, easy to use, and safe. If you're looking to create one that combines both style and function, you're in the right place.</p>
<p>In this guide, we'll explore how to build a login page with <a href="https://material-tailwind.com/" target="_blank">Material Tailwind</a> and Tailwind CSS that not only looks great but also works seamlessly across devices. </p>
<p>Whether you're new to web design or just curious about these tools, we'll walk you through each step. Let's dive in and start building! </p>
<p>Happy coding 🤘🏼</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<p>Check out the article chapters:</p>
<ol>
<li><a class="post-section-overview" href="#heading-1-introduction-to-tailwind-css-and-material-tailwind">Introduction to Tailwind CSS and Material Tailwind</a> </li>
<li><a class="post-section-overview" href="#heading-2-how-to-set-up-your-development-environment">How to Set Up Your Development Environment</a> </li>
<li><a class="post-section-overview" href="#heading-3-how-to-install-necessary-dependencies-for-your-login-page">How to Install Necessary Dependencies for your Login Page</a> </li>
<li><a class="post-section-overview" href="#heading-4-how-to-design-the-login-page-layout-with-tailwind-css">How to Design the Layout with Tailwind CSS</a>  </li>
<li><a class="post-section-overview" href="#heading-5-how-to-integrate-material-tailwind-components">How to Integrate Material Tailwind Components</a> </li>
<li><a class="post-section-overview" href="#heading-6-how-to-style-input-fields-and-buttons">How to Style Input Fields and Buttons</a>  </li>
<li><a class="post-section-overview" href="#heading-7-how-to-implement-responsive-design">How to Implement Responsive Design</a>  </li>
<li><a class="post-section-overview" href="#heading-8-how-to-add-interactivity-and-validation">How to Add Interactivity and Validation</a></li>
<li><a class="post-section-overview" href="#heading-9-security-and-best-practices-for-login-pages">Security and Best Practices for Login Pages</a> </li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusions</a> </li>
</ol>
<h2 id="heading-1-introduction-to-tailwind-css-and-material-tailwind">1. Introduction to Tailwind CSS and Material Tailwind</h2>
<p>Web design has many tools to help make good-looking and easy-to-use websites. </p>
<p>Two useful tools are <a href="https://tailwindcss.com/" target="_blank">Tailwind CSS</a> and <a href="https://material-tailwind.com/" target="_blank">Material Tailwind</a>. Tailwind CSS helps web designers make websites quickly without writing a lot of extra code. Material Tailwind adds nice-looking designs that are easy to change. Together, they can help you make a <a href="https://www.material-tailwind.com/blocks/authentication" target="_blank">Tailwind CSS login page</a> that looks good and works well. </p>
<p>In this section, you can see more details about these tools and why they're useful.</p>
<h3 id="heading-what-is-tailwind-css">What is <a href="https://tailwindcss.com/" target="_blank">Tailwind CSS</a>?</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/tailwind-css.jpg" alt="tailwind css" width="600" height="400" loading="lazy">
<em>Tailwind CSS homepage</em></p>
<p>Tailwind is a utility-first CSS framework. Instead of pre-designed components, it provides low-level utility classes that let you build custom designs without leaving your HTML. </p>
<p>Since its launch, Tailwind CSS has taken the web development community by storm. By 2023, the framework had gained over <a href="https://github.com/tailwindlabs/tailwindcss" target="_blank">400 million downloads on Github</a>, a proof of its growing adoption among developers. Several popular companies and platforms also started adopting Tailwind, for example: <a href="https://vercel.com/" target="_blank">Vercel</a>, <a href="https://www.algolia.com/" target="_blank">Algolia</a>, <a href="https://www.netlify.com/" target="_blank">Netlify</a>, and others. The Github statistics show over 74,000 stars, which indicates a strong community backing. </p>
<p><strong>Why use Tailwind CSS:</strong></p>
<ul>
<li>Speed: Quickly build UIs with utility classes instead of writing custom CSS.</li>
<li>Flexibility: You can customize designs without restrictions.</li>
<li>Responsiveness: Easily make designs that work on all device sizes.</li>
</ul>
<p>Example: Instead of writing custom CSS for a button, use utility classes directly in your HTML.</p>
<pre><code>&lt;button <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"</span>&gt;
   View More
&lt;/button&gt;
</code></pre><h3 id="heading-what-is-material-tailwind">What is <a href="https://material-tailwind.com/" target="_blank">Material Tailwind</a>?</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/material-tailwind.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Material Tailwind homepage</em></p>
<p>Material Tailwind is a components library for Tailwind CSS that implements Google's Material Design guidelines. It combines the best of both worlds: the utility-first approach of Tailwind and the design philosophy of Material Design.</p>
<p><strong>Why use Material Tailwind:</strong></p>
<ul>
<li>Consistency: Follows Material Design guidelines, ensuring familiar <a href="https://www.material-tailwind.com/roots-of-ui-ux-design" target="_blank">UI/UX patterns</a>.</li>
<li>Customizable: Being built on Tailwind, it's highly customizable.</li>
<li>Component-rich: Comes with pre-built components that speed up development.</li>
</ul>
<p>Example: Imagine you want a Material Design inspired <a href="https://www.material-tailwind.com/docs/react/button" target="_blank">button</a>. With Material Tailwind, it's straightforward.</p>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"middle none center rounded-lg bg-pink-500 py-3 px-6 font-sans text-xs font-bold uppercase text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  data-ripple-light=<span class="hljs-string">"true"</span>
&gt;
  View More
&lt;/button&gt;
</code></pre><p>If you are a React fan too, let me tell you that Material Tailwind also comes with Tailwind CSS + React components. For more details, check out the <a href="https://www.material-tailwind.com/docs/react/installation" target="_blank">Material Tailwind React documentation</a>.</p>
<p>Now let’s see how you can use these two amazing tools to create your desired Login Page.</p>
<h2 id="heading-2-how-to-set-up-your-development-environment">2. How to Set Up Your Development Environment</h2>
<p>Creating a development environment tailored for your web project ensures smooth and efficient workflow. For our <a href="https://www.material-tailwind.com/docs/html/card#login-card" target="_blank">Tailwind CSS login page</a>, we’ll start from scratch.</p>
<p>Prerequisites:</p>
<ul>
<li>Node.js and npm (Node Package Manager): These are essential for installing and managing libraries we’ll be using.</li>
<li>A Text Editor or IDE: While any text editor will work, Visual Studio Code (VS Code) is recommended due to its extensive library of extensions beneficial for web development.</li>
<li>Terminal or Command Prompt: We'll be running commands to set up and manage our project.</li>
</ul>
<h3 id="heading-install-nodejs-and-npm">Install Node.js and npm</h3>
<p>To start, visit the <a href="https://nodejs.org/" target="_blank">Node.js official website</a> and download the recommended version for your OS.</p>
<p>After installation, verify that Node.js and npm are correctly installed by running the following commands:</p>
<pre><code>node -v
npm -v
</code></pre><h3 id="heading-set-up-a-new-project">Set up a new project</h3>
<p>Create a new directory for your project:</p>
<pre><code>mkdir tailwind-material-login
cd tailwind-material-login
</code></pre><p>Then initialize a new npm project:</p>
<pre><code>npm init -y
</code></pre><h3 id="heading-text-editoride-setup-vs-code">Text Editor/IDE Setup (VS Code)</h3>
<p>If you haven’t already, download and install <a href="https://code.visualstudio.com/" target="_blank">VS Code</a>. Then go ahead and open your project directory in VS Code.</p>
<p>Optionally, you can install extensions that enhance Tailwind CSS development:</p>
<ul>
<li>Tailwind CSS IntelliSense: Offers class name autocompletions, linting, and more.</li>
<li>Live Server: Allows you to see live changes without manually refreshing the browser.</li>
</ul>
<h3 id="heading-terminal-in-vs-code">Terminal in VS Code</h3>
<p>VS Code offers an integrated terminal. This means you can run commands directly within your editor.</p>
<p>To open it, go to the top menu: View &gt; Terminal or press Ctrl + a backtick.</p>
<h3 id="heading-basic-project-structure">Basic Project Structure</h3>
<p>Create a basic structure for your project:</p>
<pre><code>mkdir src
touch src/index.html
touch src/styles.css
touch src/main.js
</code></pre><p>This creates a src directory with an HTML file, a CSS file, and a JavaScript file.</p>
<h2 id="heading-3-how-to-install-necessary-dependencies-for-your-login-page">3. How to Install Necessary Dependencies for your Login Page</h2>
<p>Setting up your libraries and frameworks is crucial to build upon a solid foundation. For our Tailwind CSS login page, we'll be primarily using Tailwind CSS and Material Tailwind. </p>
<p>Here’s a step-by-step guide to installing and configuring these dependencies.</p>
<h3 id="heading-how-to-install-tailwind-css">How to install Tailwind CSS</h3>
<p>First, you'll need to install the Tailwind CSS package. Using npm, install the Tailwind CSS package with:</p>
<pre><code>npm install tailwindcss
</code></pre><p>Then, integrate Tailwind into your CSS. Inside your src/styles.css file, add the following Tailwind directives:</p>
<pre><code>@<span class="hljs-keyword">import</span> <span class="hljs-string">'tailwindcss/base'</span>;
@<span class="hljs-keyword">import</span> <span class="hljs-string">'tailwindcss/components'</span>;
@<span class="hljs-keyword">import</span> <span class="hljs-string">'tailwindcss/utilities'</span>;
</code></pre><p>Next, you'll need to generate a configuration file. While Tailwind works out of the box, a configuration file (tailwind.config.js) provides customization capabilities. </p>
<p>To generate it:</p>
<pre><code>npx tailwindcss init
</code></pre><p>This creates a minimal config file in your root directory. You can extend this file as needed.</p>
<p>Finally, to process your CSS and apply Tailwind’s transformations, add a script in your package.json under the "scripts" section:</p>
<pre><code><span class="hljs-string">"scripts"</span>: {
<span class="hljs-string">"build-css"</span>: <span class="hljs-string">"tailwind build src/styles.css -o src/output.css"</span>
}
</code></pre><p>Run the script with:</p>
<pre><code>npm run build-css
</code></pre><p>This will generate an output.css containing all Tailwind's styles. Include this file in your HTML.</p>
<h3 id="heading-how-to-install-material-tailwind">How to install Material Tailwind</h3>
<p>First, install the Material Tailwind package. Again, you can use npm:</p>
<pre><code>npm i @material-tailwind/html
</code></pre><p>Now you need to integrate it into your project. Material Tailwind provides components with styles applied. You can use them directly in your HTML or customize them further in your CSS.</p>
<p>For instance, to use a Material Tailwind button:</p>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"middle none center rounded-lg bg-pink-500 py-3 px-6 font-sans text-xs font-bold uppercase text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  data-ripple-light=<span class="hljs-string">"true"</span>
&gt;
  View More
&lt;/button&gt;
</code></pre><p>Next, install the required fonts and icons. Material Design recommends specific fonts and icons. Include these in your <code>src/index.html</code>:</p>
<pre><code>&lt;!-- Material Icons Link --&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/icon?family=Material+Icons"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>/&gt;</span></span> 

&lt;!-- Font Awesome Link --&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w=="</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>/&gt;</span></span>
</code></pre><h3 id="heading-postcss-and-autoprefixer-recommended">PostCSS and Autoprefixer (Recommended)</h3>
<p>Install PostCSS and Autoprefixer:</p>
<pre><code>npm install postcss postcss-cli autoprefixer
</code></pre><p>Go ahead and configure them by creating a postcss.config.js in your root directory:</p>
<pre><code><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-built_in">require</span>(<span class="hljs-string">'tailwindcss'</span>),
    <span class="hljs-built_in">require</span>(<span class="hljs-string">'autoprefixer'</span>),
  ]
};
</code></pre><p>Modify your build-css script in package.json to:</p>
<pre><code><span class="hljs-string">"build-css"</span>: <span class="hljs-string">"postcss src/styles.css -o src/output.css"</span>
</code></pre><p>This ensures your Tailwind CSS styles are vendor-prefixed, making them compatible across different browsers.</p>
<p>Add into your project’s <code>&lt;head&gt;</code> the output.css styling file.</p>
<pre><code>&lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"pathTo/output.css"</span> /&gt;
</code></pre><h2 id="heading-4-how-to-design-the-login-page-layout-with-tailwind-css">4. How to Design the Login Page Layout with Tailwind CSS</h2>
<p>A <a href="https://www.material-tailwind.com/docs/html/card#login-card" target="_blank">login page</a> typically consists of a logo or branding, input fields (like username and password), a submit button, and often some secondary options or links, such as "Forgot Password" or "Sign Up".</p>
<h3 id="heading-main-container">Main Container:</h3>
<p>Start with a main container that centers its content both vertically and horizontally.</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"min-h-screen flex items-center justify-center bg-gray-100"</span>&gt;
&lt;!-- Login Card Content Will Go Here --&gt;
&lt;/div&gt;
</code></pre><h3 id="heading-login-card">Login Card:</h3>
<p>Create a centered card layout that will house the login form. Introduce the necessary input fields for the login process, typically an email/username and password.</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"relative flex flex-col text-gray-700 bg-white shadow-md w-96 rounded-xl bg-clip-border"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"relative grid mx-4 mb-4 -mt-6 overflow-hidden text-white shadow-lg h-28 place-items-center rounded-xl bg-gradient-to-tr from-gray-900 to-gray-800 bg-clip-border shadow-gray-900/20"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block font-sans text-3xl antialiased font-semibold leading-snug tracking-normal text-white"</span>&gt;</span>
      Sign In
    <span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
  <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> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-col gap-4 p-6"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative h-11 w-full min-w-[200px]"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full h-full px-3 py-3 font-sans text-sm font-normal transition-all bg-transparent border rounded-md peer border-blue-gray-200 border-t-transparent text-blue-gray-700 outline outline-0 placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-gray-900 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none !overflow-visible truncate text-[11px] font-normal leading-tight text-gray-500 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[4.1] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-gray-900 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:!border-gray-900 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:!border-gray-900 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</span>&gt;</span>
        Email
      <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">"relative h-11 w-full min-w-[200px]"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full h-full px-3 py-3 font-sans text-sm font-normal transition-all bg-transparent border rounded-md peer border-blue-gray-200 border-t-transparent text-blue-gray-700 outline outline-0 placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-gray-900 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none !overflow-visible truncate text-[11px] font-normal leading-tight text-gray-500 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[4.1] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-gray-900 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:!border-gray-900 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:!border-gray-900 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"-ml-2.5"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-flex items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative flex items-center p-3 rounded-full cursor-pointer"</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">"before:content[''] peer relative h-5 w-5 cursor-pointer appearance-none rounded-md border border-blue-gray-200 transition-all before:absolute before:top-2/4 before:left-2/4 before:block before:h-12 before:w-12 before:-translate-y-2/4 before:-translate-x-2/4 before:rounded-full before:bg-blue-gray-500 before:opacity-0 before:transition-opacity checked:border-gray-900 checked:bg-gray-900 checked:before:bg-gray-900 hover:before:opacity-10"</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"checkbox"</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute text-white transition-opacity opacity-0 pointer-events-none top-2/4 left-2/4 -translate-y-2/4 -translate-x-2/4 peer-checked:opacity-100"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-3.5 w-3.5"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 20 20"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
              <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"1"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fill-rule</span>=<span class="hljs-string">"evenodd"</span>
                <span class="hljs-attr">d</span>=<span class="hljs-string">"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"</span>
                <span class="hljs-attr">clip-rule</span>=<span class="hljs-string">"evenodd"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">svg</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">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-px font-light text-gray-700 cursor-pointer select-none"</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"checkbox"</span>&gt;</span>
          Remember Me
        <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>&gt;</span>
  <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> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-6 pt-0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"block w-full select-none rounded-lg bg-gradient-to-tr from-gray-900 to-gray-800 py-3 px-6 text-center align-middle font-sans text-xs font-bold uppercase text-white shadow-md shadow-gray-900/10 transition-all hover:shadow-lg hover:shadow-gray-900/20 active:opacity-[0.85] disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
      <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>&gt;</span>
      Sign In
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex justify-center mt-6 font-sans text-sm antialiased font-light leading-normal text-inherit"</span>&gt;</span>
      Don't have an account?
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#signup"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"block ml-1 font-sans text-sm antialiased font-bold leading-normal text-blue-gray-900"</span>&gt;</span>
        Sign up
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/div&gt;
</code></pre><h3 id="heading-login-button">Login Button:</h3>
<p>Place the primary action button (Login) below the input fields.</p>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"block w-full select-none rounded-lg bg-gradient-to-tr from-gray-900 to-gray-800 py-3 px-6 text-center align-middle font-sans text-xs font-bold uppercase text-white shadow-md shadow-gray-900/10 transition-all hover:shadow-lg hover:shadow-gray-900/20 active:opacity-[0.85] disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  type=<span class="hljs-string">"button"</span>&gt;
  Sign In
&lt;/button&gt;
</code></pre><h3 id="heading-secondary-options">Secondary Options:</h3>
<p>Beneath the login button, offer options such as "Forgot Password" or "Sign Up".</p>
<h2 id="heading-5-how-to-integrate-material-tailwind-components">5. How to Integrate Material Tailwind Components</h2>
<p>Material Design, initiated by Google, is a design system that combines classic principles of good design with innovative technologies. When fused with the utility-first approach of Tailwind CSS via <a href="https://www.material-tailwind.com" target="_blank">Material Tailwind</a>, it provides a powerful toolkit to create interactive and visually appealing user interfaces.</p>
<p>Material Tailwind provides styled components that adhere to Material Design guidelines but harness the utility of Tailwind. This means that while most of the styling is pre-configured, you can still utilize Tailwind classes for customization.</p>
<ul>
<li><strong><a href="https://www.material-tailwind.com/docs/html/input" target="_blank">Input Fields</a></strong></li>
</ul>
<p>Instead of the standard Tailwind-styled inputs, you can introduce Material-styled input fields for a more tactile and animated experience:</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"relative h-10 w-full min-w-[200px]"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"peer h-full w-full rounded-[7px] border border-blue-gray-200 border-t-transparent bg-transparent px-3 py-2.5 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-pink-500 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</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>
  /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-pink-500 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</span>&gt;</span>
    Email
  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
&lt;/div&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative h-10 w-full min-w-[200px]"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"peer h-full w-full rounded-[7px] border border-blue-gray-200 border-t-transparent bg-transparent px-3 py-2.5 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-pink-500 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
    <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span>
  /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-pink-500 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</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>
</code></pre><p>These components offer a floating label effect and subtle focus animations out of the box.</p>
<ul>
<li><strong><a href="https://www.material-tailwind.com/docs/html/button" target="_blank">Buttons</a></strong></li>
</ul>
<p>Material Tailwind offers a set of button styles that fit within the Material Design paradigm:</p>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"middle none center rounded-lg bg-pink-500 py-3 px-6 font-sans text-xs font-bold uppercase text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  data-ripple-light=<span class="hljs-string">"true"</span>
&gt;
  Login
&lt;/button&gt;
</code></pre><p>This renders a raised button with Material's signature ripple effect on click.</p>
<ul>
<li><strong><a href="https://www.material-tailwind.com/docs/html/tooltip" target="_blank">Tooltips</a></strong></li>
</ul>
<p>To enhance user guidance, you can add tooltips to your components:</p>
<pre><code>&lt;button
  data-ripple-light=<span class="hljs-string">"true"</span>
  data-tooltip-target=<span class="hljs-string">"tooltip"</span>
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"middle none center rounded-lg bg-gradient-to-tr from-pink-600 to-pink-400 py-3 px-6 font-sans text-xs font-bold uppercase text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40 active:opacity-[0.85] disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
&gt;
  Show Tooltip
&lt;/button&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">data-tooltip</span>=<span class="hljs-string">"tooltip"</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute z-50 whitespace-normal break-words rounded-lg bg-black py-1.5 px-3 font-sans text-sm font-normal text-white focus:outline-none"</span>
&gt;</span>
  Login Form
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre><p>When users hover over the element, they'll see a tooltip providing additional information.</p>
<ul>
<li><strong>Incorporating <a href="https://www.material-tailwind.com/docs/html/icon-button" target="_blank">Material Icons</a></strong></li>
</ul>
<p>Material Design also has a rich set of icons. Material Tailwind makes it easy to incorporate these:</p>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"item-center middle none center flex justify-center rounded-lg bg-pink-500 p-3 font-sans text-xs font-bold uppercase text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  data-ripple-light=<span class="hljs-string">"true"</span>
&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart text-lg leading-none"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span></span>
&lt;/button&gt;
</code></pre><p>Make sure you've already linked to Material icons in your HTML as instructed earlier.</p>
<ul>
<li><strong><a href="https://www.material-tailwind.com/docs/html/theming" target="_blank">Customizing Material Components</a></strong></li>
</ul>
<p>While Material Tailwind components come pre-styled, you can still leverage Tailwind utility classes for customizations:</p>
<pre><code>&lt;button 
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"button button-pink !bg-blue-500 px-4"</span> 
  data-ripple-light=<span class="hljs-string">"true"</span>
&gt;
  Button
&lt;/button&gt;
</code></pre><p>Here, we've overridden the default primary color with a custom shade of purple from Tailwind's color palette.</p>
<p>Incorporating Material Tailwind components provides an elegant touch to the login page, combining the efficiency of Tailwind CSS with the visual and interactive richness of Material Design. The result is a responsive, user-friendly, and visually cohesive login experience. </p>
<p>Also, you can check the components library’s <a href="https://www.material-tailwind.com/blocks/authentication" target="_blank">Tailwind CSS login page templates</a>.</p>
<h2 id="heading-6-how-to-style-input-fields-and-buttons">6. How to Style Input Fields and Buttons</h2>
<p>The appearance and feel of input fields and buttons play a pivotal role in user experience. They're the primary interaction points. Let's focus on giving them a sleek, user-friendly design using Material Tailwind and Tailwind CSS.</p>
<ol>
<li><strong><a href="https://www.material-tailwind.com/docs/html/input" target="_blank">Material Tailwind Input Fields</a>:</strong></li>
</ol>
<p>Material Tailwind offers a more polished look for input fields out of the box. Here's how you can integrate them:</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"relative h-11 w-full min-w-[200px]"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"peer h-full w-full border-b border-blue-gray-200 bg-transparent pt-4 pb-1.5 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border-blue-gray-200 focus:border-pink-500 focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</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>
  /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-500 transition-all after:absolute after:-bottom-1.5 after:block after:w-full after:scale-x-0 after:border-b-2 after:border-pink-500 after:transition-transform after:duration-300 peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[4.25] peer-placeholder-shown:text-blue-gray-500 peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:after:scale-x-100 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</span>&gt;</span>
    Email
  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
&lt;/div&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative h-11 w-full min-w-[200px]"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"peer h-full w-full border-b border-blue-gray-200 bg-transparent pt-4 pb-1.5 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border-blue-gray-200 focus:border-pink-500 focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
    <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span>
  /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-500 transition-all after:absolute after:-bottom-1.5 after:block after:w-full after:scale-x-0 after:border-b-2 after:border-pink-500 after:transition-transform after:duration-300 peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[4.25] peer-placeholder-shown:text-blue-gray-500 peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:after:scale-x-100 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</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>
</code></pre><p>These fields come with improved focus effects and an overall refined look, making them more engaging.</p>
<ol start="2">
<li><strong><a href="https://www.material-tailwind.com/docs/html/icon-button" target="_blank">Enhancing with Icons</a>:</strong></li>
</ol>
<p>Integrating Material icons can guide users and enhance visual cues:</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"relative h-10 w-full min-w-[200px]"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute top-2/4 right-3 grid h-5 w-5 -translate-y-2/4 place-items-center text-blue-gray-500"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
  <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">input</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"peer h-full w-full rounded-[7px] border border-blue-gray-200 border-t-transparent bg-transparent px-3 py-2.5 !pr-9 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-pink-500 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</span>
  /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-pink-500 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</span>&gt;</span>
    Input With Icon
  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
&lt;/div&gt;
</code></pre><p>The email field now includes a mail icon on the left, giving users a clear indication of the input type.</p>
<ol start="3">
<li><strong><a href="https://www.material-tailwind.com/docs/html/button" target="_blank">Material Tailwind Buttons</a>:</strong></li>
</ol>
<p>Material Tailwind buttons come pre-styled with Material aesthetics. However, you can further customize their appearance with Tailwind utility classes:</p>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"middle none center rounded-lg bg-pink-500 py-3 px-6 font-sans text-xs font-bold uppercase text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  data-ripple-light=<span class="hljs-string">"true"</span>
&gt;
  Login
&lt;/button&gt;
</code></pre><p>In this example, the button has a primary blue background color with different shades on hover and active states.</p>
<ol start="4">
<li><strong><a href="https://www.material-tailwind.com/docs/html/button" target="_blank">Button Variations</a>:</strong></li>
</ol>
<p>Offering secondary or tertiary actions? You can use outlined or text buttons:</p>
<ul>
<li>Gradient Button:</li>
</ul>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"middle none center mr-3 rounded-lg bg-gradient-to-tr from-pink-600 to-pink-400 py-3 px-6 font-sans text-xs font-bold uppercase text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40 active:opacity-[0.85] disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  data-ripple-light=<span class="hljs-string">"true"</span>
&gt;
  Gradient Button
&lt;/button&gt;
</code></pre><ul>
<li>Outlined Button:</li>
</ul>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"middle none center mr-3 rounded-lg border border-pink-500 py-3 px-6 font-sans text-xs font-bold uppercase text-pink-500 transition-all hover:opacity-75 focus:ring focus:ring-pink-200 active:opacity-[0.85] disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  data-ripple-dark=<span class="hljs-string">"true"</span>
&gt;
  Outlined Button
&lt;/button&gt;
</code></pre><ul>
<li>Text Button:</li>
</ul>
<pre><code>&lt;button
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"middle none center rounded-lg py-3 px-6 font-sans text-xs font-bold uppercase text-pink-500 transition-all hover:bg-pink-500/10 active:bg-pink-500/30 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  data-ripple-dark=<span class="hljs-string">"true"</span>
&gt;
  Text Button
&lt;/button&gt;
</code></pre><ol start="5">
<li><strong>Responsiveness and Sizing:</strong></li>
</ol>
<p>Remember to consider different device sizes. Tailwind's responsive utilities can help:</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"relative h-10 w-full md:w-50 lg:w-24"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"peer h-full w-full rounded-[7px] border border-blue-gray-200 border-t-transparent bg-transparent px-3 py-2.5 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-pink-500 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
  /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-pink-500 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</span>&gt;</span>
    Required
  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
&lt;/div&gt;
</code></pre><p>This input field takes the full width on smaller screens, but only half the width on larger ones.</p>
<h2 id="heading-7-how-to-implement-responsive-design">7. How to Implement Responsive Design</h2>
<p>A significant strength of Tailwind CSS lies in its mobile-first approach to responsive design. Ensuring your login page looks good and functions well on all devices is crucial for user experience.</p>
<p>Tailwind CSS is built with a mobile-first mindset. This means that by default, the classes you apply are for mobile views. To target larger screens, you utilize breakpoints. For instance:</p>
<pre><code>.text-sm md:text-base lg:text-xl
</code></pre><p>On mobile, the text size would be sm, on medium-sized screens (like tablets) it would be base, and on large screens (like desktops) it would be xl.</p>
<ul>
<li>Responsive Card Layout: While our login card may look great on desktop, it might be too narrow on mobile. You can adjust this:</li>
</ul>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"relative flex flex-col text-gray-700 bg-white shadow-md w-96 rounded-xl bg-clip-border"</span>&gt;
&lt;!-- Login Content --&gt;
&lt;/div&gt;
</code></pre><p>Here, we're using less padding (p-4) and taking full width (w-full) on mobile, but increasing the padding (md:p-8) and setting a fixed width (md:w-96) on medium screens and above.</p>
<ul>
<li>Adapting Input Fields and Buttons: Ensure that interactive elements are easily accessible on touch devices:</li>
</ul>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"relative h-10 w-full mb-4 md:mb-0"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"peer h-full w-full rounded-[7px] border border-blue-gray-200 border-t-transparent bg-transparent px-3 py-2.5 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-pink-500 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
  /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-pink-500 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</span>&gt;</span>
    Email
  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
&lt;/div&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"middle none center mr-3 rounded-lg border border-pink-500 py-3 px-6 font-sans text-xs font-bold uppercase text-pink-500 transition-all hover:opacity-75 focus:ring focus:ring-pink-200 active:opacity-[0.85] disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
  <span class="hljs-attr">data-ripple-dark</span>=<span class="hljs-string">"true"</span>
&gt;</span>
  Login
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
</code></pre><p>On mobile, the input fields and buttons take up the full width for easier touch access but adapt to more spacious designs on larger screens.</p>
<ul>
<li>Handling Landscape Mode: Sometimes, especially on mobile devices, landscape mode can drastically alter layouts. Consider adding specific styles for this using Tailwind's landscape plugin (if you've added it to your configuration):</li>
</ul>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"mt-10 landscape:mt-4"</span>&gt;
&lt;!-- Login Content --&gt;
&lt;/div&gt;
</code></pre><p>In landscape mode, the top margin is reduced to accommodate the shorter viewport height.</p>
<ul>
<li><a href="https://www.material-tailwind.com/docs/html/typography" target="_blank">Typography</a> Adjustments: Optimal readability is essential. Ensure text sizes are appropriate for various devices:</li>
</ul>
<pre><code>&lt;h2 
  <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"block font-sans text-4xl font-semibold leading-[1.3] tracking-normal text-inherit antialiased"</span>
&gt;
  Sign In to Your Account
&lt;/h2&gt;
</code></pre><p>On mobile, the title is slightly smaller, but it scales up on medium and larger screens.</p>
<ul>
<li>Testing Responsiveness:  While Tailwind offers the tools to create responsive designs, always test your layouts on actual devices or using browser tools. This ensures a consistent experience across devices and resolutions.</li>
</ul>
<h2 id="heading-8-how-to-add-interactivity-and-validation">8. How to Add Interactivity and Validation</h2>
<p>A successful login page isn't just about looks – it must provide feedback, handle user input gracefully, and validate that input to ensure security and functionality.</p>
<p>Material Tailwind components come with built-in animations and feedback mechanisms. For instance, when you click a Material Tailwind button, you'll see a ripple effect, providing immediate feedback to the user.</p>
<h3 id="heading-toggle-password-visibility">Toggle Password Visibility:</h3>
<p>A common feature in login forms is the ability to toggle password visibility. This improves user experience, especially on mobile devices where typing mistakes are more frequent. Using Material icons and some simple JavaScript:</p>
<pre><code>
&lt;form id=<span class="hljs-string">"loginForm"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative h-10 w-full min-w-[200px]"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute top-2/4 right-3 grid h-5 w-5 -translate-y-2/4 place-items-center text-blue-gray-500"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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">class</span>=<span class="hljs-string">"peer h-full w-full rounded-[7px] border border-blue-gray-200 border-t-transparent bg-transparent px-3 py-2.5 !pr-9 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-pink-500 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</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>
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-pink-500 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</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">"middle none center mr-3 rounded-lg border border-pink-500 py-3 px-6 font-sans text-xs font-bold uppercase text-pink-500 transition-all hover:opacity-75 focus:ring focus:ring-pink-200 active:opacity-[0.85] disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"</span>
      <span class="hljs-attr">data-ripple-dark</span>=<span class="hljs-string">"true"</span>
      <span class="hljs-attr">id</span>=<span class="hljs-string">"togglePassword"</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">div</span>&gt;</span></span>
&lt;/form&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-keyword">const</span> passwordField = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'password'</span>);
    <span class="hljs-keyword">const</span> togglePasswordBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'togglePassword'</span>);

    togglePasswordBtn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">if</span> (passwordField.type === <span class="hljs-string">"password"</span>) {
            passwordField.type = <span class="hljs-string">"text"</span>;
        } <span class="hljs-keyword">else</span> {
            passwordField.type = <span class="hljs-string">"password"</span>;
        }
    });
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre><p>Clicking the visibility icon will now toggle the password field between visible and obscured states.</p>
<h3 id="heading-client-side-validation">Client-Side Validation:</h3>
<p>While never a replacement for server-side validation, client-side validation provides immediate feedback:</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"relative h-10 w-full min-w-[200px]"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute top-2/4 right-3 grid h-5 w-5 -translate-y-2/4 place-items-center text-blue-gray-500"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
  <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">input</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"peer h-full w-full rounded-[7px] border border-blue-gray-200 border-t-transparent bg-transparent px-3 py-2.5 !pr-9 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-pink-500 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" "</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">name</span>=<span class="hljs-string">"email"</span>
    <span class="hljs-attr">required</span>
    <span class="hljs-attr">pattern</span>=<span class="hljs-string">"[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"</span>
  /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-pink-500 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-pink-500 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-pink-500 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500"</span>&gt;</span>
  Email
  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
&lt;/div&gt;
</code></pre><p>The required attribute ensures the field is not left empty, and the pattern attribute uses a basic regex to validate email structure.</p>
<h3 id="heading-handling-form-submission">Handling Form Submission:</h3>
<p>When the user attempts to log in, you can provide feedback using JavaScript:</p>
<pre><code><span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'loginForm'</span>).addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    event.preventDefault();

    <span class="hljs-comment">// Fetch input values</span>
    <span class="hljs-keyword">const</span> email = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'email'</span>).value;
    <span class="hljs-keyword">const</span> password = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'password'</span>).value;

    <span class="hljs-comment">// Example: Check if credentials are empty</span>
    <span class="hljs-keyword">if</span> (!email || !password) {
        alert(<span class="hljs-string">'Please fill in all fields.'</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Handle actual submission to server</span>

    <span class="hljs-comment">// On success:</span>
    <span class="hljs-comment">// alert('Logged in successfully!');</span>

    <span class="hljs-comment">// On failure:</span>
    <span class="hljs-comment">// alert('Invalid credentials. Please try again.');</span>
});
</code></pre><p>The code above prevents the default form submission and provides feedback depending on the input. This is a simplistic approach, and in real-world scenarios, you'd interact with server endpoints to authenticate the user.</p>
<h3 id="heading-handling-errors-and-feedback">Handling Errors and Feedback:</h3>
<p>It's crucial to provide clear feedback in the event of errors or successful actions. Consider using modals, snack bars, or simple inline messages to communicate the status of actions to users.</p>
<p>For example, using Tailwind:</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"bg-red-500 text-white p-4 rounded-md mt-4"</span> id=<span class="hljs-string">"errorMessage"</span> style=<span class="hljs-string">"display: none;"</span>&gt;
Invalid credentials. Please <span class="hljs-keyword">try</span> again.
&lt;/div&gt;
</code></pre><p>Then, using JavaScript, you can show or hide this error message based on the authentication result.</p>
<h2 id="heading-9-security-and-best-practices-for-login-pages">9. Security and Best Practices for Login Pages</h2>
<p>Security is paramount when dealing with user authentication. A well-designed login page not only enhances user experience but also acts as the first line of defense against potential threats.</p>
<h3 id="heading-https-and-encryption">HTTPS and Encryption:</h3>
<p>Before any other consideration, ensure your website uses HTTPS. This encrypts data transmitted between the user's browser and your server, protecting it from eavesdropping and tampering.</p>
<h3 id="heading-password-security">Password Security:</h3>
<p>Client-Side:</p>
<ul>
<li>Never store passwords in plain text or local storage.</li>
<li>Use password input fields (type="password") to hide password characters.</li>
</ul>
<p>Server-Side:</p>
<ul>
<li>Always hash and salt passwords before storing them in your database.</li>
<li>Consider using proven libraries like bcrypt for hashing.</li>
</ul>
<h3 id="heading-avoid-verbose-error-messages">Avoid Verbose Error Messages:</h3>
<p>If authentication fails, avoid specifying whether it was the username or password that was incorrect. Instead, use generic messages:</p>
<pre><code>alert(<span class="hljs-string">'Invalid login credentials. Please try again.'</span>);
</code></pre><p>This prevents attackers from determining if a particular email or username is registered with your site.</p>
<ul>
<li><strong>Limit Login Attempts:</strong> Implement a system to track failed login attempts and consider locking out users or implementing CAPTCHAs after a certain threshold is reached. This helps prevent brute-force attacks.</li>
<li><strong>Implement CAPTCHAs:</strong> Tools like Google's reCAPTCHA can help ensure that login attempts are made by humans, not automated scripts. This further protects against brute-force and bot-driven attacks.</li>
<li><strong>Secure Password Reset</strong> - If you provide a "Forgot Password" option:<ul>
<li>Use one-time tokens that expire after a short duration.</li>
<li>Send the reset link to the registered email instead of allowing password changes directly from the login page.</li>
<li>Always ask security questions or verify user identity before allowing password resets.</li>
</ul>
</li>
<li><strong>Cross-Site Scripting (XSS) and SQL Injection</strong>: Always sanitize and validate input data to protect against XSS attacks and SQL injections. Utilize parameterized queries or prepared statements to prevent SQL injections.</li>
<li><strong>Keep Libraries and Dependencies Updated</strong>: Regularly update your server software, libraries, and other dependencies to ensure you're protected against known vulnerabilities.</li>
<li><strong>Use Content Security Policy (CSP)</strong>: A CSP helps prevent a wide range of attacks, including XSS. It limits the sources of executable scripts, ensuring that only trusted sources are allowed.</li>
<li><strong>Logging and Monitoring</strong>: Keep logs of authentication attempts, especially failed ones. Monitoring these can alert you to potential attacks or suspicious activities.</li>
<li><strong>Two-Factor Authentication (2FA)</strong>: Consider implementing 2FA for added user account protection. This requires users to provide two distinct forms of identification before gaining access.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building a login page might seem easy, but there's a lot to think about. It should look good, work well, be easy to use on any device, and be safe. Thanks to tools like <a href="https://tailwindcss.com/" target="_blank">Tailwind CSS</a> and <a href="https://material-tailwind.com/" target="_blank">Material Tailwind</a>, you can make great login pages without too much hassle.</p>
<p>In this guide, we started from scratch and ended up with a ready-to-use login page. By combining Tailwind CSS's easy style tools and Material Design's nice looks, users get a great experience every time they log in.</p>
<p>But remember, technology keeps changing. So, always keep learning and updating your skills.</p>
<h3 id="heading-useful-links">Useful links:</h3>
<ul>
<li><a href="https://tailwindcss.com/" target="_blank">Tailwind CSS Website</a></li>
<li><a href="https://github.com/tailwindlabs/tailwindcss" target="_blank">Tailwind CSS Github</a></li>
<li><a href="https://material-tailwind.com/" target="_blank">Material Tailwind Website</a></li>
<li><a href="https://github.com/creativetimofficial/material-tailwind" target="_blank">Material Tailwind Github</a></li>
<li><a href="https://www.material-tailwind.com/docs/html/installation" target="_blank">Material Tailwind Documentation</a></li>
<li><a href="https://www.creative-tim.com/templates/tailwind" target="_blank">Free Tailwind CSS Website Templates</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React Navigation – How to Build a Breadcrumb Component ]]>
                </title>
                <description>
                    <![CDATA[ I know what you're thinking – but despite the article title and cover image, this article isn't about bread or even pastries. Instead, it looks at a commonly used component in web applications for navigation called the breadcrumb component. Here, we'... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-navigation-build-a-breadcrumb-component/</link>
                <guid isPermaLink="false">66bb8914b0d3ac3d7acde3dd</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react-navigation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ routing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Jaja ]]>
                </dc:creator>
                <pubDate>Mon, 25 Mar 2024 22:33:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/Breadcrumb-article-cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I know what you're thinking – but despite the article title and cover image, this article isn't about bread or even pastries. Instead, it looks at a commonly used component in web applications for navigation called the breadcrumb component.</p>
<p>Here, we'll unravel the mysteries of breadcrumb trails in React applications. We'll dissect their types, and you'll learn how to seamlessly integrate them into your web projects for better user navigation.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Fundamentals of CSS and TailwindCSS</li>
<li>Fundamentals of ES6 JavaScript and React </li>
<li>Fundamentals of Routing and the React Router library ( check out this <a target="_blank" href="https://www.freecodecamp.org/news/improve-user-experience-in-react-by-animating-routes-using-framer-motion/">routing article</a> if you’re unfamiliar).</li>
</ul>
<h2 id="heading-what-well-cover">What We'll Cover:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-understanding-breadcrumbs">Understanding Breadcrumbs</a></li>
<li><a class="post-section-overview" href="#heading-types-of-breadcrumb-navigation">Types of Breadcrumb Navigation</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-breadcrumb-component-in-react">How to Build the Breadcrumb Component in React</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-the-breadcrumb-component-structure">How to Create the Breadcrumb Component Structure</a><br>– <a class="post-section-overview" href="#heading-location-based-breadcrumbs">Location-Based Breadcrumbs</a><br>– <a class="post-section-overview" href="#heading-path-based-breadcrumbs">Path-Based Breadcrumbs</a><br>– <a class="post-section-overview" href="#heading-attribute-based-breadcrumbs">Attribute-Based Breadcrumbs</a></li>
<li><a class="post-section-overview" href="#heading-best-practices-for-breadcrumbs-in-react">Best Practices for Breadcrumbs in React</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-understanding-breadcrumbs">Understanding Breadcrumbs</h2>
<p>Before we venture deeper into the intricacies of breadcrumbs, let's set the scene. Imagine the classic tale of <a target="_blank" href="https://en.wikipedia.org/wiki/Hansel_and_Gretel">Hansel and Gretel</a>, where they leave a trail of breadcrumbs to find their way back home through the dense forest. </p>
<p>In the digital realm, breadcrumbs serve a similar purpose, albeit with a twist.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Hansel-And-Gretel-2.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Hansel And Gretel</em></p>
<p>Breadcrumbs, in the context of web navigation, are a series of hierarchical links typically displayed at the top of a webpage. These links reflect the user's path from the homepage to the current page, allowing them to retrace their steps or navigate to higher-level pages.</p>
<p>These navigation aids have a fascinating history and a crucial role in guiding users through a digital space.</p>
<p>A typical example of what this component looks like is shown below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Breadcrumb-example.png" alt="Image" width="600" height="400" loading="lazy">
<em>Breadcrumb example: Home Page &gt; Products Page &gt; Single Product Page (current page)</em></p>
<h3 id="heading-types-of-breadcrumb-navigation">Types of Breadcrumb Navigation</h3>
<ul>
<li><strong>Location-based Breadcrumbs</strong>: Like landmarks in a forest, location-based breadcrumbs show users where they are within the website's hierarchy. They show the current page's position relative to other pages on the site.</li>
<li><strong>Path-based Breadcrumbs</strong>: Like retracing your steps in the forest, path-based breadcrumbs display the user's journey through the website. They show the sequence of pages visited, helping users understand how they arrived at the current page.</li>
<li><strong>Attribute-based Breadcrumbs</strong>: These breadcrumbs highlight specific attributes or characteristics of the current page. They offer more context to the user's navigation, akin to discovering unique features along a trail.</li>
</ul>
<h2 id="heading-how-to-build-the-breadcrumb-component-in-react">How to Build the Breadcrumb Component in React</h2>
<p>The first step in this section involves creating a React environment. Before you begin, make sure to install <a target="_blank" href="https://nodejs.org/en/download">Node.js</a> on your computer if you don't have it already.</p>
<h3 id="heading-how-to-set-up-a-react-environment">How to Set Up a React Environment</h3>
<p>After installing Node.js, use <a target="_blank" href="https://vitejs.dev/guide/">Vite</a> (a modern build tool for React projects) to create a new React project. In your local terminal, run the command:</p>
<pre><code class="lang-bash">npm create vite@latest
</code></pre>
<p>Select React as your framework and your preferred variant.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Setting-up-a-react-environment-with-Vite.png" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up a react environment with Vite</em></p>
<p>To install the necessary packages, run <code>npm install</code> and open it in your IDE.</p>
<p>Finally, clear the boilerplate code and start up your server using the command <code>npm run dev</code>.</p>
<p>This project will use Tailwind for styling. To get that set up, run the following command:</p>
<pre><code class="lang-bash">npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre>
<p>After this command, a <code>tailwind.config.js</code> file will be created. Head into the config file, delete its content, and paste this in there instead:</p>
<pre><code class="lang-js"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Then, open your <code>index.css</code> file and paste the style configs (preferably at the top):</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>Finally, restart your dev server to get access to Tailwind.</p>
<h2 id="heading-how-to-create-the-breadcrumb-component-structure">How to Create the Breadcrumb Component Structure</h2>
<p>Rather than building one breadcrumb component, we’re going to build all three types mentioned above so you can see how they work.</p>
<h3 id="heading-location-based-breadcrumbs">Location-Based Breadcrumbs</h3>
<p>This type is the most basic type of breadcrumb that involves showing all the routes readily available for the user to navigate.</p>
<p>To begin, start by creating a Breadcrumb file and pasting in these styles:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> SlashImg <span class="hljs-keyword">from</span> <span class="hljs-string">"./assets/slash.png"</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">Breadcrumb</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white "</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" flex border p-2 gap-6 text-xl text-[#2E4053] items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" cursor-pointer hover:bg-[#E8DAEF] p-4 rounded-md"</span>&gt;</span>
          Home
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" cursor-pointer hover:bg-[#E8DAEF] p-4 rounded-md transition-all duration-300"</span>&gt;</span>
          Products
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" cursor-pointer hover:bg-[#E8DAEF] p-4 rounded-md transition-all duration-300"</span>&gt;</span>
          About
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" cursor-pointer hover:bg-[#E8DAEF] p-4 rounded-md transition-all duration-300"</span>&gt;</span>
          FAQ
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>Then import this file into a <code>Home</code> component which you also need to create:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Breadcrumb <span class="hljs-keyword">from</span> <span class="hljs-string">"./Breadcrumb"</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;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" h-[100dvh] bg-gray-200"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center gap-8 "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" text-4xl text-[#2E4053 ] mt-20"</span>&gt;</span>
          My Breadcrumb Component 🍞
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Breadcrumb</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>
<p>At the moment, your component looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Breadcrumb-after-rendering-on-UI.png" alt="Image" width="600" height="400" loading="lazy">
<em>Breadcrumb after rendering on UI</em></p>
<p>To perform navigation functionalities with this component, start by installing <a target="_blank" href="https://www.npmjs.com/package/react-router-dom">React Router</a> (a widely used library for managing navigation and routing in React applications).</p>
<pre><code class="lang-bash">npm i react-router-dom
</code></pre>
<p>Then create the routes in your App component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { BrowserRouter, Navigate, Route, Routes } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">import</span> About <span class="hljs-keyword">from</span> <span class="hljs-string">"./About"</span>;
<span class="hljs-keyword">import</span> FAQ <span class="hljs-keyword">from</span> <span class="hljs-string">"./FAQ"</span>;
<span class="hljs-keyword">import</span> Home <span class="hljs-keyword">from</span> <span class="hljs-string">"./Home"</span>;
<span class="hljs-keyword">import</span> Homepage <span class="hljs-keyword">from</span> <span class="hljs-string">"./Homepage"</span>;
<span class="hljs-keyword">import</span> Products <span class="hljs-keyword">from</span> <span class="hljs-string">"./Products"</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">App</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">BrowserRouter</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">index</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Navigate</span> <span class="hljs-attr">replace</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"home"</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>}&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"home"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Homepage</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"products"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Products</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"about"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">About</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"faq"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">FAQ</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span></span>
  );
}
</code></pre>
<p>The code block above is a configuration for client-side routing in a React application using React Router v6. It sets up a <code>BrowserRouter</code> to handle dynamic routing and defines a series of Route components within Routes to map URL paths to React components.</p>
<ul>
<li><code>BrowserRouter</code> is a router implementation that uses the HTML5 history API to keep the UI in sync with the URL.</li>
<li><code>Navigate</code> redirects users to a specific route. In this case, it redirects from the index route to <code>/home</code>.</li>
<li><code>Route</code> components define a mapping between a path and a component. The <code>element</code> prop specifies what to render when the path matches the current URL.</li>
<li>The <code>path="/" element={&lt;Home /&gt;}</code> route is a nested route that serves as a layout for its child routes. It renders the Home component when the URL is <code>/</code>. Nested inside the <code>Home</code> route are routes for <code>home</code>, <code>products</code>, <code>about</code>, and <code>faq</code>, each rendering their respective components when their path matches the URL.</li>
</ul>
<p>Next, head over to your Breadcrumb component and change the list elements to <code>Link</code> elements (imported from React Router) to aid routing between routes.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> SlashImg <span class="hljs-keyword">from</span> <span class="hljs-string">"./assets/slash.png"</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">Breadcrumb</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white "</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" flex border p-2 gap-6 text-xl text-[#2E4053] items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">home</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">" cursor-pointer hover:bg-[#E8DAEF] p-4 rounded-md"</span>&gt;</span>
          Home
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">products</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">" cursor-pointer hover:bg-[#E8DAEF] p-4 rounded-md transition-all duration-300"</span>&gt;</span>
          Products
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">about</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">" cursor-pointer hover:bg-[#E8DAEF] p-4 rounded-md transition-all duration-300"</span>&gt;</span>
          About
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">faq</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">" cursor-pointer hover:bg-[#E8DAEF] p-4 rounded-md transition-all duration-300"</span>&gt;</span>
          FAQ
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>Then use the <code>Outlet</code> component provided by React Router to display the content of each route in the <code>Home</code> component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Breadcrumb <span class="hljs-keyword">from</span> <span class="hljs-string">"./Breadcrumb"</span>;
<span class="hljs-keyword">import</span> { Outlet } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</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;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" h-[100dvh] bg-gray-200"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center gap-8 "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" text-4xl text-[#2E4053 ] mt-20"</span>&gt;</span>
          My Breadcrumb Component 🍞
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Breadcrumb</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Outlet</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>
<p>Testing out your component in the browser now gives the following result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Routing-with-the-Breadcrumb-component.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Routing with the Breadcrumb component</em></p>
<p>With this, your location-based breadcrumbs are functional, but we can take it a step further. To improve the UX, we can add an active class to the currently active route, creating a visual indicator of where the user is at every point in time.</p>
<p>Start by extracting the current location of the user in the Breadcrumb component:</p>
<pre><code class="lang-jsx">  <span class="hljs-keyword">const</span> location = useLocation();
</code></pre>
<p>Then use the pathname property to add an active class to each link:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Link, useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">import</span> SlashImg <span class="hljs-keyword">from</span> <span class="hljs-string">"./assets/slash.png"</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">Breadcrumb</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> location = useLocation();
  <span class="hljs-built_in">console</span>.log(location.pathname);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white "</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" flex border p-2 gap-6 text-xl text-[#2E4053] items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">home</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">E8DAEF</span>] <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> ${
            <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/home"</span> &amp;&amp; "<span class="hljs-attr">bg-</span>[#<span class="hljs-attr">b572d6</span>] <span class="hljs-attr">text-white</span>"
          }`}&gt;</span>
          Home
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">products</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">E8DAEF</span>] <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> ${
            <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/products"</span> &amp;&amp; "<span class="hljs-attr">bg-</span>[#<span class="hljs-attr">b572d6</span>] <span class="hljs-attr">text-white</span>"
          }`}&gt;</span>
          Products
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">about</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">E8DAEF</span>] <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> ${
            <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/about"</span> &amp;&amp; "<span class="hljs-attr">bg-</span>[#<span class="hljs-attr">b572d6</span>] <span class="hljs-attr">text-white</span>"
          }`}&gt;</span>
          About
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SlashImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">faq</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">E8DAEF</span>] <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> ${
            <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/faq"</span> &amp;&amp; "<span class="hljs-attr">bg-</span>[#<span class="hljs-attr">b572d6</span>] <span class="hljs-attr">text-white</span>"
          }`}&gt;</span>
          FAQ
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This now gives the following result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Routing-with-the-Breadcrumb-component-after-adding-an-active-class.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Routing with the Breadcrumb component after adding an active class</em></p>
<p>Tasty! 🍩</p>
<h3 id="heading-path-based-breadcrumbs">Path-Based Breadcrumbs</h3>
<p>This breadcrumb type uses the pattern of progressive reveal to better guide the users on where they are based on their actions.</p>
<p>Here we’re going to create two routes and move from the first to the third (Home to Single product page).</p>
<p>Start by modifying your <code>Home</code> component a bit.</p>
<pre><code class="lang-jsx"><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;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" h-[100dvh] bg-gray-200"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center gap-8 "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" text-4xl text-[#2E4053 ] mt-20"</span>&gt;</span>
          My Breadcrumb Component 🍞
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Breadcrumb</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Outlet</span> /&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-4 p-2 "</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"products"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" rounded-md p-2 bg-[#777] text-white"</span>&gt;</span>
            Products
          <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
            <span class="hljs-attr">to</span>=<span class="hljs-string">"products/1"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">" rounded-md p-2 bg-[#777] text-white"</span>&gt;</span>
            Single Product
          <span class="hljs-tag">&lt;/<span class="hljs-name">Link</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>&gt;</span></span>
  );
}
</code></pre>
<p>The changes include creating a single product page which we'll route to later.</p>
<p>Then make a nested route path for the page in the App component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { BrowserRouter, Navigate, Route, Routes } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">import</span> Home <span class="hljs-keyword">from</span> <span class="hljs-string">"./Home"</span>;
<span class="hljs-keyword">import</span> Homepage <span class="hljs-keyword">from</span> <span class="hljs-string">"./Homepage"</span>;
<span class="hljs-keyword">import</span> Products <span class="hljs-keyword">from</span> <span class="hljs-string">"./Products"</span>;
<span class="hljs-keyword">import</span> SingleProduct <span class="hljs-keyword">from</span> <span class="hljs-string">"./SingleProduct"</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">App</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">BrowserRouter</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">index</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Navigate</span> <span class="hljs-attr">replace</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"home"</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>}&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"home"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Homepage</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"products"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Products</span> /&gt;</span>}&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">":productId"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">SingleProduct</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span></span>
  );
}
</code></pre>
<p>For the already existing Products page, add these styles and changes:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Outlet } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</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">Products</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-[#EDBB99] p-2 w-96 h-96 flex flex-col items-center"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Products Page<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Outlet</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Page-after-creating-the-Single-Product-page-without-any-routing-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Page after creating the Single Product page without any routing</em></p>
<p>Finally, modify your breadcrumb component to display the routes when you route to them from the home page.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Link, useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">import</span> RightArrowImg <span class="hljs-keyword">from</span> <span class="hljs-string">"./assets/right-icon.png"</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">Breadcrumb</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> location = useLocation();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white "</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" flex border p-2 gap-6 text-xl text-[#2E4053] items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">home</span>"}
          <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">E8DAEF</span>] <span class="hljs-attr">hover:text-black</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> ${
            <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/home"</span> &amp;&amp; "<span class="hljs-attr">bg-</span>[#<span class="hljs-attr">b572d6</span>] <span class="hljs-attr">text-white</span>"
          }`}&gt;</span>
          Home
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        {location.pathname.includes("/products") &amp;&amp; (
          <span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{RightArrowImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
              <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">products</span>"}
              <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>` <span class="hljs-attr">hover:text-black</span> <span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">E8DAEF</span>] <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> ${
                <span class="hljs-attr">location.pathname.includes</span>("/<span class="hljs-attr">products</span>") &amp;&amp;
                " <span class="hljs-attr">bg-</span>[#<span class="hljs-attr">b572d6</span>] <span class="hljs-attr">text-white</span>"
              } ${
                <span class="hljs-attr">location.pathname.includes</span>("/<span class="hljs-attr">products</span>/") &amp;&amp;
                " <span class="hljs-attr">bg-</span>[#<span class="hljs-attr">E8DAEF</span>] <span class="hljs-attr">text-black</span>"
              }`}&gt;</span>
              Products
            <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
          <span class="hljs-tag">&lt;/&gt;</span>
        )}
        {location.pathname.includes(`/products/`) &amp;&amp; (
          <span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{RightArrowImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
              <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">products</span>"}
              <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">hover:text-black</span>  <span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">E8DAEF</span>] <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> ${
                <span class="hljs-attr">location.pathname.includes</span>("/<span class="hljs-attr">products</span>") &amp;&amp;
                "<span class="hljs-attr">bg-</span>[#<span class="hljs-attr">b572d6</span>] <span class="hljs-attr">text-white</span>"
              }`}&gt;</span>
              Single Product
            <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
          <span class="hljs-tag">&lt;/&gt;</span></span>
        )}
      &lt;/ul&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>In the code above, we’re displaying more breadcrumbs based on the route we’re in and applying styles to reflect the route changes.</p>
<p>Testing our component now gives the following result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Routing-with-the-Breadcrumb-component-after-adding-a-nested-route.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Routing with the Breadcrumb component after adding a nested route</em></p>
<p>Sweet! 🍬</p>
<h3 id="heading-attribute-based-breadcrumbs">Attribute-Based Breadcrumbs</h3>
<p>Attribute-based breadcrumbs focus on highlighting specific attributes or characteristics of the current page, such as tags, categories, or any other relevant metadata.</p>
<p>Instead of simply showing the user's path through the website hierarchy, they provide additional context that can aid in navigation and understanding. </p>
<p>A common use case for them is on E-commerce sites where you go through multiple items and filter through multiple product properties to find your desired product.</p>
<p>To begin, our app component is going to look drastically different with no routing done.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Products <span class="hljs-keyword">from</span> <span class="hljs-string">"./Products"</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">App</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> <span class="hljs-attr">className</span>=<span class="hljs-string">" h-[100dvh] bg-[#EDBB99]"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Products</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>Then add this JSX to your <code>Products</code> component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Breadcrumb <span class="hljs-keyword">from</span> <span class="hljs-string">"./Breadcrumb"</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">Products</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> dogsArray = [
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"S"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"white"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/small-white-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Gigi"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">1</span>,
    },
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"M"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"white"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/medium-white-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Tom"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">2</span>,
    },
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"L"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"white"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/big-white-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Jake"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">3</span>,
    },
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"S"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"black"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/small-black-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Hill"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">1</span>,
    },
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"M"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"black"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/medium-black-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Jack"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">2</span>,
    },
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"L"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"black"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/big-black-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Jones"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">3</span>,
    },
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"S"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"brown"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/small-brown-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Herbert"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">1</span>,
    },
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"M"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"brown"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/medium-brown-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Coco"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">2</span>,
    },
    {
      <span class="hljs-attr">size</span>: <span class="hljs-string">"L"</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">"brown"</span>,
      <span class="hljs-attr">image</span>: <span class="hljs-string">"/big-brown-dog.jpg"</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Benny"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">3</span>,
    },
  ];


  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center p-2"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-4"</span>&gt;</span>Adopt Page<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Breadcrumb</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative grid grid-cols-5 gap-6"</span>&gt;</span>
          {dogsArray.map((dog) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{dog.name}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" w-[225px] rounded-md overflow-hidden"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full "</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{dog.image}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid items-center grid-cols-2 gap-2 mt-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-2"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Name:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white bg-orange-900 border rounded-[4px] p-1.5 min-w-14"</span>&gt;</span>
                    {dog.name}
                  <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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Size:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white min-w-14"</span>&gt;</span>{dog.size}<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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Color:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white capitalize min-w-14"</span>&gt;</span>
                    {dog.color}
                  <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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Age:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white min-w-14"</span>&gt;</span>
                    {`${dog.age + " " + "year"}${dog.age &gt; 1 ? "s" : ""}`}
                  <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>&gt;</span>
          ))}

          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute bottom-0 left-0 p-1 translate-y-[110%]"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-2"</span>&gt;</span>Filter by<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center mb-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12"</span>&gt;</span>Size:<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-2</span> <span class="hljs-attr">text-center</span> <span class="hljs-attr">bg-white</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">min-w-14</span> `}&gt;</span>
                  All
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-2</span> <span class="hljs-attr">text-center</span> <span class="hljs-attr">bg-white</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">min-w-14</span> `}&gt;</span>
                  S
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-2</span> <span class="hljs-attr">text-center</span> <span class="hljs-attr">bg-white</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">min-w-14</span> `}&gt;</span>
                  M
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-2</span> <span class="hljs-attr">text-center</span> <span class="hljs-attr">bg-white</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">min-w-14</span> `}&gt;</span>
                  L
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</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">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12"</span>&gt;</span>Color:<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-2</span> <span class="hljs-attr">text-center</span> <span class="hljs-attr">bg-white</span>  <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">min-w-14</span> `}&gt;</span>
                  All
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-2</span> <span class="hljs-attr">text-center</span> <span class="hljs-attr">bg-white</span>  <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">min-w-14</span>`}&gt;</span>
                  White
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-2</span> <span class="hljs-attr">text-center</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">bg-white</span> <span class="hljs-attr">min-w-14</span> `}&gt;</span>
                  Brown
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-2</span> <span class="hljs-attr">text-center</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">bg-white</span>  <span class="hljs-attr">min-w-14</span> `}&gt;</span>
                  Black
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</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>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This JSX uses dummy dog data to create a template and style it with tailwind.</p>
<p>At the moment, your app should look like this;</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Page-after-looping-over-the-dummy-dog-data.png" alt="Image" width="600" height="400" loading="lazy">
<em>Page after looping over the dummy dog data</em></p>
<p>To implement the attribute-based breadcrumbs, start by creating two states for the attributes you want to filter by:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> [sizeFilter, setSizeFilter] = useState(<span class="hljs-literal">null</span>);
<span class="hljs-keyword">const</span> [colorFilter, setColorFilter] = useState(<span class="hljs-literal">null</span>);
</code></pre>
<p>Then create a function to filter based on the value passed in:</p>
<pre><code class="lang-jsx">  <span class="hljs-keyword">const</span> filteredDogs = dogsArray.filter(<span class="hljs-function">(<span class="hljs-params">dog</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (sizeFilter &amp;&amp; dog.size !== sizeFilter) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;

    <span class="hljs-keyword">if</span> (colorFilter &amp;&amp; dog.color !== colorFilter) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;

    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
  });
</code></pre>
<p>After that, change the array you used used to create the JSX to the array returned from the function:</p>
<pre><code class="lang-jsx"> {filteredDogs.map(<span class="hljs-function">(<span class="hljs-params">dog</span>) =&gt;</span> (
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{dog.name}</span>&gt;</span></span>
</code></pre>
<p>Finally, use the setter function to pass in the values you want to filter by:</p>
<pre><code class="lang-jsx">&lt;main&gt;
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative grid grid-cols-5 gap-6"</span>&gt;</span>
          {filteredDogs.map((dog) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{dog.name}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" w-[225px] rounded-md overflow-hidden"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full "</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{dog.image}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid items-center grid-cols-2 gap-2 mt-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-2"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Name:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white bg-orange-900 border rounded-[4px] p-1.5 min-w-14"</span>&gt;</span>
                    {dog.name}
                  <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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Size:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white min-w-14"</span>&gt;</span>{dog.size}<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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Color:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white capitalize min-w-14"</span>&gt;</span>
                    {dog.color}
                  <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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Age:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white min-w-14"</span>&gt;</span>
                    {`${dog.age + " " + "year"}${dog.age &gt; 1 ? "s" : ""}`}
                  <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>&gt;</span>
          ))}

          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute bottom-0 left-0 p-1 translate-y-[110%]"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-2"</span>&gt;</span>Filter by<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center mb-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12"</span>&gt;</span>Size:<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  // <span class="hljs-attr">Reset</span> <span class="hljs-attr">size</span> <span class="hljs-attr">state</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSizeFilter(null)}
                  className={`p-2 text-center bg-white rounded-md min-w-14 `}&gt;
                  All
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  // <span class="hljs-attr">Set</span> <span class="hljs-attr">filter</span> <span class="hljs-attr">to</span> <span class="hljs-attr">small</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSizeFilter("S")}
                  className={`p-2 text-center bg-white rounded-md min-w-14 `}&gt;
                  S
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  // <span class="hljs-attr">Set</span> <span class="hljs-attr">filter</span> <span class="hljs-attr">to</span> <span class="hljs-attr">medium</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSizeFilter("M")}
                  className={`p-2 text-center bg-white rounded-md min-w-14 `}&gt;
                  M
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  // <span class="hljs-attr">Set</span> <span class="hljs-attr">filter</span> <span class="hljs-attr">to</span> <span class="hljs-attr">large</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSizeFilter("L")}
                  className={`p-2 text-center bg-white rounded-md min-w-14 `}&gt;
                  L
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</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">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12"</span>&gt;</span>Color:<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  // <span class="hljs-attr">Reset</span> <span class="hljs-attr">color</span> <span class="hljs-attr">state</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setColorFilter(null)}
                  className={`p-2 text-center bg-white  rounded-md min-w-14 `}&gt;
                  All
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  // <span class="hljs-attr">Set</span> <span class="hljs-attr">color</span> <span class="hljs-attr">to</span> <span class="hljs-attr">white</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setColorFilter("white")}
                  className={`p-2 text-center bg-white  rounded-md min-w-14 `}&gt;
                  White
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  // <span class="hljs-attr">Set</span> <span class="hljs-attr">color</span> <span class="hljs-attr">to</span> <span class="hljs-attr">brown</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setColorFilter("brown")}
                  className={`p-2 text-center rounded-md bg-white min-w-14 `}&gt;
                  Brown
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  // <span class="hljs-attr">Set</span> <span class="hljs-attr">color</span> <span class="hljs-attr">to</span> <span class="hljs-attr">black</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setColorFilter("black")}
                  className={`p-2 text-center rounded-md bg-white  min-w-14 `}&gt;
                  Black
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</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>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      &lt;/main&gt;
</code></pre>
<p>Testing your component now gives the following result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Filtering-by-properties.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Filtering by properties working</em></p>
<p>Exquisite!🍦</p>
<p>To add our breadcrumb functionality, pass in the filter props to the component like this:</p>
<pre><code class="lang-jsx">&lt;Breadcrumb sizeFilter={sizeFilter} colorFilter={colorFilter} /&gt;
</code></pre>
<p>Then use those props to render the headers:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> RightArrowImg <span class="hljs-keyword">from</span> <span class="hljs-string">"./assets/right-icon.png"</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">Breadcrumb</span>(<span class="hljs-params">{ sizeFilter, colorFilter }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-4 bg-gray-200 rounded-md "</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"   flex items-center  text-xl text-[#2E4053] text-left"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> `}&gt;</span>All<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>

        {sizeFilter &amp;&amp; (
          <span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{RightArrowImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">capitalize</span>`}&gt;</span>
              {sizeFilter}
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;/&gt;</span>
        )}
        {colorFilter &amp;&amp; (
          <span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{RightArrowImg}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-5 h-5 "</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">capitalize</span>`}&gt;</span>
              {colorFilter}
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;/&gt;</span></span>
        )}
      &lt;/ul&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Before we see the final result, let’s add indicators of what filter props are currently active:</p>
<pre><code class="lang-jsx">&lt;main&gt;
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative grid grid-cols-5 gap-6"</span>&gt;</span>
          {filteredDogs.map((dog) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{dog.name}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" w-[225px] rounded-md overflow-hidden"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full "</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{dog.image}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid items-center grid-cols-2 gap-2 mt-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-2"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Name:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white bg-orange-900 border rounded-[4px] p-1.5 min-w-14"</span>&gt;</span>
                    {dog.name}
                  <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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Size:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white min-w-14"</span>&gt;</span>{dog.size}<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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Color:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white capitalize min-w-14"</span>&gt;</span>
                    {dog.color}
                  <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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 "</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Age:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-white min-w-14"</span>&gt;</span>
                    {`${dog.age + " " + "year"}${dog.age &gt; 1 ? "s" : ""}`}
                  <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>&gt;</span>
          ))}

          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute bottom-0 left-0 p-1 translate-y-[110%]"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-2"</span>&gt;</span>Filter by<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center mb-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12"</span>&gt;</span>Size:<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSizeFilter(null)}
                  className={`p-2 text-center  rounded-md min-w-14 ${
                    // Dynamic background color add
                    sizeFilter === null
                      ? "bg-orange-900 text-white"
                      : "bg-white text-black "
                  }`}&gt;
                  All
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSizeFilter("S")}
                  className={`p-2 text-center  rounded-md min-w-14 ${
                    // Dynamic background color add
                    sizeFilter === "S"
                      ? "bg-orange-900 text-white"
                      : "bg-white text-black "
                  }`}&gt;
                  S
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSizeFilter("M")}
                  className={`p-2 text-center  rounded-md min-w-14 ${
                    // Dynamic background color add
                    sizeFilter === "M"
                      ? "bg-orange-900 text-white"
                      : "bg-white text-black "
                  }`}&gt;
                  M
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSizeFilter("L")}
                  className={`p-2 text-center  rounded-md min-w-14 ${
                    // Dynamic background color add
                    sizeFilter === "L"
                      ? "bg-orange-900 text-white"
                      : "bg-white text-black "
                  }`}&gt;
                  L
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</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">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12"</span>&gt;</span>Color:<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setColorFilter(null)}
                  className={`p-2 text-center   rounded-md min-w-14 ${
                    // Dynamic background color add
                    colorFilter === null
                      ? "bg-orange-900 text-white"
                      : "bg-white text-black "
                  }`}&gt;
                  All
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setColorFilter("white")}
                  className={`p-2 text-center   rounded-md min-w-14 ${
                    // Dynamic background color add
                    colorFilter === "white"
                      ? "bg-orange-900 text-white"
                      : "bg-white text-black "
                  }`}&gt;
                  White
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setColorFilter("brown")}
                  className={`p-2 text-center rounded-md   min-w-14 ${
                    // Dynamic background color add
                    colorFilter === "brown"
                      ? "bg-orange-900 text-white"
                      : " bg-white text-black "
                  }`}&gt;
                  Brown
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setColorFilter("black")}
                  className={`p-2 text-center rounded-md    min-w-14 ${
                    // Dynamic background color add
                    colorFilter === "black"
                      ? "bg-orange-900 text-white"
                      : " bg-white text-black"
                  }`}&gt;
                  Black
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</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>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      &lt;/main&gt;
</code></pre>
<p>A final test on your component now gives this result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Filtering-by-properties-tested.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Filtering via properties and showing it in the breadcrumb</em></p>
<p>And voilà! Your component filters perfectly, and also shows a useful breadcrumb to help users know what properties they’ve filtered by.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/bruce-almighty-jim-carrey.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Bruce Almighty Jim Carrey beautiful gif</em></p>
<h2 id="heading-best-practices-for-breadcrumbs-in-react">Best Practices for Breadcrumbs in React</h2>
<p>When implementing breadcrumbs in React, it's crucial to follow certain best practices to ensure a seamless user experience. Here are some key points to consider:</p>
<ol>
<li><strong>Consistency with React Routing</strong>: Breadcrumbs should align with the application's routing structure. You can do this by implementing dynamic breadcrumbs using a good routing library (React Router). By defining routes and generating an array of breadcrumbs based on the user's current route, you can ensure that the breadcrumb trail reflects the user's navigation path.</li>
<li><strong>Breadth and Depth of Breadcrumb Trails</strong>: Breadcrumb trails should represent the user's location within the application. This includes using a separator, such as a slash ("/"), to distinguish between different parts of the breadcrumb trail.</li>
<li><strong>Naming and Navigation</strong>: Breadcrumbs should be easy to understand and navigate. This involves using clear and descriptive names for each breadcrumb and ensuring that each breadcrumb link is clickable, leading the user to the appropriate page.</li>
<li><strong>Ensuring Accessibility</strong>: Breadcrumbs should be accessible to all users. This can be achieved by using the <code>aria-label</code> attribute to identify the breadcrumb trail as a navigation landmark. This makes it easier for users with assistive technologies to locate and navigate the breadcrumb trail.</li>
<li><strong>Customization and Ease-of-Use</strong>: When using a component for creating breadcrumbs, consider its customization options and ease of use. Look for components that provide useful defaults and allow for easy customization of texts, links, and separators.</li>
</ol>
<p>By adhering to these best practices, you can create effective and user-friendly breadcrumbs in your React applications. </p>
<p>Here are links to the repositories on GitHub:</p>
<ul>
<li><a target="_blank" href="https://github.com/Daiveedjay/React-breadcrumb-article-location-based">Location-based</a></li>
<li><a target="_blank" href="https://github.com/Daiveedjay/React-breadcrumb-article-path-based">Path-based</a></li>
<li><a target="_blank" href="https://github.com/Daiveedjay/React-breadcrumb-article-attribute-based">Attribute-based</a></li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Implementing breadcrumbs in React applications not only provides a navigational aid but also contributes to a seamless and intuitive user experience. Adhering to best practices enhances the usability and accessibility of applications.</p>
<p>Just as the aroma of freshly baked bread entices hungry people to come visit the bakery, a well-structured breadcrumb trail can entice users to explore and navigate an application with ease, ultimately improving user navigation and experience. And that's how the cookie crumbles, leaving a trail of delightful navigation in its wake.</p>
<h3 id="heading-contact-information">Contact Information</h3>
<p>Want to connect or contact me? Feel free to hit me up on the following:</p>
<ul>
<li>Twitter / X: <a target="_blank" href="https://twitter.com/JajaDavid8">@jajadavid8</a></li>
<li>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/david-jaja-8084251b4/">David Jaja</a></li>
<li>Email: Jajadavidjid@gmail.com  </li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Tailwind CSS in Spanish – Full Course ]]>
                </title>
                <description>
                    <![CDATA[ Tailwind is a CSS framework with pre-defined classes that you can combine to build modern and responsive websites. We just published a course on the freeCodeCamp.org Spanish YouTube channel that will teach you Tailwind CSS. You will go from the basic... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-tailwind-css-in-spanish-full-course/</link>
                <guid isPermaLink="false">66b1f8533073482cdedea6b7</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Español ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Estefania Cassingena Navone ]]>
                </dc:creator>
                <pubDate>Thu, 07 Mar 2024 15:30:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/Version-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Tailwind is a CSS framework with pre-defined classes that you can combine to build modern and responsive websites.</p>
<p>We just published a course on the <a target="_blank" href="https://www.youtube.com/freecodecampespanol">freeCodeCamp.org Spanish YouTube channel</a> that will teach you Tailwind CSS. You will go from the basics to actually building your own real-world projects. By the end of the course, you will be able to design and implement responsive websites with Tailwind CSS.</p>
<p>If you have Spanish-speaking friends, you are welcome to share the <strong><a target="_blank" href="https://www.freecodecamp.org/espanol/news/aprende-tailwind-css-curso-completo-con-proyectos/">Spanish version of this article</a></strong> with them.</p>
<p>This course was created by David Ruiz. David is a Front-End Web Developer with more than 5 years of experience. He shares projects and courses on Tailwind CSS, HTML, CSS, JavaScript, and React on his YouTube channel.</p>
<p>He will teach you how to create and implement modern and responsive web designs step by step with the pre-defined Tailwind CSS classes.</p>
<p>Before we start diving into Tailwind CSS with David's course, let's have a quick introduction. We'll talk about what Tailwind is and why it's useful to learn if your goal is to design and create modern and responsive websites quickly and efficiently.</p>
<h2 id="heading-what-is-tailwind-css"><strong>What is Tailwind CSS?</strong></h2>
<p>Tailwind CSS is a CSS framework with pre-defined classes that you can use for styling HTML elements.</p>
<p>With Tailwind, you can add CSS classes to your HTML elements directly within the HTML file and see how the pre-defined styles are applied immediately.</p>
<p><strong>💡 Tip:</strong> Yes! With Tailwind CSS, you write your CSS within the HTML file. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-23-at-8.47.14-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Tailwind CSS (Official Website).</em></p>
<h2 id="heading-why-choose-tailwind-css"><strong>Why Choose Tailwind CSS?</strong></h2>
<p>Learning Tailwind CSS can bring many advantages to your web development workflow, including:</p>
<ul>
<li><strong>Faster development</strong> because you can use pre-defined CSS classes instead of defining the CSS styles yourself.</li>
<li><strong>Responsive design</strong> because these pre-defined CSS classes support variants for different screen sizes. You can develop responsive website directly from your HTML file, without additional media queries.</li>
<li><strong>Simpler CSS files</strong> because you will not be defining all the styles yourself in custom CSS files.</li>
<li><strong>Consistency</strong> because you and your colleagues will be using the same CSS classes with the same naming conventions throughout the project.</li>
<li><strong>Customization features</strong> because you can customize and configure many of these pre-defined CSS classes to fit your needs.</li>
</ul>
<p>Sounds great, right? 🙂</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/frame-5.png" alt="Image" width="600" height="400" loading="lazy">
<em>Styling a Form with Tailwind CSS (Course Screenshot).</em></p>
<p>By learning Tailwind CSS, you will be acquiring valuable skills for your web development career.</p>
<p>If you are ready to start incorporating Tailwind CSS into your workflow, let's check out the course content.</p>
<h2 id="heading-tailwind-css-course-in-spanish"><strong>Tailwind CSS Course in Spanish</strong></h2>
<p>During the course, you will learn the following aspects of Tailwind CSS:</p>
<ul>
<li>How to install and configure Tailwind CSS</li>
<li>Colors</li>
<li>Size</li>
<li>States</li>
<li>Pseudo-classes</li>
<li>Responsive Web Design</li>
<li>CSS Flexbox with Tailwind CSS</li>
<li>CSS Grid with Tailwind CSS</li>
<li>And more!</li>
</ul>
<p>Plus, you will learn how to toggle dark mode with pre-defined styles from Tailwind CSS and you will build three full real projects:</p>
<ul>
<li>A notifications panel</li>
<li>A dashboard</li>
<li>A landing page</li>
</ul>
<p>If you are ready to learn Tailwind CSS, check out the course in Spanish on the <a target="_blank" href="https://www.youtube.com/freecodecampespanol">freeCodeCamp.org Spanish YouTube channel</a>:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/5HtRcMSO1Ro" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>✍️ Course created by David Ruiz.</p>
<ul>
<li>YouTube: <a target="_blank" href="https://www.youtube.com/CodingTube">@CodingTube</a></li>
<li>Twitter: <a target="_blank" href="https://twitter.com/CodingTube">@CodingTube</a></li>
<li>Página web: <a target="_blank" href="https://www.codingtube.dev/">https://www.codingtube.dev/</a></li>
<li>GitHub: <a target="_blank" href="https://github.com/Davichobits">@Davichobits</a></li>
<li>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/davidirc/">David Israel Ruiz Cabrera</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add Tailwind CSS to Your React Native Expo App ]]>
                </title>
                <description>
                    <![CDATA[ Tailwind CSS has been quite popular in the web development world due to its utility-first approach and seamless integration.  However, when developing mobile apps with React Native, integrating Tailwind CSS may be challenging. But guess what? Not any... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/tailwindcss-in-react-native-expo/</link>
                <guid isPermaLink="false">66b9ee776a5986e4892f960b</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React Native ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ John Caleb ]]>
                </dc:creator>
                <pubDate>Tue, 27 Feb 2024 09:22:11 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/FREECODE-CAMP-DEFAULT-1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Tailwind CSS has been quite popular in the web development world due to its utility-first approach and seamless integration. </p>
<p>However, when developing mobile apps with React Native, integrating Tailwind CSS may be challenging. But guess what? Not anymore. With the development of tools such as <a target="_blank" href="https://www.nativewind.dev/">NativeWind</a>, React Native developers can leverage Tailwind CSS power to design stunning and responsive mobile UIs easily.</p>
<p>In this tutorial, you'll learn the process of integrating Tailwind CSS to your React Native <a target="_blank" href="https://expo.io/">Expo</a> app using NativeWind. We'll also build a simple login screen with NativeWind.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><a class="post-section-overview" href="#heading-whats-nativewind">What's NativeWind?</a></li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-getting-started">Getting Started</a> </li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-new-expo-app">How to Create A New Expo App</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-nativewind">How to Install NativeWind</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-tailwind-css">How to Set Up Tailwind CSS</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-nativewind-with-babel">How to Configure NativeWind With Babel</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-with-nativewind">How to Style with NativeWind</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-a-simple-login-screen">How to Build A Simple Login Screen</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-whats-nativewind">What's NativeWind?</h2>
<p>NativeWind acts as a bridge between Tailwind CSS and React Native Expo, allowing developers to take advantage of Tailwind's utility-first approach in their mobile app development workflow. </p>
<p>NativeWind provides various benefits to developers, some of these benefits include:</p>
<ul>
<li><strong>Familiar Syntax</strong>: Developers that are familiar with Tailwind CSS can easily migrate to using NativeWind in their React Native projects, easing the learning curve.</li>
<li><strong>Consistent Styling:</strong> NativeWind ensures consistent styling across platforms by offering a single collection of components and services.</li>
<li><strong>Flexibility</strong>: NativeWind allows developers to easily adapt and extend styles to meet the app's design specifications.</li>
</ul>
<p>Overall, It provides a collection of components and tools that are very similar to Tailwind CSS, allowing developers to create shorter, more concise code while preserving flexibility and consistency across platforms.</p>
<blockquote>
<p>Tailwind makes writing code feel like I’m using a design tool - Didier Catz</p>
</blockquote>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Basic Understanding of React Native Expo and Tailwind CSS.</li>
<li>Node.js and npm (or yarn) installed.</li>
<li>Willingness to learn :)</li>
</ul>
<h2 id="heading-getting-started">Getting Started</h2>
<p>Before you dive into integrating Tailwind CSS into your React Native Expo app, you'll need to ensure that you have the necessary tools set up.</p>
<p>If you haven't already installed Expo and <a target="_blank" href="https://www.npmjs.com/package/expo-cli">expo-cli</a> globally, you can do so using npm or yarn:</p>
<pre><code class="lang-bash">npm install -g expo-cli
</code></pre>
<p>or </p>
<pre><code class="lang-bash">yarn global add expo-cli
</code></pre>
<h2 id="heading-how-to-create-a-new-expo-app">How to Create A New Expo App</h2>
<p>With expo-cli installed, you can now create a new React Native Expo project. </p>
<p>Navigate to the directory where you wish to create your project and open the terminal. You can do this by pressing <em>CTRL + `</em> on Visual Studio Code. Then execute this command in the terminal:</p>
<pre><code class="lang-bash">npx create-expo-app simpleproject
</code></pre>
<p>This command creates an expo project in your directory.</p>
<h2 id="heading-how-to-install-nativewind">How to Install NativeWind</h2>
<p>After creating your expo project, you can install NativeWind and its dependencies by running the following commands in your project's directory:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> simpleproject
npm i nativewind
npm i --dev tailwindcss@3.3.2
</code></pre>
<p>or</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> simpleproject
yarn add nativewind
yarn add --dev tailwindcss@3.3.2
</code></pre>
<p>Next, you'll need to create a <code>tailwind.config.js</code> file. To do this, run this command in your terminal:</p>
<pre><code class="lang-bash">npx tailwindcss init
</code></pre>
<p>This would result in a <code>tailwind.config.js</code> file in your project's root directory. </p>
<h2 id="heading-how-to-set-up-tailwind-css">How to Set Up Tailwind CSS</h2>
<p>To set up Tailwind CSS in your project, navigate to your <code>tailwind.config.js</code> file, and under <code>content</code>, enter the paths to your components. Your <code>tailwind.config.js</code> file would then look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./App.{js,jsx,ts,tsx}"</span>,
    <span class="hljs-string">"./&lt;custom directory&gt;/**/*.{js,jsx,ts,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
};
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/code-1.png" alt="A screenshot of tailwind.conf.js file after adding path to components" width="600" height="400" loading="lazy">
<em>tailwind.conf.js file after adding paths to components</em></p>
<p>In the above example, you can replace <code>&lt;custom directory&gt;</code> with your directory's real name.</p>
<h2 id="heading-how-to-configure-nativewind-with-babel">How to Configure NativeWind with Babel</h2>
<p>You'll also need to configure NativeWind with Babel. To do this, include the NativeWind plugin in your project's <code>babel.conf.js</code> file:</p>
<pre><code class="lang-javascript">plugins: [<span class="hljs-string">"nativewind/babel"</span>],
</code></pre>
<p>The <code>babel.conf.js</code> file would look like this after adding the NativeWind plugin:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">api</span>) </span>{
  api.cache(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">presets</span>: [<span class="hljs-string">"babel-preset-expo"</span>],
    <span class="hljs-attr">plugins</span>: [<span class="hljs-string">"nativewind/babel"</span>],
  };
};
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/code2-2.png" alt="babel.conf.js file after adding nativewind plugin" width="600" height="400" loading="lazy">
<em>babel.conf.js file after adding nativewind plugin</em></p>
<p>By including the NativeWind plugin in the Babel configuration file you ensure that NativeWind's functionality is properly incorporated into your project's JavaScript codebase.</p>
<p> 🎉With this, NativeWind has been successfully integrated into your Expo app. The next step is to begin styling the app with NativeWind.</p>
<h2 id="heading-how-to-style-with-nativewind">How to Style with NativeWind</h2>
<p>To begin styling with NativeWind, go to your project's <code>App.js</code> file or the component you would like to style, which would look like this by default:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { StatusBar } <span class="hljs-keyword">from</span> <span class="hljs-string">"expo-status-bar"</span>;
<span class="hljs-keyword">import</span> { StyleSheet, Text, View } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-native"</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">App</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">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.container}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>Open up App.js to start working on your app!<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StatusBar</span> <span class="hljs-attr">style</span>=<span class="hljs-string">'auto'</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">const</span> styles = StyleSheet.create({
  <span class="hljs-attr">container</span>: {
    <span class="hljs-attr">flex</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">backgroundColor</span>: <span class="hljs-string">"#fff"</span>,
    <span class="hljs-attr">alignItems</span>: <span class="hljs-string">"center"</span>,
    <span class="hljs-attr">justifyContent</span>: <span class="hljs-string">"center"</span>,
  },
});
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/appjs-default.png" alt="App.js boilerplate code" width="600" height="400" loading="lazy">
<em>App.js boilerplate code</em></p>
<p>Next, modify your component to get rid of any instances of <code>StyleSheet</code> abstraction. In this example, we'll modify the <code>App.js</code> code. After adjustments, we should have something like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { StatusBar } <span class="hljs-keyword">from</span> <span class="hljs-string">"expo-status-bar"</span>;
<span class="hljs-comment">// import { StyleSheet, Text, View } from "react-native";</span>
<span class="hljs-keyword">import</span> { Text, View } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-native"</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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// &lt;View style={styles.container}&gt;</span>
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex-1 justify-center items-center bg-white'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>Open up App.js to start working on your app!<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StatusBar</span> <span class="hljs-attr">style</span>=<span class="hljs-string">'auto'</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// const styles = StyleSheet.create({</span>
<span class="hljs-comment">//   container: {</span>
<span class="hljs-comment">//     flex: 1,</span>
<span class="hljs-comment">//     backgroundColor: "#fff",</span>
<span class="hljs-comment">//     alignItems: "center",</span>
<span class="hljs-comment">//     justifyContent: "center",</span>
<span class="hljs-comment">//   },</span>
<span class="hljs-comment">// });</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/newww.png" alt="App.js component after modification " width="600" height="400" loading="lazy">
<em>App.js component after modification</em></p>
<p>In the modified codeblock, we remove all occurrences of <code>StyleSheet</code> abstractions, including the import statement for <code>stylesheet</code> and the <code>StyleSheet.create</code> function, and we replace <code>style</code> with <code>className</code> in the <code>App.js</code> return function.</p>
<p>Having cleared that up, all you need to do is write Tailwind CSS classes into your app <code>className</code> to begin implementing Tailwind CSS in your application. You'll see this in a bit as we build a simple login screen with NativeWind.</p>
<h2 id="heading-how-to-build-a-simple-login-screen">How to Build A Simple Login Screen</h2>
<p>Now, let's dive into building a simple login screen using NativeWind. We'll continue with the initial setup in the <code>App.js</code> file and gradually add components to create the login UI.</p>
<p>First, let's replace the existing code in the <code>App.js</code> file with the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { StatusBar } <span class="hljs-keyword">from</span> <span class="hljs-string">"expo-status-bar"</span>;
<span class="hljs-keyword">import</span> { Text, View } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-native"</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">App</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">View</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex-1 justify-center items-center bg-white'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StatusBar</span> <span class="hljs-attr">style</span>=<span class="hljs-string">'auto'</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-center mt-3 text-2xl font-light text-orange-300'</span>&gt;</span>
        Login
      <span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
      {/* Additional components goes here */}
    <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span></span>
 );
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/firstt.png" alt="Starter code for login screen UI" width="600" height="400" loading="lazy">
<em>Starter code for login screen UI</em></p>
<p>The code above imports the essential components from React Native and Expo. We then use a <code>View</code> component to define the structure of our login screen, which is styled with NativeWind's utility classes. Inside the <code>View</code>, we have a <code>Text</code> component that displays "Login" with styling applied using NativeWind classes.</p>
<p>Next, you can add your login form components, such as username and password input fields, a login button, and any other necessary elements. Here is an example of how you can extend the login screen:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { StatusBar } <span class="hljs-keyword">from</span> <span class="hljs-string">"expo-status-bar"</span>;
<span class="hljs-keyword">import</span> { Text, View, TouchableOpacity, TextInput } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-native"</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">App</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">View</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex-1 justify-center items-center bg-white'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StatusBar</span> <span class="hljs-attr">style</span>=<span class="hljs-string">'auto'</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-center mt-3 text-2xl font-light text-orange-300'</span>&gt;</span>
        Login
      <span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
      {/* Additional components goes here */}
      <span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'mt-5 mx-5'</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">View</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-gray-400'</span>&gt;</span>EMAIL:<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TextInput</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Enter Email...'</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">'border border-dotted p-2 text-gray-500 border-amber-400 mt-1'</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'mt-3'</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-gray-400'</span>&gt;</span>PASSWORD:<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TextInput</span>
            <span class="hljs-attr">secureTextEntry</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Enter Password...'</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">'border text-gray-500 border-dotted p-2 border-amber-400 mt-1'</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">TouchableOpacity</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'bg-orange-300 p-3 mt-4'</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-center text-base text-white'</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">TouchableOpacity</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-center font-normal text-gray-500 text-base mt-3'</span>&gt;</span>
          OR
        <span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'mt-4'</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TouchableOpacity</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex flex-row items-center justify-center p-2 bg-orange-300'</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-white mx-2 text-sm'</span>&gt;</span>Sign In With Google<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">TouchableOpacity</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'mt-6 flex-row justify-center'</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">''</span>&gt;</span>New to FreeCodeCamp? <span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TouchableOpacity</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-amber-500'</span>&gt;</span>Create an Account<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">TouchableOpacity</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span></span>
  );
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/nextend-1.png" alt="Extended login screen UI with additional components " width="600" height="400" loading="lazy">
<em>Extended login screen UI with additional components</em></p>
<p>In this expanded version, we've included <code>TextInput</code> components for the username and password input fields, as well as a <code>TouchableOpacity</code> for the login button. Styling is done with NativeWind's utility classes to provide a clean and consistent appearance.</p>
<p>Furthermore, once you've finished creating your login screen using NativeWind in your React Native Expo project, you'll want to test it to check if everything works properly. You can do this by running this command on your terminal:</p>
<pre><code class="lang-bash">expo start
</code></pre>
<p>This command will launch the bundler and generate a QR code. To open the app, scan the QR code displayed in the terminal with your emulator's camera, or press "a" for Android or "i" for iOS.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot_20240223-015329.png" alt="Output of the code in an emulator" width="600" height="400" loading="lazy">
<em>Output of the code in an emulator</em></p>
<p>If you need to, you can access <a target="_blank" href="https://github.com/thejohncaleb/simpleproject">the complete project code</a> on GitHub.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Integrating Tailwind CSS into a React Native Expo project with NativeWind has various benefits, including increased developer efficiency, code consistency, and performance. Developers can easily create amazing mobile applications by leveraging the power of Tailwind CSS's utility-first approach and React Native's native features.</p>
<p>NativeWind makes it easy to apply Tailwind CSS to your React Native Expo app. Using Tailwind CSS in your mobile app development workflow opens up new possibilities for UI design and customization.</p>
<p>Remember, if you have any questions or just want to say hi, feel free to reach me on <a target="_blank" href="https://twitter.com/thejohncaleb">X(Twitter)</a> or my <a target="_blank" href="https://thejohncaleb.netlify.app/contact">website</a>. :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Custom HTML5 Video Player Using TailwindCSS and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ HTML5 comes equipped with a native video player. It's shipped with a simple user interface, functionality, and some basic controls in the browser. And while the functionality of the default video player via the browser works perfectly, the user inter... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-custom-video-player-using-javascript-and-tailwind-css/</link>
                <guid isPermaLink="false">66ba596b256e9dbeab31aaaf</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Franklin Okolie ]]>
                </dc:creator>
                <pubDate>Tue, 13 Feb 2024 15:14:21 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/how-to-build-a-custom-video-player-using-tailwindcss-and-javascript.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>HTML5 comes equipped with a native video player. It's shipped with a simple user interface, functionality, and some basic controls in the browser. And while the functionality of the default video player via the browser works perfectly, the user interface isn't so beautiful and fancy, and it's just not generally aesthetically pleasing. </p>
<p>For this reason, most modern web applications and platforms like Udemy, Netflix, YouTube, and Amazon Prime don't ship the default built-in HTML5 video player to their users. Instead, they build their own customized versions with a sleek user interface to make their platforms more attractive and user-friendly.</p>
<p>If you have ever been curious how these companies and web platforms are able to pull off such feat, then this article is for you.</p>
<p>You'll get some hands-on experience while following along with a step-by-step guide that teaches you how you can build and customize your own HTML5 video player. You'll learn how to customize the user interface, extend the functionality, and build your own fantastic custom controls and features.</p>
<p>You'll also learn how to build all this using nothing other than the native Video API provided by JavaScript in the browser – no external libraries or tools required.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Fundamental knowledge of HTML5 and CSS</li>
<li>Fundamental knowledge of Tailwind CSS</li>
<li>Fundamental knowledge of JavaScript (ES6)</li>
<li>A code editor of your choice</li>
<li>A browser that supports modern features of JavaScript (for example Chrome or Mozilla Firefox)</li>
</ul>
<h2 id="heading-heres-what-well-cover">Here's what we'll cover:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-getting-started">Getting Started</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-the-development-environment">How to Set Up the Development Environment</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-a-customized-ui-using-tailwind-css">How to Build a Customized UI using Tailwind CSS</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-play-and-pause-functionality">How to Implement the Play and Pause Functionality</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-rewind-and-fast-forward-functionality">How to Implement the Rewind and Fast Forward Functionality</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-mute-and-unmute-functionality">How to Implement the Mute and Unmute Functionality</a></li>
<li><a class="post-section-overview" href="#heading-how-to-update-the-progress-bar-relative-to-the-video-time">How to Update the Progress Bar Relative to the Video Time</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-seeking-functionality">How to Implement the Seeking Functionality</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-keyboard-navigation-for-accesibility">How to Implement Keyboard Navigations for Accessibility</a></li>
<li><a class="post-section-overview" href="#heading-where-to-go-from-here">Where to Go from Here</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-getting-started">Getting Started</h2>
<p>In this article, we will be using Tailwind CSS as the styling tool to build out the custom video player UI. We'll also use JavaScript to build out the functionality of the controls.</p>
<p>Note that using Tailwind CSS is optional, as any styling tool will suffice here like SCSS, CSS, styled-components and so on – it's totally up to you.</p>
<p>I've split this tutorial into different sections, each addressing a specific aspect of the custom video player functionality. Each new section will build upon the previous ones to complete the player. By the end of the article, you will have a fully functional HTML5 custom video player.</p>
<p>In this tutorial, we'll concentrate on specific features of the video player. These features will offer opportunities and ideas for building additional functionalities. The features we'll cover are:</p>
<ul>
<li>Play and Pause</li>
<li>Rewind and Fast Forward</li>
<li>Mute and Unmute</li>
<li>Video Seeking</li>
<li>Keyboard navigations (utilizing the Spacebar for play and pause, and the Arrow keys for rewind and fast forward).</li>
</ul>
<p>We won't address responsive design here, as we won't be focusing on making the video player mobile responsive. This omission should present a challenge and a learning opportunity for you.</p>
<p>Now, let's delve into setting up our development environment so we can start building.</p>
<h2 id="heading-how-to-set-up-the-development-environment">How to Set Up the Development Environment</h2>
<p>The initial step is to setup an efficient development environment to ensure smooth workflow. We'll use <a target="_blank" href="https://vitejs.dev/">Vite</a> for this purpose. </p>
<p>Before progressing to the next part of this section, make sure that you have <a target="_blank" href="https://nodejs.org/en">NodeJS</a> and <a target="_blank" href="https://www.npmjs.com/">NPM</a> or <a target="_blank" href="https://yarnpkg.com/">Yarn</a> installed on your computer, as they are necessary for installing tools and setting up your development environment seamlessly.</p>
<h3 id="heading-how-to-setup-the-project-with-vite">How to setup the project with Vite</h3>
<p>To scaffold a project in Vite, open your terminal and type in the following command:</p>
<pre><code class="lang-bash">yarn create vite
</code></pre>
<p>Vite will guide you in configuring and selecting the appropriate tools for your development environment. </p>
<p>The first step is to choose a project name – you have the freedom to choose any name you prefer. In this article, I'll be using "html5-video-player" as the project name.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-81.png" alt="Image" width="600" height="400" loading="lazy">
<em>Terminal output after running 'yarn create vite' command</em></p>
<p>The next step is selecting the project framework. This project will be written in pure JavaScript, so choose "Vanilla" and then select "JavaScript" on the next prompt.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-82.png" alt="Image" width="600" height="400" loading="lazy">
<em>Terminal output after inputting a project name, asking to select a framework for the project</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-83.png" alt="Image" width="600" height="400" loading="lazy">
<em>Terminal output afte selecting the 'Vanilla' framwork</em></p>
<p>Now, Vite has successfully set up your environment using the selected tools. It's time to install the dependencies necessary for Vite to function properly. Follow the instructions provided by Vite in the CLI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-84.png" alt="Image" width="600" height="400" loading="lazy">
<em>Terminal showing a success message on setting up the environment</em></p>
<p>If you named your project like mine, then run the command below exactly as it is. If you chose a different name, simply substitute my project name with yours and proceed in the same manner.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> html5-video-player
</code></pre>
<p>This command will navigate to the project directory where your development environment resides. From there, you can proceed to install the dependencies.</p>
<pre><code class="lang-bash">yarn
</code></pre>
<p>Once the dependencies are installed, let's proceed to the next step, which involves setting up Tailwind CSS as our styling tool. This process is straightforward, similar to how we set up Vite.</p>
<p>Open your terminal and execute the following commands:</p>
<pre><code class="lang-bash">yarn add -D tailwindcss postcss autoprefixer
</code></pre>
<p>This will install Tailwind CSS, our styling tool, as well as PostCSS and Autoprefixer. These tools will assist Tailwind CSS in functioning effectively within your project. </p>
<p>The next command involves setting up the configuration files for Tailwind CSS and PostCSS.</p>
<p>Open your local terminal once again and type in the following command:</p>
<pre><code class="lang-bash">npx tailwindcss init
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-85.png" alt="Image" width="600" height="400" loading="lazy">
<em>Creating Tailwind CSS configuration file.</em></p>
<p>As mentioned in the command message, a file named <code>tailwind.config.js</code> will be generated at the root of the project folder. This file will contain your configuration for styling, including settings for fonts, colors, plugins, and more. For further details, refer to the <a target="_blank" href="https://tailwindcss.com/">TailwindCSS documentation</a>.</p>
<p>Open the Tailwind CSS configuration file that was generated in your code editor and make the following edits to it:</p>
<pre><code class="lang-js"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [<span class="hljs-string">'./index.html'</span>],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Here, we simply edited the <code>content</code> key to specify the file where TailwindCSS should read for Tailwind CSS classes. This file happens to be the <code>index.html</code> file, where our main work will take place.</p>
<p>Next, you'll need to configure PostCSS, which doesn't have an automated setup command like TailwindCSS. So you'll create the configuration file manually. Navigate to the project's root folder and create a file named <code>postcss.config.js</code>.</p>
<p>After creating the <code>postcss.config.js</code> file, simply copy and paste the provided code snippet into the file.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">plugins</span>: {
    <span class="hljs-attr">tailwindcss</span>: {},
    <span class="hljs-attr">autoprefixer</span>: {},
  },
};
</code></pre>
<p>Next, configure your <code>style.css</code> file to utilize Tailwind CSS defaults. This saves you from the tedious task of setting up CSS defaults manually. </p>
<p>Open the <code>style.css</code> file in your code editor, delete its contents, and then paste the following code snippet into the file:</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>
<h3 id="heading-delete-unneccasary-files-and-code">Delete unneccasary files and code</h3>
<p>The files generated by Vite primarily serve as guides for adding your own files and using the bundler effectively. So you can delete most of them since they are unnecessary for this project.</p>
<p>Below are the files to be removed from the project:</p>
<ol>
<li><code>counter.js</code></li>
<li><code>javascript.svg</code></li>
</ol>
<p>Once you've done that, you can move on to the next step in this section, which involves removing unnecessary code.</p>
<p>Open the <code>main.js</code> file located at the root of the project, and delete all the code within it.</p>
<p>Then, navigate to the <code>index.html</code> file and delete all of its current content. Afterwards, copy and paste the below code snippet into the file:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/svg+xml"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/vite.svg"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./style.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>HTML5 Custom Video Player<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl font-bold underline text-red-800"</span>&gt;</span>Hello world!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>And with that, you're finished with this part! Your development environment is now set up, ready for you to begin building your custom HTML5 video player</p>
<p>To confirm that your environment is set up properly, check the following:</p>
<ol>
<li>The project files and folders should resemble the following structure:</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-108.png" alt="Image" width="600" height="400" loading="lazy">
<em>Project setup completed: Visual Studio Code displaying project structure.</em></p>
<ol start="2">
<li>Open your terminal and run the following command:</li>
</ol>
<pre><code class="lang-bash">yarn dev
</code></pre>
<p>This will create a development server where your webpage will be hosted. Open the URL provided by Vite.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-109.png" alt="Image" width="600" height="400" loading="lazy">
<em>Launching Vite development server with 'yarn dev' command.</em></p>
<p>Upon opening the link <code>http://localhost:5173/</code>, you should see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-110.png" alt="Image" width="600" height="400" loading="lazy">
<em>The initial user interface displayed after executing 'yarn dev' command.</em></p>
<p>Congratulations! You have successfully completed this section on setting up your development environment, which will let us work effectively as we build our custom HTML5 video player.</p>
<p><strong>Troubleshooting:</strong> If you find that your setup isn't working as expected, don't worry. Simply delete the project folder and repeat the process. You may have missed a step or some tools might not have installed correctly. Also, double-check your Tailwind CSS and PostCSS configuration files to ensure they contain the correct code as shown above.</p>
<h2 id="heading-how-to-build-a-customized-ui-using-tailwind-css">How to Build a Customized UI using Tailwind CSS</h2>
<p>This section covers all the styling required to construct the UI of the custom HTML5 video player. We'll take a step-by-step walkthrough of the process.</p>
<p>First, copy and paste the following link tag into the head of your HTML, above the link to the stylesheet:</p>
<pre><code class="lang-css">&lt;<span class="hljs-selector-tag">link</span>
  <span class="hljs-selector-tag">href</span>="<span class="hljs-selector-tag">https</span>://<span class="hljs-selector-tag">fonts</span><span class="hljs-selector-class">.googleapis</span><span class="hljs-selector-class">.com</span>/<span class="hljs-selector-tag">icon</span>?<span class="hljs-selector-tag">family</span>=<span class="hljs-selector-tag">Material</span>+<span class="hljs-selector-tag">Icons</span>"
  <span class="hljs-selector-tag">rel</span>="<span class="hljs-selector-tag">stylesheet</span>"
/&gt;
</code></pre>
<p>This allows us to use <a target="_blank" href="https://materializecss.com/icons.html">Materialize CSS icons</a>, which are essential for styling our buttons in the UI. </p>
<p>Next, let's focus on styling the video element in our markup. Simply replace the <code>body</code> element with the provided code snippet below:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-indigo-950 p-10"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
    <span class="hljs-attr">id</span>=<span class="hljs-string">"container"</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"w-4/5 h-4/5 mx-auto rounded-lg overflow-hidden relative group"</span>
  &gt;</span>
    <span class="hljs-comment">&lt;!-- VIDEO --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">figure</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">video</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">source</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/your-video.mp4"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">video</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">figure</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">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<p>The provided code snippet includes the markup and styling for the video element, as well as an outer div acting as a container for the entire video player UI. The video element is nested within a figure element.</p>
<p>For the <code>source</code> element, specify the path to the video you wish to play. You can find videos online, download them, and add them to the "public" directory within the project folder. Then, link the <code>src</code> attribute of the <code>source</code> element to the video file. You can find free downloadable videos <a target="_blank" href="https://www.pexels.com/search/videos/online/">here</a>.</p>
<p>Next, let's style the controls using the <a target="_blank" href="https://materializecss.com/icons.html">Materialize CSS Icons</a> you linked in your HTML. Place the following code snippet below the <code>figure</code> element inside the body element.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- CONTROLS --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">id</span>=<span class="hljs-string">"controls"</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"opacity-0 p-5 absolute bottom-0 left-0 w-full transition-opacity duration-300 ease-linear group-hover:opacity-100"</span>
&gt;</span>
  <span class="hljs-comment">&lt;!-- PROGRESS BAR --&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"progress-bar"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-1 w-full bg-white cursor-pointer mb-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">id</span>=<span class="hljs-string">"progress-indicator"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"h-full w-9 bg-indigo-800 transition-all duration-500 ease-in-out"</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">"flex items-center justify-between"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center justify-between"</span>&gt;</span>
      <span class="hljs-comment">&lt;!-- REWIND BUTTON --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"rewind"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-3xl w-12"</span>&gt;</span>replay_10 <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

      <span class="hljs-comment">&lt;!-- PLAY BUTTON --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"play-pause"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-5xl inline-block w-12"</span>
          &gt;</span>play_arrow<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>
        &gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

      <span class="hljs-comment">&lt;!-- FAST FORWARD BUTTON --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"fast-forward"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-3xl w-12"</span>&gt;</span>forward_10 <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</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;!-- VOLUME BUTTON --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"volume"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-3xl"</span>&gt;</span>volume_up<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</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>&gt;</span>
</code></pre>
<p>This code segment defines the layout and behavior of the controls for a video player. It begins by setting up a container div (<code>&lt;div id="controls"&gt;</code>) that holds all the control elements. The container is initially invisible (<code>opacity-0</code>) and becomes visible with a smooth transition (<code>transition-opacity duration-300 ease-linear</code>) when the user hovers over it (<code>group-hover:opacity-100</code>).</p>
<p>Within the container, there's a progress bar (<code>&lt;div id="progress-bar"&gt;</code>) for tracking the video's progress. It consists of a white background bar (<code>bg-white</code>) with a movable indicator (<code>&lt;div id="progress-indicator"&gt;</code>) colored in indigo (<code>bg-indigo-800</code>). The progress bar is responsive and allows users to seek to different parts of the video.</p>
<p>Below the progress bar are control buttons for various functions. The rewind, play/pause, and fast forward buttons are grouped together within a flex container (<code>&lt;div class="flex items-center justify-between"&gt;</code>). Each button (<code>&lt;button&gt;</code>) is styled to enlarge slightly (<code>hover:scale-125</code>) when hovered over.</p>
<ul>
<li>The rewind button (<code>&lt;button id="rewind"&gt;</code>) contains an icon (<code>&lt;i class="material-icons text-white text-3xl w-12"&gt;replay_10&lt;/i&gt;</code>) indicating a ten-second rewind.</li>
<li>The play/pause button (<code>&lt;button id="play-pause"&gt;</code>) contains an icon (<code>&lt;i class="material-icons text-white text-5xl w-12"&gt;play_arrow&lt;/i&gt;</code>) toggling between play and pause states.</li>
<li>The fast forward button (<code>&lt;button id="fast-forward"&gt;</code>) contains an icon (<code>&lt;i class="material-icons text-white text-3xl w-12"&gt;forward_10&lt;/i&gt;</code>) indicating a ten-second fast forward.</li>
</ul>
<p>Separately, there's a volume button (<code>&lt;button id="volume"&gt;</code>) located to the right of the control buttons. It contains a volume icon (<code>&lt;i class="material-icons text-white text-3xl w-12"&gt;volume_up&lt;/i&gt;</code>).</p>
<p>Overall, this code segment combines HTML and Tailwind CSS classes to create a functional and visually appealing set of controls for a video player.</p>
<p>The final puzzle is disabling the default browser feature, and we wouldn't want our custom HTML5 video player to clash with or be overridden by the default styling provided by browsers.</p>
<p>Copy and paste the code snippet below into your <code>style.css</code> file, directly below the Tailwind CSS directives:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@layer</span> base {
  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-play-button</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-volume-slider</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-mute-button</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-timeline</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-current-time-display</span> {
    <span class="hljs-attribute">display</span>: none;
  }
}
</code></pre>
<p>This piece of code is used to customize the appearance and behavior of the default media controls provided by the WebKit browser engine (commonly used in browsers like Safari and some versions of Google Chrome) for the <code>&lt;video&gt;</code> element.</p>
<p>Each CSS rule within the <code>@layer base</code> block targets specific parts of the default media controls and hides them from view by setting their <code>display</code> property to <code>none</code>. Here's a breakdown of each rule:</p>
<ol>
<li><code>video::-webkit-media-controls</code>: This rule targets the entire set of media controls for the <code>&lt;video&gt;</code> element and hides them completely. By hiding the controls, you can implement your own custom controls using JavaScript and CSS, providing a more tailored and consistent user experience across different browsers.</li>
<li><code>video::-webkit-media-controls-play-button</code>: This rule targets the play button within the default media controls and hides it. We might want to hide the play button if we're using a custom play button design or handling playback control programmatically.</li>
<li><code>video::-webkit-media-controls-volume-slider</code>: This rule targets the volume slider within the default media controls and hides it. Similar to hiding the play button, you might choose to hide the volume slider if you're implementing your own volume control UI.</li>
<li><code>video::-webkit-media-controls-mute-button</code>: This rule targets the mute button within the default media controls and hides it. If you have a custom mute/unmute button or want to manage audio muting programmatically, you can hide the default mute button.</li>
<li><code>video::-webkit-media-controls-timeline</code>: This rule targets the timeline (progress bar) within the default media controls and hides it. By hiding the timeline, you can implement your own progress bar with custom styling and additional functionality.</li>
<li><code>video::-webkit-media-controls-current-time-display</code>: This rule targets the current time display within the default media controls and hides it. If you're implementing a custom UI for displaying the current playback time, you can hide the default display.</li>
</ol>
<p>Overall, this code allows for complete customization of the default media controls provided by WebKit browsers, letting you create a unique and tailored user experience for video playback on your websites.</p>
<p>Check your localhost URL to see a customized UI displayed like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-114.png" alt="Image" width="600" height="400" loading="lazy">
<em>Custom video player UI without mouse hover, controls hidden.</em></p>
<p>However, upon hovering, the controls will fade in and the UI will be displayed like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-113.png" alt="Image" width="600" height="400" loading="lazy">
<em>Custom video player UI with visible controls upon mouse hover.</em></p>
<p>And there you have it! You've successfully constructed a customized HTML5 video player. Now, it's time to breathe life into it by using JavaScript to develop the controls and functionality, which we'll tackle in the upcoming sections.</p>
<h2 id="heading-how-to-implement-the-play-and-pause-functionality">How to Implement the Play and Pause Functionality</h2>
<p>To implement the play and pause feature on the HTML5 custom video player, you'll start by selecting the play and pause buttons using their respective IDs from the markup. You can also select the video element. Then you'll programmatically control the playback using the Video API provided by JavaScript in the browser. Let's get started.</p>
<pre><code class="lang-js"><span class="hljs-meta">"use strict"</span>;

<span class="hljs-keyword">const</span> playNpauseBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#play-pause"</span>);
<span class="hljs-keyword">const</span> video = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"video"</span>);
</code></pre>
<p>From the code snippet above:</p>
<ul>
<li><code>"use strict";</code> ensures JavaScript runs in strict mode, catching common coding mistakes.</li>
<li><code>const playNpauseBtn = document.querySelector("#play-pause");</code> selects the play/pause button from the HTML using its ID.</li>
<li><code>const video = document.querySelector("video");</code> selects the video element from the HTML.</li>
</ul>
<p>Next, let's create two functions:</p>
<ol>
<li><code>playNpauseFn</code>: This function will handle playing and pausing the video.</li>
<li><code>updatePlayNPauseIcon</code>: This function will update the play and pause icons based on the video's current state. For example, if the video is playing, it will show the pause icon, and vice versa.</li>
</ol>
<p>Now, let's examine how this will function in the following code snippet.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">playNpauseFn</span>(<span class="hljs-params"></span>) </span>{
  video.paused ? video.play() : video.pause();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updatePlayNPauseIcon</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> icon = playNpauseBtn.querySelector(<span class="hljs-string">"i"</span>);
  icon.textContent = <span class="hljs-string">""</span>;

  icon.textContent = video.paused ? <span class="hljs-string">"play_arrow"</span> : <span class="hljs-string">"paused"</span>;
}
</code></pre>
<p>Let's understand what's happening. Beginning with the <code>playNpauseFn</code> function, when it's called, it checks the current state of the video using the <code>paused</code> method available in the Video API. If the video is paused, it plays the video. Otherwise, it pauses the video. This is accomplished using the ternary operator in JavaScript.</p>
<p>Alternatively, you can rewrite this using the if/else statement, as shown below:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">playNpauseFn</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (video.paused) {
    video.play();
  } <span class="hljs-keyword">else</span> {
    video.paused();
  }
}
</code></pre>
<p>The code sample above accomplishes the same task as the previous version – either one will work.</p>
<p>Now, let's move on to the second function, <code>updatePlayNPauseIcon</code>. This function updates the play and pause icons based on the current state of the video. Let's review how it's implemented.</p>
<p>Check out the icon styling below:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
  <span class="hljs-attr">id</span>=<span class="hljs-string">"play-pause"</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-5xl inline-block w-12"</span>&gt;</span>play_arrow<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>This code makes a button with the ID "play-pause" that holds an icon specified by the <code>&lt;i&gt;</code> tag. Materialize CSS uses the text "play_arrow" inside the <code>&lt;i&gt;</code> tag to show the matching icon. If you change the text inside <code>&lt;i&gt;</code>, Materialize CSS updates the icon accordingly.</p>
<p>Now, let's focus on the function responsible for updating the icon. Take a look at it below in isolation:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updatePlayNPauseIcon</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> icon = playNpauseBtn.querySelector(<span class="hljs-string">"i"</span>);
  icon.textContent = <span class="hljs-string">""</span>;

  icon.textContent = video.paused ? <span class="hljs-string">"play_arrow"</span> : <span class="hljs-string">"paused"</span>;
}
</code></pre>
<p>This function, <code>updatePlayNPauseIcon()</code>, is responsible for updating the play/pause icon based on the current state of the video.</p>
<ol>
<li>It first selects the icon element inside the play/pause button.</li>
<li>Then, it clears any existing text content within the icon.</li>
<li>Finally, it sets the text content of the icon to "play_arrow" if the video is paused, or "paused" if the video is currently playing. This dynamically changes the icon displayed on the button to reflect the current playback state.</li>
</ol>
<p><strong>Note:</strong> The way you update icons programmatically can vary based on the icon service and its API. This particular implementation is specific to Materialize CSS icons.</p>
<p>Next, let's connect these functions to the events that trigger them. Let's see how that works below:</p>
<pre><code class="lang-js">video.addEventListener(<span class="hljs-string">"play"</span>, updatePlayNPauseIcon);
video.addEventListener(<span class="hljs-string">'click'</span>, playNpauseFn)
video.addEventListener(<span class="hljs-string">"pause"</span>, updatePlayNPauseIcon);
playNpauseBtn.addEventListener(<span class="hljs-string">"click"</span>, playNpauseFn);
</code></pre>
<p>In this code:</p>
<ul>
<li><code>video.addEventListener("play", updatePlayNPauseIcon);</code>: This line adds an event listener to the video element, specifically listening for the "play" event. When the video starts playing, it triggers the <code>updatePlayNPauseIcon</code> function, updating the play/pause icon accordingly.</li>
<li><code>video.addEventListener('click', playNpauseFn)</code>: This line adds an event listener to the video element for the "click" event. When the video is clicked, it triggers the <code>playNpauseFn</code> function, which plays or pauses the video.</li>
<li><code>video.addEventListener("pause", updatePlayNPauseIcon);</code>: This line adds an event listener to the video element, listening for the "pause" event. When the video is paused, it triggers the <code>updatePlayNPauseIcon</code> function to update the play/pause icon.</li>
<li><code>playNpauseBtn.addEventListener("click", playNpauseFn);</code>: This line adds an event listener to the play/pause button element. When the button is clicked, it triggers the <code>playNpauseFn</code> function, which plays or pauses the video.</li>
</ul>
<p>We have four event listeners on the two selected elements. Let's break down what's happening:</p>
<ul>
<li>The video element listens for the "play" event. When the video starts playing, it triggers <code>updatePlayNPauseIcon</code>, updating the icon based on the video's current state.</li>
<li>The video element also listens for a click event. When clicked, it triggers <code>playNpauseFn</code>, which toggles between playing and pausing the video.</li>
<li>Also, the video element listens for the "pause" event. When the video is paused, it triggers <code>playNpauseFn</code>, toggling the video's playback state.</li>
<li>The play/pause button element also listens for a click event. When clicked, it triggers <code>playNpauseFn</code>, toggling between playing and pausing the video.</li>
</ul>
<p>That concludes this section. You can now try the play and pause functionality. The video should pause and play effectively, with the icons updating correctly.</p>
<p>At the moment your custom video should player should be doing this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/play-pause.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Testing out the play and pause functionality</em></p>
<p>In the next section, we'll be implementing the rewind and fast forward feature.</p>
<h2 id="heading-how-to-implement-the-rewind-and-fast-forward-functionality">How to Implement the Rewind and Fast Forward Functionality</h2>
<p>Now that we've implemented the play and pause functionality, the next features to add are rewind and fast forward. These common features allow users to skip forward or backward in the video by a set number of seconds. </p>
<p>First, let's begin by selecting the corresponding buttons from the HTML document using their IDs and storing them in variables:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> rewindBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#rewind"</span>);
<span class="hljs-keyword">const</span> fastForwardBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#fast-forward"</span>);
</code></pre>
<p>Once that's completed, you need to construct the function responsible for implementing the rewind and fast forward functionality. Below is the code snippet:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rewindNforwardFn</span>(<span class="hljs-params">type</span>) </span>{
  video.currentTime += type === <span class="hljs-string">"rewind"</span> ? <span class="hljs-number">-10</span> : <span class="hljs-number">10</span>;
}
</code></pre>
<p>This function, called <code>rewindNforward</code>, is responsible for rewinding or fast-forwarding the video. Here's how it works:</p>
<ul>
<li>It takes a parameter called <code>type</code>, which indicates whether you want to rewind or fast forward.</li>
<li>If <code>type</code> is "rewind", it subtracts 10 seconds from the current playback time of the video (<code>video.currentTime</code>).</li>
<li>If <code>type</code> is not "rewind" (indicating that you want to fast forward), it adds 10 seconds to the current playback time of the video. This allows users to navigate through the video either backward or forward by 10-second intervals, depending on the value of <code>type</code>.</li>
</ul>
<p>Next, you need to connect the event listeners on the buttons to trigger the <code>rewindNforward</code> function.</p>
<pre><code class="lang-js">rewindBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> rewindNforwardFn(<span class="hljs-string">"rewind"</span>));
fastForwardBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> rewindNforwardFn(<span class="hljs-string">"forward"</span>));
</code></pre>
<p>This code adds event listeners to the rewind and fast forward buttons. When the rewind button is clicked, it triggers the <code>rewindNforward</code> function with the argument "rewind", indicating that you want to rewind the video. </p>
<p>Similarly, when the fast forward button is clicked, it triggers the <code>rewindNforward</code> function with the argument "forward", indicating that you want to fast forward the video.</p>
<p>Feel free to test it out and observe how it functions on the user interface (UI).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/rewind-1.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Testing the rewind and fast forward functionality</em></p>
<h2 id="heading-how-to-implement-the-mute-and-unmute-functionality">How to Implement the Mute and Unmute Functionality</h2>
<p>To add the mute and unmute functionality, you'll follow the same process as you did for the previous functionalities.</p>
<p>You'll begin by selecting the volume button from the HTML document using the <code>querySelector</code> method:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> volumeBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#volume"</span>);
</code></pre>
<p>Then, create the functions responsible for muting and unmuting the video, and update the icon accordingly when either of these events occurs.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">muteNunmuteFn</span>(<span class="hljs-params"></span>) </span>{
  video.muted = video.muted ? <span class="hljs-literal">false</span> : <span class="hljs-literal">true</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateVolumeIcon</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> icon = volumeBtn.querySelector(<span class="hljs-string">"i"</span>);
  icon.textContent = <span class="hljs-string">""</span>;
  icon.textContent = video.muted ? <span class="hljs-string">"volume_off"</span> : <span class="hljs-string">"volume_up"</span>;
}
</code></pre>
<p>This code comprises two functions:</p>
<ol>
<li><code>muteNunmuteFn()</code>: This function toggles the video's mute state. If the video is currently muted, it unmutes it. Otherwise, it mutes the video.</li>
<li><code>updateVolumeIcon()</code>: This function updates the volume icon displayed on the volume button. It clears any existing icon content, then sets the icon text to "volume_off" if the video is muted, and "volume_up" if the video is not muted.</li>
</ol>
<p>The final step is to connect the functions with event listeners so that they are executed when the event is triggered. Below are the code snippets for this:</p>
<pre><code class="lang-js">video.addEventListener(<span class="hljs-string">"volumechange"</span>, updateVolumeIcon);
volumeBtn.addEventListener(<span class="hljs-string">"click"</span>, muteNunmuteFn);
</code></pre>
<p>This code sets up two things:</p>
<ol>
<li>It adds an event listener to the video element, listening for the "volumechange" event. When this event occurs (that is, when the volume is changed), it triggers the <code>updateVolumeIcon</code> function to update the volume icon accordingly.</li>
<li>It adds an event listener to the volume button. When the volume button is clicked, it triggers the <code>muteNunmuteFn</code> function, toggling between muting and unmuting the video.</li>
</ol>
<p>Similar to the <code>play</code> and <code>pause</code> events, the video also has a <code>volumechange</code> event triggered when the volume or mute status changes. You set up the video to listen for this event, so when it occurs, the event listener runs the function to update the volume icon accordingly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/volume.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Testing out the mute and unmute functionality</em></p>
<h2 id="heading-how-to-update-the-progress-bar-relative-to-the-video-time">How to Update the Progress Bar Relative to the Video Time</h2>
<p>In this section, you'll see how to update the progress bar as the video plays, allowing users to track their progress through the video.</p>
<p>The progress bar currently doesn't move as the video plays and the time changes. We're going to fix that</p>
<p>To start, you'll remove the fixed width styling for the progress bar. Originally added for styling purposes, it's no longer needed as you'll dynamically adjust the width using JavaScript. Update the class from <code>w-9</code> to <code>w-0</code> in the div element with the id "progress-indicator".</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- PROGRESS BAR --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"progress-bar"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-1 w-full bg-white cursor-pointer mb-4"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
    <span class="hljs-attr">id</span>=<span class="hljs-string">"progress-indicator"</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"h-full w-0 bg-indigo-800 transition-all duration-500 ease-in-out"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Moving on to implementing the progress bar update, the first step is to select the progress bar indicator element. This element's width will increase as the video time progresses. Below is the code snippet to achieve this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> progressIndicator = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#progress-indicator"</span>);
</code></pre>
<p>Once the progress indicator is selected, your next task is to implement the function responsible for updating it.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateProgress</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> progressPercentage = (video.currentTime / video.duration) * <span class="hljs-number">100</span>;

  progressIndicator.style.width = <span class="hljs-string">`<span class="hljs-subst">${progressPercentage}</span>%`</span>;
}
</code></pre>
<p>In the code snippet above, the function called <code>updateProgress</code> calculates the percentage of video progress by dividing the current time of the video by its total duration and then multiplying by 100. This percentage is used to set the width of the progress indicator element, visually representing how much of the video has been watched.</p>
<p>Let's break down the function. In the first line of code, you calculate the percentage of the video's current time compared to its total duration. You do this by dividing the current time by the total duration of the video. For example, if a video is 30 seconds long and the current time is 3 seconds, 3 divided by 30 equals 0.1. </p>
<p>You then multiply this decimal by 100 to get the percentage. So, 0.1 multiplied by 100 equals 10. This means you are 10% into the 30-second video. </p>
<p>Finally, you use this percentage to update the width of the progress indicator accordingly.</p>
<p>Next, let's add an event listener that triggers this function. See the code snippet below:</p>
<pre><code class="lang-js">video.addEventListener(<span class="hljs-string">'timeupdate'</span>, updateProgress);
</code></pre>
<p>Similar to other events in the Video API, there's another one called <code>timeupdate</code>. This event is triggered as the <code>currentTime</code> of the video changes. So, as the time updates, the <code>updateProgress</code> function is automatically executed each time the event is fired, causing the progress indicator to update accordingly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/progress.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Testing out the time progress functionality</em></p>
<h2 id="heading-how-to-implement-the-seeking-functionality">How to Implement the Seeking Functionality</h2>
<p>The seeking functionality is a vital aspect of video players. While rewind and fast forward are efficient for small skips, users often want to make larger jumps to specific parts of the video. Clicking rewind or fast forward, which only moves in fixed increments, can be frustrating for users. So the seeking functionality proves invaluable in such scenarios.</p>
<p>Let's begin by selecting the progress bar element from the Document Object Model (DOM).</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> progessBar = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#progress-bar"</span>);
</code></pre>
<p>Having obtained the progress bar from the DOM using its ID, your next step is to construct the seeking function. You can find the implementation in the following code snippet:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">seekingFn</span>(<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">const</span> updatedTime = (e.offsetX / progessBar.offsetWidth) * video.duration;

  video.currentTime = updatedTime;
}
</code></pre>
<p>Let's breakdown the function and understand what's going on.</p>
<p>The function, <code>seekingFn</code>, adjusts the current playback time of the video based on the position where the user clicks on the progress bar. It calculates the updated time by dividing the horizontal offset of the click relative to the progress bar width by the total width of the progress bar. Then it multiplies it by the total duration of the video. Finally, it sets the current time of the video to the calculated updated time.</p>
<p>Next, add the event listener:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> mouseIsDown = <span class="hljs-literal">false</span>;

progessBar.addEventListener(<span class="hljs-string">"mousedown"</span>, <span class="hljs-function">() =&gt;</span> (mouseIsDown = <span class="hljs-literal">true</span>));
progessBar.addEventListener(<span class="hljs-string">"mouseup"</span>, <span class="hljs-function">() =&gt;</span> (mouseIsDown = <span class="hljs-literal">false</span>));
progessBar.addEventListener(<span class="hljs-string">"click"</span>, seekingFn);
progessBar.addEventListener(<span class="hljs-string">"mousemove"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> mouseIsDown &amp;&amp; seekingFn);
</code></pre>
<p>In the code snippet above, the code handles mouse events on the progress bar for seeking functionality:</p>
<ul>
<li><code>mouseIsDown</code> is a variable that tracks whether the mouse button is pressed down.</li>
<li>When the mouse button is pressed down (<code>mousedown</code> event), <code>mouseIsDown</code> is set to true.</li>
<li>When the mouse button is released (<code>mouseup</code> event), <code>mouseIsDown</code> is set to false.</li>
<li>When the progress bar is clicked (<code>click</code> event), the <code>seekingFn</code> function is triggered to seek to the clicked position.</li>
<li>When the mouse moves over the progress bar (<code>mousemove</code> event), if <code>mouseIsDown</code> is true, meaning the mouse button is pressed, then the <code>seekingFn</code> function is triggered, allowing seeking while dragging the mouse.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/seeking.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Testing the seeking functionality</em></p>
<h2 id="heading-how-to-add-keyboard-navigation-for-accesibility">How to Add Keyboard Navigation for Accesibility</h2>
<p>Our video player currently supports pointer devices like mice and light pens. But we aim to ensure accessibility for users who may not have or be able to use such devices. So we're striving to make our custom HTML5 video player usable without the need for a pointer device, utilizing keyboards instead.</p>
<h3 id="heading-using-the-space-bar-for-play-and-pause">Using the space bar for play and pause</h3>
<p>Let's start by improving the play and pause functionality. In most video players, it's common to use the space bar on the keyboard to toggle between playing and pausing a video. This is the first keyboard navigation feature we'll implement.</p>
<p>Below is a code snippet demonstrating how to achieve this:</p>
<pre><code class="lang-js"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keyup"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.code === <span class="hljs-string">"Space"</span>) {
    playNpauseFn();
  }
});
</code></pre>
<p>This code listens for when a key on your keyboard is released, known as a "keyup" event. If the key released happens to be the space bar, it triggers the function that toggles between playing and pausing the video. You'll just be reusing the function you made earlier for this.</p>
<p>Here's a step-by-step explanation of the code:</p>
<ol>
<li><p><strong><code>window.addEventListener("keyup", (e) =&gt; { ... })</code>:</strong></p>
</li>
<li><p>You're adding an event listener to the <code>window</code> object.</p>
</li>
<li><p>This listener is triggered when a key is released (<code>keyup</code> event).</p>
</li>
<li><p><strong>  <code>(e) =&gt; { ... }</code>:</strong></p>
</li>
<li><p>This is an arrow function that gets executed when the <code>keyup</code> event occurs.</p>
</li>
<li><p>The <code>e</code> parameter represents the event object containing information about the event.</p>
</li>
<li><p><strong><code>if (e.code === "Space") { ... }</code>:</strong></p>
</li>
<li><p>This condition checks if the key that was released is the space bar.</p>
</li>
<li><p><code>e.code</code> provides the code of the key that triggered the event.</p>
</li>
<li><p><strong><code>playNpauseFn();</code>:</strong></p>
</li>
<li><p>If the released key is the space bar, this function is called.</p>
</li>
<li>The <code>playNpauseFn</code> function is responsible for toggling between playing and pausing the video.</li>
</ol>
<h3 id="heading-using-the-arrow-keys-to-rewind-and-fast-forward">Using the arrow keys to rewind and fast forward</h3>
<p>You can use the left arrow key to rewind and the right arrow key to fast forward a video, in addition to the space bar for playing and pausing.</p>
<p>Building upon the previous code snippet for play and pause functionality, you can incorporate the arrow keys for rewinding and fast forwarding the video.</p>
<pre><code class="lang-js"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keyup"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.code === <span class="hljs-string">"Space"</span>) {
    playNpauseFn();
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e.code === <span class="hljs-string">"ArrowLeft"</span>) {
    rewindNforwardFn(<span class="hljs-string">"rewind"</span>);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e.code === <span class="hljs-string">"ArrowRight"</span>) {
    rewindNforwardFn(<span class="hljs-string">"forward"</span>);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span>;
  }
});
</code></pre>
<p>This code snippet sets up an event listener on the window object for the keyup event. When any key is released, the provided callback function is triggered with an event parameter. Inside the callback function, there are conditional statements to check which key was pressed:</p>
<ul>
<li>If the pressed key is the Space bar ("Space"), the <code>playNpauseFn</code> function is executed, toggling between play and pause of the video.</li>
<li>If the pressed key is the left arrow key ("ArrowLeft"), the <code>rewindNforwardFn</code> function is called with the argument "rewind", indicating the video should be rewound.</li>
<li>If the pressed key is the right arrow key ("ArrowRight"), the <code>rewindNforwardFn</code> function is called with the argument "forward", indicating the video should be fast forwarded.</li>
<li>If the pressed key isn't the space bar, left arrow, or right arrow, the function returns without performing any action.</li>
</ul>
<h3 id="heading-how-your-code-should-look-now">How your code should look now</h3>
<p>We've now finished building our custom HTML5 video player. Congratulations to you on learning this.</p>
<p>If you encountered any difficulties or missed any steps along the way, don't worry. You can find the code snippets for each major file below:</p>
<p><strong>index.html</strong></p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/svg+xml"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/vite.svg"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span>
      <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/icon?family=Material+Icons"</span>
      <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./style.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>HTML5 Custom Video Player<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-indigo-950 p-10"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">id</span>=<span class="hljs-string">"container"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"w-4/5 h-4/5 mx-auto rounded-lg overflow-hidden relative group"</span>
    &gt;</span>
      <span class="hljs-comment">&lt;!-- VIDEO --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">figure</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">video</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">source</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/oceans.mp4"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">video</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span>

      <span class="hljs-comment">&lt;!-- CONTROLS --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"controls"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"opacity-0 p-5 absolute bottom-0 left-0 w-full transition-opacity duration-300 ease-linear group-hover:opacity-100"</span>
      &gt;</span>
        <span class="hljs-comment">&lt;!-- PROGRESS BAR --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"progress-bar"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-1 w-full bg-white cursor-pointer mb-4"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"progress-indicator"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"h-full w-0 bg-indigo-800 transition-all duration-500 ease-in-out"</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">"flex items-center justify-between"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center justify-between"</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- REWIND BUTTON --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"rewind"</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-3xl w-12"</span>&gt;</span>replay_10 <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

            <span class="hljs-comment">&lt;!-- PLAY BUTTON --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"play-pause"</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-5xl inline-block w-12"</span>
                &gt;</span>play_arrow<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>
              &gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

            <span class="hljs-comment">&lt;!-- FAST FORWARD BUTTON --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"fast-forward"</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-3xl w-12"</span>&gt;</span>forward_10 <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</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;!-- VOLUME BUTTON --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"volume"</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"transition-all duration-100 ease-linear hover:scale-125"</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"material-icons text-white text-3xl"</span>&gt;</span>volume_up<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</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>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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><strong>style.css</strong></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;

<span class="hljs-keyword">@layer</span> base {
  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-play-button</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-volume-slider</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-mute-button</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-timeline</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-tag">video</span><span class="hljs-selector-pseudo">::-webkit-media-controls-current-time-display</span> {
    <span class="hljs-attribute">display</span>: none;
  }
}
</code></pre>
<p><strong>main.js</strong></p>
<pre><code class="lang-js"><span class="hljs-meta">"use strict"</span>;

<span class="hljs-keyword">const</span> playNpauseBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#play-pause"</span>);
<span class="hljs-keyword">const</span> video = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"video"</span>);
<span class="hljs-keyword">const</span> rewindBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#rewind"</span>);
<span class="hljs-keyword">const</span> fastForwardBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#fast-forward"</span>);
<span class="hljs-keyword">const</span> volumeBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#volume"</span>);
<span class="hljs-keyword">const</span> progressIndicator = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#progress-indicator"</span>);
<span class="hljs-keyword">const</span> progessBar = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#progress-bar"</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">playNpauseFn</span>(<span class="hljs-params"></span>) </span>{
  video.paused ? video.play() : video.pause();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updatePlayNPauseIcon</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> icon = playNpauseBtn.querySelector(<span class="hljs-string">"i"</span>);
  icon.textContent = <span class="hljs-string">""</span>;

  icon.textContent = video.paused ? <span class="hljs-string">"play_arrow"</span> : <span class="hljs-string">"paused"</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rewindNforwardFn</span>(<span class="hljs-params">type</span>) </span>{
  video.currentTime += type === <span class="hljs-string">"rewind"</span> ? <span class="hljs-number">-10</span> : <span class="hljs-number">10</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">muteNunmuteFn</span>(<span class="hljs-params"></span>) </span>{
  video.muted = video.muted ? <span class="hljs-literal">false</span> : <span class="hljs-literal">true</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateVolumeIcon</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> icon = volumeBtn.querySelector(<span class="hljs-string">"i"</span>);
  icon.textContent = <span class="hljs-string">""</span>;
  icon.textContent = video.muted ? <span class="hljs-string">"volume_off"</span> : <span class="hljs-string">"volume_up"</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateProgress</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> progressPercentage = (video.currentTime / video.duration) * <span class="hljs-number">100</span>;

  progressIndicator.style.width = <span class="hljs-string">`<span class="hljs-subst">${progressPercentage}</span>%`</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">seekingFn</span>(<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">const</span> updatedTime = (e.offsetX / progessBar.offsetWidth) * video.duration;

  video.currentTime = updatedTime;
}

<span class="hljs-comment">// PLAY AND PAUSE FUNCTIONALITY</span>
video.addEventListener(<span class="hljs-string">"play"</span>, updatePlayNPauseIcon);
video.addEventListener(<span class="hljs-string">"click"</span>, playNpauseFn);
video.addEventListener(<span class="hljs-string">"pause"</span>, updatePlayNPauseIcon);
playNpauseBtn.addEventListener(<span class="hljs-string">"click"</span>, playNpauseFn);

<span class="hljs-comment">// REWIND AND FAST FORWARD</span>
rewindBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> rewindNforwardFn(<span class="hljs-string">"rewind"</span>));
fastForwardBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> rewindNforwardFn(<span class="hljs-string">"forward"</span>));

<span class="hljs-comment">// MUTE AND UNMUTE</span>
video.addEventListener(<span class="hljs-string">"volumechange"</span>, updateVolumeIcon);
volumeBtn.addEventListener(<span class="hljs-string">"click"</span>, muteNunmuteFn);

<span class="hljs-comment">// PROGRESS</span>
video.addEventListener(<span class="hljs-string">"timeupdate"</span>, updateProgress);

<span class="hljs-comment">// SEEKING</span>
<span class="hljs-keyword">let</span> mouseIsDown = <span class="hljs-literal">false</span>;

progessBar.addEventListener(<span class="hljs-string">"mousedown"</span>, <span class="hljs-function">() =&gt;</span> (mouseIsDown = <span class="hljs-literal">true</span>));
progessBar.addEventListener(<span class="hljs-string">"mouseup"</span>, <span class="hljs-function">() =&gt;</span> (mouseIsDown = <span class="hljs-literal">false</span>));
progessBar.addEventListener(<span class="hljs-string">"click"</span>, seekingFn);
progessBar.addEventListener(<span class="hljs-string">"mousemove"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> mouseIsDown &amp;&amp; seekingFn);

<span class="hljs-comment">// KEYBOARD NAVIGATIONS</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keyup"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.code === <span class="hljs-string">"Space"</span>) {
    playNpauseFn();
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e.code === <span class="hljs-string">"ArrowLeft"</span>) {
    rewindNforwardFn(<span class="hljs-string">"rewind"</span>);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e.code === <span class="hljs-string">"ArrowRight"</span>) {
    rewindNforwardFn(<span class="hljs-string">"forward"</span>);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span>;
  }
});
</code></pre>
<p>Alternatively, you can find all the code on the <a target="_blank" href="https://github.com/DeveloperAspire/custom-html5-video-player">GitHub repository</a> I've created for this project. If you find it helpful, consider giving the repository a star – I'd really appreciate it!</p>
<p>You can access the live site by <a target="_blank" href="https://custom-html5-video-player5.netlify.app/">visiting here</a>.</p>
<h2 id="heading-where-to-go-from-here">Where to Go from Here</h2>
<p>Now that you've finished reading this article, remember that your journey doesn't end here. There's a whole world of possibilities waiting for you to explore and build upon what you've learned.</p>
<p>The Video API offers a wide range of features you can experiment with, such as adding playback rate controls, volume adjustment, or even subtitles. You can also enhance your project with animations, and interactions, and ensure it's mobile responsive, perhaps even enabling landscape mode for mobile devices.</p>
<p>For further inspiration and ideas, feel free to check out my version of the project <a target="_blank" href="https://aspire-video-player.netlify.app/">here</a> – although it's still a work in progress. I'm excited to see what you'll create! </p>
<p>If you decide to share your project, don't forget to tag me—I'd love to give it a review and offer any feedback. Keep pushing forward, and happy coding!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! You've reached the end of this article and have gained valuable hands-on experience in building your own custom HTML5 video player. By incorporating keyboard navigation and optimizing for accessibility, you've ensured a seamless user experience.</p>
<p>I'm excited to see what you'll create with your newfound knowledge, so don't forget to share your projects with me.</p>
<p> Thank you for reading, and see you next time!</p>
<h3 id="heading-contact-information">Contact information</h3>
<p>Would you like to get in touch with me? Don't hesitate to reach out through any of the following channels:</p>
<ul>
<li>Twitter / X: <a target="_blank" href="https://twitter.com/developeraspire">@developeraspire</a></li>
<li>Email: developeraspire5@gmail.com</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a GitHub Template Repository for Scaffolding with React, Vite, and TailwindCSS ]]>
                </title>
                <description>
                    <![CDATA[ Developers love productivity. When it comes to coding, we want to do things fast and we look out for opportunities to re-use things as much as possible. Say, you are getting started with a ReactJS project and want to use TailwindCSS for the same. The... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-github-template-repository-with-react-vite-and-tailwindcss/</link>
                <guid isPermaLink="false">66bdffd0422f318982ba47be</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tapas Adhikary ]]>
                </dc:creator>
                <pubDate>Tue, 09 Jan 2024 18:04:08 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/github-template-vite-react-tailwind.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Developers love productivity. When it comes to coding, we want to do things fast and we look out for opportunities to re-use things as much as possible.</p>
<p>Say, you are getting started with a <code>ReactJS</code> project and want to use <code>TailwindCSS</code> for the same. The first time, it would be fine for you to create a project using the <code>ViteJS</code> tool, and then configure TailwindCSS on top of it.</p>
<p>But the next time (and many more times after that), if you want to start a new React project, would you like to repeat these same steps over and over? A clever developer wouldn't do that. Instead, they'd create a "template" and use it every time they needed something similar in the future.</p>
<p>In this article, we are going to learn how to create a <code>GitHub</code> template repository for scaffolding a new React project with Vite and TailwindCSS. The steps explained in this article will also help you to set up React using Vite, and configure TailwindCSS with it, even if you have reasons not to create the template repository. So, read on.</p>
<p>If you like to learn from video content as well, this article is also available as a video tutorial here: 🙂</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Zk2YJUvfsOA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-is-vite">What is Vite?</h2>
<p><a target="_blank" href="https://vitejs.dev/">Vite (aka ViteJS)</a> is a next generation frontend tooling system that helps developers get started with local development quickly and easily. It supports super fast hot module replacement (HMR) so that there's hardly any lag between changing the source code and seeing it rendered on the browser.</p>
<p>Vite is way faster in starting the dev server than its predecessors like create-react-app (CRA) which was a go-to option for scaffolding React applications. Vite supports JSX, TypeScript, and CSS out-of-the box. It creates optimized builds and manages dependencies in an efficient manner. </p>
<p>Vite comes with templates available for all modern web technologies like vanilla JavaScript/TypeScript, React, Vue, Preact, Lit, Svelte, Solid, and Qwik.</p>
<p>At this moment, Vite is the most viable tooling system available to get started with React development.</p>
<h2 id="heading-how-to-set-up-a-react-project-with-vite">How to Set Up a React Project with Vite</h2>
<p>To get started, make sure you have <code>Node.js</code> version 18+ installed. You can check this by executing the following command from your command prompt (terminal):</p>
<pre><code class="lang-bash">node -v
</code></pre>
<p>This will print the Node.js version you have installed. If you do not have Node.js installed or you have a lower version than v18, go ahead and download and install it from <a target="_blank" href="https://nodejs.org/en">here</a>.</p>
<p>You can use the <code>--template</code> option of the <code>vite</code> library to create a React project using the template. Just copy-paste the following command on your terminal and press enter to execute it:</p>
<pre><code class="lang-bash">npm create vite@latest your_app_name -- --template react
</code></pre>
<p>Note that you need to replace the <code>your_app_name</code> with the name of your project/application. The <code>vite</code> tool will create a directory with the same name with the generated source code under it.</p>
<p>Next, change directory to your project:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> your_app_name
</code></pre>
<p>Now, install the dependencies using this command:</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>Once successful, run the app locally using the following command:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Vite will run the app locally on the URL <code>http://localhost:5173</code> by default.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-16.png" alt="Image" width="600" height="400" loading="lazy">
<em>Vite running the app locally at <code>http://localhost:5173</code></em></p>
<p>You can now open a browser tab and try the URL to see your React application up and running.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-17.png" alt="Image" width="600" height="400" loading="lazy">
<em>React app up and running</em></p>
<p>Congratulations! You have now successfully set up a React app with Vite. Feel free to make any changes to the <code>src/App.jsx</code> source code file to see the changes reflected instantly on the browser.</p>
<h2 id="heading-how-to-configure-tailwindcss-with-vite">How to Configure TailwindCSS with Vite</h2>
<p><a target="_blank" href="https://tailwindcss.com/">TailwindCSS</a> is a utility-first CSS framework that can help make you more productive with its rapid development cycle. It provides utility classes you can use to translate any design into markup effortlessly. </p>
<p>Tailwind works quite well with React, and the two have become a modern combo for building fast websites and web applications.</p>
<h3 id="heading-install-tailwindcss">Install TailwindCSS</h3>
<p>We will now install and configure TailwindCSS with the React application we have created so far with Vite. You can now stop the Vite server if it's running locally for you.</p>
<p>First, let's install <code>tailwindcss</code>, <code>postcss</code>, and <code>autoprefixer</code> as the dev dependencies of the project:</p>
<pre><code class="lang-bash">npm install -D tailwindcss postcss autoprefixer
</code></pre>
<p>A few points worth mentioning about the <code>postcss</code> and <code>autoprefixer</code> here:</p>
<ul>
<li>The <code>tailwindcss</code> framework doesn't provide us the CSS styles that the bowser understands directly. It provides us the utility classes that some tool has to translate to regular CSS that the browser understands.</li>
<li>Also, the produced CSS from the utility classes must work across all browsers (Edge, Chrome, Firefox, Safari, and so on).</li>
</ul>
<p>So we need to have PostCSS and Autoprefixer along with TailwindCSS to set up the expected CSS output at the build phase.</p>
<h3 id="heading-configure-tailwindcss">Configure TailwindCSS</h3>
<p>Now create the configuration file for Tailwind and PostCSS using this command:</p>
<pre><code class="lang-bash">npx tailwindcss init -p
</code></pre>
<p>It will create two files for you:</p>
<ul>
<li><code>tailwind.config.js</code>: the configuration file for TailwindCSS. We will have to change this file to provide some basic configuration to start with. The same file must be edited with additional settings when you want to extend TailwindCSS for any advanced use cases.</li>
<li><code>postcss.config.js</code>: the configuration file for PostCSS. In most cases you do not have to change anything in this file.</li>
</ul>
<p>Open the <code>tailwind.config.js</code> file and replace the existing content with the following:</p>
<pre><code class="lang-js"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Note that we have added a couple of entries to the <code>content</code> array value to tell TailwindCSS what to consider for its utility classes to work. In our case, it must be the <code>index.html</code> file and any <code>.js</code> | <code>.ts</code> or <code>.jsx</code> | <code>.tsx</code> files under the <code>src/</code> directory.</p>
<p>Now open the <code>./src/index.css</code> file and add the <code>@tailwind</code> directives for each of Tailwind’s layers:</p>
<pre><code class="lang-js">@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>
<p>That's it. We have done all the required configuration for TailwindCSS to run with a Vite app.</p>
<h2 id="heading-lets-run-things-together">Let's Run Things Together</h2>
<p>It's time to run things together. Start the Vite server back up locally using the command:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Now edit the <code>src/App.jsx</code> file to replace its content with the following code snippet: </p>
<pre><code class="lang-js">

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</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">h1</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl text-center text-red-700"</span>
      &gt;</span>Welcome to Vite with TailwindCSS and React<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App
</code></pre>
<p>Here, the JSX of the App component returns a heading tag (h1) with some welcome text. Notice the class names used with the <code>&lt;h1&gt;</code> tag. These are all utility classes from the TailwindCSS framework. You can even read them like plain English. We asked TailwindCSS to render a bigger text (3XL), that should be center aligned, and in a shade of red.</p>
<p>Now access the app like before using the URL <code>http://localhost:5173</code>. You should see the output as expected:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-18.png" alt="Image" width="600" height="400" loading="lazy">
<em>Welcome screen in your React/Vite app</em></p>
<p>Congratulations, again! You have now set up React and TailwindCSS with Vite and everything is working as expected.</p>
<h2 id="heading-how-to-create-the-template-repository-on-github">How to Create the Template Repository on GitHub</h2>
<p>All the hard work is done. Now we want to save this work somewhere so you can use it like a template every time you want to start a React project with TailwindCSS. There is no better place than GitHub to store and manage the source code.</p>
<p>Login to your GitHub account and create a new repository by clicking the <code>New</code> button from the <code>repositories</code> tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-19.png" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new repo on GitHub</em></p>
<p>Now, provide a repository name and description and create the repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-20.png" alt="Image" width="600" height="400" loading="lazy">
<em>Enter your repo details and click "create repository"</em></p>
<p>Next, commit, and push the entire project code to this repository. After pushing the project code, go to the <code>Settings</code> of the repository. Under the general settings, you will find a checkbox with a label <code>Template repository</code>. Check that checkbox to make this repository a template repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-21.png" alt="Image" width="600" height="400" loading="lazy">
<em>Make this repo a template repository by checking the checkbox</em></p>
<p>Great! Now you have created a template repository that'll let you create a React and TailwindCSS project with a single-click in the future. </p>
<p>Now, you will find a new button called <code>Use this template</code> at the top-right corner of your repository. You can click on it to create a new project repository from this template. If your template repository is public, anyone else from the developer community can use it to create their project repository. Amazing, isn't it?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-22.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I've created a template repository using the same steps we discussed in this article. Please feel free to check it out and if you like the work, give the repository a star ⭐.</p>
<p><a target="_blank" href="https://github.com/atapas/vite-tailwind-react">https://github.com/atapas/vite-tailwind-react</a></p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>That's all for now. I hope you found this article informative and insightful. I regularly publish meaningful posts on my <a target="_blank" href="https://blog.greenroots.info/">GreenRoots Blog</a>, and I think you'll find them helpful, too.</p>
<p>Let's connect.</p>
<ul>
<li>I am an educator on my YouTube channel, <code>tapaScript</code>. Please <a target="_blank" href="https://www.youtube.com/tapasadhikary?sub_confirmation=1">SUBSCRIBE</a> to the channel if you want to learn JavaScript, ReactJS, Next.js, Node.js, Git, and all about Web Development in the fundamental way.</li>
<li><a target="_blank" href="https://twitter.com/tapasadhikary">Follow me on X (Twitter</a>) or <a target="_blank" href="https://www.linkedin.com/in/tapasadhikary/">LinkedIn</a> if you don't want to miss the daily dose of Web Development and Programming Tips.</li>
<li>Find all my public speaking talks <a target="_blank" href="https://www.tapasadhikary.com/talks">here</a>.</li>
<li>Check out and follow my Open Source work on <a target="_blank" href="https://github.com/atapas">GitHub</a>.</li>
</ul>
<p>See you soon with my next article. Until then, please take care of yourself, and stay happy.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Scroll Animations with React, Tailwind CSS, and Framer Motion ]]>
                </title>
                <description>
                    <![CDATA[ By Manu Arora Scroll-based animations are triggered when a user scrolls on a webpage. Recently, I built a Scroll Animation with Framer Motion that moves grids in uneven directions. This project prompted me to write a tutorial about how I did that her... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-scroll-animations-with-framer-motion-and-react/</link>
                <guid isPermaLink="false">66d46012c7632f8bfbf1e443</guid>
                
                    <category>
                        <![CDATA[ animations ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 27 Nov 2023 18:47:58 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/FreeCodeCamp.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Manu Arora</p>
<p>Scroll-based animations are triggered when a user scrolls on a webpage. Recently, I built a <a target="_blank" href="https://www.aceternity.com/components/container-scroll-animation">Scroll Animation with Framer Motion</a> that moves grids in uneven directions. This project prompted me to write a tutorial about how I did that here on freeCodeCamp.  </p>
<p>The Framer Motion library makes it super easy to integrate animations into your React applications. With a few lines of code, you can achieve what might seem like a difficult task.</p>
<p>Today, we are going to build a scroll-triggered animation that rotates, translates, and scales a card (or a container) when the user scrolls.</p>
<h2 id="heading-what-were-building">What We're Building:</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://stackblitz.com/edit/stackblitz-starters-2mybwg?embed=1&amp;file=src%2FScroll.tsx&amp;view=preview">https://stackblitz.com/edit/stackblitz-starters-2mybwg?embed=1&amp;file=src%2FScroll.tsx&amp;view=preview</a></div>
<p>Here, as the user scrolls, three things are going to happen:</p>
<ol>
<li>The text <code>Unleash the power of Scroll Animations</code> moves up a little bit.</li>
<li>The Frame (the black container which is holding the cards) rotates and aligns perfectly with the page.</li>
<li>The cards inside the frame translate up a bit – providing a parallax effect.</li>
</ol>
<p>All of these actions are achieved with the help of the <code>scrollYProgress</code> value from the <code>useScroll()</code> function from Framer Motion. The <code>scrollYProgress</code> value (more on this later) gives you the progress between <code>0</code> to <code>1</code>, determining where the user currently is on the page.</p>
<p>Let's jump into the code and see how to implement this animation from scratch.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>For this demo, we are going to use:</p>
<ul>
<li>Next.js for writing our component</li>
<li>Tailwind CSS for styling and CSS</li>
<li>Framer Motion for animations</li>
</ul>
<h2 id="heading-how-to-setup-the-project"><strong>How to Setup the Project</strong></h2>
<p>Setting up the project is pretty simple. Here are the steps you should follow:</p>
<p>First, you'll need to install Next.js if you don't already have it.</p>
<p>Open your terminal and type the following command:</p>
<pre><code>npx create-next-app@latest scroll-animation --typescript --eslint
</code></pre><p>This will initialize a <code>Next.js</code> application where you can go to the <code>app</code> directly for routes and the <code>components</code> folder to create your components.</p>
<p>We are going to keep it really simple for this demo and add the component in the <code>components</code> folder.</p>
<p>Next you'll need to install Tailwind CSS, which you can do like this:</p>
<pre><code>npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre><p>Now add Tailwind to your project by copying the following file contents into the <code>tailwind.config.ts</code> file which gets created after you run the above step:</p>
<pre><code>tailwind.config.js

<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./app/**/*.{js,ts,jsx,tsx,mdx}"</span>,
    <span class="hljs-string">"./pages/**/*.{js,ts,jsx,tsx,mdx}"</span>,
    <span class="hljs-string">"./components/**/*.{js,ts,jsx,tsx,mdx}"</span>,

    <span class="hljs-comment">// Or if using `src` directory:</span>
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx,mdx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre><p>Now add the following global styles in the <code>globals.css</code> file:</p>
<pre><code>@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre><p>With these steps completed, you should be able to write the component and add it to your project seamlessly.  </p>
<p>Now that we are done with the setup, let's deep dive into the component code that we are going to build.</p>
<h2 id="heading-how-to-build-the-scroll-component">How to Build the Scroll Component</h2>
<p>There's essentially only one main component that we are going to work with. We are calling it <code>Scroll</code>, because why not? Here's the code:</p>
<pre><code class="lang-tsx">export const Scroll = () =&gt; {
  return (
    &lt;div className="flex flex-col bg-white h-screen w-screen"&gt;
      &lt;ScrollCore /&gt;
    &lt;/div&gt;
  );
};
export const ScrollCore = () =&gt; {
  return (
    &lt;div className="h-[120vh] p-10 flex items-center justify-center relative "&gt;
      &lt;div
        className="py-40 w-full relative"
      &gt;
        &lt;Header /&gt;
        &lt;Card /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export const Header = () =&gt; {
  return (
    &lt;div
      className="div max-w-5xl mx-auto text-center"
    &gt;
      &lt;h1 className="text-4xl font-semibold"&gt;
        Unleash the power of &lt;br /&gt;{' '}
        &lt;span className="text-5xl lg:text-6xl  font-bold mt-1 leading-none"&gt;
          Scroll Animations
        &lt;/span&gt;
      &lt;/h1&gt;
    &lt;/div&gt;
  );
};

export const Card = () =&gt; {
    // Going to implement later in the blog
};
</code></pre>
<p>The <code>Scroll</code> components is the container which contains a <code>ScrollCore</code> component.</p>
<p>The <code>ScrollCore</code> component holds the <code>Header</code> and the <code>Card</code> components:</p>
<ul>
<li>The <code>Header</code> is the text component that translates to the top (as we saw in the preview</li>
<li>The <code>Card</code> component is the <code>Frame</code> that we talked about earlier.</li>
</ul>
<p>Both of these components are styles with Tailwind CSS. We give a class of h-screen and w-screen to the container, and we want the container to take the entire height and width of the screen.</p>
<h2 id="heading-how-to-build-the-card-component">How to Build the Card Component</h2>
<p>The <code>Card</code> component is pretty basic (without the animation) since we are going to render multiple cards inside a container with <code>grids</code> from Tailwind CSS. Here's the code:</p>
<pre><code class="lang-tsx">import {users} from './users';

export const Card = () =&gt; {
  return (
    &lt;div
      style={{
        boxShadow:
          '0 0 #0000004d, 0 9px 20px #0000004a, 0 37px 37px #00000042, 0 84px 50px #00000026, 0 149px 60px #0000000a, 0 233px 65px #00000003',
      }}
      className="max-w-5xl -mt-12 mx-auto h-[30rem] md:h-[40rem] w-full border-4 border-[#6C6C6C] p-6 bg-[#222222] rounded-[30px] shadow-2xl"
    &gt;
      &lt;div className="bg-gray-100 h-full w-full rounded-2xl grid grid-cols-2 md:grid-cols-4 gap-4 overflow-hidden p-4"&gt;
        {users.map((user, idx) =&gt; (
          &lt;div
            key={`user-${idx}`}
            className="bg-white rounded-md cursor-pointer relative"
          &gt;
            &lt;div className="absolute top-2 right-2 rounded-full text-xs font-bold bg-white px-2 py-1"&gt;
              {user.badge}
            &lt;/div&gt;
            &lt;img
              src={user.image}
              className="rounded-tr-md rounded-tl-md text-sm "
            /&gt;
            &lt;div className="p-4"&gt;
              &lt;h1 className="font-semibold text-sm "&gt;{user.name}&lt;/h1&gt;
              &lt;h2 className=" text-gray-500 text-xs "&gt;{user.designation}&lt;/h2&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        ))}
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>Here, we are giving a <code>box shadow</code> which I have taken from <a target="_blank" href="https://manuarora.in/boxshadows">Box shadows for Tailwind CSS</a>. Also we are giving it a background of <code>#22222</code>. In tailwind, we can use arbitrary values using the <code>[]</code> notation. For example, we have given the class <code>bg-[#22222]</code> for the background.</p>
<p>We are also using a <code>users</code> array to render out a list of users. The user array looks something like this:</p>
<pre><code class="lang-tsx">export const users = [
  {
    name: 'Manu Arora',
    designation: 'Founder, Algochurn',
    image: 'https://picsum.photos/id/10/300/300',
    badge: 'Mentor',
  },
  {
    name: 'Sarah Singh',
    designation: "Founder, Sarah's Kitchen",
    image: 'https://picsum.photos/id/11/300/300',
    badge: 'Mentor',
  },
  // Rest of the users...
];
</code></pre>
<p>Here, we are rendering out the user's name, designation, image, and badge.</p>
<p>Now we're done with the basic <code>cards</code> and <code>header</code> design, so we can move on to animating these using Framer Motion.</p>
<h2 id="heading-how-to-add-the-animation-functions">How to Add the Animation Functions</h2>
<p>Framer Motion provides helpful functions that you can use to animate anything on a webpage. Some of the use cases of these animations might be:</p>
<ul>
<li>Animate when the user drags and drops</li>
<li>Animate when the user scrolls</li>
<li>Animate when the user clicks or hovers</li>
<li>Animate on page load</li>
</ul>
<p>In this demo, we want to animate on <code>scroll</code>. For that, we can use the <code>useScroll()</code> function provided by Framer Motion.</p>
<p>To animate using scroll, we are going to:</p>
<ol>
<li>Get <code>scrollYProgress</code> from <code>useScroll()</code> method</li>
<li>Use the <code>useTransform</code> hook to transform <code>scrollYProgress</code> values</li>
<li>Use the transformed values to animate our cards.</li>
</ol>
<p>Let's take a look at the code snippet for the same:</p>
<pre><code class="lang-tsx">import { useScroll, useTransform, motion } from 'framer-motion';

import { users } from './users';

export const Scroll = () =&gt; {
  return (
    &lt;div className="flex flex-col bg-white h-screen w-screen"&gt;
      &lt;ScrollCore /&gt;
    &lt;/div&gt;
  );
};
export const ScrollCore = () =&gt; {
  const { scrollYProgress } = useScroll();

  const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
  const scale = useTransform(scrollYProgress, [0, 1], [1.05, 1]);
  const translate = useTransform(scrollYProgress, [0, 1], [0, -100]);

  return (
    &lt;div className="h-[120vh] transform scale-[0.8] p-10 flex items-center justify-center relative "&gt;
      &lt;div
        className="py-40 w-full relative"
        style={{
          perspective: '1000px',
        }}
      &gt;
        &lt;Header /&gt;
        &lt;Card /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
// rest of the code
</code></pre>
<p>Let's break down what's happening in the code:</p>
<p>We are using the <code>scrollYProgress</code> value from the <code>useScroll()</code> function. This <code>scrollYProgress</code> is a <code>motion value</code> which we can use with <code>motion.div</code> from Framer Motion to animate various CSS properties. The CSS properties we are going to animate are <code>rotate</code>, <code>scale</code> and <code>translate</code>.</p>
<p>We combine these <code>scrollYProgress</code> values with another hook from framer-motion which is <code>useTransform</code>.  </p>
<p>The <code>useTransform</code> hook is responsible from converting one value to another. For example, if we want to rotate the card from 45 degrees to 90 degrees when the user scrolls from the top of the page to bottom of the page, we could use something like this:</p>
<pre><code class="lang-tsx"> const rotate = useTransform(scrollYProgress, [0, 1], [45, 90]);
</code></pre>
<p>This <code>rotate</code> value can now be passed to the <code>style</code> tag of a <code>motion.div</code> element. Remember that a regular a div cannot be used here since rotate is a MOTION VALUE and should be used with a motion.div element.</p>
<p>Similarly, we are going to add all the values for <code>rotate</code>, <code>scale</code> and <code>translate</code> like this:</p>
<pre><code class="lang-tsx">const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
const scale = useTransform(scrollYProgress, [0, 1], [1.05, 1]);
const translate = useTransform(scrollYProgress, [0, 1], [0, -100]);
</code></pre>
<p>Here:</p>
<ol>
<li>When a user scrolls from top to bottom (0 is starting, 1 is ending, meaning the user has scrolled from top to bottom), we want to rotate the card from 20 degrees to 0 degrees. But here's a catch: we also specify the <code>perspective</code> property and set it to <code>800px</code> so that it gives a 3D effect.</li>
<li>We want the scale to go from <code>1.05</code> to <code>1</code> when the user scrolls.</li>
<li>And finally we want to translate the cards from 0 to -100 px in the Y direction (later on how we are going to animate the Y direction).</li>
</ol>
<p>Now that we have all the animations setup, we just need to pass these values to the components' <code>style</code> tags and get the animations working.</p>
<h2 id="heading-how-to-add-the-animations">How to Add the Animations</h2>
<p>We saw earlier how we use the <code>useScroll()</code> hook to get scroll progress and get the <code>rotate</code>, <code>scale</code> and <code>translate</code> values (these values are called motion values, because they can only be used with a <code>motion.div</code> block). Now it's time to actually use these values within our component.</p>
<p>We can do this by passing down the <code>rotate</code>, <code>scale</code> and <code>translate</code> values to the <code>Header</code> and <code>Card</code> component and using them in their respective style tags.  </p>
<p>Let's have a look at the code snippet to understand it better:</p>
<pre><code class="lang-tsx">import { useScroll, useTransform, motion } from 'framer-motion';

import { users } from './users';

export const Scroll = () =&gt; {
  return (
    &lt;div className="flex flex-col bg-white h-screen w-screen"&gt;
      &lt;ScrollCore /&gt;
    &lt;/div&gt;
  );
};
export const ScrollCore = () =&gt; {
  const { scrollYProgress } = useScroll();

  const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
  const scale = useTransform(scrollYProgress, [0, 1], [1.05, 1]);
  const translate = useTransform(scrollYProgress, [0, 1], [0, -100]);

  return (
    &lt;div className="h-[120vh] transform scale-[0.8] p-10 flex items-center justify-center relative "&gt;
      &lt;div
        className="py-40 w-full relative"
        style={{
          perspective: '1000px',
        }}
      &gt;
        &lt;Header translate={translate} /&gt;
        &lt;Card rotate={rotate} translate={translate} scale={scale} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
// rest of the code
</code></pre>
<p>Here, we are passing down the translate, rotate, and scale values inside the components:</p>
<pre><code class="lang-tsx">&lt;Header translate={translate} /&gt;
&lt;Card rotate={rotate} translate={translate} scale={scale} /&gt;
</code></pre>
<ul>
<li>For <code>Header</code>, we only want to translate the text from bottom to top a bit (sort of giving a parallax effect)</li>
<li>For <code>Card</code>, we want to do a bit more with rotation and translation.</li>
</ul>
<p>Here's the code for the <code>Header</code> component:</p>
<pre><code class="lang-tsx">export const Header = ({ translate }: any) =&gt; {
  return (
    &lt;motion.div
      style={{
        translateY: translate,
      }}
      className="div max-w-5xl mx-auto text-center"
    &gt;
      &lt;h1 className="text-4xl font-semibold"&gt;
        Unleash the power of &lt;br /&gt;{' '}
        &lt;span className="text-5xl lg:text-6xl  font-bold mt-1 leading-none"&gt;
          Scroll Animations
        &lt;/span&gt;
      &lt;/h1&gt;
    &lt;/motion.div&gt;
  );
};
</code></pre>
<p>Here, we converted the <code>div</code> to <code>motion.div</code> so that it can accept motion values.</p>
<p>We also animated the <code>translateY</code> property given by Framer Motion and added the <code>translate</code> value. Remember, translate goes from <code>0</code> to <code>-100</code>.</p>
<p>And that is it to animate the text. Essentially for the <code>Header</code> component, we have written just three lines of code to animate it. Pretty cool, huh?</p>
<p>Here's the code for the <code>Card</code> component:</p>
<pre><code class="lang-tsx">export const Card = ({
  rotate,
  scale,
  translate,
}: {
  rotate: any;
  scale: any;
  translate: any;
}) =&gt; {
  return (
    &lt;motion.div
      style={{
        rotateX: rotate, // rotate in X-axis
        scale,
        boxShadow:
          '0 0 #0000004d, 0 9px 20px #0000004a, 0 37px 37px #00000042, 0 84px 50px #00000026, 0 149px 60px #0000000a, 0 233px 65px #00000003',
      }}
      className="max-w-5xl -mt-12 mx-auto h-[30rem] md:h-[40rem] w-full border-4 border-[#6C6C6C] p-6 bg-[#222222] rounded-[30px] shadow-2xl"
    &gt;
      &lt;div className="bg-gray-100 h-full w-full rounded-2xl grid grid-cols-2 md:grid-cols-4 gap-4 overflow-hidden p-4"&gt;
        {users.map((user, idx) =&gt; (
          &lt;motion.div
            key={`user-${idx}`}
            className="bg-white rounded-md cursor-pointer relative"
            style={{ translateY: translate }}
            whileHover={{
              boxShadow:
                '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
            }}
          &gt;
            &lt;div className="absolute top-2 right-2 rounded-full text-xs font-bold bg-white px-2 py-1"&gt;
              {user.badge}
            &lt;/div&gt;
            &lt;img
              src={user.image}
              className="rounded-tr-md rounded-tl-md text-sm "
            /&gt;
            &lt;div className="p-4"&gt;
              &lt;h1 className="font-semibold text-sm "&gt;{user.name}&lt;/h1&gt;
              &lt;h2 className=" text-gray-500 text-xs "&gt;{user.designation}&lt;/h2&gt;
            &lt;/div&gt;
          &lt;/motion.div&gt;
        ))}
      &lt;/div&gt;
    &lt;/motion.div&gt;
  );
};
</code></pre>
<p>This is the same code as before with just one difference: the <code>div</code> is now converted to a <code>motion.div</code> to accept values wherever required. Here, we want the <code>cards</code> inside of the container to translate, and the entire card itself to rotate.</p>
<p>From the code above, let's look at this part closely:</p>
<pre><code class="lang-tsx">&lt;motion.div
      style={{
        rotateX: rotate, // rotate in X-axis
        scale,
        boxShadow:
          '0 0 #0000004d, 0 9px 20px #0000004a, 0 37px 37px #00000042, 0 84px 50px #00000026, 0 149px 60px #0000000a, 0 233px 65px #00000003',
      }}
      className="max-w-5xl -mt-12 mx-auto h-[30rem] md:h-[40rem] w-full border-4 border-[#6C6C6C] p-6 bg-[#222222] rounded-[30px] shadow-2xl"
    &gt;
    // rest of the code...
&lt;/motion.div&gt;
</code></pre>
<p>Here, we want to: </p>
<ol>
<li><code>rotateX</code> so that it goes from giving an illusion of lying flat on the screen to standing straight. (Remember, as the user scrolls, the value goes from <code>20deg</code> to <code>0deg</code>.)</li>
<li>The <code>scale</code> of the container (or the frame) is also animated to go from <code>1.05</code> to <code>1</code>.</li>
</ol>
<p>Let's look at the cards inside this frame now:</p>
<pre><code class="lang-tsx">{users.map((user, idx) =&gt; (
          &lt;motion.div
            key={`user-${idx}`}
            className="bg-white rounded-md cursor-pointer relative"
            style={{ translateY: translate }}
            whileHover={{
              boxShadow:
                '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
            }}
          &gt;
            &lt;div className="absolute top-2 right-2 rounded-full text-xs font-bold bg-white px-2 py-1"&gt;
              {user.badge}
            &lt;/div&gt;
            &lt;img
              src={user.image}
              className="rounded-tr-md rounded-tl-md text-sm "
            /&gt;
            &lt;div className="p-4"&gt;
              &lt;h1 className="font-semibold text-sm "&gt;{user.name}&lt;/h1&gt;
              &lt;h2 className=" text-gray-500 text-xs "&gt;{user.designation}&lt;/h2&gt;
            &lt;/div&gt;
          &lt;/motion.div&gt;
        ))}
</code></pre>
<p>Here, the <code>translateY</code> property is animated and the div is converted to a <code>motion.div</code>.</p>
<p>And that's it. That's all it takes to animate on scroll using Framer Motion.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you learned how to animate using Framer Motion. Essentially, we looked at two core functions:</p>
<ul>
<li><code>useScroll()</code></li>
<li><code>useTransform()</code></li>
</ul>
<p>There are other functions you can use which can help you achieve your animation goals. But I believe Framer Motion is a really simple animation API to work with, given its intuitive functions and ease of use.  </p>
<p>Here's the complete <a target="_blank" href="https://stackblitz.com/edit/stackblitz-starters-2mybwg?file=src%2FScroll.tsx">source code</a> for the demo.</p>
<p>I've created various other demos like this <a target="_blank" href="https://www.aceternity.com/components/parallax-scroll">Parallax Effect Using Tailwind CSS and Framer Motion</a> which essentially uses the same pattern that we discussed in this guide.  </p>
<p>If you liked this demo and want me to create more of these cool <a target="_blank" href="https://aceternity.com/components">Tailwind CSS and Framer motion components</a>, let me know on <a target="_blank" href="https://twitter.com/mannupaaji">Twitter</a> and I'd be more than happy to work on it. :)</p>
<p>Happy coding ✨</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn SvelteKit and Tailwind CSS by Building a Web Portfolio ]]>
                </title>
                <description>
                    <![CDATA[ Whether you're a seasoned developer or just starting your journey in the world of web development, keeping up with the latest tools and frameworks is essential. And you can learn about two popular web dev tools in the new course we just posted on the... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-sveltekit-and-tailwind-css-by-building-a-web-portfolio/</link>
                <guid isPermaLink="false">66b20517297cd6de0bd54676</guid>
                
                    <category>
                        <![CDATA[ Svelte ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 03 Oct 2023 15:11:31 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/sveltekit.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Whether you're a seasoned developer or just starting your journey in the world of web development, keeping up with the latest tools and frameworks is essential.</p>
<p>And you can learn about two popular web dev tools in the new course we just posted on the freeCodeCamp.org YouTube channel. In this course, you'll learn how to create a web portfolio using SvelteKit &amp; TailwindCSS.</p>
<p>SvelteKit is more than just a flavor of the month; it's a game-changer in building web applications. It comes with built-in server-side rendering, routing, and many other features that simplify the developer's journey.</p>
<p>But what's web development without a touch of aesthetics? Enter Tailwind CSS. A utility-first CSS framework, Tailwind enables developers to rapidly craft custom user interfaces without getting bogged down by custom CSS.</p>
<p>The combination of SvelteKit and Tailwind CSS promises a development experience like no other, allowing for the creation of interactive, responsive, and beautiful web applications.</p>
<p>James McArthur created this course. He is a full-stack developer with a knack for creating engaging tutorials. James delves deep into the process of building a web portfolio from scratch, making it a perfect watch for those aiming to showcase their skills and projects to potential employers or simply wanting to mark their presence on the internet.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/image-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>This is the front page of the page you will create.</em></p>
<p>The tutorial takes a hands-on approach, guiding viewers step by step. By the end of it, not only will you have a fully functional web portfolio, but you'll also have it live on the internet. As James amusingly puts it, "you can send it to your mum."</p>
<p>The portfolio demo features a captivating landing page for a fictitious character, Samuel Oak (or as James calls him, "fake small James"). It boasts aesthetically pleasing elements like particle animation effects that add depth and dynamism to the page. The navigation is seamless, leading to sections that display projects, an 'about me' segment, and a unique table that playfully yet effectively pitches you as the top candidate for job applications.</p>
<p>For anyone eager to establish a striking online presence, this course is a great place to start. Not only does it teach the technicalities of web development but also emphasizes the importance of aesthetics and user experience.</p>
<p>You can watch the full course on <a target="_blank" href="https://youtu.be/-2UjwQzxvBQ">the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/-2UjwQzxvBQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Design System in Next.js with Tailwind CSS and Class Variance Authority ]]>
                </title>
                <description>
                    <![CDATA[ By Olasunkanmi Balogun Building a web application and producing a smooth user experience in the always changing world of web development requires more than just good looks – you also need to make sure that your application's design is efficient and c... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-a-design-system-in-next-js-with-tailwind-css-and-class-variance-authority/</link>
                <guid isPermaLink="false">66d4608c9f2bec37e2da065e</guid>
                
                    <category>
                        <![CDATA[ Design Systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 21 Sep 2023 07:58:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/09/martin-adams-_OZCl4XcpRw-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Olasunkanmi Balogun</p>
<p>Building a web application and producing a smooth user experience in the always changing world of web development requires more than just good looks – you also need to make sure that your application's design is efficient and consistent throughout. </p>
<p>If you have a design system in place, you can easily build UI components that are consistent and can be reused across numerous projects while still looking nice.</p>
<p>A design system is a set of reusable UI components and design tokens. These tokens are like building blocks that include things like buttons, colors, and fonts. Its goal is to enable developers and designers to create engaging product experiences by providing consistent user experience across all products.</p>
<p>Designers often use tools like Figma to create these systems. Here's an example of a design system made in Figma:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/Design-System.jpg" alt="Design-System" width="600" height="400" loading="lazy"></p>
<p>After designers make these parts, developers can use different tools to actually use them and build the website. There are various frameworks that frontend engineers can choose from to do this. Some popular ones are <code>MaterialUI</code> and <code>ChakraUI</code>. These libraries can make things easier, but they might not cover all the special cases you want.</p>
<p>If you or your design team have specific designs in mind that don't match what existing libraries offer, you'll need to dive into writing a substantial amount of code. However, this process can quickly become tiresome and burdensome, especially when the code you're creating lacks consistency or when you find yourself repeatedly constructing the same UI elements.</p>
<p>This is where a design system becomes incredibly valuable. Armed with the knowledge of how to implement a design system, you gain the ability to craft a tailored design that seamlessly integrates with the existing design system, offering the level of flexibility you desire.</p>
<p>Furthermore, this approach offers the advantage of only having to construct only the components you truly need. Unlike using libraries, which often come with numerous pre-built components you might never use, this method keeps your codebase focused and efficient.</p>
<p>Creating your own design might sound appealing, but it's not as straightforward as it might appear. </p>
<p>Nonetheless, with the assistance of styling tools such as <code>Tailwind CSS</code> (which is ideal due to its high level of customization), a library known as <a class="post-section-overview" href="#https://cva.style/docs"><code>cva</code></a> (which is short for <code>class-variance-authority</code>), and along with <code>TypeScript</code>, the process of establishing your custom design system within <code>Next.js</code> becomes notably achievable.</p>
<p>Now that you have a clear understanding of what a design system is and its significance, continue reading to discover how these tools can be effectively combined to achieve our objectives.</p>
<h2 id="heading-project-setup-installation-of-the-cva-library">Project Setup: Installation Of The CVA Library</h2>
<p>For the purpose of this article, it is assumed that you have already set up your <code>Next.js</code> project and incorporated <code>TypeScript</code> and <code>Tailwind CSS</code>. </p>
<p>If not, integrating these two elements into a new <code>Next.js</code> project is a straightforward process.</p>
<p>Following this initial setup, you can effortlessly add the <code>CVA</code> library to your project using the following command:</p>
<pre><code class="lang-npm">npm i class-variance-authority
</code></pre>
<p>With the inclusion of <code>CVA</code>, a user-friendly interface is at your disposal, simplifying the definition of variants. </p>
<p>These variants enable the conditional application of class sets, while also offering the means to express default variations. If this sounds a bit complex at the moment, don't worry – we'll delve into practical examples as we move forward.</p>
<h2 id="heading-practical-example-implementing-a-custom-design-system">Practical Example: Implementing a Custom Design System</h2>
<p>Imagine a situation where you're constructing a CRUD application, equipped with <code>buttons</code> to carry out various actions such as <code>create</code>, <code>read</code>, <code>update</code>, and <code>delete</code>.</p>
<p>While these <code>buttons</code> might share common attributes like <code>font-size</code> and <code>border-radius</code>, they might differ slightly – for instance, in their colors. Perhaps you'd prefer a red <code>button</code> for the delete action, a blue one for create, and a black one for update.</p>
<p>Traditionally, you might consider creating separate components for each <code>button</code> and assigning them distinct colors. For instance, to make the black <code>button</code>, you'd include the <code>Tailwind CSS</code> utility class <code>bg-black</code> to set the background color:</p>
<pre><code class="lang-tsx">export default function Button() {
  return (
    &lt;button
      className="bg-black rounded-3xl py-2 text-white w-80 font-sm"
      // other button attributes
    &gt;
      //button text
    &lt;/button&gt;
  );
}
</code></pre>
<p>However, instead of creating separate components for each <code>button</code>, you can streamline the process by designing a single <code>button</code> component that can adapt to the various button variants you need. This concept is the heart of a design system. </p>
<p>Let's explore how we can transform this same button component we previously created to suit our needs.</p>
<p>Our first step is to import the <code>cva</code> function and the <code>VariantProps</code> from the <code>class-variance-authority</code> library. As we proceed, the significance of these imports will become apparent:</p>
<pre><code class="lang-tsx">import { cva, VariantProps } from 'class-variance-authority'
</code></pre>
<p>Subsequently, we will define a variable named <code>buttonStyles</code>. This variable will house the invocation of the cva function. </p>
<p>Within this function, we'll first provide the default <code>styles</code> for the buttons, followed by the <code>variants</code> object. This object will contain the variations for the different types of buttons we desire. This segment is pivotal when implementing the design system. </p>
<p>In your code, include the following code snippet:</p>
<pre><code class="lang-tsx">const buttonStyles = cva("rounded-3xl py-2 text-white w-80 font-sm", {
  variants: {
    intent: {
      primary: "bg-blue-700",
      secondary: "bg-black",
      danger: "bg-red-600",
    },
    defaultVariants: {
      intent: "primary",
    },
  },
});
</code></pre>
<p>Within the embedded <code>variants</code> object, there exists another object named <code>intent</code>. This is where you assign names to the distinct button variations you desire – for instance, <code>primary</code>, <code>secondary</code>, and <code>danger</code>, corresponding to the <code>create</code>, <code>update</code>, and <code>delete</code> buttons respectively. </p>
<p>The style defined for the <code>intent</code> specified when rendering the <code>button</code> will be applied to that <code>button</code>, wherever it appears.</p>
<p>Additionally, the <code>variants</code> object features another property called <code>defaultVariants</code>. The <code>intent</code> within the <code>defaultVariant</code> object will be applied when no explicit <code>intent</code> is provided. In this instance, we have set the default <code>intent</code> to <code>primary</code>.</p>
<p>Next, we'll define an interface named <code>ButtonProps</code> which extends the <code>VariantProps</code> type provided by the <code>class-variance-authority</code> library:</p>
<pre><code class="lang-tsx">interface ButtonProps extends VariantProps&lt;typeof buttonStyles&gt; {
  text: string;
}
</code></pre>
<p>This interface is used to define the props that the <code>Button</code> component will accept. The <code>VariantProps</code> type from <code>class-variance-authority</code> adds the <code>intent</code> property, which will be used to determine the <code>variant</code> of the <code>button</code>. The interface also includes an additional property called <code>text</code>, which is of type <code>string</code>, it is the actual text that will appear on the <code>button</code>. </p>
<p>Having done this, we can go ahead and implement the <code>Button</code> component with respect to this defined <code>interface</code>:</p>
<pre><code class="lang-tsx">export default function Button({ intent, text, ...props }: ButtonProps) {
  return (
    &lt;button className={buttonStyles({ intent })} {...props}&gt;
      {text}
    &lt;/button&gt;
  );
}
</code></pre>
<p>In the <code>Button</code> component function, we used object destructuring to receive the <code>intent</code> property from the <code>ButtonProps</code> passed to the component. </p>
<p>Additionally, we used the spread operator <code>(...props)</code> to spread any other props that might be passed to the component. This allows us to easily pass down any other attributes or event handlers to the <code>&lt;button&gt;</code> element.</p>
<p>This also makes our <code>props</code> accurately typed, with their types precisely aligned as you hover over each one. For instance, the <code>intent</code> prop strictly adheres to the <code>intents</code> we defined within the <code>buttonStyles</code> variable, ensuring a robust type correspondence:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/Screenshot--166-.png" alt="Screenshot--166-" width="600" height="400" loading="lazy"></p>
<p>Moving on, the crucial part is the <code>className</code> attribute on the <code>&lt;button&gt;</code> element. We're using the <code>buttonStyles</code> function to pass in an object with the <code>intent</code> property, which corresponds to the desired button variant (<code>primary</code>, <code>secondary</code>, or <code>danger</code>). </p>
<p>This dynamically generates the appropriate <code>styles</code> based on the selected <code>intent</code>, applying the associated <code>styles</code> to the button.</p>
<p>At last, we've streamlined the process of creating <code>buttons</code> with different <code>styles</code>, allowing us to utilize a single <code>Button</code> component while dynamically altering its appearance through the <code>intent</code> prop, showcasing the effective implementation of our personalized design system.</p>
<p>With this in place, you're now equipped to render the <code>Button</code> component according to your needs and preferences. As a visual demonstration, let's observe the outcomes after rendering each of the variants:</p>
<pre><code class="lang-tsx">&lt;&gt;
  &lt;Button intent="primary" text="Create" /&gt; &lt;br /&gt; &lt;br /&gt;
  &lt;Button intent="secondary" text="Update" /&gt; &lt;br /&gt; &lt;br /&gt;
  &lt;Button intent="danger" text="Delete" /&gt; &lt;br /&gt; &lt;br /&gt;
&lt;/&gt;
</code></pre>
<p>Visual result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/chrome-capture-2023-7-15--1-.png" alt="chrome-capture-2023-7-15--1-" width="600" height="400" loading="lazy"></p>
<p>This visual representation underscores the versatility and adaptability of our streamlined <code>Button</code> component, showcasing the distinct styling achieved through the intent-based approach within our own crafted design system.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Throughout this journey, you've gained insights into the potential of a design system to produce a versatile button component that dynamically changes colors in response to the provided intent. </p>
<p>It's important to note that this adaptability isn't confined solely to colors – you can similarly experiment with font sizes, text colors, and various other CSS properties.</p>
<p>Empowered by the insights you've gathered, you're now positioned to leverage this newfound knowledge to not only enhance your understanding of design systems but to also implement creative solutions that align with your unique project requirements. Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Meteor Effect with React and TailwindCSS ]]>
                </title>
                <description>
                    <![CDATA[ By Manu Arora A while ago, I saw a post on Twitter that had some fancy beams of light emanating out from behind the main image. It looked like a meteor was blazing softly behind the card, and I thought it was a really cool UI component to have in ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-meteor-effect-with-react-and-tailwindcss/</link>
                <guid isPermaLink="false">66d4601b3a8352b6c5a2aaa8</guid>
                
                    <category>
                        <![CDATA[ animations ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 11 Aug 2023 16:14:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/Screenshot-2023-08-11-at-9.20.20-PM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Manu Arora</p>
<p>A while ago, I saw a post on Twitter that had some fancy beams of light emanating out from behind the main image. It looked like a meteor was blazing softly behind the card, and I thought it was a really cool UI component to have in a project.</p>
<p>Looking at that, I thought of creating a React component of my own that would do the same job – that is, adding this meteor glowing effect to the background of a card.  </p>
<p>This would make the card stand out instantly and would be really useful if you wanted to highlight a specific card from your set of UI cards.</p>
<p>So that's what we're going to build in this tutorial.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>For creating this effect, we are going to use:</p>
<ul>
<li><strong>Next.js</strong> for our framework (because we are going to create a component)</li>
<li><strong>TailwindCSS</strong> for styling</li>
</ul>
<h2 id="heading-how-to-setup-the-project">How to Setup the Project</h2>
<p>To setup a project in Next.js and TailwindCSS, simply follow these steps:</p>
<p>First, head over to the terminal and enter the following command:</p>
<pre><code class="lang-bash">npx create-next-app@latest meteor-effect --typescript --eslint
</code></pre>
<p>Once you have <code>Next.js</code> installed, cd into the project like this:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> meteor-effect
</code></pre>
<p>Then install TailwindCSS like this:</p>
<pre><code class="lang-bash">npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre>
<p>Setup the <code>tailwind.config.js</code> file so that Tailwind knows where to find your styles:</p>
<pre><code class="lang-js">tailwind.config.js

<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./app/**/*.{js,ts,jsx,tsx,mdx}"</span>,
    <span class="hljs-string">"./pages/**/*.{js,ts,jsx,tsx,mdx}"</span>,
    <span class="hljs-string">"./components/**/*.{js,ts,jsx,tsx,mdx}"</span>,

    <span class="hljs-comment">// Or if using `src` directory:</span>
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx,mdx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Once you've setup the config file, add the base styles for Tailwind to the global stylesheet like this:</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>With these steps, you should be good to go.</p>
<h2 id="heading-what-are-we-building">What Are We Building?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Screenshot-2023-08-11-at-9.20.20-PM-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>the cool lines at the background are meteors</em></p>
<p>A <strong>Meteor</strong> (as I call it, you can call it anything really) is basically a UI element with a head and a tail. It's a sort of beam which has a gradient.  </p>
<p>In the image, the background lines are what I call meteors. We are going to animate these lines to go from the left side of the card container to the right side, giving an illusion of a meteor shower.</p>
<p>We are going to build:</p>
<ul>
<li>A beautiful Card Component</li>
<li>A Meteor component to add to the card</li>
</ul>
<p>But before we build the core Meteor component, let's create the container that holds the meteors.</p>
<h2 id="heading-how-to-create-the-card-container">How to Create the Card Container</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Screenshot-2023-08-11-at-9.21.23-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>The card container we'll build here</em></p>
<p>Before we create the <code>Meteor</code> component, let's first create the Container that will hold down our meteors.</p>
<p>The Card Container has four parts to it:</p>
<ul>
<li>An SVG icon</li>
<li>A headline</li>
<li>Content Section</li>
<li>Call to Action</li>
</ul>
<p>We are going to style this card with TailwindCSS and give it a background gradient too so that it looks good.</p>
<pre><code class="lang-tsx">import React from "react";

export const MeteorPreview = () =&gt; {
  return (
    &lt;div className=" h-[40rem]"&gt;
      &lt;div className=" h-3/4 md:h-1/2 w-3/4  relative max-w-sm"&gt;
        &lt;div className="absolute inset-0 h-full w-full bg-gradient-to-r from-blue-500 to-teal-500 transform scale-[0.80] bg-red-500 rounded-full blur-3xl" /&gt;
        &lt;div className="relative shadow-xl bg-gray-900 border border-gray-800  px-4 py-8 h-full overflow-hidden rounded-2xl flex flex-col justify-end items-start"&gt;
          &lt;div className="h-5 w-5 rounded-full border flex items-center justify-center mb-4 border-gray-500"&gt;
            &lt;svg
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 24 24"
              stroke-width="1.5"
              stroke="currentColor"
              className="h-2 w-2 text-gray-300"
            &gt;
              &lt;path
                stroke-linecap="round"
                stroke-linejoin="round"
                d="M4.5 4.5l15 15m0 0V8.25m0 11.25H8.25"
              /&gt;
            &lt;/svg&gt;
          &lt;/div&gt;

          &lt;h1 className="font-bold text-xl text-white mb-0 mt-4 relative z-50"&gt;
            Meteors because they're cool
          &lt;/h1&gt;

          &lt;p className="font-normal text-base text-slate-500 mb-4 relative z-50"&gt;
            I don't know what to write so I'll just paste something cool here.
            One more sentence because lorem ipsum is just unacceptable.
          &lt;/p&gt;

          &lt;button className="border px-4 py-1 rounded-lg !text-sm  border-gray-500 text-gray-300"&gt;
            Explore &amp;rarr;
          &lt;/button&gt;

        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>Firstly, we created a container that holds the content of the card. This container contains the following:</p>
<ul>
<li>An SVG Icon at the top of the container</li>
<li>A headline, which is a one liner that shows the purpose of the card</li>
<li>Some content, which is contained in a paragraph tag explaining the contents of the card</li>
<li>CTA, which is a button element that takes the user to some other part of the website.</li>
</ul>
<p>Now that we have the Card component in place (the container), lets create the <code>Meteor</code> component.</p>
<h2 id="heading-how-to-build-the-meteor-component">How to Build the Meteor Component</h2>
<p>Like I explained before, a meteor is nothing but a UI element with a head and a tail that has a gradient. We are going to build exactly that here.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Screenshot-2023-08-11-at-9.04.17-AM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Standalone meteors</em></p>
<p>Every meteor will have:</p>
<ul>
<li>A tail of width of <code>50px</code></li>
<li>A tail height of <code>1px</code></li>
<li>A background gradient (for the tail) which goes from <code>#64748b</code> to <code>transparent</code></li>
<li>A head of height and width <code>2px</code></li>
</ul>
<pre><code class="lang-tsx">import clsx from "clsx";
import React from "react";

export const Meteors = ({ number }: { number?: number }) =&gt; {
  const meteors = new Array(number || 20).fill(true);
  return (
    &lt;&gt;
      {meteors.map((el, idx) =&gt; (
        &lt;span
          key={"meteor" + idx}
          className={clsx(
            "animate-meteor-effect absolute top-1/2 left-1/2 h-0.5 w-0.5 rounded-[9999px] bg-slate-500 shadow-[0_0_0_1px_#ffffff10] rotate-[215deg]",
            "before:content-[''] before:absolute before:top-1/2 before:transform before:-translate-y-[50%] before:w-[50px] before:h-[1px] before:bg-gradient-to-r before:from-[#64748b] before:to-transparent"
          )}
          style={{
            top: 0,
            left: Math.floor(Math.random() * (400 - -400) + -400) + "px",
            animationDelay: Math.random() * (0.8 - 0.2) + 0.2 + "s",
            animationDuration: Math.floor(Math.random() * (10 - 2) + 2) + "s",
          }}
        &gt;&lt;/span&gt;
      ))}
    &lt;/&gt;
  );
};
</code></pre>
<p>Let's understand the Meteor component.</p>
<ul>
<li>The <code>Meteor</code> component takes in a prop of <code>numbers</code>. This is created in such a way that if you pass the numbers, the meteors increase. The default is set to 20.</li>
<li>We create a <code>span</code> element which will essentially be our meteor that goes from left to right.</li>
<li>the <code>before:</code> class creates a pseudo before element which is essentially the <code>line</code> part of the meteor. We are giving it a linear gradient and a width of <code>50px</code>.</li>
<li>The <code>style</code> tag decides where the meteor is currently in the DOM. We are going to use <code>Math.random()</code> to randomly place meteors on the background.</li>
<li>To actually animate the meteors, we are using the class of <code>animate-meteor-effect</code>. This class is actually added in the <code>tailwind.config.js</code> file to add moving animation.</li>
</ul>
<pre><code class="lang-js"> theme: {
    <span class="hljs-attr">extend</span>: {
      <span class="hljs-attr">animation</span>: {
        <span class="hljs-string">"meteor-effect"</span>: <span class="hljs-string">"meteor 5s linear infinite"</span>,
      },
      <span class="hljs-attr">keyframes</span>: {

        <span class="hljs-attr">meteor</span>: {
          <span class="hljs-string">"0%"</span>: { <span class="hljs-attr">transform</span>: <span class="hljs-string">"rotate(215deg) translateX(0)"</span>, <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span> },
          <span class="hljs-string">"70%"</span>: { <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span> },
          <span class="hljs-string">"100%"</span>: {
            <span class="hljs-attr">transform</span>: <span class="hljs-string">"rotate(215deg) translateX(-500px)"</span>,
            <span class="hljs-attr">opacity</span>: <span class="hljs-number">0</span>,
          },
        },
      },
    },
  },
</code></pre>
<p>Here, we are basically moving the beam to negative <code>500px</code> in a span of 5 seconds. This is what causes the meteor to move from left to right. It also nicely animates the opacity from visible to hidden (when we are 70% of the way through).</p>
<h2 id="heading-how-to-use-the-meteor-component">How to Use the Meteor Component</h2>
<p>Now since we have the <code>Meteor</code> component in place, we can easily embed it into our card component that we created previously:</p>
<pre><code class="lang-tsx">import React from "react";
import { Meteors } from "./Meteors";

export const MeteorPreview = () =&gt; {
  return (
    &lt;div className=" h-[40rem]"&gt;
      &lt;div className=" h-3/4 md:h-1/2 w-3/4  relative max-w-sm"&gt;
        &lt;div className="absolute inset-0 h-full w-full bg-gradient-to-r from-blue-500 to-teal-500 transform scale-[0.80] bg-red-500 rounded-full blur-3xl" /&gt;
        &lt;div className="relative shadow-xl bg-gray-900 border border-gray-800  px-4 py-8 h-full overflow-hidden rounded-2xl flex flex-col justify-end items-start"&gt;
          &lt;div className="h-5 w-5 rounded-full border flex items-center justify-center mb-4 border-gray-500"&gt;
            &lt;svg
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 24 24"
              stroke-width="1.5"
              stroke="currentColor"
              className="h-2 w-2 text-gray-300"
            &gt;
              &lt;path
                stroke-linecap="round"
                stroke-linejoin="round"
                d="M4.5 4.5l15 15m0 0V8.25m0 11.25H8.25"
              /&gt;
            &lt;/svg&gt;
          &lt;/div&gt;

          &lt;h1 className="font-bold text-xl text-white mb-0 mt-4 relative z-50"&gt;
            Meteors because they're cool
          &lt;/h1&gt;

          &lt;p className="font-normal text-base text-slate-500 mb-4 relative z-50"&gt;
            I don't know what to write so I'll just paste something cool here.
            One more sentence because lorem ipsum is just unacceptable. 
          &lt;/p&gt;

          &lt;button className="border px-4 py-1 rounded-lg !text-sm  border-gray-500 text-gray-300"&gt;
            Explore &amp;rarr;
          &lt;/button&gt;

          {/* Meaty part - Meteor effect */}
          &lt;Meteors number={10} /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>Here, we are embedding the <code>&lt;Meteor /&gt;</code> component with a <code>number={10}</code> prop to only have 10 meteors in our component.</p>
<p>The final component looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Screenshot-2023-08-11-at-9.20.20-PM-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>The final Card Component with Meteors</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>It is really easy to create beautiful components that stand out with TailwindCSS  </p>
<p>I loved creating this component from scratch and hope it helps you make your components stand out.  </p>
<p>If you'd like to see more of these cool <a target="_blank" href="https://aceternity.com/components">TailwindCSS and Framer motion components</a>, let me know on <a target="_blank" href="https://twitter.com/mannupaaji">Twitter</a> and I'd be more than happy to work on it. :)  </p>
<p>Happy coding! ✨</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Tailwind CSS with NextJS ]]>
                </title>
                <description>
                    <![CDATA[ By Kelvin Moses Tailwind CSS is a popular utility-first CSS framework that offers a unique approach to building modern and responsive user interfaces.  Unlike traditional CSS frameworks that provide pre-designed components, Tailwind CSS focuses on pr... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-tailwind-css-with-next-js/</link>
                <guid isPermaLink="false">66d45f31a3a4f04fb2dd2e59</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 07 Jun 2023 21:37:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/Tutorials.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Kelvin Moses</p>
<p>Tailwind CSS is a popular utility-first CSS framework that offers a unique approach to building modern and responsive user interfaces. </p>
<p>Unlike traditional CSS frameworks that provide pre-designed components, Tailwind CSS focuses on providing a comprehensive set of utility classes that you can directly apply to your HTML elements. </p>
<p>When combined with Next.js, a powerful React framework for building server-side rendered applications, you can unlock a seamless development experience and create performant, scalable web applications. </p>
<p>In this tutorial, I'll will walk you through the process of setting up Tailwind CSS with Next.js, so you can harness the power of Tailwind's utility classes in your Next.js projects. </p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before getting started, make sure you have the following prerequisites:</p>
<ol>
<li>Node.js installed on your machine</li>
<li>Basic knowledge of JavaScript and React</li>
<li>Familiarity with Next.js</li>
</ol>
<h2 id="heading-how-tailwind-css-and-utility-classes-work">How Tailwind CSS and Utility Classes Work</h2>
<p>Tailwind CSS follows a utility-first approach to styling. It provides a vast collection of small, single-purpose utility classes that you can apply directly to your HTML elements. </p>
<p>Each utility class represents a specific CSS property or combination of properties, making it easy to build complex UI components by composing these classes together. </p>
<p>For example, instead of defining a custom CSS class for setting the color of a button, you can simply apply the "text-blue-500" utility class to achieve the desired effect.</p>
<p>The utility classes in Tailwind CSS are designed to be highly flexible and customizable. You can easily adjust properties such as margin, padding, font size, colors, and more by leveraging the intuitive naming conventions provided by Tailwind. </p>
<p>This approach offers a fine-grained level of control over your styles, eliminating the need to write custom CSS for most common styling scenarios.</p>
<h2 id="heading-why-use-tailwind-css-with-nextjs">Why Use Tailwind CSS with Next.js?</h2>
<p>Next.js is a powerful framework for building server-side rendered React applications. It provides an excellent development experience and offers features such as automatic code splitting, server-side rendering, and static site generation. </p>
<p>When you use Next together with Tailwind CSS, it enables you to effortlessly integrate Tailwind's utility classes into your application development workflow.</p>
<p>Next.js optimizes the delivery of CSS styles by automatically tree-shaking unused CSS during the build process. This means that even though Tailwind CSS provides an extensive set of utility classes, only the styles you actually use in your application will be included in the final bundle. This approach ensures that your application remains lightweight and performs efficiently.</p>
<p>By leveraging the combination of Tailwind CSS and Next.js, you can quickly prototype, design, and build responsive user interfaces while enjoying the benefits of a streamlined development process and optimized performance.</p>
<p>Now that you understand the benefits and synergy between Tailwind CSS and Next.js, let's dive into the step-by-step process of setting up Tailwind CSS with Next.js.</p>
<h2 id="heading-step-1-create-a-new-nextjs-project">Step 1: Create a New Next.js Project</h2>
<p>To begin, let's create a new Next.js project. Open your terminal and run the following command:</p>
<pre><code class="lang-bash">npx create-next-app my-tailwind-project
</code></pre>
<p>This will create a new Next.js project named "my-tailwind-project" in a directory with the same name.</p>
<h2 id="heading-step-2-install-tailwind-css">Step 2: Install Tailwind CSS</h2>
<p>Navigate to the project directory by running the following command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> my-tailwind-project
</code></pre>
<p>Next, install Tailwind CSS and its dependencies by running the following command:</p>
<pre><code class="lang-bash">    npm install tailwindcss postcss autoprefixer
</code></pre>
<h2 id="heading-step-3-configure-tailwind-css">Step 3: Configure Tailwind CSS</h2>
<p>After installing Tailwind CSS, we need to configure it to work with Next.js. Create a new file named postcss.config.js in the project's root directory and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-string">'tailwindcss'</span>,
    <span class="hljs-string">'postcss-flexbugs-fixes'</span>,
    <span class="hljs-string">'postcss-preset-env'</span>,
    [
      <span class="hljs-string">'postcss-normalize'</span>,
      {
        <span class="hljs-attr">allowDuplicates</span>: <span class="hljs-literal">false</span>,
      },
    ],
    [
      <span class="hljs-string">'@fullhuman/postcss-purgecss'</span>,
      {
        <span class="hljs-attr">content</span>: [<span class="hljs-string">'./pages/**/*.{js,jsx,ts,tsx}'</span>, <span class="hljs-string">'./components/**/*.{js,jsx,ts,tsx}'</span>],
        <span class="hljs-attr">defaultExtractor</span>: <span class="hljs-function">(<span class="hljs-params">content</span>) =&gt;</span> content.match(<span class="hljs-regexp">/[\w-/:]+(?&lt;!:)/g</span>) || [],
      },
    ],
    <span class="hljs-string">'autoprefixer'</span>,
  ],
};
</code></pre>
<p>This configuration sets up Tailwind CSS, adds necessary PostCSS plugins, and includes PurgeCSS to remove unused CSS in production builds.</p>
<h2 id="heading-step-4-create-tailwind-css-configuration">Step 4: Create Tailwind CSS Configuration</h2>
<p>Next, we need to generate a default configuration file for Tailwind CSS. This configuration file allows you to customize Tailwind CSS according to your project's specific needs. It defines the color palette, typography settings, spacing utilities, and more.</p>
<p>The tailwind.config.js file serves as the central configuration hub for Tailwind CSS. It provides a JavaScript object where you can define your customizations and extend the default configuration.</p>
<p>By generating the default configuration file, you have the flexibility to modify various aspects of Tailwind CSS, including theme customization, extending utility classes, and purging unused CSS during production builds.</p>
<p>Customizing the configuration file allows you to tailor the utility classes and overall design system to align with your application's requirements and branding guidelines.</p>
<p>Let's now generate the default configuration file for Tailwind CSS. Run the following command:</p>
<pre><code class="lang-bash">npx tailwindcss init
</code></pre>
<p>This will create a tailwind.config.js file in your project's root directory.</p>
<h2 id="heading-step-5-customize-tailwind-css">Step 5: Customize Tailwind CSS</h2>
<p>Open the tailwind.config.js file and customize Tailwind CSS according to your project's needs. Here's an example of what a customized tailwind.config.js file might look like:</p>
<pre><code class="lang-javascript">
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">purge</span>: [<span class="hljs-string">'./pages/**/*.{js,ts,jsx,tsx}'</span>, <span class="hljs-string">'./components/**/*.{js,ts,jsx,tsx}'</span>],
  <span class="hljs-attr">darkMode</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {
      <span class="hljs-attr">colors</span>: {
        <span class="hljs-attr">primary</span>: <span class="hljs-string">'#FF0000'</span>,
        <span class="hljs-attr">secondary</span>: <span class="hljs-string">'#00FF00'</span>,
      },
      <span class="hljs-attr">fontFamily</span>: {
        <span class="hljs-attr">sans</span>: [<span class="hljs-string">'Roboto'</span>, <span class="hljs-string">'Arial'</span>, <span class="hljs-string">'sans-serif'</span>],
      },
    },
  },
  <span class="hljs-attr">variants</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
};
</code></pre>
<p>In this example, we're customizing the Tailwind CSS configuration by:</p>
<ul>
<li>Specifying the files to be purged using the purge option. This ensures that only the CSS classes used in your project are included in the final build.</li>
<li>Disabling the dark mode functionality by setting <strong>darkMode</strong> to <strong>false</strong>.</li>
<li>Extending the color palette with two custom colors: <strong>primary</strong> and <strong>secondary</strong>.</li>
<li>Adding a custom font family called <strong>sans</strong> that includes the fonts Roboto, Arial, and the generic sans-serif fallback.</li>
<li>Keeping the <strong>variants</strong> and <strong>plugins</strong> sections empty for this basic configuration.</li>
</ul>
<p>You can further customize Tailwind CSS by exploring other configuration options available in the official documentation. Tailwind CSS provides extensive flexibility, allowing you to tailor the framework to your project's specific design requirements.</p>
<p>Once you've customized the tailwind.config.js file, save it, and proceed to the next step.</p>
<h2 id="heading-step-6-import-tailwind-css-styles">Step 6: Import Tailwind CSS Styles</h2>
<p>To use Tailwind CSS styles in your Next.js project, import the styles into your pages/_app.js file. Open the file and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">'tailwindcss/tailwind.css'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyApp</span>(<span class="hljs-params">{ Component, pageProps }</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span></span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MyApp;
</code></pre>
<p>This imports the compiled Tailwind CSS styles and applies them to your entire application.</p>
<h2 id="heading-step-7-start-the-development-server">Step 7: Start the Development Server</h2>
<p>Now, you're ready to start the development server and see Tailwind CSS in action. Run the following command:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Visit <code>http://localhost:3000</code> in your browser, and you should see your Next.js application with Tailwind CSS styling applied.</p>
<p>This will generate a production-ready build of your application, including only the necessary CSS styles.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! You have successfully set up Tailwind CSS with Next.js. You can now start leveraging Tailwind's utility classes to rapidly build beautiful and responsive UI components in your Next.js projects. </p>
<p>Feel free to explore the Tailwind CSS documentation to discover the wide range of utility classes available.</p>
<p>Remember to regularly update Tailwind CSS and its dependencies to benefit from the latest features and bug fixes.</p>
<p>Happy coding!😊
Let's connect <a target="_blank" href="https://twitter.com/iam_kelvinjnr">IamKelv</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Tailwind CSS ]]>
                </title>
                <description>
                    <![CDATA[ Tailwind CSS is a powerful tool for creating beautiful and functional designs. We just published a video course on the freeCodeCamp.org channel that is designed to introduce you to the basics of using Tailwind CSS, and to help you understand how to u... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-tailwind-css/</link>
                <guid isPermaLink="false">66b2051c27569435a9255ad4</guid>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 19 Jan 2023 16:50:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/tailwindcss.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Tailwind CSS is a powerful tool for creating beautiful and functional designs.</p>
<p>We just published a video course on the freeCodeCamp.org channel that is designed to introduce you to the basics of using Tailwind CSS, and to help you understand how to use it to create effective and efficient designs for your projects.</p>
<p>Guillaume Duhan created this course. He has been a frontend developer for 10 years and he has created many courses on Udemy and his own YouTube channel.</p>
<p>The course is divided into several sections, each of which covers a different aspect of using Tailwind CSS. The first section, "Setup," will teach you how to install and configure Tailwind CSS in your project. You'll learn how to set up your project to use Tailwind CSS, and how to customize the settings to fit your specific needs.</p>
<p>The next section, "Colors," will cover how to use Tailwind's color utility classes to style your text, backgrounds, and other elements. You'll learn how to use the color palette provided by Tailwind CSS, and how to customize it to match your brand colors.</p>
<p>"Customization" is the next section, in this section you'll learn how to customize the styles provided by Tailwind CSS to better fit your design. You'll learn about how to create custom classes, how to extend the default classes, and how to customize the colors and other settings to fit your design.</p>
<p>"Typography" is the next section, in this section you'll learn how to use Tailwind CSS to create beautiful and consistent typography across your website. You'll learn about the different typography utility classes available, and how to use them to create a consistent and professional look for your text.</p>
<p>"Spaces &amp; sizes" is the next section, in this section you'll learn how to use Tailwind CSS to control the spacing and size of your elements. You'll learn about the different spacing and size utility classes available, and how to use them to create a consistent and professional look for your layout.</p>
<p>The next three sections are "Flex", "Grids", and "Layouts". In these sections, you'll learn how to use Tailwind CSS to create flexible and responsive layouts. You'll learn about the different layout utility classes available, and how to use them to create a responsive and flexible layout that looks great on any device.</p>
<p>In the "Borders" section you'll learn how to use Tailwind CSS to create borders and dividers. </p>
<p>"Effects &amp; filters" is the next section, in this section you'll learn how to use Tailwind CSS to create various effects and filters. You'll learn about the different effect and filter utility classes available, and how to use them to create a consistent and professional look for your layout.</p>
<p>"Animations" is the next section, in this section you'll learn how to use Tailwind CSS to create animations. </p>
<p>"Design System" is the next section, in this section you'll learn how to use Tailwind CSS to create a design system. You'll learn about how to use the utility classes to create a consistent and professional look for your layout, and how to organize and structure your design system to ensure consistency and efficiency in your design process.</p>
<p>Finally, in the "Core concepts" section, you'll learn about the core concepts behind Tailwind CSS, such as the utility-first approach, the use of modular and composable classes, and the importance of accessibility and performance in web design.</p>
<p>And the last section "Dark mode" will be the last one in the course, in this section you'll learn how to use Tailwind CSS to create a dark mode. </p>
<p>By the end of this course, you'll have a solid understanding of how to use Tailwind CSS to create beautiful and functional designs, and you'll be able to use it to create your own projects with confidence.</p>
<p>You can watch the full course on <a target="_blank" href="https://youtu.be/ft30zcMlFao">the freeCodeCamp.org YouTube channel</a> (4-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/ft30zcMlFao" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
