<?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[ CSS - 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[ CSS - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 07 Jul 2026 22:44:41 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/css/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Dark Mode Toggle Without JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Over the years, I've worked on many Static Site Generated (SSG) websites that work without JavaScript. And during that time, I've created a few solutions. One of them is a dark mode toggle that doesn' ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-dark-mode-toggle-without-javascript/</link>
                <guid isPermaLink="false">6a4bb3bf6aa89de94c683aa8</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ dark mode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jakub T. Jankiewicz ]]>
                </dc:creator>
                <pubDate>Mon, 06 Jul 2026 13:55:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/166e89af-ab96-48c8-bf0b-cbc5972aad2c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Over the years, I've worked on many Static Site Generated (SSG) websites that work without JavaScript. And during that time, I've created a few solutions. One of them is a dark mode toggle that doesn't require JavaScript.</p>
<p>I created this solution for my <a href="https://jakub.jankiewicz.org/blog/">own blog</a>, and then I enhanced it for the <a href="https://wikizeit.edu.pl/">WikiZEIT project</a>. I also included the improved version in my <a href="https://complite.jcubic.pl/">Eleventy starter "Complite"</a>.</p>
<p>In this article, I'll explain how to create a dark mode toggle with just HTML and CSS with help from the new <code>:has()</code> selector. I will also use CSS variables.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-why-should-websites-work-without-javascript">Why Should Websites Work Without JavaScript?</a></p>
</li>
<li><p><a href="#heading-html-structure">HTML Structure</a></p>
</li>
<li><p><a href="#heading-css-code">CSS Code</a></p>
<ul>
<li><p><a href="#heading-styling-the-toggle">Styling the Toggle</a></p>
</li>
<li><p><a href="#heading-css-variables-and-the-website-style">CSS Variables and the Website Style</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-should-websites-work-without-javascript">Why Should Websites Work Without JavaScript?</h2>
<p>If you're wondering why a website may benefit from using no JavaScript, there are several reasons, like accessibility (a11y), SEO, and AI visibility.</p>
<p>Modern screen readers can handle JavaScript websites, but this isn't the only thing you should care about. People might be using old computers or phones. Others might have poor internet connections and may disable JavaScript to use less bandwidth.</p>
<p>As for SEO, Google is one of the few major crawlers that can reliably render JavaScript-heavy pages, but rendering can still take extra time compared with indexing server-rendered HTML. Many AI crawlers and answer engines don't appear to execute JavaScript the way a browser does. They often work from the raw HTML response.</p>
<p>So if, for example, you have a React app that's purely client-side rendered, a bot that only reads the initial HTML may see little more than <code>&lt;div id="root"&gt;&lt;/div&gt;</code> until JavaScript runs.</p>
<p>To be clear, the issue isn't React itself, but client-side rendering. Server-side rendering, static generation, or prerendering can make your content available in the initial HTML so search engines and AI crawlers can read it more reliably.</p>
<p>But the point remains: if you want your website to be reliably more accessible to all people and machines, you should make it work even without JavaScript.</p>
<p>Note that your website doesn't need to be written in pure/only HTML and CSS. As mentioned above, if you use solutions like React, consider also using a framework like <a href="https://nextjs.org/">Next.js</a> with Static Site Rendering (SSR) or a Static Site Generator (SSG) like <a href="https://gohugo.io/">Hugo</a>. You can also consider modern SSG solutions like <a href="https://www.11ty.dev/">Eleventy</a> that I'm using.</p>
<p>To learn more about Elventy and Hugo, you can read those two articles:</p>
<ul>
<li><p><a href="https://www.freecodecamp.org/news/your-first-hugo-blog-a-practical-guide/">How to Create Your First Hugo Blog: a Practical Guide</a></p>
</li>
<li><p><a href="https://www.freecodecamp.org/news/learn-eleventy/">Learn the Eleventy Static Site Generator by Building and Deploying a Portfolio Website</a></p>
</li>
</ul>
<p>To learn more about Next.js, you can <a href="https://www.youtube.com/results?search_query=Next.js+tutorial">search videos on YouTube</a> (but remember to check if they explain the modern App Router, not the old Page Router).</p>
<h2 id="heading-html-structure">HTML Structure</h2>
<p>Alright, now that you understand why this approach can be useful, let's dive in and build our dark mode toggle. The HTML of the toggle uses HTML radio buttons:</p>
<pre><code class="language-html">&lt;div class="theme-toggle"&gt;
    &lt;input type="radio" id="mode_dark"
           name="mode" value="dark"&gt;
    &lt;label for="mode_dark"
           aria-label="Switch to dark mode"&gt;
      🌙
    &lt;/label&gt;
    &lt;input type="radio" id="mode_light"
            name="mode" value="light"&gt;
    &lt;label for="mode_light"
           aria-label="Switch to light mode"&gt;
      ☀️
     &lt;/label&gt;
&lt;/div&gt;
</code></pre>
<p>The above code uses moon and sun emojis.</p>
<h2 id="heading-css-code">CSS Code</h2>
<p>Now let's see the CSS part of the solution. The trick to making this work without JavaScript involves using this CSS <code>:has</code> pseudo-class:</p>
<pre><code class="language-css">:has(#mode_dark:checked)
</code></pre>
<p>The way this works is a bit like a parent selector that was always missing in CSS. If you have some code like this <code>p:has(img)</code>, it will match all <code>&lt;p&gt;</code> tags that have images inside.</p>
<p>In our case, <code>:has</code> will match when there's a radio button or checkbox selected anywhere on the page. This is the <code>:checked</code> part and and <code>#mode_dark</code> is the <code>id</code> of the input for the dark mode that we have in the <a href="#heading-html-structure">HTML Structure</a> section.</p>
<p>So to summarize, you can add <code>:has</code> to any element that can be styled when dark mode is selected. Here's an example:</p>
<pre><code class="language-css">html:has(#mode_dark:checked) p {
  color: white;
  background: black;
}
</code></pre>
<p>This CSS will style all <code>&lt;p&gt;</code> tags when dark mode is enabled.</p>
<p>The above example (plus the HTML) is everything you need to create a CSS-only dark mode switch.</p>
<h3 id="heading-styling-the-toggle">Styling the Toggle</h3>
<p>Here's the CSS that styles the toggle so only one emoji (sun or moon) is displayed at a time. This is only to make the toggle look nice.</p>
<p>The code also makes sure that the initially selected value is always the system-preferred mode.</p>
<pre><code class="language-css">/* style of the toggle */
.theme-toggle {
  display: flex;
  align-items: center;
}

/* hide the input */
.theme-toggle input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.theme-toggle label {
    width: 40px; height: 40px;
    display: grid;
    place-items: center;
    cursor: pointer;
}

/* icon visibility
 *
 * default light with system and radio button overwrite
 */
label[for="mode_light"] { display: none; }
label[for="mode_dark"] { display: grid; }

@media (prefers-color-scheme: dark) {
  label[for="mode_light"] { display: grid; }
  label[for="mode_dark"] { display: none; }
}
:root:has(#mode_dark:checked) label[for="mode_light"] {
   display: grid;
}
:root:has(#mode_dark:checked) label[for="mode_dark"] {
   display: none;
}
:root:has(#mode_light:checked) label[for="mode_light"] {
   display: none;
}
:root:has(#mode_light:checked) label[for="mode_dark"] {
   display: grid;
}
</code></pre>
<p>The <code>:root</code> selector above means the root of the HTML tree. It's often used instead of <code>html</code>.</p>
<p>The CSS code <code>@media (prefers-color-scheme: dark)</code> is a media query, a way to add CSS code when special conditions are met. Here the media query is checking whether the user has system settings set to dark mode.</p>
<p>The code hides the inputs, and the labels control the toggle of the radio button. This is a common way to style radio buttons and checkboxes.</p>
<div>
<div>💡</div>
<div>The toggle always displays the mode that it's switching into. That's why in dark mode the sun is showing, not the moon.</div>
</div>

<h3 id="heading-css-variables-and-the-website-style">CSS Variables and the Website Style</h3>
<p>The last part is to style the website. Here we have CSS variables with only two colors. But in a real website, you might have all colors and settings for the dark/light mode that's applied to the whole page:</p>
<pre><code class="language-css">@media (prefers-color-scheme: dark) {
  :root {
    --bg: #252525; /* dark gray */
    --fg: #fff;    /* white */
  }
}
:root:has(#mode_dark:checked) {
  --bg: #252525; /* dark gray */
  --fg: #fff;    /* white */
}
:root:has(#mode_light:checked) {
  --bg: #fff;    /* white */
  --fg: #252525; /* dark gray */
}
</code></pre>
<p>It's useful to use CSS variables because you can put styles that change between dark and light mode in one place. And then you can use one variable instead of hardcoding each style all over your CSS file.</p>
<p>So instead of using code like this:</p>
<pre><code class="language-css">html:has(#mode_dark:checked) p {
  color: #fff;
  background: #252525;
}
</code></pre>
<p>You can use variables:</p>
<pre><code class="language-css">html:has(#mode_dark:checked) p {
    background: var(--bg);
    color: var(--fg);
}
</code></pre>
<p>In case of background and foreground colors, you only need this code:</p>
<pre><code class="language-css">body {
  background: var(--bg);
  color: var(--fg);
}
</code></pre>
<p>You can read more about CSS variables with the <code>:root</code> selector in this article:</p>
<ul>
<li><a href="https://www.freecodecamp.org/news/what-are-css-variables-and-how-to-use-them/">CSS Variables Definition – What are CSS Vars and How to Use Them?</a></li>
</ul>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>When creating a website, it's always worth making it work without JavaScript. It's good for accessibility and SEO.</p>
<p>Now with modern CSS, most of the things a website needs can be done without JS. You should incorporate <a href="https://en.wikipedia.org/wiki/Progressive_enhancement">progressive enhancement</a> and add JavaScript on top of the existing HTML/CSS foundation.</p>
<p>To read more about progressive enhancement, check this article: <a href="https://www.freecodecamp.org/news/what-is-progressive-enhancement-and-why-it-matters-e80c7aaf834a/">What is Progressive Enhancement, and why it matters</a>.</p>
<p>And here is a <a href="https://codepen.io/jcubic/pen/myRqrmj">CodePen demo of the whole solution</a>.</p>
<p>If you have any questions, you can contact me on <a href="https://x.com/jcubic">Twitter/X</a>. My DMs are open. You can also check out my <a href="https://jakub.jankiewicz.org/blog/">personal blog</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Text Compare Tool with HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever tried to spot the differences between two long paragraphs of text? Reading line-by-line to find a missing word or a new sentence is a massive headache. In this tutorial, you'll build you ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-text-compare-tool-html-css-javascript/</link>
                <guid isPermaLink="false">6a4533f11247307c0491a76f</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bansidhar Kadiya ]]>
                </dc:creator>
                <pubDate>Wed, 01 Jul 2026 15:36:17 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/632b58c6-8930-421d-b17d-847b24bb0e9e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever tried to spot the differences between two long paragraphs of text? Reading line-by-line to find a missing word or a new sentence is a massive headache.</p>
<p>In this tutorial, you'll build your very own browser-based Text Compare Tool. It will take an original piece of text, compare it against a changed version, and instantly highlight exactly what was added or removed.</p>
<p>Building this project will help you level up your JavaScript skills. You'll also create a tool that's highly secure, because everything happens locally in the user's browser. No sensitive data is ever sent to a server.</p>
<p>Let’s get started.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along easily, you should know:</p>
<ul>
<li><p><strong>Basic HTML and CSS knowledge:</strong> How to structure a page and use Flexbox to put items side-by-side.</p>
</li>
<li><p><strong>Basic JavaScript knowledge:</strong> How to write functions, use arrays, and listen for button clicks.</p>
</li>
<li><p><strong>Your Setup:</strong> A code editor (like VS Code) and a web browser to view your work.</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-step-1-set-up-your-project-files">Step 1: Set Up Your Project Files</a></p>
</li>
<li><p><a href="#heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</a></p>
</li>
<li><p><a href="#heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</a></p>
</li>
<li><p><a href="#heading-step-4-write-the-javascript-engine">Step 4: Write the JavaScript Engine</a></p>
</li>
<li><p><a href="#heading-step-5-test-your-application">Step 5: Test Your Application</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-step-1-set-up-your-project-files">Step 1: Set Up Your Project Files</h2>
<p>First, you need a place to store your code. Create a new folder on your computer and name it <code>text-compare-tool</code>.</p>
<p>Inside that folder, create three empty files:</p>
<ul>
<li><p><code>index.html</code> (This holds the structure of your app)</p>
</li>
<li><p><code>style.css</code> (This makes your app look good)</p>
</li>
<li><p><code>script.js</code> (This makes your app actually work)</p>
</li>
</ul>
<h2 id="heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</h2>
<p>Open your <code>index.html</code> file. You need to create a simple layout with two large text boxes: one for the original text, and one for the updated text.</p>
<p>Copy and paste this code into your HTML file:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Text Compare Tool&lt;/title&gt;
    &lt;link rel="stylesheet" href="style.css"&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;h1&gt;Text Compare Tool&lt;/h1&gt;
    &lt;p class="description"&gt;
        Quickly find every addition and deletion between two versions of your text. Just paste them into our tool, and we’ll show you exactly what’s been changed.
    &lt;/p&gt;

    &lt;div class="container"&gt;
        
        &lt;div class="panels-wrapper"&gt;
            &lt;!-- Left Side: Original Text --&gt;
            &lt;div class="panel"&gt;
                &lt;textarea id="text1" placeholder="Paste your original text here..."&gt;&lt;/textarea&gt;
                &lt;div id="result1" class="result-box"&gt;&lt;/div&gt;
            &lt;/div&gt;
            
            &lt;!-- Right Side: Changed Text --&gt;
            &lt;div class="panel"&gt;
                &lt;textarea id="text2" placeholder="Paste your changed text here..."&gt;&lt;/textarea&gt;
                &lt;div id="result2" class="result-box"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Action Buttons --&gt;
        &lt;div class="controls"&gt;
            &lt;button class="btn-compare" onclick="compareText()"&gt;Compare&lt;/button&gt;
            &lt;button class="btn-clear" onclick="clearText()"&gt;Clear&lt;/button&gt;
        &lt;/div&gt;

    &lt;/div&gt;

    &lt;script src="script.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Understanding the HTML:</p>
<ul>
<li><p><strong>The two panels:</strong> Inside the <code>.panels-wrapper</code>, you have a left side and a right side.</p>
</li>
<li><p><strong>Textareas vs results:</strong> Each side has a <code>&lt;textarea&gt;</code> where the user can type. Right below the text area is a <code>&lt;div&gt;</code> with the class <code>.result-box</code>. Right now, those result boxes are invisible. Later, JavaScript will hide the text areas and show the result boxes instead.</p>
</li>
<li><p><strong>The buttons:</strong> The "Compare" and "Clear" buttons are hooked up to JavaScript functions using <code>onclick</code>.</p>
</li>
</ul>
<h2 id="heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</h2>
<p>A good utility tool should be easy on the eyes. You'll use a clean white and blue design, and apply soft red and green colors to highlight the text changes.</p>
<p>Open your <code>style.css</code> file and add this code:</p>
<pre><code class="language-css">:root {
    --primary-blue: #007bff;
    --background-color: #f8f9fa;
    --text-color: #202124;
    --border-color: #dadce0;
    
    /* Highlight Colors */
    --red-bg: #fce8e6;
    --red-text: #c5221f;
    --green-bg: #e6f4ea;
    --green-text: #137333;
}

body {
    font-family: Arial, sans-serif;
    background-color: var(--background-color);
    color: var(--text-color);
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 40px 20px;
    margin: 0;
}

h1 {
    margin-bottom: 10px;
}

.description {
    text-align: center;
    max-width: 600px;
    color: #5f6368;
    margin-bottom: 30px;
    line-height: 1.5;
}

.container {
    background: white;
    padding: 20px;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    width: 100%;
    max-width: 1000px;
    box-shadow: 0 4px 10px rgba(0,0,0,0.05);
}

.panels-wrapper {
    display: flex;
    gap: 20px;
    margin-bottom: 20px;
}

.panel {
    flex: 1;
    display: flex;
    flex-direction: column;
}

textarea, .result-box {
    width: 100%;
    height: 300px;
    padding: 15px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    font-size: 16px;
    line-height: 1.5;
    box-sizing: border-box;
    resize: vertical;
}

textarea:focus {
    outline: none;
    border-color: var(--primary-blue);
}

/* Hidden by default */
.result-box {
    display: none; 
    background-color: #fafafa;
    overflow-y: auto;
    white-space: pre-wrap; 
}

.controls {
    display: flex;
    justify-content: center;
    gap: 15px;
}

button {
    padding: 10px 25px;
    font-size: 16px;
    font-weight: bold;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

.btn-compare {
    background-color: var(--primary-blue);
    color: white;
}

.btn-clear {
    background-color: white;
    color: var(--primary-blue);
    border: 1px solid var(--border-color);
}

/* How the differences will look */
.deleted {
    background-color: var(--red-bg);
    color: var(--red-text);
    padding: 2px 4px;
    border-radius: 3px;
}

.added {
    background-color: var(--green-bg);
    color: var(--green-text);
    padding: 2px 4px;
    border-radius: 3px;
}
</code></pre>
<p>Understanding the CSS:</p>
<ul>
<li><p><strong>Flexbox layout:</strong> <code>display: flex;</code> inside <code>.panels-wrapper</code> is what places your two text boxes neatly side-by-side.</p>
</li>
<li><p><strong>The highlighters:</strong> The <code>.deleted</code> and <code>.added</code> classes are the most important part of the visual design. When a user deletes a word, we give it a soft red background. When they add a word, it gets a soft green background.</p>
</li>
</ul>
<p>This is what your tool will look like once it's finished:</p>
<img src="https://cdn.hashnode.com/uploads/covers/699c7b22cf5def0f6aaf982b/6676b86c-c0ea-4dec-b5b7-489e8e06f58b.png" alt="Text Compare Tool Preview" style="display:block;margin:0 auto" width="1874" height="872" loading="lazy">

<h2 id="heading-step-4-write-the-javascript-engine">Step 4: Write the JavaScript Engine</h2>
<p>Now you need to make the tool actually work. How does your computer know if a word has changed?</p>
<p>We have to write logic that breaks paragraphs down into individual words. The code will look at the original list of words and compare it to the new list. If a word from the original text is missing, it gets marked as "deleted." If a brand new word appears, it gets marked as "added."</p>
<p>Open your <code>script.js</code> file and paste in this complete, working code:</p>
<pre><code class="language-javascript">function compareText() {
    // 1. Grab the text from the text boxes
    const text1 = document.getElementById('text1').value;
    const text2 = document.getElementById('text2').value;

    // 2. Chop the text up into an array of words (and keep the spaces)
    const words1 = text1.split(/(\s+)/);
    const words2 = text2.split(/(\s+)/);

    // 3. Find the differences
    const { diff1, diff2 } = calculateDifferences(words1, words2);

    const resultBox1 = document.getElementById('result1');
    const resultBox2 = document.getElementById('result2');
    
    // 4. Turn those differences into HTML with colors
    resultBox1.innerHTML = createColoredHTML(diff1, 'deleted');
    resultBox2.innerHTML = createColoredHTML(diff2, 'added');

    // 5. Hide the text boxes and show the final results
    document.getElementById('text1').style.display = 'none';
    document.getElementById('text2').style.display = 'none';
    resultBox1.style.display = 'block';
    resultBox2.style.display = 'block';
}

// The engine that compares the two lists of words
function calculateDifferences(arr1, arr2) {
    const n = arr1.length;
    const m = arr2.length;
    
    // Create a grid to keep track of matching words
    const grid = Array.from({ length: n + 1 }, () =&gt; Array(m + 1).fill(0));

    for (let i = 1; i &lt;= n; i++) {
        for (let j = 1; j &lt;= m; j++) {
            if (arr1[i - 1] === arr2[j - 1]) {
                grid[i][j] = grid[i - 1][j - 1] + 1;
            } else {
                grid[i][j] = Math.max(grid[i - 1][j], grid[i][j - 1]);
            }
        }
    }

    let i = n, j = m;
    const diff1 = [];
    const diff2 = [];

    // Walk backwards through the grid to mark what changed
    while (i &gt; 0 || j &gt; 0) {
        if (i &gt; 0 &amp;&amp; j &gt; 0 &amp;&amp; arr1[i - 1] === arr2[j - 1]) {
            diff1.unshift({ value: arr1[i - 1], type: 'equal' });
            diff2.unshift({ value: arr2[j - 1], type: 'equal' });
            i--;
            j--;
        } else if (j &gt; 0 &amp;&amp; (i === 0 || grid[i][j - 1] &gt;= grid[i - 1][j])) {
            diff2.unshift({ value: arr2[j - 1], type: 'changed' });
            j--;
        } else if (i &gt; 0 &amp;&amp; (j === 0 || grid[i][j - 1] &lt; grid[i - 1][j])) {
            diff1.unshift({ value: arr1[i - 1], type: 'changed' });
            i--;
        }
    }

    return { diff1, diff2 };
}

// Packages the text safely into HTML span elements
function createColoredHTML(diffArray, colorClass) {
    return diffArray.map(wordItem =&gt; {
        // Replace dangerous characters so the browser doesn't crash
        const safeText = wordItem.value.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
        
        // If the word was changed (and isn't just a blank space), wrap it in color
        if (wordItem.type === 'changed' &amp;&amp; !/^\s+$/.test(wordItem.value)) {
            return `&lt;span class="${colorClass}"&gt;${safeText}&lt;/span&gt;`;
        }
        return safeText;
    }).join('');
}

// Puts the tool back to its default state
function clearText() {
    document.getElementById('text1').value = '';
    document.getElementById('text2').value = '';
    
    document.getElementById('text1').style.display = 'block';
    document.getElementById('text2').style.display = 'block';
    
    document.getElementById('result1').style.display = 'none';
    document.getElementById('result2').style.display = 'none';
}
</code></pre>
<p>Understanding the JavaScript:</p>
<ol>
<li><p><strong>Keeping the formatting:</strong> In the first function, you see <code>.split(/(\s+)/)</code>. This splits the text up by spaces, but <em>keeps</em> the spaces and line-breaks. If you don't do this, all of the user's paragraphs will mash into one giant block of text!</p>
</li>
<li><p><strong>The grid system:</strong> The <code>calculateDifferences</code> function creates an invisible grid. It compares every word in the first box with every word in the second box. If it sees the same word in the same order, it leaves it alone. If it hits a snag, it marks the word as a change.</p>
</li>
<li><p><strong>Safety first:</strong> The <code>createColoredHTML</code> function wraps our changed words in <code>&lt;span class="added"&gt;</code> or <code>&lt;span class="deleted"&gt;</code> so CSS can color them. But before it does that, it removes any <code>&lt;</code> or <code>&gt;</code> symbols using <code>.replace()</code>. This stops hackers from pasting malicious code into your app.</p>
</li>
</ol>
<h2 id="heading-step-5-test-your-application">Step 5: Test Your Application</h2>
<p>You're completely done coding! Now it’s time to see it in action.</p>
<ol>
<li><p>Open your <code>text-compare-tool</code> folder.</p>
</li>
<li><p>Double-click the <code>index.html</code> file. It will open in your default web browser.</p>
</li>
<li><p>Type a sentence into the left box: <em>"The quick brown fox jumps over the lazy dog."</em></p>
</li>
<li><p>Type a slightly different sentence into the right box: <em>"The fast brown fox jumps over the sleepy dog."</em></p>
</li>
<li><p>Click <strong>Compare</strong>.</p>
</li>
</ol>
<p>You will instantly see the word "quick" highlight in red on the left, and the word "fast" highlight in green on the right. If you want to start over, just click <strong>Clear</strong>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Great job! You just built a highly practical, browser-based text comparison utility using nothing but pure HTML, CSS, and JavaScript.</p>
<p>You learned how to break text into arrays, compare them using a grid-based algorithm, and manipulate the DOM to show those differences to the user safely. Because this tool relies on local browser processing, it's incredibly fast and 100% private.</p>
<p>If you want to see this exact logic running in a live production environment, or if you need to bookmark a fast tool for your own writing tasks, check out the live <a href="https://99tools.net/text-compare-tool/">Text Compare Tool</a>. Keep experimenting with the code, and happy building!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Case Converter Tool Using HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ If you're looking to level up your front-end development skills by building a practical web utility, this is the guide for you. We'll code a fully functional Case Converter Tool from scratch using onl ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-case-converter-tool/</link>
                <guid isPermaLink="false">6a2bba6e86b91d1d78662a12</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bansidhar Kadiya ]]>
                </dc:creator>
                <pubDate>Fri, 12 Jun 2026 07:51:10 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/72153c4c-a59f-4cc8-a6c5-2bd457c729ab.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're looking to level up your front-end development skills by building a practical web utility, this is the guide for you.</p>
<p>We'll code a fully functional Case Converter Tool from scratch using only HTML, CSS, and vanilla JavaScript.</p>
<p>This lightweight application allows users to paste their content and immediately transform it into standard formats like UPPERCASE, lowercase, Title Case, and Sentence case.</p>
<p>Alongside the text formatting, we'll integrate a live character counter and set up functionality to export the final text as a PDF or Word document.</p>
<p>Grab your favorite code editor, and let's dive in.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you begin, you should have a basic familiarity with the following tools and concepts:</p>
<ul>
<li><p><strong>Core Web Technologies:</strong> A fundamental understanding of HTML structure, basic CSS styling, and JavaScript concepts like functions, array methods, and string manipulation.</p>
</li>
<li><p><strong>Development Environment:</strong> A code editor installed on your computer (for example, Visual Studio Code) and a modern web browser to test your application locally.</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-step-1-set-up-your-project">Step 1: Set Up Your Project</a></p>
</li>
<li><p><a href="#heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</a></p>
</li>
<li><p><a href="#heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</a></p>
</li>
<li><p><a href="#heading-step-4-add-javascript-functionality">Step 4: Add JavaScript Functionality</a></p>
</li>
<li><p><a href="#heading-step-5-test-your-tool">Step 5: Test Your Tool</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-step-1-set-up-your-project">Step 1: Set Up Your Project</h2>
<p>Before writing any code, you need to establish a clean directory structure for your application files.</p>
<p>First, you'll need to initialize a workspace. Open your file manager and create a brand new directory to keep your work organized. Let's name this directory <code>case-converter-app</code>.</p>
<p>Then you'll generate the required files. Inside your newly created directory, set up the following three blank files:</p>
<ul>
<li><p><code>index.html</code></p>
</li>
<li><p><code>styles.css</code></p>
</li>
<li><p><code>script.js</code></p>
</li>
</ul>
<h2 id="heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</h2>
<p>Open the <code>index.html</code> file in your code editor. You'll add the structural foundation of the tool here.</p>
<p>Add the following code into your <code>index.html</code> file:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Case Converter Tool&lt;/title&gt;
    &lt;link rel="stylesheet" href="styles.css"&gt;
    
    &lt;!-- jsPDF library for generating PDF files --&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
    &lt;!-- Google Fonts for a modern look --&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap" rel="stylesheet"&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;div class="app-container"&gt;
        
        &lt;div class="editor-section"&gt;
            &lt;div class="textarea-header"&gt;
                &lt;span class="tip-badge"&gt;💡 Tip: Use Download buttons to save results&lt;/span&gt;
            &lt;/div&gt;
            &lt;textarea id="inputText" placeholder="Type or paste your content here..."&gt;&lt;/textarea&gt;
        &lt;/div&gt;
        
        &lt;!-- Case Conversion Buttons --&gt;
        &lt;div class="button-grid case-buttons"&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'upper')"&gt;UPPER CASE&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'lower')"&gt;lower case&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'capitalized')"&gt;Capitalized Case&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'title')"&gt;Title Case&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'sentence')"&gt;Sentence case&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'inverse')"&gt;iNvErSe CaSe&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'alternate')"&gt;aLtErNaTiNg cAsE&lt;/button&gt;
        &lt;/div&gt;

        &lt;div class="divider"&gt;&lt;/div&gt;

        &lt;!-- Action Buttons --&gt;
        &lt;div class="button-grid action-buttons"&gt;
            &lt;button class="action-btn primary-action copy-btn" onclick="copyToClipboard()"&gt;Copy To Clipboard&lt;/button&gt;
            &lt;button class="action-btn" onclick="downloadPDF()"&gt;Download PDF&lt;/button&gt;
            &lt;button class="action-btn" onclick="downloadWord()"&gt;Download Word&lt;/button&gt;
            &lt;button class="action-btn danger-action" onclick="clearText()"&gt;Clear Text&lt;/button&gt;
        &lt;/div&gt;

        &lt;!-- Real-time Statistics --&gt;
        &lt;div class="stats-panel"&gt;
            &lt;div class="stat-box"&gt;
                &lt;span class="stat-value" id="charCount"&gt;0&lt;/span&gt;
                &lt;span class="stat-label"&gt;Characters&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;span class="stat-value" id="wordCount"&gt;0&lt;/span&gt;
                &lt;span class="stat-label"&gt;Words&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;span class="stat-value" id="paragraphCount"&gt;0&lt;/span&gt;
                &lt;span class="stat-label"&gt;Paragraphs&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;span class="stat-value" id="sentenceCount"&gt;0&lt;/span&gt;
                &lt;span class="stat-label"&gt;Sentences&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

    &lt;/div&gt;

    &lt;script src="script.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Understanding this HTML:</p>
<ul>
<li><p><code>&lt;script src="...jspdf..."&gt;&lt;/script&gt;</code>: This links to an external library that allows JavaScript to generate PDF files directly in the user's browser.</p>
</li>
<li><p><code>&lt;textarea id="inputText"&gt;</code>: This creates the main text box where users will paste their content.</p>
</li>
<li><p><code>&lt;div class="stats-panel"&gt;</code>: This section contains <code>span</code> elements with unique IDs. You'll target these IDs with JavaScript to update the text statistics in real-time.</p>
</li>
</ul>
<h2 id="heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</h2>
<p>Next, you'll give the tool a clean, professional design. Open your <code>styles.css</code> file and add the following code:</p>
<pre><code class="language-css">* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Inter', sans-serif;
}

body {
    background: linear-gradient(135deg, #e0eafc 0%, #cfdef3 100%);
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 2rem;
    color: #1e293b;
}

.app-container {
    background: #ffffff;
    width: 100%;
    max-width: 900px;
    border-radius: 24px;
    box-shadow: 0 20px 40px rgba(0,0,0,0.08);
    padding: 2.5rem;
}

.textarea-header {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 0.5rem;
}

.tip-badge {
    background: #fef08a;
    color: #854d0e;
    padding: 0.35rem 0.85rem;
    border-radius: 20px;
    font-size: 0.75rem;
    font-weight: 600;
}

textarea {
    width: 100%;
    height: 220px;
    padding: 1.5rem;
    border: 2px solid #e2e8f0;
    border-radius: 16px;
    font-size: 1rem;
    resize: vertical;
    outline: none;
    transition: all 0.3s ease;
    background: #f8fafc;
}

textarea:focus {
    border-color: #007bff;
    background: #fff;
    box-shadow: 0 0 0 4px rgba(0, 123, 255, 0.1);
}

.button-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1.5rem;
}

button {
    padding: 0.75rem 1.25rem;
    border: none;
    border-radius: 12px;
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
}

.case-btn {
    background: #f1f5f9;
    color: #475569;
    border: 1px solid #e2e8f0;
}

.case-btn:hover { 
    background: #e2e8f0; 
}

/* The active class highlights the selected button */
.case-btn.active {
    background: #007bff;
    color: #fff;
    border-color: #007bff;
    box-shadow: 0 4px 12px rgba(0, 123, 255, 0.25);
}

.divider {
    height: 1px;
    background: #e2e8f0;
    margin: 1.5rem 0;
}

.action-btn { 
    background: #fff; 
    border: 1px solid #cbd5e1; 
}

.action-btn:hover { 
    background: #f8fafc; 
    border-color: #94a3b8; 
}

.primary-action { 
    background: #007bff; 
    color: #fff; 
    border-color: #007bff; 
}

.primary-action:hover { 
    background: #0056b3; 
    border-color: #0056b3; 
}

.danger-action { 
    color: #ef4444; 
    border-color: #fca5a5; 
    background: #fef2f2; 
}

.danger-action:hover { 
    background: #fee2e2; 
    border-color: #f87171; 
}

.stats-panel {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: 1rem;
    margin-top: 2rem;
    background: #f8fafc;
    padding: 1.5rem;
    border-radius: 16px;
    border: 1px solid #e2e8f0;
}

.stat-box { 
    display: flex; 
    flex-direction: column; 
    align-items: center; 
}

.stat-value { 
    font-size: 1.75rem; 
    font-weight: 700; 
}

.stat-label { 
    font-size: 0.75rem; 
    color: #64748b; 
    text-transform: uppercase; 
}
</code></pre>
<p>Understanding this CSS:</p>
<ul>
<li><p><code>body</code>: You use Flexbox to center the tool perfectly on the screen and apply a soft gradient background.</p>
</li>
<li><p><code>.app-container</code>: This creates a white, rounded card with a soft shadow to hold the user interface.</p>
</li>
<li><p><code>.case-btn.active</code>: You define an active state here. You'll use JavaScript to apply this class to the specific button the user clicks.</p>
</li>
</ul>
<p>At this stage, we've completely structured and styled the user interface. The tool will look like this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/699c7b22cf5def0f6aaf982b/53e128aa-fb0d-47f3-8dca-9e3e0aa130c1.png" alt="Case Converter Tool screenshot" style="display:block;margin:0 auto" width="1315" height="887" loading="lazy">

<p>Right now, the front-end is visible, but the buttons are entirely static. To make the transformations actually work, we have to write the logic in JavaScript.</p>
<h2 id="heading-step-4-add-javascript-functionality">Step 4: Add JavaScript Functionality</h2>
<p>Now you need to make the tool interactive. Open the <code>script.js</code> file and add this code:</p>
<pre><code class="language-javascript">const textArea = document.getElementById('inputText');

// Listen for typing to update statistics in real-time
textArea.addEventListener('input', updateStats);

function updateStats() {
    const text = textArea.value;
    
    document.getElementById('charCount').textContent = text.length;
    
    const words = text.trim().split(/\s+/).filter(word =&gt; word.length &gt; 0);
    document.getElementById('wordCount').textContent = words.length;
    
    const sentences = text.split(/[.!?]+/).filter(sentence =&gt; sentence.trim().length &gt; 0);
    document.getElementById('sentenceCount').textContent = sentences.length;
    
    const paragraphs = text.split(/\n+/).filter(paragraph =&gt; paragraph.trim().length &gt; 0);
    document.getElementById('paragraphCount').textContent = paragraphs.length;
}

function convertCase(event, type) {
    let text = textArea.value;
    if (!text) return; 

    // Highlight the active button
    const buttons = document.querySelectorAll('.case-btn');
    buttons.forEach(btn =&gt; btn.classList.remove('active'));
    if (event) {
        event.target.classList.add('active');
    }

    // Process the text
    switch (type) {
        case 'upper':
            text = text.toUpperCase();
            break;
        case 'lower':
            text = text.toLowerCase();
            break;
        case 'capitalized':
            text = text.toLowerCase().replace(/\b\w/g, c =&gt; c.toUpperCase());
            break;
        case 'title':
            const minorWords = ['a', 'an', 'the', 'and', 'but', 'or', 'for', 'nor', 'on', 'at', 'to', 'from', 'by'];
            text = text.toLowerCase().split(' ').map((word, index) =&gt; {
                if (index !== 0 &amp;&amp; minorWords.includes(word)) return word;
                return word.charAt(0).toUpperCase() + word.slice(1);
            }).join(' ');
            break;
        case 'sentence':
            text = text.toLowerCase().replace(/(^\s*\w|[\.\!\?]\n*\s*\w)/g, c =&gt; c.toUpperCase());
            break;
        case 'inverse':
            text = text.split('').map(c =&gt; c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase()).join('');
            break;
        case 'alternate':
            text = text.toLowerCase().split('').map((c, i) =&gt; i % 2 === 0 ? c : c.toUpperCase()).join('');
            break;
    }

    textArea.value = text;
    updateStats(); 
}

function copyToClipboard() {
    if (!textArea.value) return;
    textArea.select();
    document.execCommand('copy');
    
    const copyBtn = document.querySelector('.copy-btn');
    copyBtn.textContent = 'Copied!';
    setTimeout(() =&gt; copyBtn.textContent = 'Copy To Clipboard', 1500);
}

function clearText() {
    textArea.value = '';
    updateStats();
    document.querySelectorAll('.case-btn').forEach(btn =&gt; btn.classList.remove('active'));
}

function downloadWord() {
    if (!textArea.value) return;
    const blob = new Blob([textArea.value], { type: 'application/msword' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'converted_text.doc';
    a.click();
    URL.revokeObjectURL(url);
}

function downloadPDF() {
    if (!textArea.value) return;
    const { jsPDF } = window.jspdf;
    const doc = new jsPDF();
    const splitText = doc.splitTextToSize(textArea.value, 180);
    doc.text(splitText, 15, 15);
    doc.save('converted_text.pdf');
}
</code></pre>
<p>Understanding this JavaScript:</p>
<ul>
<li><p><code>addEventListener('input', ...)</code>: This listens to every single keystroke. Every time you type, it instantly recalculates the words, characters, and sentences.</p>
</li>
<li><p><code>convertCase(event, type)</code>: This function takes the selected style (like <code>upper</code> or <code>sentence</code>) and applies Regular Expressions (Regex) or array mapping to format the string. It also dynamically adds the <code>.active</code> CSS class to the specific button you clicked.</p>
</li>
<li><p><code>document.execCommand('copy')</code>: This is a browser command that copies the selected text directly to the user's clipboard.</p>
</li>
<li><p><code>new Blob()</code>: You use a Blob (Binary Large Object) to construct a file out of the text on the fly. This allows users to download a <code>.doc</code> file without needing a backend server.</p>
</li>
</ul>
<h2 id="heading-step-5-test-your-tool">Step 5: Test Your Tool</h2>
<p>You're now ready to evaluate your code in a real browser environment.</p>
<ol>
<li><p>Open the <code>case-converter-app</code> folder on your computer.</p>
</li>
<li><p>Double-click the <code>index.html</code> file to launch the application.</p>
</li>
<li><p>Paste a long paragraph into the text area to verify that the live statistics update accurately.</p>
</li>
<li><p>Switch between the formatting options to observe the immediate DOM manipulation, and test the export buttons to ensure files are downloading correctly.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you successfully engineered a browser-based Case Converter Tool using vanilla JavaScript.</p>
<p>You learned how to handle continuous user inputs, manipulate string data using Regular Expressions, and trigger local file downloads directly from the front end.</p>
<p>Most importantly, you learned that modern web browsers are highly capable of handling complex document modifications locally, removing the strict need for external backend servers. This method guarantees fast processing speeds and keeps user data completely private.</p>
<p>For a live demonstration of these concepts in a production environment, feel free to test out this <a href="https://99tools.net/case-converter/">Case Converter</a> and experience how seamlessly these text transformations operate.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Responsive Designs and Scroll Effects with CSS Container Queries ]]>
                </title>
                <description>
                    <![CDATA[ Container queries let you target specific sections of your webpage and apply styles to create customizable, responsive designs based on the container's size rather than that of the viewport. This guid ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-responsive-designs-and-scroll-effects-with-css-container-queries/</link>
                <guid isPermaLink="false">6a1f667b3090fee41f05a0d3</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Container queries ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scroll ]]>
                    </category>
                
                    <category>
                        <![CDATA[ media queries ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scroll animation ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ jabo Landry ]]>
                </dc:creator>
                <pubDate>Tue, 02 Jun 2026 23:25:47 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/dc70f649-2837-4768-be69-e4c1e85dcb03.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Container queries let you target specific sections of your webpage and apply styles to create customizable, responsive designs based on the container's size rather than that of the viewport.</p>
<p>This guide will teach you this useful alternative for responsive designs. You'll also learn about on-scroll effects in CSS.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before diving into scrollable queries and container-based scroll effects, make sure you have:</p>
<ul>
<li><p><strong>Basic familiarity with CSS features and key terms</strong> — understanding properties like <code>height</code>, <code>overflow</code>, and how they affect layout.</p>
</li>
<li><p><strong>Experience writing vanilla CSS</strong> — being comfortable with selectors, rulesets, and applying styles without relying on frameworks.</p>
</li>
<li><p><strong>A working knowledge of HTML structure</strong> — since scroll behavior depends on parent elements, knowing how containers and child elements interact is essential.</p>
</li>
<li><p><em>(Optional but helpful)</em> Some exposure to responsive design concepts, so you can see how scrollable parents fit into broader layout strategies.</p>
</li>
</ul>
<h2 id="heading-what-well-cover">What We'll Cover:</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-how-to-use-container-queries">How to Use Container Queries</a></p>
</li>
<li><p><a href="#heading-difference-between-container-queries-and-media-queries">Difference Between Container Queries and Media queries</a></p>
</li>
<li><p><a href="#heading-container-scroll-state">Container Scroll-state</a></p>
</li>
<li><p><a href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-how-to-use-container-queries">How to Use Container Queries</h2>
<p>Usually, when making responsive designs, you use a media query that adds style to the page based on the page's width viewport, but container queries shift the narrative by adding styles based on the specific elements container or parent element size.</p>
<p>Take a navbar as an example. Let's say you want to have its links wrapped on top of each other when the size of a <code>nav</code> element size is less than <code>450px</code> and also change the background color when the screen size is less than <code>450px</code>.</p>
<p>To make <code>nav</code> element links wrap on top of each other when the <code>nav</code> element size is less than <code>450px</code>, we start by wrapping the links inside a single parent <code>nav</code>:</p>
<pre><code class="language-html">   &lt;nav&gt;
      &lt;img src="" alt="logo" /&gt;
      &lt;ul&gt;
        &lt;a href=""&gt;home&lt;/a&gt;
        &lt;a href=""&gt;about&lt;/a&gt;
        &lt;a href=""&gt;services&lt;/a&gt;
        &lt;a href=""&gt;contact&lt;/a&gt;
      &lt;/ul&gt;
    &lt;/nav&gt;
</code></pre>
<p>Then use <code>container-type</code> to define a container query on the wrapping element or parent (<code>nav</code>) element. It can have one of the following values:</p>
<ul>
<li><p><code>inline-size</code>: Used when you want to track the x-axis of the parent element on the page (width)</p>
</li>
<li><p><code>block-size</code>: Used when you want to track the y-axis of the parent element on the page (height)</p>
</li>
<li><p>Container Scroll-state: Used when you want to track the parent element when scrolled and when elements with position <code>sticky</code> are stuck on the screen.</p>
</li>
</ul>
<p>For most cases, you will need to track the size of an element against the x-axis size, and you must use the <code>container-type</code> on a parent element because you want to apply styles based on the parent element size. Here, we'll use the <code>nav</code> because it is the parent element.</p>
<pre><code class="language-css">nav { 
  container-type: inline-size;
}
</code></pre>
<p>You then define a container query with an at symbol (<code>@</code>) followed by the <code>container</code> keyword.</p>
<p>Then check if the <code>width</code> of the parent container (<code>nav</code>) gets narrower than <code>450px</code>. If the condition is true, add a <code>flex-wrap</code> to links element:</p>
<pre><code class="language-css">@container (width &gt; 450px) {
  ul {
    flex-wrap: wrap;
  }
}
</code></pre>
<h2 id="heading-difference-between-container-queries-and-media-queries">Difference Between Container Queries and Media queries</h2>
<p>Take a look at the code below for media queries:</p>
<pre><code class="language-css">
@media (width &lt; 450px) {
  nav {
    background: green;
  }
}
</code></pre>
<p>In the media query above, the <code>nav</code> element has a <code>green</code> background-color when the screen size is less than <code>450px</code>.</p>
<p>For container queries, the elements are getting stacked on top of each other when the <code>nav</code> element size is less than <code>450px</code>.</p>
<p>So, the difference is that media-queries considers <strong>the whole screen size</strong> while container-queries consider the <strong>parent element size</strong>.</p>
<p><strong>Example:</strong></p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/jabo-arnold/embed/dPprjqM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<h2 id="heading-container-scroll-state">Container Scroll-state</h2>
<p>You can also use container queries to style an element on scroll or to style an element with a position sticky when it's stuck on the page.</p>
<p><strong>Note</strong>: Container <strong>scroll-state</strong> browser support isn't very good. It's supported in about 71% of all browsers currently, with the notable exceptions of Safari and Firefox. Be cautious when using it in production.</p>
<h3 id="heading-scrollable-queries">Scrollable Queries</h3>
<p>A scrollable query is used to check for parent element scrollbar behavior. If there is a scroll behavior on the parent element, then the container query for scrolling will be applied.</p>
<p>To use the scrollable queries, you need to have a parent that has a scroll behavior. HTML is the best fit for such an example because by default HTML has a scroll behavior on it.</p>
<p>To make an element scrollable, set a fixed height and apply <code>overflow</code>. This ensures a scroll bar appears whenever content exceeds that defined space.</p>
<h4 id="heading-example">Example:</h4>
<p>We’ll design a simple page that displays a sidebar table of contents when the page is scrolled down and hides it when the user returns to the top.</p>
<p>First, make HTML a <code>container-type</code> of <code>scroll-state</code>:</p>
<pre><code class="language-css">html {
  container-type: scroll-state;
}
</code></pre>
<p>Style the table of contents section and position it at the top right of the page.</p>
<pre><code class="language-css">.toc {
  position: fixed;
  top: 5rem;
  left:90%;
  align-self: start;
  opacity: 0;
}
</code></pre>
<p>You positioned the table of contents to the right of the page and fixed it to the top of the screen by <code>5rem</code>. You also hid it by default by adding an <code>opacity</code> of <code>0</code>.</p>
<p>Now you need to check if the container scrollbar can be scrolled to the top of its container. If true, unhide the table of contents:</p>
<pre><code class="language-css">@container scroll-state(scrollable: top) {
  .toc {
    transition: opacity 0.4s linear;
    opacity: 1;  
  }
}
</code></pre>
<p>You defined a container query, then followed it with the scroll-state keyword. This checks whether the scrollbar on a parent element can be scrolled.</p>
<p>Use <code>scrollable</code> to define the scroll direction. Setting it to <code>top</code> tests whether the container can scroll upward, while setting it to <code>bottom</code> tests whether it can scroll downward.</p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/jabo-arnold/embed/GgjewRp" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<p>You can see that as long there's a space for scrolling to the top, the table-of- contents will be visible, but when you can't scroll to the top, the table of contents will be hidden.</p>
<p>You can also check whether you can scroll in both top and bottom directions using the <code>y</code> specifier to specify the container's top and bottom scroll directions.</p>
<h4 id="heading-example">Example:</h4>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/jabo-arnold/embed/ogYgvJb?editors=1100" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<p>You can see from the example that the table of contents will always be visible on the screen because the scroll bar can scroll both ways (top and bottom)</p>
<h3 id="heading-stuck-queries">Stuck Queries</h3>
<p>Stuck queries are used to inspect the element that have a <code>position</code> of <code>sticky</code> and applies styles when the element is stuck on the page.</p>
<p>These are great if you want to style elements that have a sticky position and you want them to have a certain style(s) when they get stuck on the page.</p>
<h4 id="heading-example">Example:</h4>
<p>We'll design a simple navbar that will change its <code>box-shadow</code> when its stuck at the top of the screen.</p>
<p>Here, you'll set a wrapper element that wraps <code>nav</code> and makes it a container query, and also gives it a <code>position</code> of <code>sticky</code> to be able to track if it is stuck at the top of the screen.</p>
<pre><code class="language-css">header {
  position: sticky;
  top: 0rem;
  container-type: scroll-state;
}
</code></pre>
<p>You can then define <code>@container</code> to execute when the element gets stuck on the screen:</p>
<pre><code class="language-css">@container scroll-state(stuck:top) {
  nav {
    box-shadow:
      0 14px 28px rgba(0, 0, 0, 0.55),
      0 1px 0 rgba(255, 255, 255, 0.06) inset;
    background-color: #0a0a0a;
  }
}
</code></pre>
<p>You checked for <code>scroll-state</code> behavior and then, inside the parenthesis, checked if the element with <code>position: sticky</code> is <strong>stuck</strong> at top of the page. If true, the background color of the <code>nav</code> changes and a <code>box-shadow</code> is added.</p>
<p>Final results should look like this:</p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/jabo-arnold/embed/gbwEQZR" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<p>By default, the <code>nav</code> doesn't have a <code>box-shadow</code>, but when you scroll and the <code>nav</code> element gets stuck at the top of the page, <code>box-shadow</code> and other rules defined within the stuck <code>@container</code> query definition will be applied to the <code>nav</code> element.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this article, you learned how to set up and work with container queries to make more customizable and responsive designs, and some cool scroll effects.</p>
<p>The examples provided in this guide are not the only things you can do with container queries. The examples were designed to help you think of other alternatives and contexts where you can apply container queries to match your exact needs and design.</p>
<p>Container queries have high browser support, which is currently at 95% and supported in all major browsers.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Atomic CSS and Functional Programming Are Related ]]>
                </title>
                <description>
                    <![CDATA[ Hello, friends! My name is Ramazan, and I'm a front-end developer and enthusiast who loves looking at familiar things in web development from new perspectives. You might have heard of functional progr ]]>
                </description>
                <link>https://www.freecodecamp.org/news/atomic-and-functional-css/</link>
                <guid isPermaLink="false">69bc348fb238fd45a320a2f7</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ layout ]]>
                    </category>
                
                    <category>
                        <![CDATA[ atomic css ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mlut ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tailwind CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ramazan Maksyutov ]]>
                </dc:creator>
                <pubDate>Thu, 19 Mar 2026 17:38:23 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/64d0d72a-075e-42a9-b0fb-ca577f950999.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hello, friends!</p>
<p>My name is Ramazan, and I'm a front-end developer and enthusiast who loves looking at familiar things in web development from new perspectives.</p>
<p>You might have heard of functional programming (FP). It's a paradigm characterised by the use of pure functions and the preservation of data immutability. There are even languages in which FP principles prevail, like Haskell, OCaml, and Elixir. Other languages like JavaScript, Python, and C++ also support this approach, although they're not limited to it.</p>
<p>But an attentive reader will look at the title and ask: "Functional programming is fine. But what does Atomic CSS have to do with it?" I'll answer that now!</p>
<p>The thing is, back when the atomic approach first appeared, it had another name: Functional CSS. Some people still use it quite often today to avoid confusion with other terms with the same <a href="https://css-tricks.com/the-atomics/">name</a>. But why is this approach to writing CSS called functional?</p>
<p>That's the question I'll try to answer in this article. First, I'll describe the basic principles of FP. Then, I'll talk about the basics of Atomic CSS (which I'll refer to here as ACSS), drawing analogies with functional programming. I'll also try to use simple examples to show what problems you can solve by applying Atomic CSS to styling.</p>
<p>When preparing the materials for this article, I relied a lot on <a href="https://www.youtube.com/watch?v=7g0BHu0kWXo">this tutorial</a> on FP and <a href="https://www.youtube.com/watch?v=uHVqbCPnOwU">this one</a> on Functional CSS.</p>
<p>Well, let's get started!</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this article, all you need is a basic understanding of HTML, CSS, and JavaScript. There are also some examples where we'll use the Atomic CSS framework <a href="https://mlut.style/">mlut</a>, but you don't have to know its syntax because I've provided the text with the equivalent CSS styles.</p>
<h2 id="heading-what-well-cover">What We'll Cover:</h2>
<ol>
<li><p><a href="#heading-the-basic-principles-of-functional-programming">The Basic Principles of Functional Programming</a></p>
<ul>
<li><p><a href="#heading-pure-functions">Pure Functions</a></p>
</li>
<li><p><a href="#heading-immutability">Immutability</a></p>
</li>
<li><p><a href="#heading-function-composition">Function Composition</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-how-the-principles-of-fp-meet-their-incarnation-in-atomic-css">How the Principles of FP Meet Their Incarnation in Atomic CSS</a></p>
<ul>
<li><p><a href="#heading-purity">Purity</a></p>
</li>
<li><p><a href="#heading-immutability-in-acss">Immutability in ACSS</a></p>
</li>
<li><p><a href="#heading-composition">Composition</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-the-basic-principles-of-functional-programming">The Basic Principles of Functional Programming</h2>
<p>Functional programming is a broad field that has been the subject of numerous complex articles and an entire scientific <a href="https://www.cambridge.org/core/journals/journal-of-functional-programming">journal</a>. So in my article, I'll focus on exploring only the basic principles of FP and drawing analogies with them in Atomic CSS.</p>
<p>This approach is based on the idea that all the actions we need to perform in a program should be done by calling certain functions and their compositions.</p>
<p>Let me outline the main concepts I'll use to explain the core idea of this approach:</p>
<ol>
<li><p>Pure functions</p>
</li>
<li><p>Immutability</p>
</li>
<li><p>Function composition</p>
</li>
</ol>
<h3 id="heading-pure-functions">Pure Functions</h3>
<p>A function is called pure if it:</p>
<ul>
<li><p>returns the same value for the same input parameters;</p>
</li>
<li><p>has no side effects (changes to external values or entities).</p>
</li>
</ul>
<p>Here are a couple of examples to illustrate this:</p>
<pre><code class="language-javascript">let c = 10;
let s = 0;

// Pure function
function pureSum(a,b) {
  return a + b
}

// Impure function
function notPureSum (a) {
  s = a + c
  return s
}
</code></pre>
<p>The first function is pure because if we enter the same arguments, we'll get the same result. Also, this function doesn't change any global variables and doesn't mutate objects.</p>
<p>The second function doesn't meet the concept of purity on both points. It changes the external variable <code>s</code> and uses another external variable <code>c</code> for calculations, which can change.</p>
<p>Pure functions allow you to write more predictable code. An application can grow and a function with an implicit parameter that can change over time can lead to great difficulties in debugging and maintaining the codebase.</p>
<h3 id="heading-immutability">Immutability</h3>
<p>Immutability is a principle which states that data objects shouldn't change after they're created. To make changes to data, you need to create a new instance of it and then work with that new copy.</p>
<p>At first glance, it may seem that this approach limits flexibility in the development process and reduces the speed of the program. But in reality, if the language or runtime has optimisations for immutable data, adhering to this principle helps you avoid many errors and use parallel calculations.</p>
<p>Here's a fairly simple example: let's take a React component that renders a task in a to-do list. The state of the task is described by an object. And in order for React to correctly redraw the state of the task component when the user changes something in it, it's necessary to pass as the new state not the mutated old object, but a new instance of the object with the current state. Here is the example where an immutable object is used for the state of the to-do:</p>
<pre><code class="language-JS">import { useState } from "react";

function TodoItem() {
  const [todo, setTodo] = useState({
    text: "Write article",
    done: false
  });

  const toggleDone = () =&gt; {
    setTodo({
      ...todo,
      done: !todo.done
    });
  };

  return (
    &lt;div&gt;
      &lt;span&gt;
        {todo.text} — {todo.done ? "✅" : "❌"}
      &lt;/span&gt;
      &lt;button onClick={toggleDone}&gt;
        Toggle
      &lt;/button&gt;
    &lt;/div&gt;
  );
}

export default TodoItem;
</code></pre>
<p>Here we'll see that clicking on the button will lead to changes in the state of the to-do and will cause re-rendering of the component. But we could define the function <code>toggleDone()</code> in a different way using mutations of the object:</p>
<pre><code class="language-JS">const toggleDone = () =&gt; {
  todo.done = !todo.done;
  setTodo(todo);
};
</code></pre>
<p>With such an event handler, the effect will not work because the link to the object is still the same, even though the object itself was mutated.</p>
<h3 id="heading-function-composition">Function Composition</h3>
<p>Function composition involves using the result of one function as an argument for another function. Here's an example of a program that capitalises the first letter of each word:</p>
<pre><code class="language-javascript">function compose(f1, f2) {
  return function (str) {
    return f1(f2(str));
  }
}

function makeFirstCapital(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

function upperEveryFirst(str) {
  return str.split(' ').map(makeFirstCapital).join(' ');
}

function lower(str) {
  return str.toLowerCase();
}

const capitalize = compose(upperEveryFirst, lower);
const string = 'this sTring should be Capitalized'

console.log(capitalize(string)) // 'This String Should Be Capitalized'
</code></pre>
<p>Here, we have defined the function <code>compose(f1, f2)</code>, which returns the composition of the functions passed to it in arguments. Next, we use this function to create the function <code>capitalise()</code>, which will capitalise only the first letters of words by composing the functions <code>lower()</code> and <code>upperEveryFirst()</code>. The first one is executed first and returns a string with all lowercase letters to the second function as an argument. The second function capitalises the first letter of each word.</p>
<p>In large projects and when complex calculations are required, such compositions can be larger, and the logic in them can be much richer. In such cases, this approach to calculations helps break down very large transformations into a series of relatively simple and compact functions that are applied one after the other. This makes it easier to develop, refactor, and debug code.</p>
<h2 id="heading-how-the-principles-of-fp-meet-their-incarnation-in-atomic-css">How the Principles of FP Meet Their Incarnation in Atomic CSS</h2>
<p>Now that we know a little about functional programming, let's try to answer the question: ‘What does Atomic CSS have to do with it?’ Let's draw an analogy between these approaches at the level of the principles described above.</p>
<h3 id="heading-what-is-atomic-css">What is Atomic CSS?</h3>
<p>But first, let's take a moment to explain what Atomic CSS is. It's a methodology of styling layouts in which we use small Atomic CSS rules, each of which performs a single action. These classes are called utilities. They usually apply one CSS property, but not necessarily just one.</p>
<p>For example, in the mlut framework, the <code>Bgc-red</code> utility corresponds to the <code>background-colour: red</code> property, and the <code>-Sz50p</code> utility corresponds to two properties at once: <code>width: 50%</code> and <code>height: 50%</code>.</p>
<p>Modern Atomic CSS frameworks, such as mlut and Tailwind, use a so-called JIT engine. This is a component that generates CSS based only on the utilities you used in your markup.</p>
<h3 id="heading-purity">Purity</h3>
<p>Purity in CSS is determined by which selectors, or more specifically, classes, are used to style elements. In clean CSS, the behaviour of an element should be determined solely by the classes that are attached to it in the <code>class</code> attribute. This means that, ideally, a stylesheet should not contain selectors such as <code>section</code> or <code>div &gt; ul</code>.</p>
<p>Firstly, they set too general rules, which are likely to be broken or supplemented in one part of the project or another. So when we style specific elements, we'll have to constantly keep these styles in mind in order to understand how to achieve the necessary styling without spoiling anything.</p>
<p>Secondly, the purity of CSS for each specific element is violated. Let's say we have the following markup:</p>
<pre><code class="language-html">&lt;button class="greeting"&gt;Hello!&lt;/button&gt;
&lt;div class="wrapper"&gt;
  &lt;button class="greeting"&gt;Hello!&lt;/button&gt;
&lt;/div&gt;
</code></pre>
<p>With the CSS styles below:</p>
<pre><code class="language-CSS">.wrapper &gt; .greeting {
  background-color: green;
}
.greeting {
  background-color: red;
}
</code></pre>
<p>As a result, we get that the first button is red, and the nested one is green. It seems pretty harmless in this simple case, but it will cause inconvenience when the project structure grows significantly.</p>
<p>Here we see that the result of the <code>.greeting</code> class's custom styles depends on where the corresponding element is located. This is similar to a function that produces different results for the same input data depending on where it's called.</p>
<p>Atomic CSS allows you to avoid this effect. In this approach, in most cases, styles are applied only to those elements for which the corresponding classes are specified. If you need to make several identical elements, the same utility is specified in the <code>class</code> attribute of each such element.</p>
<p>A similar example can be rewritten in mlut like this:</p>
<pre><code class="language-HTML">&lt;button class="Bgc-red"&gt;Hello!&lt;/button&gt;
&lt;div&gt;
  &lt;button class="Bgc-green"&gt;Hello!&lt;/button&gt;
&lt;/div&gt;
</code></pre>
<p>And JIT-engine will generate the following styles:</p>
<pre><code class="language-CSS">.Bgc-red {
  background-color: red;
}

.Bgc-green {
  background-color: green;
}
</code></pre>
<p>Here we can see that the styles of elements in this approach will depend only on the classes assigned to them.</p>
<p>It's worth noting here that the mlut syntax allows you to do things that deviate from the strict concept of CSS purity. Sometimes this is necessary to create relatively more complex effects.</p>
<p>Let's say we want to implement a card that changes the background colour of the button inside it when hovered. Then we would need to write the following in mlut:</p>
<pre><code class="language-html">&lt;div class="-Ctx"&gt;
  &lt;button class="^:h_Bgc-red"&gt;Greeting&lt;/button&gt;
&lt;/div&gt;
</code></pre>
<p>CSS:</p>
<pre><code class="language-css">.-Ctx:hover .\^\:h_Bgc-red {
  background-color: red;
}
</code></pre>
<h3 id="heading-immutability-in-acss">Immutability in ACSS</h3>
<p>By CSS immutability, I mean that element styles are not rewritten. Immutability in ACSS means that utilities do not typically mutate each other.</p>
<p>For example, in BEM (Block Element Modifier), the main styles are set by a block or element, and a modifier mutates these styles. In other approaches that use combined selectors, such mutations occur more often and less explicitly.</p>
<p>Let me give you a simple example. Suppose we have a product card that can be in its default state or in a highlighted state. In BEM it would look like this:</p>
<pre><code class="language-html">&lt;div class="product-card"&gt;Card 1&lt;/div&gt;
&lt;div class="product-card product-card--selected"&gt;Card 2&lt;/div&gt;
&lt;div class="product-card"&gt;Card 3&lt;/div&gt;
</code></pre>
<pre><code class="language-css">.product-card {
  background-color: red;
  padding: 5px;
}

.product-card--selected {
  background-color: green;
}
</code></pre>
<p>In this example, we see that the <code>product-card</code> class sets the default background colour of the card to red. And in order to somehow mark the selected card with a different background colour we have to add a modifier class that changes the colour from red to green. It does this by rewriting the <code>background-color</code> property, that is by mutating the block styles.</p>
<p>In the Atomic CSS approach, this problem is solved because utilities allow you to set CSS properties independently of each other and apply modifications without resorting to mutation.</p>
<p>Here's what this example would look like if you used mlut:</p>
<pre><code class="language-html">&lt;div class="P5 Bgc-red"&gt;Card 1&lt;/div&gt;
&lt;div class="P5 Bgc-green"&gt;Card 2&lt;/div&gt;
&lt;div class="P5 Bgc-red"&gt;Card 3&lt;/div&gt;
</code></pre>
<pre><code class="language-css">.P5 {
  padding: 5px;
}

.Bgc-red {
  background-color: red;
}

.Bgc-green {
  background-color: green;
}
</code></pre>
<h3 id="heading-composition">Composition</h3>
<p>Functional programming makes extensive use of function composition. In Atomic CSS, function composition is analogous to utility composition when styling elements. Just as in FP we obtain complex behaviour through the sequential application of a set of simple functions, so in ACSS we can obtain non-trivial styling through a set of simple utilities.</p>
<p>As an example, I'll show a simple smiley face made using only Atomic CSS:</p>
<pre><code class="language-html">&lt;div class="-Sz150 Bgc-yellow Bdrd100p M-a Ps"&gt;
  &lt;div class="-Sz20p Bgc-gray Bdrd100p Ps-a T30p L20p"&gt;&lt;/div&gt;
  &lt;div class="-Sz20p Bgc-gray Bdrd100p Ps-a T30p R20p"&gt;&lt;/div&gt;
  &lt;div class="W50p H40p Bdb5;s;gray Bdrd100p Ps-a T40p L25p"&gt;&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-css">.-Sz150 {
  width: 150px;
  height: 150px;
}

.Bgc-yellow {
  background-color: yellow;
}

.Bdrd100p {
  border-radius: 100%;
}

.M-a {
  margin: auto;
}

.Ps {
  position: relative;
}

.-Sz20p {
  width: 20%;
  height: 20%;
}

.Bgc-gray {
  background-color: gray;
}

.Ps-a {
  position: absolute;
}

.T30p {
  top: 30%;
}

.L20p {
  left: 20%;
}

.R20p {
  right: 20%;
}

.W50p {
  width: 50%;
}

.H40p {
  height: 40%;
}

.Bdb5;s;gray {
  border-bottom: 5px solid gray;
}

.T40p {
  top: 40%;
}

.L25p {
  left: 25%;
}
</code></pre>
<p>This is how our result will look:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69844106d7e8f3ad9f2dd000/de19a0ca-af4e-45f4-8a90-1a0f55db7950.png" alt="Smiley face" style="display:block;margin:0 auto" width="249" height="219" loading="lazy">

<p>Thus, by applying the utilities one after another, we even made some small CSS art.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>To sum up, I will say that Atomic CSS truly embodies the basic principles of functional programming. Of course, not literally – but in the sense that is relevant for front-end developers and layout designers.</p>
<p>I would be happy to hear your additions and objections – it'll be interesting to read and think about them.</p>
<p>Finally, I would like to say: look at familiar things with a fresh perspective. And, as usual, I wish you success on your exciting journey of front-end development!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Center Any Element in CSS: 7 Methods That Always Work ]]>
                </title>
                <description>
                    <![CDATA[ Centering elements in CSS often seems straightforward at first, but it quickly becomes confusing once you start building real layouts. A property like text-align: center; works perfectly for text, yet ]]>
                </description>
                <link>https://www.freecodecamp.org/news/center-any-element-in-css/</link>
                <guid isPermaLink="false">69ab2d340bca1a397644f893</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ flexbox ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Fanny Nyayic ]]>
                </dc:creator>
                <pubDate>Fri, 06 Mar 2026 19:38:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/0dcc3b91-56fd-4102-8d3c-2490ea67b7e8.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Centering elements in CSS often seems straightforward at first, but it quickly becomes confusing once you start building real layouts. A property like <code>text-align: center;</code> works perfectly for text, yet it fails when you try to center an image or a block element.</p>
<p>Then you experiment with <code>margin: auto;</code>, which centers a <code>div</code> horizontally but doesn’t help with vertical alignment. Before long, you find yourself searching through solutions involving Flexbox, Grid, transforms, and other techniques that appear complicated and inconsistent.</p>
<p>The reality is that CSS does not provide a single universal property that can center everything. Instead, each layout scenario requires the right method, and understanding when to use each technique is the key to mastering CSS centering.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-first-understand-the-two-types-of-centering">First: Understand the Two Types of Centering</a></p>
</li>
<li><p><a href="#heading-method-1-center-inline-content-text-inline-elements">Method 1: Center Inline Content (text, inline elements)</a></p>
</li>
<li><p><a href="#heading-method-2-center-a-block-horizontally">Method 2: Center a Block Horizontally</a></p>
</li>
<li><p><a href="#heading-method-3-perfect-center-horizontal-vertical-with-flexbox">Method 3: Perfect Center (Horizontal + Vertical) with Flexbox</a></p>
</li>
<li><p><a href="#heading-method-4-center-using-css-grid-the-easiest-method-ever">Method 4: Center Using CSS Grid (The Easiest Method Ever)</a></p>
</li>
<li><p><a href="#heading-method-5-center-with-absolute-position-transform">Method 5: Center with Absolute Position + Transform</a></p>
</li>
<li><p><a href="#heading-method-6-vertical-center-single-line-text">Method 6: Vertical Center Single Line Text</a></p>
</li>
<li><p><a href="#heading-method-7-the-table-cell-method-old-but-useful">Method 7: The Table-Cell Method (Old but Useful)</a></p>
</li>
<li><p><a href="#heading-quick-decision-guide">Quick Decision Guide</a></p>
</li>
<li><p><a href="#heading-common-beginner-problems-and-fixes">Common Beginner Problems (And Fixes)</a></p>
</li>
<li><p><a href="#heading-pro-tips-youll-use-forever">Pro Tips You’ll Use Forever</a></p>
</li>
<li><p><a href="#heading-summary">Summary</a></p>
</li>
<li><p><a href="#heading-the-7-methods-you-should-memorize">The 7 Methods You Should Memorize</a></p>
</li>
</ul>
<h2 id="heading-first-understand-the-two-types-of-centering"><strong>First: Understand the Two Types of Centering</strong></h2>
<p>Before diving into centering techniques, it’s important to understand the two types of centering in CSS, because different methods work along different axes. CSS layouts operate on two axes: Knowing which axis you want to center along helps you choose the right approach.</p>
<p>There are two axes in CSS layout:</p>
<table style="width:709px"><colgroup><col style="width:385px"><col style="width:324px"></colgroup><tbody><tr><td><p><strong>Axis</strong></p></td><td><p><strong>Direction</strong></p></td></tr><tr><td><p>Horizontal</p></td><td><p>Left to Right</p></td></tr><tr><td><p>Vertical</p></td><td><p>Top to Bottom</p></td></tr></tbody></table>

<p>When someone says <em>“center this element”</em>, they usually mean one of four things:</p>
<ul>
<li><p>Center text inside a container</p>
</li>
<li><p>Center a block horizontally</p>
</li>
<li><p>Center vertically</p>
</li>
<li><p>Center both horizontally and vertically</p>
</li>
</ul>
<p>Each requires a different solution.</p>
<h2 id="heading-method-1-center-inline-content-text-inline-elements"><strong>Method 1: Center Inline Content (text, inline elements)</strong></h2>
<p>This method centers inline content such as text, links, inline images, and sometimes buttons, using the <code>text-align: center;</code> property. This is the simplest centering method in CSS, but it is often misunderstood because it only affects the content inside a block container, not the container itself.</p>
<h3 id="heading-example"><strong>Example</strong></h3>
<pre><code class="language-html">&lt;div class="box"&gt;
&nbsp;&nbsp;&lt;h2&gt;Hello World&lt;/h2&gt;
&nbsp;&nbsp;&lt;p&gt;This text is centered.&lt;/p&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-css">.box {
&nbsp;&nbsp;text-align: center;
&nbsp;&nbsp;border: 2px solid #444;
&nbsp;&nbsp;padding: 20px;
}
</code></pre>
<h3 id="heading-output">Output</h3>
<img src="https://cdn.hashnode.com/uploads/covers/5db98fb03e191a79566dbb68/f9c2bb33-f6aa-4b12-b5cc-2d3cc9d76032.png" alt="f9c2bb33-f6aa-4b12-b5cc-2d3cc9d76032" style="display:block;margin:0 auto" width="1519" height="957" loading="lazy">

<h3 id="heading-why-it-works"><strong>Why It Works</strong></h3>
<p>When you apply <code>text-align: center;</code> to a parent element, the browser horizontally aligns all inline and inline-block children within that container. This makes it perfect for centering headings, paragraphs, navigation links, or small inline elements, but it won’t work for block-level elements like divs unless their display is changed to inline-block.</p>
<p>So this will NOT work:</p>
<pre><code class="language-css">.box {
&nbsp;&nbsp;width: 300px;
&nbsp;&nbsp;text-align: center; /* does NOT center the box */
}
</code></pre>
<h3 id="heading-real-world-use-cases"><strong>Real-world use cases</strong></h3>
<ul>
<li><p>Center headings</p>
</li>
<li><p>Center button labels</p>
</li>
<li><p>Navigation menus</p>
</li>
<li><p>Card content alignment</p>
</li>
</ul>
<h3 id="heading-beginner-mistake"><strong>Beginner Mistake</strong></h3>
<p>Most people try to center a <code>&lt;div&gt;</code> with <code>text-align: center;</code>. This won’t move the <code>div</code>, only its contents.</p>
<h2 id="heading-method-2-center-a-block-horizontally"><strong>Method 2: Center a Block Horizontally</strong></h2>
<p>This method centers a block element horizontally using <code>margin: 0 auto;</code>, which is one of the oldest and most reliable CSS techniques. It works by automatically distributing the available horizontal space equally on the left and right sides of the element. When you set the left and right margins to auto, the browser calculates the remaining space in the container and splits it evenly, pushing the element into the center.</p>
<p>Works when:</p>
<ul>
<li><p>Element has a width</p>
</li>
<li><p>Element is block-level</p>
</li>
</ul>
<h3 id="heading-example-center-a-card"><strong>Example, Center a Card</strong></h3>
<pre><code class="language-html">&lt;div class="card"&gt;
&nbsp;&nbsp;I am centered!
&lt;/div&gt;
</code></pre>
<pre><code class="language-css">.card {
&nbsp;&nbsp;width: 300px;
&nbsp;&nbsp;margin: 0 auto;
&nbsp;&nbsp;padding: 20px;
&nbsp;&nbsp;background: #eee;
}
</code></pre>
<h3 id="heading-output">Output</h3>
<img src="https://cdn.hashnode.com/uploads/covers/5db98fb03e191a79566dbb68/1f1e1df4-7e33-4f54-a9f7-c688c5432782.png" alt="1f1e1df4-7e33-4f54-a9f7-c688c5432782" style="display:block;margin:0 auto" width="1810" height="975" loading="lazy">

<h3 id="heading-why-it-works"><strong>Why It Works</strong></h3>
<p>When you set the elements' margins to auto, the browser calculates the remaining horizontal space in the container after accounting for the element’s width. It then distributes this extra space equally between the left and right margins, which pushes the element into the center. This happens automatically, making <code>margin: 0 auto;</code> a simple and reliable way to horizontally center block elements with a fixed width.</p>
<pre><code class="language-xml">|----auto----|---element---|----auto----|
</code></pre>
<p>Browser calculates: left margin = right margin. So the element sits in the middle.</p>
<h3 id="heading-important-rule"><strong>Important Rule</strong></h3>
<p>If width is not defined, it won't work:</p>
<pre><code class="language-css">.card {
&nbsp;&nbsp;margin: auto; /* won't center , takes full width */
}
</code></pre>
<p>Because block elements default to <code>width: 100%;</code>.</p>
<h3 id="heading-real-world-use-cases"><strong>Real-world use cases</strong></h3>
<ul>
<li><p>Center website layout container</p>
</li>
<li><p>Center forms</p>
</li>
<li><p>Center blog content area</p>
</li>
</ul>
<h2 id="heading-method-3-perfect-center-horizontal-vertical-with-flexbox"><strong>Method 3: Perfect Center (Horizontal + Vertical) with Flexbox</strong></h2>
<p>This method uses Flexbox to center an element both horizontally and vertically, making it one of the most reliable modern CSS solutions. When you set a container to <code>display: flex;</code>, you activate the Flexbox layout system, which gives you powerful alignment controls. The property <code>justify-content: center;</code> centers the content along the main axis (usually horizontal), while <code>align-items: center;</code> centers it along the cross axis (usually vertical).</p>
<h3 id="heading-example-center-login-box"><strong>Example, Center Login Box</strong></h3>
<pre><code class="language-html">&lt;div class="page"&gt;
&nbsp;&nbsp;&lt;div class="login"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;Login Form
&nbsp;&nbsp;&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-css">
.page {
&nbsp;&nbsp;height: 100vh;
&nbsp;&nbsp;display: flex;
&nbsp;&nbsp;justify-content: center;
&nbsp;&nbsp;align-items: center;
}

.login {
&nbsp;&nbsp;padding: 40px;
&nbsp;&nbsp;background: white;
&nbsp;&nbsp;border: 2px solid #333;
}
</code></pre>
<h3 id="heading-output">Output</h3>
<img src="https://cdn.hashnode.com/uploads/covers/5db98fb03e191a79566dbb68/e7c4abc2-1d23-4933-9b3a-dad52326e924.png" alt="e7c4abc2-1d23-4933-9b3a-dad52326e924" style="display:block;margin:0 auto" width="1834" height="997" loading="lazy">

<h3 id="heading-why-it-works"><strong>Why It Works</strong></h3>
<p>Flexbox treats the container and its children as a flexible layout system, automatically distributing space along the main and cross axes. This allows any element, regardless of its size, to sit perfectly in the middle of the container, making it ideal for centering modals, hero sections, and other dynamic content.</p>
<table style="width:733px"><colgroup><col style="width:536px"><col style="width:197px"></colgroup><tbody><tr><td><p><strong>Property</strong></p></td><td><p><strong>Controls</strong></p></td></tr><tr><td><p>justify-content</p></td><td><p>horizontal</p></td></tr><tr><td><p>align-items</p></td><td><p>vertical</p></td></tr></tbody></table>

<h3 id="heading-this-works-regardless-of"><strong>This Works Regardless Of:</strong></h3>
<ul>
<li><p>Unknown height</p>
</li>
<li><p>Unknown width</p>
</li>
<li><p>Responsive layouts</p>
</li>
<li><p>Dynamic content</p>
</li>
</ul>
<p>That’s why it’s widely used today.</p>
<h3 id="heading-real-world-use-cases"><strong>Real-world use cases</strong></h3>
<p>Developers commonly use Flexbox centering to place important interface elements directly in the middle of the screen. For example, it helps center modal dialogs, loading spinners, hero section content, and other full-screen UI components. Hence, they remain visually balanced and easy for users to notice, regardless of screen size.</p>
<h2 id="heading-method-4-center-using-css-grid-the-easiest-method-ever"><strong>Method 4: Center Using CSS Grid (The Easiest Method Ever)</strong></h2>
<p>CSS Grid offers one of the simplest ways to center elements both horizontally and vertically. By setting a container to <code>display: grid;</code> and applying <code>place-items: center;</code>, you can position any child element perfectly in the middle with just a few lines of code. This method works because Grid provides built-in alignment controls that automatically handle positioning along both axes.</p>
<h3 id="heading-example"><strong>Example</strong></h3>
<pre><code class="language-html">&lt;div class="wrapper"&gt;
&nbsp;&nbsp;&lt;div class="box"&gt;Centered!&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-css">
.wrapper {
&nbsp;&nbsp;height: 100vh;
&nbsp;&nbsp;display: grid;
&nbsp;&nbsp;place-items: center;
}
.box {
  width: 200px;
  padding: 30px;
  text-align: center;
  background: white;
  border: 2px solid #333;
}
</code></pre>
<h3 id="heading-output">Output</h3>
<img src="https://cdn.hashnode.com/uploads/covers/5db98fb03e191a79566dbb68/fb959081-1adb-467b-bf7d-754ec2777566.png" alt="fb959081-1adb-467b-bf7d-754ec2777566" style="display:block;margin:0 auto" width="1830" height="988" loading="lazy">

<p>In this example, the <code>.wrapper</code> acts as the grid container, and the <code>.box</code> element becomes a grid item. The property <code>place-items: center;</code> automatically aligns the box in the middle of the container, both horizontally and vertically.</p>
<p><code>100vh</code> means 100% of the viewport height, which is the full height of the visible browser window. When you set <code>height: 100vh;</code> on a container, it expands to fill the entire screen from top to bottom.</p>
<h3 id="heading-why-it-works">Why It Works</h3>
<p>The property <code>place-items: center</code> is actually shorthand for two grid alignment properties:</p>
<pre><code class="language-css">align-items: center;
justify-items: center;
</code></pre>
<ul>
<li><p><code>align-items</code> controls vertical alignment inside the grid.</p>
</li>
<li><p><code>justify-items</code> controls horizontal alignment.</p>
</li>
</ul>
<p>By combining both in one line, Grid centers elements in both directions automatically without needing additional layout rules.</p>
<h3 id="heading-when-to-prefer-grid-over-flexbox"><strong>When to Prefer Grid Over Flexbox</strong></h3>
<p>CSS Grid is ideal when you only need simple centering and don’t require complex layout control. It keeps your code short and easy to read.</p>
<p><strong>Use Grid when:</strong></p>
<ul>
<li><p>You only need to center a single element</p>
</li>
<li><p>You are not building complex layouts</p>
</li>
<li><p>You want the simplest and cleanest code</p>
</li>
</ul>
<p><strong>Use Flexbox when:</strong></p>
<ul>
<li><p>You are aligning multiple items</p>
</li>
<li><p>Layout direction matters (row vs column)</p>
</li>
<li><p>You need spacing control between elements</p>
</li>
</ul>
<h2 id="heading-method-5-center-with-absolute-position-transform"><strong>Method 5: Center with Absolute Position + Transform</strong></h2>
<p>This centers an element using absolute positioning combined with CSS transforms, and it works even when you are not using Flexbox or Grid. In this approach, you position the element with <code>position: absolute;</code> and move it to the middle of its parent using <code>top: 50%;</code> and <code>left: 50%;</code>.</p>
<h3 id="heading-example-center-popup"><strong>Example, Center Popup</strong></h3>
<pre><code class="language-html">&lt;div class="container"&gt;
&nbsp;&nbsp;&lt;div class="popup"&gt;I'm centered&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-css">.container {
&nbsp;&nbsp;position: relative;
&nbsp;&nbsp;height: 400px;
}

.popup {
&nbsp;&nbsp;position: absolute;
&nbsp;&nbsp;top: 50%;
&nbsp;&nbsp;left: 50%;
&nbsp;&nbsp;transform: translate(-50%, -50%);
}
</code></pre>
<h3 id="heading-output">Output</h3>
<img src="https://cdn.hashnode.com/uploads/covers/5db98fb03e191a79566dbb68/bc20cc8d-090c-45cf-a240-8792c6963784.png" alt="bc20cc8d-090c-45cf-a240-8792c6963784" style="display:block;margin:0 auto" width="1837" height="990" loading="lazy">

<h3 id="heading-why-it-works"><strong>Why It Works</strong></h3>
<ol>
<li><p><code>top: 50%</code> moves the top edge to the middle</p>
</li>
<li><p><code>left: 50%</code> moves the left edge to the middle</p>
</li>
<li><p><code>translate(-50%, -50%)</code> shifts the element back by half its size</p>
</li>
</ol>
<p>So the center becomes the element’s midpoint, not the corner.</p>
<h3 id="heading-explanation"><strong>Explanation</strong></h3>
<p><strong>Without transform:</strong> The element corner sits at the center, which means this places the top-left corner of the element at the center point.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5db98fb03e191a79566dbb68/f579c42a-074f-4bda-8fab-5c8163ff8fac.png" alt="f579c42a-074f-4bda-8fab-5c8163ff8fac" style="display:block;margin:0 auto" width="1819" height="973" loading="lazy">

<p>To fix that, you apply <code>transform: translate(-50%, -50%);</code>, which shifts the element back by half of its own width and height. This adjustment ensures the actual center of the element aligns with the center of the container. Developers often use this technique for overlays, modals, tooltips, and floating UI components.</p>
<h3 id="heading-real-world-use-cases"><strong>Real-world use cases</strong></h3>
<ul>
<li><p>Modals</p>
</li>
<li><p>Tooltips</p>
</li>
<li><p>Floating labels</p>
</li>
<li><p>Overlays</p>
</li>
</ul>
<h2 id="heading-method-6-vertical-center-single-line-text"><strong>Method 6: Vertical Center Single Line Text</strong></h2>
<p>This method vertically centers single-line text inside a container by using the <code>line-height</code> property. When you set the <code>line-height</code> to the same value as the container’s height, the browser places the text in the vertical middle of that space because the line box expands to fill the container evenly.</p>
<h3 id="heading-example-center-text-in-button"><strong>Example, Center Text in Button</strong></h3>
<pre><code class="language-html">&lt;button class="btn"&gt;Click Me&lt;/button&gt;
</code></pre>
<pre><code class="language-css">.btn {
&nbsp;&nbsp;height: 60px;
&nbsp;&nbsp;line-height: 60px;
&nbsp;&nbsp;text-align: center;
}
</code></pre>
<h3 id="heading-output">Output</h3>
<img src="https://cdn.hashnode.com/uploads/covers/5db98fb03e191a79566dbb68/be456849-c4b8-49aa-a330-fe448b9a5ee6.png" alt="be456849-c4b8-49aa-a330-fe448b9a5ee6" style="display:block;margin:0 auto" width="1839" height="978" loading="lazy">

<h3 id="heading-why-it-works"><strong>Why It Works</strong></h3>
<p>This technique works best for elements with a fixed height, such as buttons, badges, or navigation items. However, it only works reliably when the text stays on one line, because multiple lines will break the vertical alignment.</p>
<h3 id="heading-limitations"><strong>Limitations</strong></h3>
<p>The main limitation of using <code>line-height</code> to vertically center text is that it only works for single-line text. If the text wraps onto multiple lines, the line-height no longer matches the container height for each line, causing the vertical centering to break.</p>
<p>This makes the method unsuitable for paragraphs, headings, or any content that can expand beyond one line, so it’s best reserved for buttons, labels, or other fixed-height, single-line elements.</p>
<h2 id="heading-method-7-the-table-cell-method-old-but-useful"><strong>Method 7: The Table-Cell Method (Old but Useful)</strong></h2>
<p>This method uses the table-cell technique to center content vertically and horizontally, a reliable approach for older CSS layouts and email templates. By setting a container to <code>display: table;</code> and its child element to <code>display: table-cell;</code> with <code>vertical-align: middle;</code> and <code>text-align: center;</code>, The browser treats the child like a table cell and automatically centers its content.</p>
<h3 id="heading-example"><strong>Example</strong></h3>
<pre><code class="language-html">&lt;div class="outer"&gt;
&nbsp;&nbsp;&lt;div class="inner"&gt;Centered&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-css">.outer {
&nbsp;&nbsp;display: table;
&nbsp;&nbsp;width: 100%;
&nbsp;&nbsp;height: 300px;
}

.inner {
&nbsp;&nbsp;display: table-cell;
&nbsp;&nbsp;vertical-align: middle;
&nbsp;&nbsp;text-align: center;
}
</code></pre>
<h3 id="heading-output">Output</h3>
<img src="https://cdn.hashnode.com/uploads/covers/5db98fb03e191a79566dbb68/e0960bcf-bd1c-43c0-9d1b-afa48530fd8d.png" alt="e0960bcf-bd1c-43c0-9d1b-afa48530fd8d" style="display:block;margin:0 auto" width="1845" height="994" loading="lazy">

<h3 id="heading-how-it-works"><strong>How It Works</strong></h3>
<ul>
<li><p>The <code>.outer</code> container acts as a table.</p>
</li>
<li><p>The <code>.inner</code> element behaves like a table cell.</p>
</li>
<li><p>Table cells automatically respect vertical alignment rules.</p>
</li>
<li><p>Combining <code>vertical-align: middle;</code> and <code>text-align: center;</code> perfectly centers the content both vertically and horizontally.</p>
</li>
</ul>
<h3 id="heading-why-use-this-method">Why Use This Method</h3>
<ul>
<li><p>It works in <strong>older browsers</strong> that don’t fully support Flexbox or Grid.</p>
</li>
<li><p>It’s especially useful in <strong>email templates</strong> or <strong>legacy layouts</strong>.</p>
</li>
<li><p>No knowledge of height calculation or transforms is required.</p>
</li>
</ul>
<h2 id="heading-quick-decision-guide"><strong>Quick Decision Guide</strong></h2>
<table style="width:754px"><colgroup><col style="width:491px"><col style="width:263px"></colgroup><tbody><tr><td><p><strong>Situation</strong></p></td><td><p><strong>Best Method</strong></p></td></tr><tr><td><p>Center text</p></td><td><p>text-align</p></td></tr><tr><td><p>Center block horizontally</p></td><td><p>margin auto</p></td></tr><tr><td><p>Center anything modern</p></td><td><p>flexbox</p></td></tr><tr><td><p>Simplest full center</p></td><td><p>grid</p></td></tr><tr><td><p>Overlay/modal</p></td><td><p>absolute + transform</p></td></tr><tr><td><p>Single-line text vertical</p></td><td><p>line-height</p></td></tr><tr><td><p>Legacy/email support</p></td><td><p>table-cell</p></td></tr></tbody></table>

<h2 id="heading-common-beginner-problems-and-fixes"><strong>Common Beginner Problems (And Fixes)</strong></h2>
<h3 id="heading-problem-1-margin-auto-not-working"><strong>Problem 1: “margin auto not working.”</strong></h3>
<p>You forgot the width.</p>
<pre><code class="language-css">width: 300px;
margin: auto;
</code></pre>
<h3 id="heading-problem-2-align-items-center-not-working"><strong>Problem 2: “align-items center not working.”</strong></h3>
<p>Parent needs height.</p>
<pre><code class="language-css">height: 100vh;
</code></pre>
<h3 id="heading-problem-3-absolute-centering-weird-position"><strong>Problem 3: “absolute centering weird position.”</strong></h3>
<p>Parent missing positioning.</p>
<pre><code class="language-css">parent { position: relative; }
</code></pre>
<h3 id="heading-problem-4-flexbox-vertical-centering-fails"><strong>Problem 4: Flexbox vertical centering fails</strong></h3>
<p>Check direction:</p>
<pre><code class="language-css">flex-direction: column;
</code></pre>
<p>Now vertical/horizontal axes swap!</p>
<h2 id="heading-pro-tips-youll-use-forever"><strong>Pro Tips You’ll Use Forever</strong></h2>
<blockquote>
<p>1. Flexbox = alignment tool</p>
<p>2. Grid = placement tool</p>
<p>3. Margin auto = layout tool</p>
</blockquote>
<p>Different tools, different jobs.</p>
<h3 id="heading-remember-this-rule"><strong>Remember This Rule</strong></h3>
<ul>
<li><p>If you are centering one thing, use Grid</p>
</li>
<li><p>If centering many things, use Flexbox</p>
</li>
</ul>
<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>CSS centering often feels difficult because beginners expect a single magic property that works in every situation, but no such property exists. Instead, CSS provides multiple layout systems, each designed to solve specific alignment problems.</p>
<p>These include inline alignment for text and inline elements, flow layout for standard block elements, Flexbox for flexible row or column arrangements, Grid for two-dimensional layouts, and positioned layouts for absolute or fixed elements. Once you understand which system applies to your scenario, centering becomes predictable and much easier to implement.</p>
<h2 id="heading-the-7-methods-you-should-memorize"><strong>The 7 Methods You Should Memorize</strong></h2>
<ol>
<li><p><code>text-align: center</code></p>
</li>
<li><p><code>margin: 0 auto</code></p>
</li>
<li><p>Flexbox centering</p>
</li>
<li><p>Grid <code>place-items: center</code></p>
</li>
<li><p>Absolute + transform</p>
</li>
<li><p>Line-height trick</p>
</li>
<li><p>Table-cell fallback</p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn CSS Flexbox for Beginners [Free 2-hour course] ]]>
                </title>
                <description>
                    <![CDATA[ Flexbox is a powerful CSS feature that lets you build user interfaces that fit any screen size. freeCodeCamp just published a Flexbox for beginners course where you'll learn the concepts and code syntax by building your own website navigation bar. If... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-css-flexbox-for-beginners-free-2-hour-course/</link>
                <guid isPermaLink="false">6925eef4915ee815a9b000df</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ flexbox ]]>
                    </category>
                
                    <category>
                        <![CDATA[ course ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Tue, 25 Nov 2025 18:01:24 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764093380745/42ec6fe5-355a-4a0d-acb1-8e63e2b1c240.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Flexbox is a powerful CSS feature that lets you build user interfaces that fit any screen size. freeCodeCamp just published a Flexbox for beginners course where you'll learn the concepts and code syntax by building your own website navigation bar.</p>
<p>If you’ve ever struggled to center something with CSS or tried to make columns line up nicely, Flexbox simplifies this dramatically. With just a few properties, you can build modern layouts that “flex” to different screen sizes, without needing to write a bunch of custom media queries.</p>
<p>Developer and teacher Indra (CodeWithIndra) will walk you through every Flexbox property step by step. You’ll learn how to align items along the main and cross axis, reorder elements without changing the HTML, control how items grow and shrink, and finally understand what flex-grow, flex-shrink, and flex-basis are really doing. He also shows real examples from sites like GitHub and DataDog, then demonstrates how to recreate pieces of their layout.</p>
<p>The course ends with two small projects: centering an element vertically and horizontally (the thing everyone Googles at some point) and building a clean navigation bar using only Flexbox.</p>
<p>If you want to improve your front end development skills, this course is a good way to invest a couple hours of your weekend. [2 hour YouTube course]:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/nbsz6muQRT4" 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 Implement a Service Worker with WorkBox in a Progressive Web App ]]>
                </title>
                <description>
                    <![CDATA[ Imagine having a web app that looks and feels just like a native mobile app. It launches from your home screen, runs in full-screen mode, and responds smoothly to your interactions. But here’s the surprising part: it wasn’t downloaded from an app sto... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/implement-a-service-worker-with-workbox-in-a-pwa/</link>
                <guid isPermaLink="false">68595d64f486735784954566</guid>
                
                    <category>
                        <![CDATA[ PWA ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Service Workers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ workbox ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web apps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ manifest ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Damilola Oniyide ]]>
                </dc:creator>
                <pubDate>Mon, 23 Jun 2025 13:57:56 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750687028879/b12e57cb-290a-4562-8584-95eb5713a871.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Imagine having a web app that looks and feels just like a native mobile app. It launches from your home screen, runs in full-screen mode, and responds smoothly to your interactions. But here’s the surprising part: it wasn’t downloaded from an app store. It’s a Progressive Web App (PWA).</p>
<p>PWAs bring the power of the web to your fingertips with the experience of a mobile app. Even better? If you lose internet connection while on the go, the app can still function, showing your previously loaded data and getting updates once you’re back online.</p>
<p>In this tutorial, you’ll learn how to implement a service worker with WorkBox in a weather app using HTML, CSS, and JavaScript. We’ll start by understanding what a PWA is, the core components behind the scenes, especially service workers, and how to use Workbox to supercharge your app with offline capabilities.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-well-cover">What We’ll Cover</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-progressive-web-app-pwa">What is a Progressive Web App (PWA)?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-makes-a-web-app-progressive">What Makes a Web App “Progressive”?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-components-of-a-pwa">Components of a PWA</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-service-worker-in-pwa">What is a Service Worker in PWA?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-use-workbox-instead-of-manual-service-workers">Why Use Workbox Instead of Manual Service Workers?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-introduction-to-workbox">Introduction to WorkBox</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-creating-the-offline-html-structure">Creating the Offline HTML Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-styling-with-css">Styling with CSS</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-appjs-and-configjs">How to Set Up app.js and config.js</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-manifest-file">How to Create a Manifest File</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-workbox-to-your-service-workerjs-file">How to Add WorkBox to Your service-worker.js File</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-your-service-worker-in-the-service-workerjs-file">How to Create your Service Worker in the service-worker.js File</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-app-installation">How to Set Up App Installation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-install-the-weather-app">How to Install the Weather App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-well-cover">What We’ll Cover</h2>
<ul>
<li><p><strong>Setting Up the Project:</strong> We'll build a simple weather app using HTML, CSS, and JavaScript. This approach is perfect for this tutorial because it keeps things simple and accessible while focusing on core PWA concepts without the added complexity of frameworks like React or Vue.</p>
</li>
<li><p><strong>Turning the App into a PWA:</strong> Next, we’ll walk through the concept of a Progressive Web App, covering the key features and best practices of PWAs.</p>
</li>
<li><p><strong>Implementing Service Worker via WorkBox:</strong> Finally, we’ll dive deeper into how service workers function and explore why using Workbox simplifies the process.</p>
</li>
</ul>
<p>Here’s what the final application will look like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747272664555/8ec876bc-0881-4a63-8010-02136de91db3.png" alt="Weatherly app interface showing Tokyo weather with 24°C temperature, overcast clouds, city search functionality, and location services button" class="image--center mx-auto" width="720" height="1417" loading="lazy"></p>
<h3 id="heading-audience"><strong>Audience</strong></h3>
<p>This tutorial is for web developers of all levels. Whether you're new to Progressive Web Apps (PWAs) or just starting to explore service workers, this guide will walk you through the core concepts and demonstrate why using a Google-backed library like Workbox to implement service workers can be more efficient than manual implementation.</p>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>Before you begin</p>
<ol>
<li><p>Get a free API key from the <a target="_blank" href="https://openweathermap.org/">OpenWeatherAPI</a> website</p>
</li>
<li><p>Make sure you’re familiar with HTML, CSS, and JavaScript.</p>
</li>
<li><p>If you’re new to PWAs, you might want to read some introductory articles to get a quick overview.</p>
<ul>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps">Progressive web apps</a></p>
</li>
<li><p><a target="_blank" href="https://web.dev/articles/workbox">Workbox</a></p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-what-is-a-progressive-web-app-pwa">What is a Progressive Web App (PWA)?</h2>
<p>A PWA is a web application that combines the best of web and mobile apps. It’s built using standard web technologies like HTML, CSS, and JavaScript, but it behaves and feels like a native mobile app on your phone or tablet.</p>
<p>Think of apps like Instagram Web, Twitter Lite, or Spotify Web Player. Even though you’re not using a native app from an app store:</p>
<ul>
<li><p>You can still scroll your feed, view media, and send messages.</p>
</li>
<li><p>It works even on slow or unstable networks.</p>
</li>
<li><p>You can “install” it on your home screen and launch it like a regular app.</p>
</li>
<li><p>You even get push notifications just like a mobile app!</p>
</li>
</ul>
<p>With PWAs, you get the reach of the web and the feel of an app without the heavy storage or installation process.</p>
<h2 id="heading-what-makes-a-web-app-progressive">What Makes a Web App “Progressive”?</h2>
<p>A PWA is not just any website. It’s built to progressively enhance the user experience, depending on their device and browser capabilities. Here are the core characteristics that define a PWA:</p>
<ul>
<li><p><strong>Responsive</strong>: Works on all screen sizes, that is, phones, tablets, and desktops.</p>
</li>
<li><p><strong>Reliable</strong>: Loads instantly, even when offline or on poor networks.</p>
</li>
<li><p><strong>Installable</strong>: Can be added to the home screen without needing an app store.</p>
</li>
<li><p><strong>Engaging</strong>: Supports features like push notifications and background sync.</p>
</li>
</ul>
<h2 id="heading-components-of-a-pwa"><strong>Compone</strong>nts of a PWA</h2>
<p>Before your web app can be considered a PWA, it must include the following:</p>
<h3 id="heading-a-web-application-manifest">A Web Application Manifest</h3>
<p>The web app manifest is a JSON file that tells the browser about your web app, how it should appear, and behave when installed on a user's device.</p>
<p>Think of it like your app’s business card. It includes details like:</p>
<ul>
<li><p><strong>App name and short name</strong> – How your app is labeled on the home screen or app list.</p>
</li>
<li><p><strong>Icons</strong> – Images used for app icons on different screen sizes and resolutions.</p>
</li>
<li><p><strong>Theme color and background color</strong> – Defines the look of your app’s UI and loading screen.</p>
</li>
<li><p><strong>Start URL</strong> – The page that opens when the app is launched.</p>
</li>
<li><p><strong>Display mode</strong> – Controls whether the app opens in a browser tab, fullscreen, or a native-like window.</p>
</li>
<li><p><strong>Screenshots</strong> – Optional preview images that show how your app looks on different devices in app stores or installation prompts.</p>
</li>
</ul>
<h3 id="heading-a-service-worker">A Service Worker</h3>
<p>This is a script that runs in the background. It handles offline behaviour, caching, background sync, and push notifications needed to make your PWA function.</p>
<p>More details about the service worker will be discussed later in this article.</p>
<h3 id="heading-https">HTTPS</h3>
<p>PWAs must be served over HTTPS. This is not optional. Here’s why:</p>
<ul>
<li><p>It protects users by ensuring secure data transfer.</p>
</li>
<li><p>It enables important features like service workers and push notifications.</p>
</li>
<li><p>Browsers won’t allow service workers to register on non-secure origins.</p>
</li>
</ul>
<p>If you're testing locally, you can use <a target="_blank" href="http://localhost"><code>localhost</code></a> (which is treated as secure), But for production, your site must have an SSL certificate.</p>
<h2 id="heading-what-is-a-service-worker-in-pwa">What is a Service Worker in PWA?</h2>
<p>In PWAs, a service worker is a JavaScript file that runs in the background, separate from your main app, and acts like a network proxy. It can:</p>
<ul>
<li><p>Cache resources and serve them offline</p>
</li>
<li><p>Intercept network requests and apply caching strategies</p>
</li>
<li><p>Handle background syncs</p>
</li>
<li><p>Manage push notifications</p>
</li>
</ul>
<p>Think of it as your app’s behind-the-scenes assistant—makes it load fast, works offline, and stays updated, even when you're not looking.</p>
<h2 id="heading-why-use-workbox-instead-of-manual-service-workers">Why Use Workbox Instead of Manual Service Workers?</h2>
<p>Service workers are essential in creating a PWA, but getting started with them can be challenging. Writing service worker code from scratch can often be tedious and prone to errors. For example, you'd need to:</p>
<ul>
<li><p>Manually configure caching strategies</p>
</li>
<li><p>Handle service worker updates</p>
</li>
<li><p>Write and maintain a lot of repetitive boilerplate code</p>
</li>
</ul>
<p>Workbox, a library from Google, makes things easier by letting developers focus on what matters, without worrying about the complicated parts of service workers.</p>
<p>However, it’s still important to understand how service workers function, since they handle some complex tasks under the hood.</p>
<p>Here are key things a service worker (with or without Workbox) does:</p>
<ul>
<li><p><strong>Install event</strong>: Set up cache</p>
</li>
<li><p><strong>Activate event</strong>: Clean up old caches</p>
</li>
<li><p><strong>Fetch event</strong>: Intercept network requests and serve from cache</p>
</li>
</ul>
<p>With Workbox, these are wrapped in easy-to-use functions.</p>
<h2 id="heading-introduction-to-workbox">Introduction to WorkBox</h2>
<p>Workbox is a collection of libraries that helps developers build efficient service workers quickly, with best practices built right in. It supports strategies like:</p>
<ul>
<li><p><code>CacheFirst</code>: Load from cache, fall back to network</p>
</li>
<li><p><code>NetworkFirst</code> : Try network, fall back to cache</p>
</li>
<li><p><code>StaleWhileRevalidate</code>: Serve from cache and update in the background</p>
</li>
</ul>
<h3 id="heading-understanding-workbox-modules">Understanding Workbox Modules</h3>
<p>Workbox is more than just a tool. It is a collection of powerful modules, each designed to simplify different parts of working with service workers. These modules are flexible and can be used in three key contexts:</p>
<ul>
<li><p><strong>Service Worker Context</strong> – Inside your service worker file, where you handle caching, routing, and other background tasks.</p>
</li>
<li><p><strong>Window Context</strong> – Inside your main application (the client-side JS), where you register and communicate with the service worker.</p>
</li>
<li><p><strong>Build Tools Integration</strong> – Tools like Webpack use Workbox to generate service worker files and precache manifests during your build process.</p>
</li>
</ul>
<p>Let’s break down some of the most popular and essential modules Workbox offers:</p>
<ol>
<li><strong>workbox-routing</strong></li>
</ol>
<p>This module handles routing network requests within your service worker. Think of it like a traffic director that listens for <code>fetch</code> events and decides what to do with them.</p>
<p><strong>Use case:</strong> Route API requests to the network while routing static asset requests to the cache.</p>
<ol start="2">
<li><strong>workbox-strategies</strong></li>
</ol>
<p>This is where caching strategies like <code>CacheFirst</code>, <code>NetworkFirst</code>, and <code>StaleWhileRevalidate</code> are used. It provides a clean and consistent API for handling how your app responds to different requests.</p>
<p><strong>Use case:</strong> Apply different caching behaviours for images, fonts, or dynamic data with minimal code.</p>
<ol start="3">
<li><strong>workbox-precaching</strong></li>
</ol>
<p>This module handles precaching by storing static assets during the service worker’s install phase. It makes it easy to cache files ahead of time and ensures that updates are managed efficiently.</p>
<p><strong>Use case:</strong> Preload essential assets (like HTML, CSS, and logo images) so your app loads instantly, even offline.</p>
<ol start="4">
<li><strong>workbox-expiration</strong></li>
</ol>
<p>It is used as a plugin alongside caching strategies. This module adds smart cache expiration. You can automatically remove old or excessive items from the cache based on how long they've been stored or how many items exist.</p>
<p><strong>Use case:</strong> Keep your cache size under control without manually tracking and deleting outdated files.</p>
<p><strong>workbox-window</strong></p>
<p>This module is designed for the browser (window) side of your app. It simplifies service worker registration and allows you to communicate with the service worker from your page easily.</p>
<p><strong>Use case:</strong> Detect when a new service worker is available and prompt the user to refresh the app to update.</p>
<p>You can use WorkBox via:</p>
<ul>
<li><p>npm</p>
</li>
<li><p>CDN (which we'll use here for simplicity)</p>
</li>
</ul>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Let's start by creating our project structure:</p>
<pre><code class="lang-plaintext">weather-pwa/
├── index.html
├── style.css
├── js/
│   ├── app.js
│   └── install.js
├── service-worker.js
├── images/
│   └── [your image files and folders here]
├── manifest.json
├── config.js  
└── offline.html
</code></pre>
<h3 id="heading-the-html-structure">The HTML Structure</h3>
<p>First, let's build our <code>index.html</code> file:</p>
<pre><code class="lang-xml">
<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-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">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/images/logo.png"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/png"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Simple Weather Progressive Web App"</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">"/styles.css"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Weatherly<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">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"header"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"logo"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/logo.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Weatherly Logo"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Weatherly<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"main"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"weather-card"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"location-container"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"location-input"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter city name"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"search-btn"</span>&gt;</span>Search<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">id</span>=<span class="hljs-string">"locationBtn"</span>&gt;</span>📍 Use My Location<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">id</span>=<span class="hljs-string">"installBtn"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"display: none;"</span>&gt;</span>Install App<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> <span class="hljs-attr">id</span>=<span class="hljs-string">"offline-message"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"offline-message"</span>&gt;</span>
                You are currently offline. Weather data may not be up-to-date.
            <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">"error"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"error-message"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"weather-container"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"weather-container"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Your last searched location weather:<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">class</span>=<span class="hljs-string">"location-info"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"city"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"date"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"current-weather"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"weather-icon"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">""</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Weather icon"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"temperature-container"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"temperature"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"weather-description"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"weather-details"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"detail"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"humidity-icon"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/images/humidity.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Humidity icon"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"label"</span>&gt;</span>Humidity<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">id</span>=<span class="hljs-string">"humidity"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"value"</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> <span class="hljs-attr">class</span>=<span class="hljs-string">"detail"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"wind-icon"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/images/wind.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Wind icon"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"label"</span>&gt;</span>Wind<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">id</span>=<span class="hljs-string">"wind"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"value"</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>

            <span class="hljs-comment">&lt;!-- Your location weather --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"location-weather"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Your location's weather:<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">class</span>=<span class="hljs-string">"weather-info"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"weatherInfo"</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">footer</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Made with ❤️ by <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"www.linkedin.com/in/damilola-oniyide"</span>&gt;</span>Damilola Oniyide<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">footer</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">"/js/app.js"</span> <span class="hljs-attr">defer</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>
<h2 id="heading-creating-the-offline-html-structure">Creating the Offline HTML Structure</h2>
<p>The <code>offline.html</code> is the page that users will see when they lose network connection and try to navigate to a page that isn’t cached.</p>
<pre><code class="lang-xml"><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">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"theme-color"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"#2196f3"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Weatherly - Offline<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">"/styles.css"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
    <span class="hljs-selector-class">.offline-icon</span> {
      <span class="hljs-attribute">font-size</span>: <span class="hljs-number">5rem</span>;
      <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1.5rem</span>;
      <span class="hljs-attribute">color</span>: <span class="hljs-number">#2196f3</span>;
    }

    <span class="hljs-selector-class">.offline-message</span> {
      <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
      <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1.5rem</span>;
    }

    <span class="hljs-selector-class">.offline-subtext</span> {
      <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
      <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">2rem</span>;
      <span class="hljs-attribute">color</span>: <span class="hljs-number">#666</span>;
    }

    <span class="hljs-selector-class">.retry-button</span> {
      <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span> <span class="hljs-number">1.5rem</span>;
      <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2196f3</span>;
      <span class="hljs-attribute">color</span>: white;
      <span class="hljs-attribute">border</span>: none;
      <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">12px</span>;
      <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
      <span class="hljs-attribute">cursor</span>: pointer;
      <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.3s</span>;
    }

    <span class="hljs-selector-class">.retry-button</span><span class="hljs-selector-pseudo">:hover</span> {
      <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2980b9</span>;
    }
  </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</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">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Weatherly<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">header</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">class</span>=<span class="hljs-string">"app-container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"weather-card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"offline-container"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"offline-icon"</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">width</span>=<span class="hljs-string">"1em"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"1em"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 16 16"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"</span>/&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5zm-2.715 5.933a.5.5 0 0 1-.183-.683A4.498 4.498 0 0 1 8 9.5a4.5 4.5 0 0 1 3.898 2.25.5.5 0 0 1-.866.5A3.498 3.498 0 0 0 8 10.5a3.498 3.498 0 0 0-3.032 1.75.5.5 0 0 1-.683.183zM10 8c-.552 0-1-.672-1-1.5S9.448 5 10 5s1 .672 1 1.5S10.552 8 10 8z"</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">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"offline-message"</span>&gt;</span>You're offline<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"offline-subtext"</span>&gt;</span>Please check your internet connection and try again.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"retry-button"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"window.location.href='/'"</span>&gt;</span>Retry<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">main</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Made with ❤️ by Damilola Oniyide<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-styling-with-css">Styling with CSS</h2>
<p>Now, let's create our <code>style.css</code> file for a responsive and user-friendly design:</p>
<pre><code class="lang-css">* {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Segoe UI'</span>, Tahoma, Geneva, Verdana, sans-serif;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f5f5f5</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.6</span>;
}

<span class="hljs-selector-class">.header</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2196f3</span>;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
}

<span class="hljs-selector-class">.header</span> <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
}


<span class="hljs-selector-class">.header</span> <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">55px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">55px</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">#ffff</span> <span class="hljs-number">1px</span> solid;
    <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">4px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10%</span>;
}


<span class="hljs-selector-class">.main</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">max-width</span>: auto;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
}

<span class="hljs-selector-class">.weather-card</span> {
    <span class="hljs-attribute">background-color</span>: white;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">10px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.5rem</span> <span class="hljs-number">3rem</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-comment">/* Location input styles */</span>
<span class="hljs-selector-class">.location-container</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1.5rem</span>;
    <span class="hljs-attribute">justify-content</span>: center;
}

<span class="hljs-selector-id">#location-input</span> {
    <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ddd</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">4px</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">240px</span>;
}

<span class="hljs-selector-id">#location-input</span><span class="hljs-selector-pseudo">:focus</span> {
    <span class="hljs-attribute">outline</span>: none;
    <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#2196f3</span>;
}
<span class="hljs-selector-id">#location-input</span><span class="hljs-selector-pseudo">::placeholder</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#999</span>;
}   

<span class="hljs-selector-id">#search-btn</span>, <span class="hljs-selector-id">#locationBtn</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2196f3</span>;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span> <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0</span> <span class="hljs-number">4px</span> <span class="hljs-number">4px</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">2.5px</span>;
}


<span class="hljs-selector-id">#installBtn</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2196f3</span>;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span> <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;

}

<span class="hljs-selector-id">#search-btn</span><span class="hljs-selector-pseudo">:focus</span>, <span class="hljs-selector-id">#locationBtn</span><span class="hljs-selector-pseudo">:focus</span>, <span class="hljs-selector-id">#installBtn</span><span class="hljs-selector-pseudo">:focus</span> {
    <span class="hljs-attribute">outline</span>: none;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">33</span>, <span class="hljs-number">150</span>, <span class="hljs-number">243</span>, <span class="hljs-number">0.5</span>);
}
<span class="hljs-selector-id">#search-btn</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-id">#locationBtn</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-id">#installBtn</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#1976d2</span>;
}

<span class="hljs-selector-class">.error</span>, <span class="hljs-selector-class">.loading</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">font-weight</span>: bold;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">display</span>: none;;
}

<span class="hljs-selector-class">.error-message</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#d32f2f</span>;

}
<span class="hljs-comment">/* Weather display styles */</span>
<span class="hljs-selector-class">.weather-container</span> {
    <span class="hljs-attribute">display</span>: none 
}

<span class="hljs-selector-id">#weather-icon</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">1000px</span>; 
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100px</span>;
  }

<span class="hljs-selector-class">.current-weather</span>{
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">2rem</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
}

<span class="hljs-selector-class">.location-weather</span>{
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2rem</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">flex-direction</span>: column;
}


<span class="hljs-selector-id">#weather-icon</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">80px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">80px</span>;
    <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-class">.location-info</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-class">.location-info</span> <span class="hljs-selector-tag">h2</span>,  <span class="hljs-selector-class">.current-weather</span> <span class="hljs-selector-tag">h3</span>, <span class="hljs-selector-class">.weather-container</span> <span class="hljs-selector-tag">h3</span>, <span class="hljs-selector-class">.location-weather</span> <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.8rem</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0.25rem</span>;
}



<span class="hljs-selector-class">.location-info</span> <span class="hljs-selector-tag">p</span>, <span class="hljs-selector-class">.current-weather</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#666</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.4rem</span>;
}

<span class="hljs-selector-class">.temperature-container</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-class">.temperature-container</span> <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.5rem</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0.25rem</span>;
}

<span class="hljs-selector-class">.temperature-container</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#666</span>;
    <span class="hljs-attribute">text-transform</span>: capitalize;
}

<span class="hljs-selector-class">.weather-details</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f9f9f9</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-id">#humidity-icon</span>, <span class="hljs-selector-id">#wind-icon</span>{
    <span class="hljs-attribute">width</span>: <span class="hljs-number">40px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">40px</span>;
}

<span class="hljs-selector-class">.detail</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-class">.label</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9rem</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#666</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0.25rem</span>;
}

<span class="hljs-selector-class">.value</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;
}

<span class="hljs-comment">/* Error and offline message styles */</span>
<span class="hljs-selector-class">.error-message</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#d32f2f</span>;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">display</span>: none;
} 

<span class="hljs-selector-class">.offline-message</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#ffab91</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#7f0000</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span>;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
    <span class="hljs-attribute">display</span>: none;
}


<span class="hljs-comment">/* 5 days forecast weather */</span>
<span class="hljs-selector-class">.forecast-container</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: space-around;
    <span class="hljs-attribute">gap</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-class">.forecast-item</span> {
    <span class="hljs-attribute">background-color</span>: white;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">10px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span> <span class="hljs-number">4rem</span>;
    <span class="hljs-attribute">text-align</span>: center;
}



<span class="hljs-selector-tag">footer</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2196f3</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">padding</span>: .<span class="hljs-number">7rem</span> <span class="hljs-number">0</span>;
}

<span class="hljs-selector-tag">footer</span> <span class="hljs-selector-tag">p</span>, <span class="hljs-selector-tag">footer</span> <span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#f9f9f9</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;
}
<span class="hljs-comment">/* Responsive styles */</span>
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">480px</span>) {
    <span class="hljs-selector-class">.header</span> <span class="hljs-selector-tag">h1</span> {
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
    }

    <span class="hljs-selector-class">.location-container</span> {
        <span class="hljs-attribute">flex-direction</span>: column;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">gap</span>: .<span class="hljs-number">6rem</span>
    }


    <span class="hljs-selector-class">.current-weather</span> {
        <span class="hljs-attribute">flex-direction</span>: column;
        <span class="hljs-attribute">justify-content</span>: center;
        <span class="hljs-attribute">align-items</span>: center;
    }

    <span class="hljs-selector-class">.weather-container</span> <span class="hljs-selector-tag">h3</span>,  <span class="hljs-selector-class">.location-weather</span> <span class="hljs-selector-tag">h3</span>, <span class="hljs-selector-class">.forecast</span> <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
    }

    <span class="hljs-selector-id">#weather-icon</span> {
        <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0</span>;
        <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
    }

    <span class="hljs-selector-class">.forecast-container</span> {
        <span class="hljs-attribute">flex-direction</span>: column;
        <span class="hljs-attribute">align-items</span>: center;
    }
}
</code></pre>
<h2 id="heading-how-to-set-up-appjs-and-configjs">How to Set Up <code>app.js</code> and <code>config.js</code></h2>
<p>Now, let's create our <code>app.js</code> file to add functionality to the weather app. Before proceeding, ensure you’ve obtained your <strong>API key</strong> from <a target="_blank" href="https://openweathermap.org/">OpenWeather</a>. For best practice, store your API key in a separate file like <code>config.js</code> to keep things organized and avoid hardcoding sensitive data.</p>
<p>Here's what your <code>config.js</code> should look like:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CONFIG = {
    <span class="hljs-attr">WEATHER_API_KEY</span>: <span class="hljs-string">"WRITE-YOUR-API-KEY-HERE"</span>,
};
</code></pre>
<p>Ensure you add the <code>config.js</code> file to <code>.gitignore</code> to avoid leaking sensitive information on a public platform like GitHub.</p>
<p>Now let’s move to <code>app.js</code>. This is where the main logic of your weather app will live. You can now reference your API key using <code>Weather_API_KEY</code> from the <code>config.js</code> file.</p>
<p>Below is the structure of your <code>app.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { CONFIG } <span class="hljs-keyword">from</span> <span class="hljs-string">'./config.js'</span>;
<span class="hljs-keyword">const</span> BASE_URL = <span class="hljs-string">`https://api.openweathermap.org/data/2.5/weather?&amp;appid=<span class="hljs-subst">${CONFIG.WEATHER_API_KEY}</span>&amp;units=metric&amp;q=`</span>;

<span class="hljs-keyword">const</span> cityName = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'location-input'</span>);
<span class="hljs-keyword">const</span> searchButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'search-btn'</span>);
<span class="hljs-keyword">const</span> weatherIcon = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'weather-icon'</span>);
<span class="hljs-keyword">const</span> locationBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'locationBtn'</span>);
<span class="hljs-keyword">const</span> weatherInfo = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'weatherInfo'</span>);


<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getWeatherIcon</span>(<span class="hljs-params">condition</span>) </span>{
  <span class="hljs-keyword">switch</span> (condition) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"Clear"</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"images/weather-icons/clear.png"</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">"Clouds"</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"images/weather-icons/clouds.png"</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">"Drizzle"</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"images/weather-icons/drizzle.png"</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">"Rain"</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"images/weather-icons/drizzle.png"</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">"Mist"</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"images/weather-icons/mist.png"</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">"Snow"</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"images/weather-icons/snow.png"</span>;
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"images/weather-icons/default.png"</span>;
  }
}
<span class="hljs-comment">//Search for weather by city name</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkWeatherBySearch</span>(<span class="hljs-params">city</span>)</span>{
    <span class="hljs-keyword">if</span>(city.length == <span class="hljs-number">0</span>) {
        <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].style.display = <span class="hljs-string">'block'</span>;
        <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].innerHTML = <span class="hljs-string">"Please enter a city name!"</span>;
        <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].style.color = <span class="hljs-string">'red'</span>;
        <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'weather-container'</span>).style.display = <span class="hljs-string">'none'</span>; 
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(BASE_URL + city);
    <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].style.display = <span class="hljs-string">'block'</span>;
    <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].innerHTML = <span class="hljs-string">"Wait a sec, your location's data will be displayed soon!"</span>;

    <span class="hljs-keyword">if</span> (response.status == <span class="hljs-number">404</span>) {
        <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].style.display = <span class="hljs-string">'block'</span>;
        <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].innerHTML = <span class="hljs-string">"City not found! Please enter a valid city name."</span>;
        <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].style.color = <span class="hljs-string">'red'</span>;
        <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'weather-container'</span>).style.display = <span class="hljs-string">'none'</span>;       
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'weather-container'</span>).style.display = <span class="hljs-string">'block'</span>;
      <span class="hljs-built_in">document</span>.getElementsByClassName(<span class="hljs-string">'error'</span>)[<span class="hljs-number">0</span>].style.display = <span class="hljs-string">'none'</span>;
      <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'lastCity'</span>, city);
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'city'</span>).innerHTML = data.name;
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'date'</span>).innerHTML = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(data.dt * <span class="hljs-number">1000</span>).toLocaleDateString();
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"temperature"</span>).innerHTML = <span class="hljs-built_in">Math</span>.round(data.main.temp) + <span class="hljs-string">"°C"</span>;
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"humidity"</span>).innerHTML = data.main.humidity + <span class="hljs-string">"%"</span>;
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"wind"</span>).innerHTML = data.wind.speed + <span class="hljs-string">"m/s"</span>;
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'weather-description'</span>).innerHTML = data.weather[<span class="hljs-number">0</span>].description;
      <span class="hljs-keyword">const</span> weatherCondition = data.weather[<span class="hljs-number">0</span>].main;
      weatherIcon.src = getWeatherIcon(weatherCondition);
    }
}

 <span class="hljs-comment">// display next 5-day forecast by coordinates</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">display5DaysForecast</span>(<span class="hljs-params">forecast</span>) </span>{
   <span class="hljs-keyword">const</span> fragment = <span class="hljs-built_in">document</span>.createDocumentFragment(); 
    <span class="hljs-keyword">const</span> forecastWrapper = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>);
    forecastWrapper.className = <span class="hljs-string">'forecast'</span>;

    <span class="hljs-keyword">const</span> heading = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'h3'</span>);
    heading.innerHTML = <span class="hljs-string">"Your location's next 5 days forecast:"</span>;

    <span class="hljs-keyword">const</span> container = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>);
    container.className = <span class="hljs-string">'forecast-container'</span>;

    <span class="hljs-keyword">const</span> addedDates = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>();
    <span class="hljs-keyword">const</span> today = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toDateString();

    forecast.forEach(<span class="hljs-function">(<span class="hljs-params">entry</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> entryDateObj = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(entry.dt * <span class="hljs-number">1000</span>);
      <span class="hljs-keyword">const</span> entryDateStr = entryDateObj.toDateString();

      <span class="hljs-keyword">if</span> (entryDateStr !== today &amp;&amp; !addedDates.has(entryDateStr)) {
        addedDates.add(entryDateStr);
        <span class="hljs-keyword">if</span> (addedDates.size &gt; <span class="hljs-number">6</span>) <span class="hljs-keyword">return</span>;


        <span class="hljs-keyword">const</span> condition = entry.weather[<span class="hljs-number">0</span>].main;
        <span class="hljs-keyword">const</span> iconSrc = getWeatherIcon(condition);

        <span class="hljs-keyword">const</span> forecastItem = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>);
        forecastItem.className = <span class="hljs-string">'forecast-item'</span>;

        <span class="hljs-keyword">const</span> date = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'p'</span>);
        date.id = <span class="hljs-string">'date'</span>;
        date.innerHTML = <span class="hljs-string">`&lt;strong&gt;<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(entry.dt * <span class="hljs-number">1000</span>).toLocaleDateString()}</span>&lt;/strong&gt;`</span>;

        <span class="hljs-keyword">const</span> icon = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'img'</span>);
        icon.loading = <span class="hljs-string">'lazy'</span>;
        icon.id = <span class="hljs-string">'weather-icon'</span>;
        icon.src = iconSrc;
        icon.alt = <span class="hljs-string">`<span class="hljs-subst">${condition}</span> icon`</span>;

        <span class="hljs-keyword">const</span> tempContainer = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>);
        tempContainer.className = <span class="hljs-string">'temperature-container'</span>;

        <span class="hljs-keyword">const</span> temp = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'h3'</span>);
        temp.id = <span class="hljs-string">'temperature'</span>;
        temp.innerHTML = <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">Math</span>.round(entry.main.temp)}</span> °C`</span>;

        <span class="hljs-keyword">const</span> description = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'p'</span>);
        description.id = <span class="hljs-string">'weather-description'</span>;
        description.innerHTML = <span class="hljs-string">`<span class="hljs-subst">${entry.weather[<span class="hljs-number">0</span>].description}</span>`</span>;

        tempContainer.appendChild(temp);
        tempContainer.appendChild(description);
        forecastItem.appendChild(date);
        forecastItem.appendChild(icon);
        forecastItem.appendChild(tempContainer);
        container.appendChild(forecastItem);
      }
    });

    forecastWrapper.appendChild(heading);
    forecastWrapper.appendChild(container);
    fragment.appendChild(forecastWrapper);
    weatherInfo.appendChild(fragment); 
}

<span class="hljs-comment">// Fetch next 5-day forecast by coordinates</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">get5DaysForecast</span>(<span class="hljs-params">lat, lon</span>) </span>{
    fetch(
      <span class="hljs-string">`https://api.openweathermap.org/data/2.5/forecast?lat=<span class="hljs-subst">${lat}</span>&amp;lon=<span class="hljs-subst">${lon}</span>&amp;appid=<span class="hljs-subst">${CONFIG.WEATHER_API_KEY}</span>&amp;units=metric`</span>
    )
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json())
      .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
        requestIdleCallback(<span class="hljs-function">() =&gt;</span> {
          <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> display5DaysForecast(data.list), <span class="hljs-number">0</span>);
        });        
      })
      .catch(<span class="hljs-function">() =&gt;</span> {
        weatherInfo.innerHTML = <span class="hljs-string">'Error fetching forecast data.'</span>;
    });
}

 <span class="hljs-comment">// Display current weather data</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">displayUserWeather</span>(<span class="hljs-params">data</span>) </span>{
    <span class="hljs-keyword">const</span> weatherCondition = data.weather[<span class="hljs-number">0</span>].main;
    <span class="hljs-keyword">const</span> iconSrc = getWeatherIcon(weatherCondition);

    weatherInfo.innerHTML = <span class="hljs-string">`
      &lt;h2 id="city"&gt;<span class="hljs-subst">${data.name}</span>, <span class="hljs-subst">${data.sys.country}</span>&lt;/h2&gt;

      &lt;div class="current-weather"&gt;
        &lt;img loading="lazy" id="weather-icon" src="<span class="hljs-subst">${iconSrc}</span>" alt="Weather icon"&gt;
        &lt;div class="temperature-container"&gt;
          &lt;h3 id="temperature"&gt; <span class="hljs-subst">${<span class="hljs-built_in">Math</span>.round(data.main.temp)}</span> °C&lt;/h3&gt;
          &lt;p id="weather-description"&gt;<span class="hljs-subst">${data.weather[<span class="hljs-number">0</span>].description}</span>&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="weather-details"&gt;
        &lt;div class="detail"&gt;
          &lt;img loading="lazy" id="humidity-icon" src="/images/humidity.png" alt="Humidity icon"&gt;
          &lt;span class="label"&gt;Humidity&lt;/span&gt;
          &lt;span id="humidity" class="value"&gt; <span class="hljs-subst">${data.main.humidity}</span>%&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="detail"&gt;
          &lt;img loading="lazy" id="wind-icon" src="/images/wind.png" alt="Wind icon"&gt;
          &lt;span class="label"&gt;Wind&lt;/span&gt;
          &lt;span id="wind" class="value"&gt; <span class="hljs-subst">${data.wind.speed}</span> m/s&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    `</span>;
  }

<span class="hljs-comment">// Fetch weather by coordinates</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getWeatherByCoords</span>(<span class="hljs-params">lat, lon</span>) </span>{
    fetch(
      <span class="hljs-string">`https://api.openweathermap.org/data/2.5/weather?lat=<span class="hljs-subst">${lat}</span>&amp;lon=<span class="hljs-subst">${lon}</span>&amp;appid=<span class="hljs-subst">${CONFIG.WEATHER_API_KEY}</span>&amp;units=metric`</span>
    )
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json())
      .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
        displayUserWeather(data);
        get5DaysForecast(lat, lon);
      })
      .catch(<span class="hljs-function">() =&gt;</span> {
        weatherInfo.innerHTML = <span class="hljs-string">'Please turn on your device&amp;apos;s location to get weather data.'</span>;;
      });
  }

<span class="hljs-comment">// Event listeners for search button and input field</span>
cityName.addEventListener(<span class="hljs-string">'keypress'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (e.key === <span class="hljs-string">'Enter'</span>) checkWeatherBySearch(cityName.value);
});

  <span class="hljs-comment">// Search button click event</span>
searchButton.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">()=&gt;</span>{
    checkWeatherBySearch(cityName.value);
});

<span class="hljs-comment">// Geolocation button</span>
locationBtn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        <span class="hljs-function"><span class="hljs-params">pos</span> =&gt;</span> {
          <span class="hljs-keyword">const</span> { latitude, longitude } = pos.coords;
          getWeatherByCoords(latitude, longitude);
        },
        <span class="hljs-function">() =&gt;</span> {
          weatherInfo.innerHTML = <span class="hljs-string">'Unable to retrieve location.'</span>;
        }
      );
    } <span class="hljs-keyword">else</span> {
      weatherInfo.innerHTML = <span class="hljs-string">'Geolocation not supported.'</span>;
    }
});


<span class="hljs-comment">// Load last searched city</span>
<span class="hljs-built_in">window</span>.onload = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> lastCity = <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'lastCity'</span>);
    <span class="hljs-keyword">if</span> (lastCity) {
        checkWeatherBySearch(lastCity);
    }

    <span class="hljs-keyword">if</span> (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
          <span class="hljs-function"><span class="hljs-params">pos</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> { latitude, longitude } = pos.coords;
            getWeatherByCoords(latitude, longitude);
          },
          <span class="hljs-function">() =&gt;</span> {
            weatherInfo.innerHTML = <span class="hljs-string">'Unable to retrieve location.'</span>;
          }
        );
      } <span class="hljs-keyword">else</span> {
        weatherInfo.innerHTML = <span class="hljs-string">'Geolocation not supported.'</span>;
      }
};
</code></pre>
<p>Now that we have our weather app. Let’s go further to make it a progressive web app.</p>
<h2 id="heading-how-to-create-a-manifest-file">How to Create a Manifest File</h2>
<p>We need to create a <code>manifest.json</code> file, a critical part of making your app a PWA. We’ll also use <a target="_blank" href="https://www.npmjs.com/package/pwa-asset-generator"><strong>pwa-asset-generator</strong></a>, a CLI tool that helps you to generate all the necessary icons and splash screens from a single image (like your logo). This tool also updates your <code>manifest.json</code> and optionally injects relevant <code>&lt;link&gt;</code> tags into <code>index.html</code>.</p>
<p>Below is the <code>manifest.json</code> file containing key properties that define how the Progressive Web App behaves and appears when installed.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Weatherly"</span>,                      <span class="hljs-comment">// The full name of your app that may be shown to users.</span>
  <span class="hljs-attr">"short_name"</span>: <span class="hljs-string">"Weatherly"</span>,               <span class="hljs-comment">// A shorter name used when space is limited, like on the home screen.</span>
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"A simple weather Progressive Web App"</span>, <span class="hljs-comment">// A short description of what your app does.</span>
  <span class="hljs-attr">"start_url"</span>: <span class="hljs-string">"/index.html"</span>,              <span class="hljs-comment">// The page that opens when the app is launched from the home screen.</span>
  <span class="hljs-attr">"display"</span>: <span class="hljs-string">"standalone"</span>,                 <span class="hljs-comment">// Makes the app look like a native app without browser UI (like address bar).</span>
  <span class="hljs-attr">"background_color"</span>: <span class="hljs-string">"#ffffff"</span>,           <span class="hljs-comment">// The background color used when the app is loading.</span>
  <span class="hljs-attr">"theme_color"</span>: <span class="hljs-string">"#2196f3"</span>,                <span class="hljs-comment">// The main color of the app’s UI, like the status bar.</span>
  <span class="hljs-attr">"orientation"</span>: <span class="hljs-string">"portrait"</span>,                <span class="hljs-comment">// Locks the screen orientation to portrait mode.</span>
   <span class="hljs-attr">"screenshots"</span>: [                         <span class="hljs-comment">//helps show users a preview of your app before installing it — especially in places like the "Add to Home screen" prompt on Android or in app stores that support PWAs.</span>
        {
          <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/screenshots/desktop-screenshot.png"</span>,
          <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"1337x645"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,
          <span class="hljs-attr">"form_factor"</span>: <span class="hljs-string">"wide"</span>
        },
        {
          <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/screenshots/mobile-screenshot.png"</span>,
          <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"720x1417"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,
          <span class="hljs-attr">"form_factor"</span>: <span class="hljs-string">"narrow"</span>
        }
      ]
}
</code></pre>
<h3 id="heading-how-to-generate-icons-and-splash-screens">How to Generate Icons and Splash Screens</h3>
<p>Inside your <code>images</code> folder, create a new folder called <code>assets</code>. This will store all the generated icons and splash screens. When your app is launched from the home screen, these splash screens will help improve the user experience on iOS devices.</p>
<p>Run the following command to generate PWA assets, update the <code>manifest.json</code>, and inject <code>&lt;link&gt;</code> tags into <code>index.html</code></p>
<pre><code class="lang-powershell">npx pwa<span class="hljs-literal">-asset</span><span class="hljs-literal">-generator</span> logo.png ./images/assets <span class="hljs-literal">-m</span> manifest.json <span class="hljs-literal">-i</span> index.html
</code></pre>
<h3 id="heading-injected-link-tags-in-indexhtml">Injected Link Tags in <code>index.html</code></h3>
<p>Once the command runs successfully, a series of <code>&lt;link&gt;</code> and <code>&lt;meta&gt;</code> Tags will be automatically added to your <code>index.html</code> <code>&lt;head&gt;</code>. These tags ensure support for splash screens and icons across various Apple devices:</p>
<pre><code class="lang-xml"><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-comment">&lt;!-- Other meta/link tags --&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"images/assets/apple-icon-180.png"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"mobile-web-app-capable"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"yes"</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-startup-image"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"images/assets/apple-splash-2048-2732.jpg"</span> <span class="hljs-attr">media</span>=<span class="hljs-string">"(device-width: 1024px) and (device-height: 1366px) and (orientation: portrait)"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-startup-image"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"images/assets/apple-splash-2732-2048.jpg"</span> <span class="hljs-attr">media</span>=<span class="hljs-string">"(device-width: 1024px) and (device-height: 1366px) and (orientation: landscape)"</span>&gt;</span>
  <span class="hljs-comment">&lt;!-- ...more splash screen tags for various devices... --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
</code></pre>
<p>Here’s how the <code>manifest.json</code> file should look like now:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Weatherly"</span>,
    <span class="hljs-attr">"short_name"</span>: <span class="hljs-string">"Weatherly"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"A simple weather Progressive Web App"</span>,
    <span class="hljs-attr">"start_url"</span>: <span class="hljs-string">"/index.html"</span>,
    <span class="hljs-attr">"display"</span>: <span class="hljs-string">"standalone"</span>,
    <span class="hljs-attr">"background_color"</span>: <span class="hljs-string">"#ffffff"</span>,
    <span class="hljs-attr">"theme_color"</span>: <span class="hljs-string">"#2196f3"</span>,
    <span class="hljs-attr">"orientation"</span>: <span class="hljs-string">"portrait"</span>,
    <span class="hljs-attr">"icons"</span>: [
        [
            {
              <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/assets/manifest-icon-192.maskable.png"</span>,
              <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"192x192"</span>,
              <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,
              <span class="hljs-attr">"purpose"</span>: <span class="hljs-string">"any"</span>
            },
            {
              <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/assets/manifest-icon-192.maskable.png"</span>,
              <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"192x192"</span>,
              <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,
              <span class="hljs-attr">"purpose"</span>: <span class="hljs-string">"maskable"</span>
            },
            {
              <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/assets/manifest-icon-512.maskable.png"</span>,
              <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"512x512"</span>,
              <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,
              <span class="hljs-attr">"purpose"</span>: <span class="hljs-string">"any"</span>
            },
            {
              <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/assets/manifest-icon-512.maskable.png"</span>,
              <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"512x512"</span>,
              <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,
              <span class="hljs-attr">"purpose"</span>: <span class="hljs-string">"maskable"</span>
            }
          ]
        ],
    <span class="hljs-attr">"screenshots"</span>: [
        {
          <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/screenshots/desktop-screenshot.png"</span>,
          <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"1337x645"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,
          <span class="hljs-attr">"form_factor"</span>: <span class="hljs-string">"wide"</span>
        },
        {
          <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/screenshots/mobile-screenshot.png"</span>,
          <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"720x1417"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,
          <span class="hljs-attr">"form_factor"</span>: <span class="hljs-string">"narrow"</span>
        }
      ]
    }
</code></pre>
<p>You can then link your manifest file to your HTML file:</p>
<pre><code class="lang-json">&lt;link rel=<span class="hljs-string">"manifest"</span> href=<span class="hljs-string">"manifest.json"</span> /&gt;
</code></pre>
<h2 id="heading-how-to-add-workbox-to-your-service-workerjs-file">How to Add WorkBox to Your <code>service-worker.js</code> File</h2>
<p>In this tutorial, WorkBox will be added to <code>index.html</code> via CDN. You can copy the import code below or visit WorkBox to get the link. You can then add it to the <code>index.html</code> file by placing the URL inside a <code>&lt;script&gt;</code> tag. You can copy the import code below or visit the WorkBox website for the latest link.</p>
<pre><code class="lang-javascript">importScripts(<span class="hljs-string">'https://storage.googleapis.com/workbox-cdn/releases/6.5.4/workbox-sw.js'</span>);
</code></pre>
<h2 id="heading-how-to-create-your-service-worker-in-the-service-workerjs-file">How to Create your Service Worker in the <code>service-worker.js</code> File</h2>
<p>Here, we’ll implement the necessary functionalities needed to make the weather app a PWA</p>
<h3 id="heading-step-1-activate-the-new-service-worker-immediately"><strong>Step 1:</strong> Activate the New Service Worker Immediately</h3>
<p>Add <code>workbox.core.skipWaiting()</code> to make the newly installed service worker activate right away instead of waiting for the old one to be removed in the <code>service-worker.js</code> file.</p>
<pre><code class="lang-javascript">workbox.core.skipWaiting();
</code></pre>
<h3 id="heading-step-2-take-control-of-open-tabs"><strong>Step 2:</strong> Take Control of Open Tabs</h3>
<p>Add <code>workbox.core.clientsClaim()</code> to ensure that the activated service worker takes control of all currently open pages, so the latest version of your app works immediately across all tabs after it becomes active.</p>
<pre><code class="lang-javascript">workbox.core.clientsClaim();
</code></pre>
<h3 id="heading-step-3-check-if-workbox-is-loaded">Step 3: Check if Workbox is Loaded</h3>
<p>Before using Workbox, make sure it has loaded properly.</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> (workbox) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Workbox loaded successfully'</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Workbox failed to load'</span>);
}
</code></pre>
<p>This confirms that the <code>workbox</code> object is available and ready to use. If not, the fallback message in the <code>else</code> block will be shown.</p>
<p>We then proceed to create the functions inside the <code>if</code> block</p>
<h3 id="heading-step-4-pre-cache-core-files">Step 4: Pre-cache Core Files</h3>
<p>Pre-cache essential files enable your app to work offline. This caches your app shell (HTML, CSS, JS), so it loads even without a network connection.</p>
<pre><code class="lang-js">workbox.precaching.precacheAndRoute([
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/index.html'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'3'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/style.css'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'11'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/app.js'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'7'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/images/logo.png'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'3'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/manifest.json'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'5'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/offline.html'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'1'</span> },
  ]);
</code></pre>
<p>The <code>revision</code> helps with updating cached files when changes are made.</p>
<h3 id="heading-step-5-cache-api-responses-dynamically">Step 5: Cache API Responses Dynamically</h3>
<p>Set up a route to cache data from your weather API using the <code>NetworkFirst</code> caching strategy. This tells Workbox to try fetching fresh data from the network first. If the network fails, it serves the cached version instead.</p>
<pre><code class="lang-js"> <span class="hljs-comment">// Cache API requests </span>
  workbox.routing.registerRoute(
    <span class="hljs-function">(<span class="hljs-params">{ url }</span>) =&gt;</span> url.origin === <span class="hljs-string">'https://api.openweathermap.org'</span>,
    <span class="hljs-keyword">new</span> workbox.strategies.NetworkFirst({
      <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'weather-api-cache'</span>,
      <span class="hljs-attr">plugins</span>: [
        <span class="hljs-keyword">new</span> workbox.expiration.ExpirationPlugin({
          <span class="hljs-attr">maxAgeSeconds</span>: <span class="hljs-number">24</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span>,
          <span class="hljs-attr">maxEntries</span>: <span class="hljs-number">10</span>,
        }),
      ],
    })
  );
</code></pre>
<h3 id="heading-step-6-dynamic-image-caching">Step 6: Dynamic Image Caching</h3>
<p>This function enables dynamic caching for images using the <code>StaleWhileRevalidate</code> strategy. When a user requests an image, Workbox first serves it from the cache (if available) for faster load times, while simultaneously fetching an updated version from the network to refresh the cache. This ensures users get a quick response without missing out on updated content. It’s a smart way to handle images by balancing speed and freshness.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Cache images</span>
  workbox.routing.registerRoute(
    <span class="hljs-function">(<span class="hljs-params">{ request }</span>) =&gt;</span> request.destination === <span class="hljs-string">'image'</span>,
    <span class="hljs-keyword">new</span> workbox.strategies.StaleWhileRevalidate({
      <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'image-cache'</span>,
    })
  );
</code></pre>
<h3 id="heading-step-7-serve-cached-resources">Step 7: Serve Cached Resources</h3>
<p>The commonly used static files (like HTML, CSS, JS, fonts, and so on) are served quickly from the cache. It uses the <code>CacheFirst</code> strategy, meaning that the service worker will look in the cache first and only fetch from the network if the file isn’t already stored. The cache is named <code>"static-cache"</code> and it’s set to automatically remove items older than seven days using the <code>expiration</code> plugin. This helps keep the cache fresh and avoids taking up too much space.</p>
<pre><code class="lang-javascript">  <span class="hljs-comment">// Serve Cached Resources </span>
  workbox.routing.registerRoute(
    <span class="hljs-function">(<span class="hljs-params">{url}</span>) =&gt;</span> url.origin === self.location.origin,  
    <span class="hljs-keyword">new</span> workbox.strategies.CacheFirst({
      <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'static-cache'</span>,  
      <span class="hljs-attr">plugins</span>: [
        <span class="hljs-keyword">new</span> workbox.expiration.ExpirationPlugin({
          <span class="hljs-attr">maxAgeSeconds</span>: <span class="hljs-number">7</span> * <span class="hljs-number">24</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span>,  <span class="hljs-comment">// Cache static resources for 7 days</span>
        }),
      ],
    })
  );
</code></pre>
<h3 id="heading-step-8-cache-html-pages-with-offline-support"><strong>Step 8: Cache</strong> HTML Pages with Offline Support</h3>
<p>The <code>index.html</code> page will be handled using the NetworkFirst strategy. This means that the service worker tries to fetch the latest version from the network first. If the user is offline or the network fails, it falls back to the cached version. The cache is named <code>"pages-cache"</code> and the offline fallback page (<code>offline.html</code>) is returned when the requested page isn’t available. This ensures that users can still navigate the app even without an internet connection.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Serve HTML pages with Network First and offline fallback</span>
workbox.routing.registerRoute(
  <span class="hljs-function">(<span class="hljs-params">{ request }</span>) =&gt;</span> request.mode === <span class="hljs-string">'navigate'</span>,
  <span class="hljs-keyword">async</span> ({ event }) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> workbox.strategies.networkFirst({
        <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'pages-cache'</span>,
        <span class="hljs-attr">plugins</span>: [
          <span class="hljs-keyword">new</span> workbox.expiration.ExpirationPlugin({
            <span class="hljs-attr">maxEntries</span>: <span class="hljs-number">50</span>,
          }),
        ],
      }).handle({ event });
      <span class="hljs-keyword">return</span> response || <span class="hljs-keyword">await</span> caches.match(<span class="hljs-string">'/offline.html'</span>);
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> caches.match(<span class="hljs-string">'/offline.html'</span>);
    }
  }
);
</code></pre>
<h3 id="heading-step-9-handle-when-workbox-doesnt-load">Step 9: Handle When Workbox Doesn’t Load</h3>
<p>You should always provide a fallback in case something goes wrong. The <code>if</code> block will have an <code>else</code> block to catch issues during development and debugging.</p>
<pre><code class="lang-js"><span class="hljs-keyword">else</span> {
     <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Workbox failed to load'</span>);
}
</code></pre>
<p>Once the service worker finishes handling the different conditions in the <code>if-else</code> block, we add a general cleanup step to remove any outdated or unused caches.</p>
<h3 id="heading-step-10-clean-up-outdated-caches"><strong>Step 10:</strong> Clean Up Outdated Caches</h3>
<p>During the service worker's activation phase, old or unused caches are removed. It compares all existing cache names with a list of current ones (<code>precache</code>, <code>weather-api-cache</code>, <code>image-cache</code>, <code>pages-cache</code>, and <code>static-resources</code>). If a cache doesn’t match the current list, it gets deleted. This helps keep the app lightweight and ensures that outdated data doesn't persist.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Clean up old/unused caches during activation</span>
self.addEventListener(<span class="hljs-string">'activate'</span>, <span class="hljs-function"><span class="hljs-params">event</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> currentCaches = [
    workbox.core.cacheNames.precache,
    <span class="hljs-string">'weather-api-cache'</span>,
    <span class="hljs-string">'image-cache'</span>,
    <span class="hljs-string">'pages-cache'</span>,
    <span class="hljs-string">'static-cache'</span>
  ];

  event.waitUntil(
    caches.keys().then(<span class="hljs-function"><span class="hljs-params">cacheNames</span> =&gt;</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.all(
        cacheNames.map(<span class="hljs-function"><span class="hljs-params">cacheName</span> =&gt;</span> {
          <span class="hljs-keyword">if</span> (!currentCaches.includes(cacheName)) {
            <span class="hljs-keyword">return</span> caches.delete(cacheName);
          }
        })
      );
    })
  );
});
</code></pre>
<p>This is what your <code>service-worker.js</code> file should look like:</p>
<pre><code class="lang-javascript">importScripts(<span class="hljs-string">'https://storage.googleapis.com/workbox-cdn/releases/6.5.4/workbox-sw.js'</span>);

<span class="hljs-comment">// Force waiting service worker to become active</span>
workbox.core.skipWaiting();
workbox.core.clientsClaim();

<span class="hljs-keyword">if</span> (workbox) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Workbox loaded successfully'</span>);

  <span class="hljs-comment">// Precache critical files with revisions (update revisions when files change)</span>
  workbox.precaching.precacheAndRoute([
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/index.html'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'3'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/style.css'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'11'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/app.js'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'7'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/images/logo.png'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'3'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/manifest.json'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'5'</span> },
    { <span class="hljs-attr">url</span>: <span class="hljs-string">'/offline.html'</span>, <span class="hljs-attr">revision</span>: <span class="hljs-string">'1'</span> },
  ]);

  <span class="hljs-comment">// Cache API requests </span>
  workbox.routing.registerRoute(
    <span class="hljs-function">(<span class="hljs-params">{ url }</span>) =&gt;</span> url.origin === <span class="hljs-string">'https://api.openweathermap.org'</span>,
    <span class="hljs-keyword">new</span> workbox.strategies.NetworkFirst({
      <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'weather-api-cache'</span>,
      <span class="hljs-attr">plugins</span>: [
        <span class="hljs-keyword">new</span> workbox.expiration.ExpirationPlugin({
          <span class="hljs-attr">maxAgeSeconds</span>: <span class="hljs-number">24</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span>,
          <span class="hljs-attr">maxEntries</span>: <span class="hljs-number">10</span>,
        }),
      ],
    })
  );

  <span class="hljs-comment">// Cache images</span>
  workbox.routing.registerRoute(
    <span class="hljs-function">(<span class="hljs-params">{ request }</span>) =&gt;</span> request.destination === <span class="hljs-string">'image'</span>,
    <span class="hljs-keyword">new</span> workbox.strategies.StaleWhileRevalidate({
      <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'image-cache'</span>,
    })
  );

    <span class="hljs-comment">// Serve Cached Resources </span>
  workbox.routing.registerRoute(
    <span class="hljs-function">(<span class="hljs-params">{url}</span>) =&gt;</span> url.origin === self.location.origin,  
    <span class="hljs-keyword">new</span> workbox.strategies.CacheFirst({
      <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'static-cache'</span>,  
      <span class="hljs-attr">plugins</span>: [
        <span class="hljs-keyword">new</span> workbox.expiration.ExpirationPlugin({
          <span class="hljs-attr">maxAgeSeconds</span>: <span class="hljs-number">7</span> * <span class="hljs-number">24</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span>,  <span class="hljs-comment">// Cache static resources for 7 days</span>
        }),
      ],
    })
  );

  <span class="hljs-comment">// Serve HTML pages with Network First and offline fallback</span>
workbox.routing.registerRoute(
  <span class="hljs-function">(<span class="hljs-params">{ request }</span>) =&gt;</span> request.mode === <span class="hljs-string">'navigate'</span>,
  <span class="hljs-keyword">async</span> ({ event }) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> workbox.strategies.networkFirst({
        <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'pages-cache'</span>,
        <span class="hljs-attr">plugins</span>: [
          <span class="hljs-keyword">new</span> workbox.expiration.ExpirationPlugin({
            <span class="hljs-attr">maxEntries</span>: <span class="hljs-number">50</span>,
          }),
        ],
      }).handle({ event });
      <span class="hljs-keyword">return</span> response || <span class="hljs-keyword">await</span> caches.match(<span class="hljs-string">'/offline.html'</span>);
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> caches.match(<span class="hljs-string">'/offline.html'</span>);
    }
  }
);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Workbox failed to load'</span>);
}

<span class="hljs-comment">// Clean up old/unused caches during activation</span>
self.addEventListener(<span class="hljs-string">'activate'</span>, <span class="hljs-function"><span class="hljs-params">event</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> currentCaches = [
    workbox.core.cacheNames.precache,
    <span class="hljs-string">'weather-api-cache'</span>,
    <span class="hljs-string">'image-cache'</span>,
    <span class="hljs-string">'pages-cache'</span>,
    <span class="hljs-string">'static-cache'</span>
  ];

  event.waitUntil(
    caches.keys().then(<span class="hljs-function"><span class="hljs-params">cacheNames</span> =&gt;</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.all(
        cacheNames.map(<span class="hljs-function"><span class="hljs-params">cacheName</span> =&gt;</span> {
          <span class="hljs-keyword">if</span> (!currentCaches.includes(cacheName)) {
            <span class="hljs-keyword">return</span> caches.delete(cacheName);
          }
        })
      );
    })
  );
});
</code></pre>
<h2 id="heading-how-to-set-up-app-installation">How to Set Up App Installation</h2>
<p>The code to install the app will be written in <code>install.js</code> following the steps below:</p>
<h3 id="heading-step-1-register-the-service-worker"><strong>Step 1:</strong> Register the Service Worker</h3>
<p>Register the service worker to activate and run it in your app.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span>(<span class="hljs-string">'serviceWorker'</span> <span class="hljs-keyword">in</span> navigator){
    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'load'</span>, <span class="hljs-function">() =&gt;</span> {
      navigator.serviceWorker.register(<span class="hljs-string">'/service-worker.js'</span>).then(<span class="hljs-function"><span class="hljs-params">reg</span> =&gt;</span> {
        reg.onupdatefound = <span class="hljs-function">() =&gt;</span> {
          <span class="hljs-keyword">const</span> newWorker = reg.installing;
          newWorker.onstatechange = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">if</span> (newWorker.state === <span class="hljs-string">'installed'</span> &amp;&amp; navigator.serviceWorker.controller) {
              <span class="hljs-built_in">window</span>.location.reload();
            }
          };
        };
      });
    })
 }
</code></pre>
<h3 id="heading-step-2-enable-custom-install-prompt">Step 2: Enable Custom Install Prompt</h3>
<p>Next, we will allow users to install the weather PWA with a custom button. Inside the <code>install.js</code>file, add the <code>beforeinstallprompt</code> event which intercepts the default prompt and shows your install button instead. When clicked, it triggers the install prompt.</p>
<pre><code class="lang-javascript">
  <span class="hljs-keyword">let</span> deferredPrompt;

<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'DOMContentLoaded'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> installBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'installBtn'</span>);

  <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'beforeinstallprompt'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault();
    deferredPrompt = e;

    <span class="hljs-comment">// Show the button</span>
    installBtn.style.display = <span class="hljs-string">'block'</span>;

    installBtn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-comment">// Directly triggered by user click</span>
      installBtn.style.display = <span class="hljs-string">'none'</span>;

      <span class="hljs-comment">// Show the install prompt</span>
      deferredPrompt.prompt();

      deferredPrompt.userChoice.then(<span class="hljs-function">(<span class="hljs-params">choiceResult</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (choiceResult.outcome === <span class="hljs-string">'accepted'</span>) {
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User accepted the install prompt'</span>);
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User dismissed the install prompt'</span>);
        }
        deferredPrompt = <span class="hljs-literal">null</span>;
      });
    });
  });
</code></pre>
<p>The <code>appinstalled</code> event confirms successful installation.</p>
<pre><code class="lang-javascript">
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'appinstalled'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'PWA was installed'</span>);
  });
});
</code></pre>
<h3 id="heading-step-3-add-script-tag-to-import-installjs-in-indexhtml">Step 3: Add script tag to import <code>install.js</code> in <code>index.html</code></h3>
<p>Add the <code>&lt;script&gt;</code> tag for <code>install.js</code> inside the <code>index.html</code> file to include the installation logic.</p>
<pre><code class="lang-xml"> <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">"/js/install.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h2 id="heading-how-to-install-the-weather-app">How to Install the Weather App</h2>
<p>You can choose to install the Weatherly app on your phone or desktop. Below is a demonstration on how to install it on your mobile phone:</p>
<p>Open the <a target="_blank" href="https://weatherly-taupe-two.vercel.app/">Weatherly</a> app in your browser. You should see an <strong>“Install App”</strong> button, as shown in the image below. Click on the button to continue.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747272209446/2ade0ad7-eda5-46df-b443-a1efce90003b.png" alt="Weatherly app interface showing Install App button along with city search field, location services, and Tokyo weather history" class="image--center mx-auto" width="720" height="1318" loading="lazy"></p>
<p>After clicking, a preview of the app will appear along with an <strong>“Install”</strong> option, as shown below. Click the Install button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748387183047/b6be3b6c-6550-4c94-a453-eb928cc70dbe.png" alt="Browser PWA installation dialog showing Weatherly app preview with Install button and app description." class="image--center mx-auto" width="720" height="1299" loading="lazy"></p>
<p>Once the installation is complete, the Weatherly app will appear on your home screen, just like a native app. And that’s it! Your weather app is now a Progressive Web App (PWA).</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Progressive Web Apps combine the best of web and native app experiences, and service workers are the backbone of that functionality. With tools like Workbox, you don’t have to worry about manually handling caching, offline support, or background sync. Its simple APIs and built-in strategies make it easier to build fast, reliable, and installable web apps. Whether it’s a small weather app like <a target="_blank" href="https://weatherly-pwa.vercel.app/">Weatherly</a> or a more complex project, Workbox helps you deliver a seamless user experience.</p>
<p>You can check out the full project and assets on <a target="_blank" href="https://github.com/LolaVictoria/weatherly">GitHub</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the CSS text-wrap Property to Create Balanced Text Layouts on Your Websites ]]>
                </title>
                <description>
                    <![CDATA[ An inconsistent text layout can really ruin the look of your website’s design. Maybe a heading has an extra word that wraps to another line, or in a paragraph some lines are longer than others. This can leave the whole thing looking messy and hard to... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-css-text-wrap-property/</link>
                <guid isPermaLink="false">67fd1442fd23722b0a451053</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Responsive Web Design ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Azubuike Duru ]]>
                </dc:creator>
                <pubDate>Mon, 14 Apr 2025 13:57:22 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744638131989/38357168-abda-4f7b-8c4f-568f64b586df.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>An inconsistent text layout can really ruin the look of your website’s design. Maybe a heading has an extra word that wraps to another line, or in a paragraph some lines are longer than others. This can leave the whole thing looking messy and hard to read.</p>
<p>Before now, developers used tags like <code>&lt;br&gt;</code> or <code>&lt;span&gt;</code> to manually adjust word spacing. But what happens in cases where you also need to consider the responsiveness of the website? Well, the new <code>text-wrap: balance</code> property in CSS can now automatically calculate and divide the lines in such a way that every word looks organized and even. Like this:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_4C8BE890CB3AB8AD50C286E15DBA884FF164212E142B1E75C767C0221DB183E7_1743742346617_Desktop+-+1-3.png" alt="Heading before and after applying 'text-wrap: balance' for improved wrapping." width="1440" height="411" loading="lazy"></p>
<p>In this article, you’ll learn how the <code>text-wrap</code> property works and how to use it in your code. You’ll also see a few examples along the way.</p>
<p>Without wasting time, let's get right into it.</p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-understanding-the-problems">Understanding the Problems</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-introducing-text-wrap">Introducing text-wrap</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-text-wrap">What is text-wrap?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-basic-syntax-of-text-wrap">Basic Syntax of text-wrap</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-text-wrap-vs-max-width-when-to-use-each">text-wrap vs. max-width : When to Use Each</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-when-to-use-max-width-vs-text-wrap">When to Use max-width vs text-wrap</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-browser-support-and-considerations">Browser Support and Considerations</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-practical-implementation-step-by-step-guide">Practical Implementation: Step-by-Step Guide</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-understanding-the-problems">Understanding the Problems</h2>
<p>Apart from making words harder to read when your text is unevenly displayed, there are other issues uneven or poorly displayed text can cause on your website as a whole. Some of these are:</p>
<ul>
<li><p><strong>Responsive Design</strong>: We wouldn't have any issues if we could manually design for every screen size and carefully place and space the lines of our text exactly as we wanted users to read them. Unfortunately, we can't do this, which is why making designs responsive is always crucial. When text adjusts from one screen size to another, lines break, and what looks good on a desktop may look terrible on a tablet view.</p>
</li>
<li><p><strong>Headings and Short Paragraphs</strong>: Since paragraphs and small blocks of text have many words, there is a high probability of words ending in a very unbalanced way. Often, we see a heading line ending with just one word, forming an awkward shape in the overall design.</p>
</li>
<li><p><strong>Dynamic Content</strong>: If your website contains dynamic content (such as cards of various sizes, product descriptions, or blog posts), having no <code>text-wrap</code> might make your layout break or appear unpredictable.</p>
</li>
</ul>
<h2 id="heading-introducing-text-wrap">Introducing <code>text-wrap</code></h2>
<p>In the last section, we looked at the problems attributed to uneven text distribution. In this section, you’ll see how <code>text-wrap</code> is a game changer in how you organize your text.</p>
<p>Before <code>text-wrap</code>, developers relied heavily on <code>max-width</code>, <code>text-align</code>, or <code>&lt;br&gt;</code> to control text lines. The new <code>text-wrap</code> CSS property was created to help break text naturally across lines without making it look out of place, preventing the need for trial and error in checking if text fits.</p>
<h3 id="heading-what-is-text-wrap">What is <code>text-wrap</code>?</h3>
<p><code>text-wrap</code> is a CSS property that can help you adjust and space text automatically, break lines evenly without using the rigid <code>&lt;br&gt;</code> tag, and not have to rely on <code>text-align</code>, which didn't work on all screen sizes.</p>
<p>With <code>text-wrap</code>, your headings and paragraphs are sure to look good. Instead of having some lines that look longer than others, <code>text-wrap</code> neatly arranges everything to appear visually appealing.</p>
<h3 id="heading-basic-syntax-of-text-wrap">Basic Syntax of <code>text-wrap</code></h3>
<p>There are two major ways to apply text-wrap to your text. These values are:</p>
<p><code>text-wrap: balance</code><strong>: (The Smart Heading Balancer)</strong></p>
<p>This is the smart way of telling the browser to adjust the text evenly, regardless of the screen size.</p>
<p>Code snippet:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">text-wrap</span>: balance;
}
</code></pre>
<p>In this code, the heading text lines will split naturally without any weird short lines.</p>
<p><code>text-wrap: pretty;</code> <strong>(The Smart Paragraph Balancer)</strong></p>
<p>Just as <code>text-wrap: balance</code> works best for headings, <code>text-wrap: pretty</code> is the best for styling long paragraphs.</p>
<p>Code snippet:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">p</span>{
  <span class="hljs-attribute">text-wrap</span>: pretty;
}
</code></pre>
<p>The <code>p</code> in this code will make sure the paragraphs maintain natural readability.</p>
<p><strong>Other values include:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Value</strong></td><td><strong>Description</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>wrap</code></td><td>Default state</td></tr>
<tr>
<td><code>nowrap</code></td><td>Prevents text from wrapping to the next line</td></tr>
<tr>
<td><code>stable</code></td><td>Ensures things are kept in place until the content changes itself</td></tr>
</tbody>
</table>
</div><h2 id="heading-text-wrap-vs-max-width-when-to-use-each"><code>text-wrap</code> vs. <code>max-width</code> : When to Use Each</h2>
<p><code>max-width</code> was always the go-to option for developers in the past. While this method works in most cases, it won’t stop uneven text distribution. Let's compare it with the new <code>text-wrap</code> CSS property so you can know when to use each one.</p>
<p><strong>Using</strong> <code>max-width</code>:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
}
</code></pre>
<p>The <code>max-width</code> here forces the heading not to exceed a 400px width. This can be good for controlling short body paragraphs but can cause unevenness for headings when dealing with multiple screen sizes.</p>
<p><strong>Using</strong> <code>text-wrap</code>:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">text-wrap</span>: balance;
}
</code></pre>
<p>Here, the browser dynamically breaks the heading text in a balanced way, preventing any weird-looking single lines.</p>
<h3 id="heading-when-to-use-max-width-vs-text-wrap">When to Use <code>max-width</code> vs <code>text-wrap</code></h3>
<p>Use <code>text-wrap: balance</code> when you want a more natural text read without any weird line breaks.</p>
<p>Use <code>max-width</code> when you need to control the text width and don't necessarily care how the lines break.</p>
<p>Use both if you want a more natural read within a confined width limit.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">500px</span>;
  <span class="hljs-attribute">text-wrap</span>: balance;
}
</code></pre>
<p>This ensures the heading stays within a 500px limit while maintaining an even text distribution.</p>
<h3 id="heading-browser-support-and-considerations">Browser Support and Considerations</h3>
<p>Currently, Chrome and Edge are the only main browsers that support the new <code>text-wrap</code> property. If you are building a project that should work on browsers like Safari and Firefox, you will need to use traditional text formatting methods like <code>text-align</code>, <code>&lt;br&gt;</code>, or <code>max-width</code> instead.</p>
<p>Fallback snippet:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@supports</span> (<span class="hljs-attribute">text-wrap:</span> balance) {
  <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">text-wrap</span>: balance;
  }
}
</code></pre>
<p>The <code>@support</code> is a pro tip to apply this style to only supported browsers.</p>
<h2 id="heading-practical-implementation-step-by-step-guide">Practical Implementation: Step-by-Step Guide</h2>
<p>Now that you’ve seen how important <code>text-wrap</code> can be and how to use it, let’s put that knowledge into practise and see real examples, compare the before and after screens of using it (and not), and check how it reacts in responsive designs as well.</p>
<h3 id="heading-1-applying-text-wrap-balance-to-headings">1. Applying <code>text-wrap: balance</code> to Headings</h3>
<p>In this section, we will see how the heading lines in the topic <strong>"How to Use CSS Text Balance: A Simple Trick for Smoother, Cleaner Designs"</strong> will break across different sizes and how it looks when <code>text-wrap: balance</code> is applied.</p>
<p><strong>Without</strong> <code>text-wrap: balance</code>:</p>
<p>HTML</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>How to Use CSS Text Balance: A Simple Trick for Smoother, Cleaner Designs<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>CSS</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.title</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.5rem</span>;
  <span class="hljs-attribute">font-weight</span>: bold;
}
</code></pre>
<p>Without any special application to the headings, it will just adjust freely.</p>
<p>Result:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_4C8BE890CB3AB8AD50C286E15DBA884FF164212E142B1E75C767C0221DB183E7_1743740541468_Screenshot+2025-04-04+at+5.21.36AM+1.png" alt="Heading without 'text-wrap: balance', showing uneven wrapping." width="3360" height="1325" loading="lazy"></p>
<p><strong>With</strong> <code>text-wrap: balance</code>:</p>
<p>CSS</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.title</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.5rem</span>;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">text-wrap</span>: balance;
}
</code></pre>
<p>Now, the browser automatically adjusts the line breaks to ensure a more even distribution.</p>
<p>Result:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_4C8BE890CB3AB8AD50C286E15DBA884FF164212E142B1E75C767C0221DB183E7_1743740708025_Screenshot+2025-04-04+at+5.24.26AM.png" alt="Heading with 'text-wrap: balance', showing smart readable behavior and wrapping" width="3360" height="1395" loading="lazy"></p>
<h3 id="heading-2-using-text-wrap-pretty-on-short-paragraphs">2. Using <code>text-wrap: pretty</code> on Short Paragraphs</h3>
<p>You’ve now seen how <code>text-wrap: balance</code> handles headings, so lets also take a look at how it breaks the lines evenly in your text paragraphs. As I mentioned above, the value <code>pretty</code> is mostly used for paragraphs or short block of words. Here’s how it works and appears on a block of text.</p>
<p><strong>Without</strong> <code>text-wrap: pretty</code>:</p>
<p>HTML</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"subText"</span>&gt;</span> Do you know that inconsistent text layout can ruin the look of your website design? Maybe a heading has an extra word that takes up another line or in a paragraph some lines are longer than others whereby leaving the whole thing looking messed up and hard to read.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>CSS</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.subText</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
}
</code></pre>
<p>In this code, text adjustment will behave normally without any smart contraints.</p>
<p>Result:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_4C8BE890CB3AB8AD50C286E15DBA884FF164212E142B1E75C767C0221DB183E7_1743739991569_Screenshot+2025-04-04+at+5.11.08AM.png" alt="Paragraph without 'text-wrap: pretty', showing uneven wrapping." width="3360" height="1231" loading="lazy"></p>
<p><strong>With</strong> <code>text-wrap: pretty</code>:</p>
<p>CSS</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.subText</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
  <span class="hljs-attribute">text-wrap</span>: pretty;
}
</code></pre>
<p>The code above makes the line breaks evenly in a way that it will be easier for someone to read through.</p>
<p>Result:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_4C8BE890CB3AB8AD50C286E15DBA884FF164212E142B1E75C767C0221DB183E7_1743739834917_Screenshot+2025-04-04+at+5.09.49AM.png" alt="Paragraph with 'text-wrap: pretty', showing smart readable behavior and wrapping" width="3360" height="1353" loading="lazy"></p>
<h3 id="heading-how-text-wrap-works-in-responsive-design">How <code>text-wrap</code> Works in Responsive Design</h3>
<p>When you use <code>text-wrap</code> on your text, you need not to worry how it is going to appear on various screen sizes. The below video shows you what I mean by that:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_4C8BE890CB3AB8AD50C286E15DBA884FF164212E142B1E75C767C0221DB183E7_1743738297774_ScreenRecording2025-04-04at4.37.18AM-ezgif.com-video-to-gif-converter.gif" alt="A gif showing how 'text-wrap' smoothly adjusts on a responsive screen " width="800" height="450" loading="lazy"></p>
<h3 id="heading-using-media-queries-for-extra-control">Using Media Queries for Extra Control</h3>
<p>Combine <code>media-queries</code> and <code>text-wrap</code> to have a great special kind of control on how your text appears on various screens.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.5rem</span>;
  <span class="hljs-attribute">text-wrap</span>: balance;
}

<span class="hljs-comment">/* On smaller screens, reduce font size and apply text-balancing */</span>
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">600px</span>) {
  <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
    <span class="hljs-attribute">text-wrap</span>: balance;
  }
}
</code></pre>
<p>This code ensures your heading text adapts and remains clean across multiple device sizes.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>How text displays is something every good developer should pay attention to. It plays a big role in user experience. By using <code>text-wrap</code>, you can ensure that your website layouts don't look messy or difficult to read.</p>
<p>One of the best things about using <code>text-wrap</code> in your text formatting is that it just works every time. You don’t need to bother with <code>&lt;br&gt;</code> tags, tweak <code>max-width</code>, or fight with text alignment.</p>
<p>Even though it's not yet supported by all browsers, adding it to your next project will future-proof your design so it’s always intact and good-looking.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Programmatically Highlight Text with the CSS Custom Highlight API ]]>
                </title>
                <description>
                    <![CDATA[ You can highlight text in the browser by clicking and dragging through the desired text. And sometimes this works fine. But there are times when you’ll want to programmatically highlight some text in an HTML document. In this article, I’ll discuss a ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-programmatically-highlight-text-with-the-css-custom-highlight-api/</link>
                <guid isPermaLink="false">6787d62041c11049156708c3</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joe Attardi ]]>
                </dc:creator>
                <pubDate>Wed, 15 Jan 2025 15:37:04 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736955360118/bd658ef5-734c-4e21-ad0e-be2dac0b7eee.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You can highlight text in the browser by clicking and dragging through the desired text. And sometimes this works fine. But there are times when you’ll want to programmatically highlight some text in an HTML document.</p>
<p>In this article, I’ll discuss a couple ways you can do this. The first is using the &lt;mark&gt; element, and the second is using the CSS Custom Highlight API. We’ll go through examples, and I’ll explain the issues with <code>&lt;mark&gt;</code>. Then you’ll learn how the Custom Highlight API solves these challenges.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-we-want-to-do">What We Want to Do</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-reasons-for-highlighting-text">Reasons for Highlighting Text</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-highlight-text-using-the-element">How to Highlight Text Using the Mark Element</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-introducing-the-css-custom-highlight-api">Introducing the CSS Custom Highlight API</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-apply-a-custom-highlight">How to Apply a Custom Highlight</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-modify-a-highlighted-range">How to Modify a Highlighted Range</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-highlight-multiple-ranges">How to Highlight Multiple Ranges</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-remove-highlights">How to Remove Highlights</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-browser-support">Browser Support</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ol>
<h2 id="heading-what-we-want-to-do">What We Want to Do</h2>
<p>We want to apply highlighting effects to some text in a document, without needing to manually select the text. Typically, we’d do this by giving the text a background color that calls attention to the highlighted text. You can see what this looks like in the following screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736729603375/cb0e081b-a848-4079-8b8e-67815d56711d.png" alt="Demonstration of highlighted text" class="image--center mx-auto" width="410" height="68" loading="lazy"></p>
<h2 id="heading-reasons-for-highlighting-text">Reasons for Highlighting Text</h2>
<p>There are several use cases for programmatically highlighting text. Before we talk about <em>how</em> to highlight, let's talk about <em>why</em> we might want to highlight.</p>
<ul>
<li><p><strong>Highlighting search results or matches</strong>: If the user reaches this page by searching, it may help them to call out the matching search text by applying highlighting.</p>
</li>
<li><p><strong>Emphasizing information</strong>: We might want to call out some important text on the page.</p>
</li>
<li><p><strong>User defined highlighting</strong>: An example would be in an e-reading app like Amazon's Kindle app. Here, users can select and save highlighted regions of a book. Later, when returning to a page, the user's previous highlights are shown.</p>
</li>
</ul>
<h2 id="heading-how-to-highlight-text-using-the-element">How to Highlight Text Using the <code>&lt;mark&gt;</code> Element</h2>
<p>One way you can highlight text is by using the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark">HTML <code>&lt;mark&gt;</code> element</a>. This has the benefit of being an HTML semantic element.</p>
<p>To highlight text using <code>&lt;mark&gt;</code>, you can wrap the text to highlight in a <code>&lt;mark&gt;</code> element. The browser will apply a highlight style to any text inside a <code>&lt;mark&gt;</code> element.</p>
<p>Consider the following HTML containing a <code>&lt;mark&gt;</code> element which surrounds the text you want to highlight.</p>
<pre><code class="lang-html">Here is some <span class="hljs-tag">&lt;<span class="hljs-name">mark</span>&gt;</span>text to highlight<span class="hljs-tag">&lt;/<span class="hljs-name">mark</span>&gt;</span>.
</code></pre>
<p><code>&lt;mark&gt;</code> can be styled with CSS like any other HTML element, so you can customize the color and style of the highlighted text.</p>
<p>Using the <code>&lt;mark&gt;</code> element has some drawbacks, though. You have to modify the DOM and insert nodes whenever you want to add highlighting. This can cause side effects such as a layout recalculation that can affect the page's performance.</p>
<p>It's also harder to highlight text that might span multiple HTML elements. Since <code>&lt;mark&gt;</code> is an HTML element, you have to use it in such a way that it produces valid HTML.</p>
<p>For the rest of this article, we'll use some example HTML markup that we want to highlight parts of. Consider this HTML markup containing an introductory paragraph and some list items. We want to apply highlighting to some of the content, but let's show the base HTML first.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Some introductory text.<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">li</span>&gt;</span>Item one<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Item two<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>
</code></pre>
<p>Now, suppose we want to highlight "Some introductory text." <em>and</em> "Item one" together. We can't use a single <code>&lt;mark&gt;</code> element, because it would be invalid HTML.</p>
<p>The closing tag would be nested inside a <code>&lt;li&gt;</code> element, which is not valid HTML. The following code shows what that might look like:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">mark</span>&gt;</span>Some introductory text.<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">li</span>&gt;</span>Item one<span class="hljs-tag">&lt;/<span class="hljs-name">mark</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Item two<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>
</code></pre>
<p>Instead, to achieve the desired effect, we would have to insert multiple <code>&lt;mark&gt;</code> elements as shown in the following code. Note that there are two <code>&lt;mark&gt;</code> elements: one in the introduction, and one in the first list item. If you wanted to highlight multiple list items, you'd need to add even more <code>&lt;mark&gt;</code> elements.</p>
<p>Here's some example code with multiple <code>&lt;mark&gt;</code> elements:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">mark</span>&gt;</span>Some introductory text.<span class="hljs-tag">&lt;/<span class="hljs-name">mark</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">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">mark</span>&gt;</span>Item one<span class="hljs-tag">&lt;/<span class="hljs-name">mark</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Item two<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>
</code></pre>
<p>This works, but it's cumbersome and does not result in a single continuous highlight.</p>
<h2 id="heading-introducing-the-css-custom-highlight-api">Introducing the CSS Custom Highlight API</h2>
<p>The solution to our problem is the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API">CSS Custom Highlight API</a>, a newer API that lets you create highlight regions and style them with CSS. Highlights are tied to <em>ranges</em>, which can span multiple HTML elements and do not add any markup or elements to the document.</p>
<p>There are several concepts you’ll need to know when using this API:</p>
<ul>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Range"><code>Range</code></a>: A <code>Range</code> is an object representing part of a document between two nodes. These can be element nodes or text nodes.</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Highlight"><code>Highlight</code></a>: A <code>Highlight</code> is an object that defines a custom highlight around one or more <code>Range</code> objects. These objects are registered with the CSS engine under a unique name.</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HighlightRegistry">CSS highlight registry</a>: A global object where <code>Highlight</code> objects are registered under unique names.</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/::highlight">The <code>::highlight</code> pseudo-element</a>: This is used in a CSS stylesheet to define the highlighting style. Each <code>::highlight</code> pseudo-element references a <code>Highlight</code> object name that was registered with the CSS highlight registry.</p>
</li>
</ul>
<h2 id="heading-how-to-apply-a-custom-highlight">How to Apply a Custom Highlight</h2>
<p>Let's return to the earlier highlighting example and use the CSS Custom Highlight API to highlight the introductory text and the first list item.</p>
<p>First, let's add some IDs so we can select the relevant elements more easily. Consider this updated code where we have added IDs to some of the elements.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intro"</span>&gt;</span>Introductory text.<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">li</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"item1"</span>&gt;</span>Item one<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"item2"</span>&gt;</span>Item two<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>
</code></pre>
<p>Let's walk through the steps to create the highlight.</p>
<h3 id="heading-create-the-range">Create the <code>Range</code></h3>
<p>First, we'll create a <code>Range</code> object that spans the desired elements. This will represent a range of elements that spans the introduction and first list item without having to modify the DOM.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> range = <span class="hljs-keyword">new</span> Range();
range.setStartBefore(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'intro'</span>));
range.setEndAfter(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'item1'</span>));
</code></pre>
<p>This <code>Range</code> object will start at the beginning of the <code>&lt;p&gt;</code> element, and will end at the end of the <code>&lt;li&gt;</code> with ID <code>item1</code>.</p>
<h3 id="heading-create-and-register-the-highlight-object">Create and Register the <code>Highlight</code> Object</h3>
<p>Now that we have a <code>Range</code>, we can create a <code>Highlight</code> for that <code>Range</code>. We do this by calling the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Highlight/Highlight"><code>Highlight</code> constructor</a>, passing the <code>Range</code> object as its argument.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> highlight = <span class="hljs-keyword">new</span> Highlight(range);
</code></pre>
<p>This creates the <code>Highlight</code> object, but we can't do anything with it yet. First, we'll need to register it with the CSS highlight registry with the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HighlightRegistry/set"><code>CSS.highlights.set</code> method</a>.</p>
<p>The following code shows how you can use <code>CSS.highlights.set</code> to register a <code>Highlight</code> object under the name <code>my-custom-highlight</code>. We'll reference this name in the CSS when we apply styling in the next step.</p>
<pre><code class="lang-javascript">CSS.highlights.set(<span class="hljs-string">'my-custom-highlight'</span>, highlight);
</code></pre>
<h3 id="heading-style-the-highlight">Style the Highlight</h3>
<p>We've created and registered the <code>Highlight</code> around a given <code>Range</code>, but at this point, we still won't see anything in the document. We need to use a CSS rule to define the highlight style.</p>
<p>To do this, we'll use the <code>::highlight</code> pseudo-element. We pass the name of the custom highlight used in the previous step to this pseudo-element. This allows us to set CSS styles that are applied to the text within the highlighted range.</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">::highlight(my-custom-highlight)</span> {
  <span class="hljs-attribute">background-color</span>: yellow;
}
</code></pre>
<p>Now, the introductory text and the first list item will be highlighted in yellow, as shown in this screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736729896575/97b15f77-e8fc-449e-8a89-ad5658e13605.png" alt="Screenshot showing the initial highlight state" class="image--center mx-auto" width="252" height="252" loading="lazy"></p>
<h2 id="heading-how-to-modify-a-highlighted-range">How to Modify a Highlighted <code>Range</code></h2>
<p>When we create a <code>Highlight</code> object around a <code>Range</code> object, the <code>Highlight</code> will be dynamically updated with any changes made to the <code>Range</code>. In our example, let's say we now want to extend the highlight to the second list item.</p>
<p>We don't need to create a new <code>Highlight</code> or <code>Range</code> – rather, we can just set the <code>Range</code>'s end position to the new element.</p>
<pre><code class="lang-javascript">range.setEndAfter(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'item2'</span>));
</code></pre>
<p>As soon as the <code>Range</code> is modified, the new text will be highlighted as shown in this screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736729961827/8633c59b-485d-4ba6-a716-7dcb084fddae.png" alt="The second list item is now highlighted" class="image--center mx-auto" width="256" height="262" loading="lazy"></p>
<h2 id="heading-how-to-highlight-multiple-ranges">How to Highlight Multiple <code>Range</code>s</h2>
<p>For even more flexibility, a single <code>Highlight</code> can cover multiple <code>Range</code>s. Let's update our example HTML to include four list items.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intro"</span>&gt;</span>Introductory text.<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">li</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"item1"</span>&gt;</span>Item one<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"item2"</span>&gt;</span>Item two<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"item3"</span>&gt;</span>Item three<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"item4"</span>&gt;</span>Item four<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>
</code></pre>
<p>So far, we're highlighting the introductory text and the first two list items. Suppose we now want to also highlight the fourth list item. We can't do this with our existing <code>Range</code> object, since it wouldn't represent a contiguous range of nodes. We'll need to create a second <code>Range</code>. This new <code>Range</code> spans the fourth list item.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> item4 = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'item4'</span>);
<span class="hljs-keyword">const</span> range2 = <span class="hljs-keyword">new</span> Range();
range2.setStartBefore(item4);
range2.setEndAfter(item4);
</code></pre>
<p>Now, we can add this new <code>Range</code> to our existing <code>Highlight</code> object by calling its <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Highlight/add"><code>add</code> method</a>. This will let us apply highlighting to the second <code>Range</code>.</p>
<pre><code class="lang-javascript">highlight.add(range2);
</code></pre>
<p>Once we do this, the fourth list item will be highlighted as well, as shown in this screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736730028688/6710e901-6027-46c5-af8e-2d2c3bc7563f.png" alt="The fourth item is now also highlighted" class="image--center mx-auto" width="256" height="256" loading="lazy"></p>
<h2 id="heading-how-to-remove-highlights">How to Remove Highlights</h2>
<p>There are two ways that you can remove highlights from the document.</p>
<p>First, let's suppose we want to remove the highlight from the introductory text and first two list items, but keep the last list item highlighted. We can use the <code>Highlight</code> object's <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Highlight/delete"><code>delete</code> method</a> to remove the first <code>Range</code> from the <code>Highlight</code> object.</p>
<pre><code class="lang-javascript">highlight.delete(range);
</code></pre>
<p>After we delete this <code>Range</code>, only the last list item will remain highlighted, as shown in this screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736730070050/626121b3-60a3-4491-b9d8-11940d4d34f2.png" alt="Only the last list item is now highlighted" class="image--center mx-auto" width="254" height="262" loading="lazy"></p>
<p>The other way to remove highlights is to un-register a <code>Highlight</code> object from the CSS highlight registry by calling <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HighlightRegistry/delete"><code>CSS.highlights.delete</code></a> with the unique name we gave the <code>Highlight</code>. This removes the <code>Highlight</code> object that we registered previously.</p>
<pre><code class="lang-javascript">CSS.highlights.delete(<span class="hljs-string">'my-custom-highlight'</span>);
</code></pre>
<p>Now, nothing will remain highlighted, as shown in this screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736730106386/593b7b46-027d-442a-b633-04ac0aecdb4a.png" alt="Nothing is highlighted" class="image--center mx-auto" width="254" height="226" loading="lazy"></p>
<h2 id="heading-browser-support">Browser Support</h2>
<p>As of January 2025, at the time of writing, the CSS Custom Highlight API is supported in Chrome, Edge, and Safari. Firefox support is starting to show up in nightly builds, so you should expect Firefox to have improved support soon. For the latest compatibility data, see <a target="_blank" href="https://caniuse.com/mdn-api_highlight">https://caniuse.com/mdn-api_highlight</a>.</p>
<p>To test if the browser supports CSS custom highlighting, you can check for the existence of the <code>highlights</code> property of the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CSS"><code>CSS</code> object</a>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (!(<span class="hljs-string">'highlights'</span> <span class="hljs-keyword">in</span> CSS)) {
  <span class="hljs-comment">// highlight API is not supported</span>
}
</code></pre>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>The CSS Custom Highlight API lets you programmatically highlight regions of text in an HTML document without having to modify the DOM or worry about inserting invalid HTML markup. Its flexible and dynamic nature lets you add, modify, and remove highlights at runtime.</p>
<h3 id="heading-further-reading">Further Reading</h3>
<ul>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Range"><code>Range</code> (MDN)</a>: API documentation for the <code>Range</code> interface.</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API">CSS Custom Highlight API (MDN)</a>: More details about the CSS Custom Highlight API.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Why developers needn't fear CSS – with the King of CSS himself Kevin Powell [Podcast #154] ]]>
                </title>
                <description>
                    <![CDATA[ On this week's episode of the podcast, freeCodeCamp founder Quincy Larson interviews Kevin Powell. He's a designer, a software engineer, and an expert in CSS. He's runs a CSS-focused YouTube channel with nearly a million subscribers. There's nothing ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-not-hate-css-kevin-powell-interview-154/</link>
                <guid isPermaLink="false">6765e0f59e51f9e8b04f2529</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ podcast ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Fri, 20 Dec 2024 21:26:13 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734729865195/15498ad9-15f9-4dc3-98cc-8d7f07cec348.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>On this week's episode of the podcast, freeCodeCamp founder Quincy Larson interviews Kevin Powell. He's a designer, a software engineer, and an expert in CSS. He's runs a CSS-focused YouTube channel with nearly a million subscribers. There's nothing sensational there – he literally just teaches people CSS.</p>
<p>Take our year-end freeCodeCamp podcast listener survey real quick: <a target="_blank" href="https://forms.gle/2M9NW776723uSdDT7">https://forms.gle/2M9NW776723uSdDT7</a></p>
<p>Support for this podcast comes from a grant from Wix Studio. Wix Studio provides developers tools to rapidly build websites with everything out-of-the-box, then extend, replace, and break boundaries with code. Learn more at <a target="_blank" href="https://wixstudio.com">https://wixstudio.com</a>.</p>
<p>Support also comes from the 11,043 kind folks who support freeCodeCamp through a monthly donation. Join these kind folks and help our mission by going to <a target="_blank" href="https://www.freecodecamp.org/donate">https://www.freecodecamp.org/donate</a></p>
<p>CORRECTION: I (Quincy) say during the interview that the Uber found a way to access microphones on iOS without users' knowledge. There have been documented cases of malware doing this (like Pegasus) but Uber didn't do this. They did do a lot of other shady things, like continue collecting data even after you deleted their app – but mic spying was not one of them. Yes, early Uber was an ethical tire fire. But it's important to get facts right here.</p>
<p>We talk about:</p>
<ul>
<li><p>Why you should still learn CSS in 2025</p>
</li>
<li><p>How teaching concepts improves your own understanding of them</p>
</li>
<li><p>How learning to skateboard helped Kevin escape Tutorial Hell</p>
</li>
<li><p>Massive improvements coming to CSS</p>
</li>
</ul>
<p>You can watch the interview on YouTube:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/OaQO5gSpE4g" 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> </p>
<p>Or you can listen to the podcast in Apple Podcasts, Spotify, or your favorite podcast app. Be sure to follow the freeCodeCamp Podcast there so you'll get new episodes each Friday.</p>
<p>Links we talk about during our conversation:</p>
<ul>
<li><p>Kevin's YouTube channel: <a target="_blank" href="https://www.youtube.com/kevinpowell">https://www.youtube.com/kevinpowell</a></p>
</li>
<li><p>Original Space Jam website Kevin mentions: <a target="_blank" href="https://www.spacejam.com/1996/">https://www.spacejam.com/1996/</a></p>
</li>
<li><p>The article that coined the term Responsive Design: <a target="_blank" href="https://alistapart.com/article/responsive-web-design/">https://alistapart.com/article/responsive-web-design/</a></p>
</li>
<li><p>Kevin's freeCodeCamp article on how learning skateboarding helped him out of tutorial hell: <a target="_blank" href="https://www.freecodecamp.org/news/how-learning-to-skateboard-helped-me-find-a-way-out-of-tutorial-hell/">https://www.freecodecamp.org/news/how-learning-to-skateboard-helped-me-find-a-way-out-of-tutorial-hell/</a></p>
</li>
<li><p>Kevin's freeCodeCamp course on building and deploying a portfolio page: <a target="_blank" href="https://www.freecodecamp.org/news/how-to-build-a-portfolio-website-and-deploy-to-digital-ocean/">https://www.freecodecamp.org/news/how-to-build-a-portfolio-website-and-deploy-to-digital-ocean/</a></p>
</li>
<li><p>Playable Minesweeper in CSS that Quincy mentions: <a target="_blank" href="https://codepen.io/bali_balo/pen/BLJONZ">https://codepen.io/bali_balo/pen/BLJONZ</a></p>
</li>
<li><p>Acknowledged mistakes that are permanently coded into CSS: <a target="_blank" href="https://wiki.csswg.org/ideas/mistakes">https://wiki.csswg.org/ideas/mistakes</a></p>
</li>
<li><p>Talk on why is CSS so weird: <a target="_blank" href="https://www.youtube.com/watch?v=aHUtMbJw8iA">https://www.youtube.com/watch?v=aHUtMbJw8iA</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Reusable Keyboard Shortcut Listener Component in React ]]>
                </title>
                <description>
                    <![CDATA[ If you’re like me and you loveeeeee shortcuts, you know how satisfying it is to press a few keys and watch the magic happen. Whether it’s the familiar Ctrl+C – Ctrl+V that devs use to “borrow code” 😉 from LLMs and code pages, or the personalised sho... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-reusable-keyboard-shortcut-listener-component-in-react/</link>
                <guid isPermaLink="false">67606d5e81f45e690a86fe00</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Jaja ]]>
                </dc:creator>
                <pubDate>Mon, 16 Dec 2024 18:11:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733895763796/17684457-fb85-48d4-b049-ddbaf0b5281e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’re like me and you loveeeeee shortcuts, you know how satisfying it is to press a few keys and watch the magic happen. Whether it’s the familiar Ctrl+C – Ctrl+V that devs use to “borrow code” 😉 from LLMs and code pages, or the personalised shortcuts we set up in our favourite tools, keyboard shortcuts save time and make us feel like a computer wiz.</p>
<p>Well, fear not! I’ve cracked the code for building components that trigger and respond to keyboard shortcuts. In this article, I’ll teach you how to create them with React, Tailwind CSS, and Framer Motion.</p>
<h2 id="heading-table-of-content">Table of Content</h2>
<p>Here’s everything we’ll cover:</p>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-keyboard-shortcut-listener-ksl-component">What Is a Keyboard Shortcut Listener (KSL) Component?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-ksl-component">How to Build the KSL Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-reveal-component">How to Create the Reveal Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-trigger-the-component-via-keyboard-shortcut">How to Trigger the Component via Keyboard Shortcut</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-animate-the-components-visibility">How to Animate the Component’s Visibility</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-optimize-your-ksl-component">How to Optimize Your KSL Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Fundamentals of HTML, CSS, and Tailwind CSS</p>
</li>
<li><p>Fundamentals of JavaScript, React, and React Hooks.</p>
</li>
</ul>
<h2 id="heading-what-is-a-keyboard-shortcut-listener-ksl-component">What Is a Keyboard Shortcut Listener (KSL) Component?</h2>
<p>A <strong>Keyboard Shortcut Listener component (KSLC)</strong> is a component that listens for specific key combinations and triggers actions in your app. It's designed to make your app respond to keyboard shortcuts, allowing for a smoother, more efficient user experience.</p>
<h3 id="heading-why-is-it-important">Why is it important?</h3>
<ul>
<li><p><strong>Accessibility</strong>: The KSL component makes it simple for people who use a keyboard to trigger actions, making your app more inclusive and easy to use.</p>
</li>
<li><p><strong>Snappier Experience</strong>: Shortcuts are quick and efficient, allowing users to get things done in less time. No more fumbling around for the mouse—just hit a key (or two) and boom, action happens!</p>
</li>
<li><p><strong>Reusability</strong>: Once you’ve set up your KSL, it can handle different shortcuts across your app, making it easy to add without rewriting the same logic.</p>
</li>
<li><p><strong>Cleaner Code</strong>: Instead of scattering keyboard event listeners everywhere, the KSL component keeps things tidy by centralising the logic. Your code stays clean, organized, and easier to maintain.</p>
</li>
</ul>
<h2 id="heading-how-to-build-the-ksl-component">How to Build the KSL Component</h2>
<p>I've prepared a GitHub repository with <a target="_blank" href="https://github.com/Daiveedjay/KSL-Component/tree/starter">starter files</a> to speed things up. Simply clone this repo and install the dependencies.</p>
<p>For this project, we’re using Tailwind’s home page as our muse and creating the KSL functionality. After installing and running the build command, here’s what your page should look like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733861510569/fd94572d-e973-4637-ab65-9dd5e944065f.png" alt="fd94572d-e973-4637-ab65-9dd5e944065f" class="image--center mx-auto" width="1920" height="912" loading="lazy"></p>
<h2 id="heading-how-to-create-the-reveal-component">How to Create the Reveal Component</h2>
<p>The reveal component is the component we want to show when we use the shortcut.</p>
<p>To begin, create a file called <code>search-box.tsx</code> and paste in this code:</p>
<pre><code class="lang-javascript"><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">SearchBox</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">"fixed top-0 left-0 w-full h-full backdrop-blur-sm bg-slate-900/50 "</span>&gt;</span>
      {" "}
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" p-[15vh] text-[#939AA7] h-full"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-xl mx-auto divide-y divide-[#939AA7] bg-[#1e293b] rounded-md"</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 flex justify-between px-4 py-2 text-sm "</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 w-full gap-2 text-white"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">BiSearch</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</span> /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full h-full p-2 bg-transparent focus-within:outline-none"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search Documentation"</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 -translate-y-1/2 right-4 top-1/2 "</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">kbd</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-1 text-xs rounded-[4px] bg-[#475569] font-sans font-semibold text-slate-400"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">abbr</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Escape"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"no-underline "</span>&gt;</span>
                  Esc{" "}
                <span class="hljs-tag">&lt;/<span class="hljs-name">abbr</span>&gt;</span>{" "}
              <span class="hljs-tag">&lt;/<span class="hljs-name">kbd</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 justify-center p-10 text-center "</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>
              How many licks does it take to get to the center of a Tootsie pop?
            <span class="hljs-tag">&lt;/<span class="hljs-name">h2</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>
  );
}
</code></pre>
<p>Ok, so what’s happening in this code?</p>
<ol>
<li><p><strong>Main Overlay (</strong><code>&lt;div className="fixed top-0 left-0 ..."&gt;</code>)</p>
<ul>
<li><p>This is the full-screen overlay that dims the background.</p>
</li>
<li><p>The <code>backdrop-blur-sm</code> adds a subtle blur to the backdrop, and <code>bg-slate-900/50</code> gives it a semi-transparent dark overlay.</p>
</li>
</ul>
</li>
<li><p><strong>Search Box Wrapper (</strong><code>&lt;div className="p-[15vh] ..."&gt;</code>)</p>
<ul>
<li><p>The content is centered using padding and flex utilities.</p>
</li>
<li><p>The <code>max-w-xl</code> makes sure that the search box stays within a reasonable width for readability.</p>
</li>
</ul>
</li>
</ol>
<p>Then in your <code>App.tsx</code>, create a state that dynamically shows that component:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [isOpen, setIsOpen] = useState&lt;boolean&gt;(<span class="hljs-literal">false</span>);
</code></pre>
<ul>
<li><p><code>useState</code>: This hook initializes <code>isOpen</code> to <code>false</code>, meaning the search box is hidden by default.</p>
</li>
<li><p>When <code>isOpen</code> is set to <code>true</code>, the <code>SearchBox</code> component will render on the screen.</p>
</li>
</ul>
<p>And render the search component:</p>
<pre><code class="lang-javascript">  {isOpen &amp;&amp; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span> /&gt;</span></span>}
</code></pre>
<p>To show the search component, add a toggle function to the input button:</p>
<pre><code class="lang-javascript">&lt;button
  type=<span class="hljs-string">"button"</span>
  className=<span class="hljs-string">"items-center hidden h-12 px-4 space-x-3 text-left rounded-lg shadow-sm sm:flex w-72 ring-slate-900/10 focus:outline-none hover:ring-2 hover:ring-sky-500 focus:ring-2 focus:ring-sky-500 bg-slate-800 ring-0 text-slate-300 highlight-white/5 hover:bg-slate-700"</span>
  onClick={<span class="hljs-function">() =&gt;</span> setIsOpen(<span class="hljs-literal">true</span>)}&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">BiSearch</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</span> /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-auto"</span>&gt;</span>Quick search...<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
   <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">kbd</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-sans font-semibold text-slate-500"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">abbr</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Control"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"no-underline text-slate-500"</span>&gt;</span>
    Ctrl{" "}
    <span class="hljs-tag">&lt;/<span class="hljs-name">abbr</span>&gt;</span>{" "}
    K
   <span class="hljs-tag">&lt;/<span class="hljs-name">kbd</span>&gt;</span></span>
&lt;/button&gt;
</code></pre>
<p>The <code>onClick</code> event sets <code>isOpen</code> to <code>true</code>, displaying the <code>SearchBox</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733861855356/87adf797-9378-4f2f-bae4-d1b45f6122d2.gif" alt="87adf797-9378-4f2f-bae4-d1b45f6122d2" class="image--center mx-auto" width="800" height="377" loading="lazy"></p>
<p>But as you’ve seen, this was triggered by a click action, not a keyboard shortcut action. Let’s do that next.</p>
<h2 id="heading-how-to-trigger-the-component-via-keyboard-shortcut">How to Trigger the Component via Keyboard Shortcut</h2>
<p>To make the reveal component open and close using a keyboard shortcut, we’ll use a <code>useEffect</code> hook to listen for specific key combinations and update the component’s state accordingly.</p>
<h3 id="heading-step-1-listen-for-keyboard-events">Step 1: Listen for Keyboard Events</h3>
<p>Add an <code>useEffect</code> hook in your <code>App.tsx</code> file to listen for key presses:</p>
<pre><code class="lang-javascript">  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> handleKeyDown = <span class="hljs-function">(<span class="hljs-params">event: KeyboardEvent</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (event.ctrlKey &amp;&amp; event.key === Key.K) {
        event.preventDefault(); <span class="hljs-comment">// Prevent default browser behavior</span>

      }    };

    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);
    };
  }, []);
</code></pre>
<p>What’s happening in this code?</p>
<ol>
<li><p><strong>Effect Setup (</strong><code>useEffect</code>)</p>
<ul>
<li><code>useEffect</code> ensures that the event listener for key presses is added when the component mounts and cleaned up when the component unmounts, preventing memory leaks.</li>
</ul>
</li>
<li><p><strong>Key Combination (</strong><code>event.ctrlKey &amp;&amp; event.key === "k"</code>)</p>
<ul>
<li><p>The <code>event.ctrlKey</code> checks if the <strong>Control</strong> key is being pressed.</p>
</li>
<li><p>The <code>event.key === "k"</code> ensures we’re listening specifically for the "K" key. Together, this checks if the <strong>Ctrl + K</strong> combination is pressed.</p>
</li>
</ul>
</li>
<li><p><strong>Prevent Default Behavior (</strong><code>event.preventDefault()</code>)</p>
<ul>
<li>Some browsers may have default behaviors tied to key combinations like <strong>Ctrl + K</strong> (e.g., focusing the browser’s address bar). Calling <code>preventDefault</code> stops this behavior.</li>
</ul>
</li>
<li><p><strong>Event Cleanup (</strong><code>return () =&gt; ...</code>)</p>
<ul>
<li>The cleanup function removes the event listener to prevent duplicate listeners from being added if the component re-renders.</li>
</ul>
</li>
</ol>
<h3 id="heading-step-2-toggle-component-visibility">Step 2: Toggle Component Visibility</h3>
<p>Next, update the <code>handleKeyDown</code> function to toggle the <code>SearchBox</code> visibility when the shortcut is pressed:</p>
<pre><code class="lang-javascript">useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> handleKeyDown = <span class="hljs-function">(<span class="hljs-params">event: KeyboardEvent</span>) =&gt;</span> {
      <span class="hljs-comment">// Listen for Ctrl + K</span>
      <span class="hljs-keyword">if</span> (event.ctrlKey &amp;&amp; event.key === Key.K) {
        event.preventDefault(); <span class="hljs-comment">// Prevent default browser behavior</span>
        setIsOpen(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> !prev); <span class="hljs-comment">// Toggle the search box</span>
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (event.key === Key.Escape) {
        setIsOpen(<span class="hljs-literal">false</span>); <span class="hljs-comment">// Close the search box</span>
      }
    };

    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);
    };
  }, []);
</code></pre>
<p>What’s happening in this code?</p>
<ol>
<li><p><strong>Toggling State</strong> (<code>setIsOpen((prev) =&gt; !prev)</code>)</p>
<ul>
<li><p>When <strong>Ctrl + K</strong> is pressed, the <code>setIsOpen</code> state setter toggles the visibility of the <code>SearchBox</code>.</p>
</li>
<li><p>The <code>prev</code> argument represents the previous state. Using <code>!prev</code> flips its value:</p>
<ul>
<li><p><code>true</code> (open) becomes <code>false</code> (close).</p>
</li>
<li><p><code>false</code> (closed) becomes <code>true</code> (open).</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Closing with the Escape Key (</strong><code>event.key === "Escape"</code>)</p>
<ul>
<li>When the <strong>Escape</strong> key is pressed, <code>setIsOpen(false)</code> explicitly sets the state to <code>false</code>, closing the <code>SearchBox</code>.</li>
</ul>
</li>
</ol>
<p>This results in the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733861983226/9c6ff7ef-a067-42c9-b6c7-afd35955731e.gif" alt="9c6ff7ef-a067-42c9-b6c7-afd35955731e" class="image--center mx-auto" width="800" height="367" loading="lazy"></p>
<h2 id="heading-how-to-animate-the-components-visibility">How to Animate the Component’s Visibility</h2>
<p>At the moment, our component works, but it lacks a little flair, wouldn’t you say? Let’s change that.</p>
<h3 id="heading-step-1-create-the-overlay-component">Step 1: Create the Overlay Component</h3>
<p>We’ll start by creating an <strong>overlay component</strong>, which acts as the dark, blurred backdrop for the search box. Here’s the base version:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { ReactNode } <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">OverlayWrapper</span>(<span class="hljs-params">{ children }: { children: ReactNode }</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">"fixed top-0 left-0 w-full h-full backdrop-blur-sm bg-slate-900/50 "</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-step-2-add-animations-to-the-overlay">Step 2: Add Animations to the Overlay</h3>
<p>Now, let’s make the overlay fade in and out using Framer Motion. Update the <code>OverlayWrapper</code> component like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { motion } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;
<span class="hljs-keyword">import</span> { ReactNode } <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">OverlayWrapper</span>(<span class="hljs-params">{ children }: { children: ReactNode }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span> }}
      <span class="hljs-attr">animate</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">1</span> }}
      <span class="hljs-attr">exit</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span> }}
      <span class="hljs-attr">className</span>=<span class="hljs-string">"fixed top-0 left-0 w-full h-full backdrop-blur-sm bg-slate-900/50 "</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
}
</code></pre>
<h5 id="heading-key-animation-props">Key animation props:</h5>
<ul>
<li><p><code>initial</code>: Sets the starting state when the component is mounted (fully transparent).</p>
</li>
<li><p><code>animate</code>: Defines the state to animate toward (fully opaque).</p>
</li>
<li><p><code>exit</code>: Specifies the animation when the component is unmounted (fading out).</p>
</li>
</ul>
<h3 id="heading-step-3-animate-the-search-box">Step 3: Animate the Search Box</h3>
<p>Next, add some motion to the search box itself. We’ll make it slide and fade in when it appears and slide out when it disappears.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { motion } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;
<span class="hljs-keyword">import</span> { BiSearch } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/bi"</span>;
<span class="hljs-keyword">import</span> OverlayWrapper <span class="hljs-keyword">from</span> <span class="hljs-string">"./overlay"</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">SearchBox</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">OverlayWrapper</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
        <span class="hljs-attr">initial</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">y:</span> "<span class="hljs-attr">-10</span>%", <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span> }}
        <span class="hljs-attr">animate</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">y:</span> "<span class="hljs-attr">0</span>%", <span class="hljs-attr">opacity:</span> <span class="hljs-attr">1</span> }}
        <span class="hljs-attr">exit</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">y:</span> "<span class="hljs-attr">-5</span>%", <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span> }}
        <span class="hljs-attr">className</span>=<span class="hljs-string">" p-[15vh] text-[#939AA7] h-full"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-xl mx-auto divide-y divide-[#939AA7] bg-[#1e293b] rounded-md"</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 flex justify-between px-4 py-2 text-sm "</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 w-full gap-2 text-white"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">BiSearch</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</span> /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full h-full p-2 bg-transparent focus-within:outline-none"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search Documentation"</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 -translate-y-1/2 right-4 top-1/2 "</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">kbd</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-1 text-xs rounded-[4px] bg-[#475569] font-sans font-semibold text-slate-400"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">abbr</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Escape"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"no-underline "</span>&gt;</span>
                  Esc{" "}
                <span class="hljs-tag">&lt;/<span class="hljs-name">abbr</span>&gt;</span>{" "}
              <span class="hljs-tag">&lt;/<span class="hljs-name">kbd</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 justify-center p-10 text-center "</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>
              How many licks does it take to get to the center of a Tootsie pop?
            <span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">motion.div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">OverlayWrapper</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-step-4-enable-animation-tracking-with-animatepresence">Step 4: Enable Animation Tracking with <code>AnimatePresence</code></h3>
<p>Finally, wrap your conditional rendering logic in the <code>AnimatePresence</code> component provided by <strong>Framer Motion</strong>. This ensures Framer Motion tracks when elements enter and leave the DOM.</p>
<pre><code class="lang-javascript">&lt;AnimatePresence&gt;{isOpen &amp;&amp; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span> /&gt;</span></span>}&lt;/AnimatePresence&gt;
</code></pre>
<p>This enables Framer Motion to track when an element enters and leaves the DOM. With this, we get the following result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733862299745/e4c9858c-d10a-4817-bf41-697fa103d096.gif" alt="e4c9858c-d10a-4817-bf41-697fa103d096" class="image--center mx-auto" width="777" height="345" loading="lazy"></p>
<p>Ah, much better!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733862351332/a1888e83-8df6-45cc-80c4-db1e2e8e7025.gif" alt="a1888e83-8df6-45cc-80c4-db1e2e8e7025" class="image--center mx-auto" width="498" height="498" loading="lazy"></p>
<h2 id="heading-how-to-optimize-your-ksl-component">How to Optimize Your KSL Component</h2>
<p>If you thought we were done, not so fast…We still have a little more to do.</p>
<p>We need to optimize for accessibility. We should add a way for users to close the search component with a mouse, as accessibility is very important.</p>
<p>To do this, start by creating a hook called <code>useClickOutside</code>. This hook uses a reference element to know when a user is clicking outside the target element (search box) which is a very popular behaviour for closing modals and KSLCs.</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

type ClickOutsideHandler = <span class="hljs-function">(<span class="hljs-params">event: Event</span>) =&gt;</span> <span class="hljs-keyword">void</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useClickOutside = <span class="hljs-function">(<span class="hljs-params">
  ref: React.RefObject&lt;HTMLElement&gt;,
  handler: ClickOutsideHandler
</span>) =&gt;</span> {
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> listener = <span class="hljs-function">(<span class="hljs-params">event: Event</span>) =&gt;</span> {
      <span class="hljs-comment">// Do nothing if clicking ref's element or descendant elements</span>
      <span class="hljs-keyword">if</span> (!ref.current || ref.current.contains(event.target <span class="hljs-keyword">as</span> Node)) <span class="hljs-keyword">return</span>;

      handler(event);
    };

    <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"mousedown"</span>, listener);
    <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"touchstart"</span>, listener);

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">document</span>.removeEventListener(<span class="hljs-string">"mousedown"</span>, listener);
      <span class="hljs-built_in">document</span>.removeEventListener(<span class="hljs-string">"touchstart"</span>, listener);
    };
  }, [ref, handler]);
};
</code></pre>
<p>To use this hook, pass in the function responsible for opening and closing the search component:</p>
<pre><code class="lang-javascript">&lt;AnimatePresence&gt; {isOpen &amp;&amp; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span> <span class="hljs-attr">close</span>=<span class="hljs-string">{setIsOpen}</span> /&gt;</span></span>} &lt;/AnimatePresence&gt;
</code></pre>
<p>Then receive the function in the search with its proper prop type:</p>
<pre><code class="lang-javascript"><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">SearchBox</span>(<span class="hljs-params">{
  close,
}: {
  close: React.Dispatch&lt;React.SetStateAction&lt;boolean&gt;&gt;;
}</span>) </span>{
</code></pre>
<p>After that, create a reference (ref) to the item you want to track and mark that element:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { motion } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;
<span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { BiSearch } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/bi"</span>;
<span class="hljs-keyword">import</span> { useClickOutside } <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useClickOutside"</span>;
<span class="hljs-keyword">import</span> OverlayWrapper <span class="hljs-keyword">from</span> <span class="hljs-string">"./overlay"</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">SearchBox</span>(<span class="hljs-params">{
  close,
}: {
  close: React.Dispatch&lt;React.SetStateAction&lt;boolean&gt;&gt;;
}</span>) </span>{
  <span class="hljs-keyword">const</span> searchboxRef = useRef&lt;HTMLDivElement&gt;(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">OverlayWrapper</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
        <span class="hljs-attr">initial</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">y:</span> "<span class="hljs-attr">-10</span>%", <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span> }}
        <span class="hljs-attr">animate</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">y:</span> "<span class="hljs-attr">0</span>%", <span class="hljs-attr">opacity:</span> <span class="hljs-attr">1</span> }}
        <span class="hljs-attr">exit</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">y:</span> "<span class="hljs-attr">-5</span>%", <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span> }}
        <span class="hljs-attr">className</span>=<span class="hljs-string">" p-[15vh] text-[#939AA7] h-full"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-xl mx-auto divide-y divide-[#939AA7] bg-[#1e293b] rounded-md"</span>
          <span class="hljs-attr">ref</span>=<span class="hljs-string">{searchboxRef}</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 flex justify-between px-4 py-2 text-sm "</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 w-full gap-2 text-white"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">BiSearch</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</span> /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full h-full p-2 bg-transparent focus-within:outline-none"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search Documentation"</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 -translate-y-1/2 right-4 top-1/2 "</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">kbd</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-1 text-xs rounded-[4px] bg-[#475569] font-sans font-semibold text-slate-400"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">abbr</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Escape"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"no-underline "</span>&gt;</span>
                  Esc{" "}
                <span class="hljs-tag">&lt;/<span class="hljs-name">abbr</span>&gt;</span>{" "}
              <span class="hljs-tag">&lt;/<span class="hljs-name">kbd</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 justify-center p-10 text-center "</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>
              How many licks does it take to get to the center of a Tootsie pop?
            <span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">motion.div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">OverlayWrapper</span>&gt;</span></span>
  );
}
</code></pre>
<p>Then pass in that ref and the function to be called when a click outside that element is detected.</p>
<pre><code class="lang-javascript">useClickOutside(searchboxRef, <span class="hljs-function">() =&gt;</span> close(<span class="hljs-literal">false</span>));
</code></pre>
<p>Testing it out now gives the following result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733862607091/5c77d8e0-6ba8-4c04-8d7d-c0d0d8f7c408.gif" alt="5c77d8e0-6ba8-4c04-8d7d-c0d0d8f7c408" class="image--center mx-auto" width="800" height="378" loading="lazy"></p>
<p>We can also optimize the code a bit more. Like we did with the accessibility feature, we can make out listener for detecting shortcuts much cleaner and efficient with the following steps.</p>
<p>First, create a <code>useKeyBindings</code> hook file for handling key press combinations.</p>
<p>Then define the hook and the Interface. The hook will accept an array of bindings, where each binding consists of:</p>
<ul>
<li><p>A <code>keys</code> array, which specifies the key combination (for example, ["Control", "k"])</p>
</li>
<li><p>A callback function, which gets called when the corresponding keys are pressed.</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-comment">// Define the structure of a keybinding</span>
interface KeyBinding {
  <span class="hljs-attr">keys</span>: string[]; <span class="hljs-comment">// Array of keys (e.g., ["Control", "k"])</span>
  callback: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">void</span>; <span class="hljs-comment">// Function to execute when the keys are pressed</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useKeyBindings = <span class="hljs-function">(<span class="hljs-params">bindings: KeyBinding[]</span>) =&gt;</span> {

};
</code></pre>
<p>Next, create the <code>handleKeyDown</code> function. Inside the hook, define a function that will listen for keyboard events. This function will check if the pressed keys match any defined key combinations.</p>
<p>We'll normalize the keys to lowercase so that the comparison is case-insensitive and track which keys are pressed by checking for <code>ctrlKey</code>, <code>shiftKey</code>, <code>altKey</code>, <code>metaKey</code>, and the key pressed (for example, "k" for Ctrl + K).</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> handleKeyDown = <span class="hljs-function">(<span class="hljs-params">event: KeyboardEvent</span>) =&gt;</span> {
  <span class="hljs-comment">// Track the keys that are pressed</span>
  <span class="hljs-keyword">const</span> pressedKeys = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>&lt;string&gt;();

  <span class="hljs-comment">// Check for modifier keys (Ctrl, Shift, Alt, Meta)</span>
  <span class="hljs-keyword">if</span> (event.ctrlKey) pressedKeys.add(<span class="hljs-string">"control"</span>);
  <span class="hljs-keyword">if</span> (event.shiftKey) pressedKeys.add(<span class="hljs-string">"shift"</span>);
  <span class="hljs-keyword">if</span> (event.altKey) pressedKeys.add(<span class="hljs-string">"alt"</span>);
  <span class="hljs-keyword">if</span> (event.metaKey) pressedKeys.add(<span class="hljs-string">"meta"</span>);

  <span class="hljs-comment">// Add the key that was pressed (e.g., "k" for Ctrl + K)</span>
  <span class="hljs-keyword">if</span> (event.key) pressedKeys.add(event.key.toLowerCase());
};
</code></pre>
<p>Next, we’ll compare the pressed keys with the keys array from our bindings to check if they match. If they do, we’ll call the associated callback function. We also ensure that the number of pressed keys matches the number of keys defined in the binding.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Loop through each keybinding</span>
bindings.forEach(<span class="hljs-function">(<span class="hljs-params">{ keys, callback }</span>) =&gt;</span> {
  <span class="hljs-comment">// Normalize the keys to lowercase for comparison</span>
  <span class="hljs-keyword">const</span> normalizedKeys = keys.map(<span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> key.toLowerCase());

  <span class="hljs-comment">// Check if the pressed keys match the keybinding</span>
  <span class="hljs-keyword">const</span> isMatch =
    pressedKeys.size === normalizedKeys.length &amp;&amp;
    normalizedKeys.every(<span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> pressedKeys.has(key));

  <span class="hljs-comment">// If the keys match, call the callback</span>
  <span class="hljs-keyword">if</span> (isMatch) {
    event.preventDefault(); <span class="hljs-comment">// Prevent default browser behavior</span>
    callback(); <span class="hljs-comment">// Execute the callback function</span>
  }
});
</code></pre>
<p>Finally, set up event listeners on the window object to listen for keydown events. These listeners will trigger the <code>handleKeyDown</code> function whenever a key is pressed. Make sure to add clean up the event listeners when the component unmounts.</p>
<pre><code class="lang-javascript">useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// Add event listeners for keydown</span>
  <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);

  <span class="hljs-comment">// Cleanup the event listeners when the component unmounts</span>
  <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);
  };
}, [bindings]);
</code></pre>
<p>The full <code>useKeyBindings</code> hook now put together looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

interface KeyBinding {
  <span class="hljs-attr">keys</span>: string[]; <span class="hljs-comment">// A combination of keys to trigger the callback (e.g., ["Control", "k"])</span>
  callback: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">void</span>; <span class="hljs-comment">// The function to execute when the keys are pressed</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useKeyBindings</span>(<span class="hljs-params">bindings: KeyBinding[]</span>) </span>{
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> handleKeyDown = <span class="hljs-function">(<span class="hljs-params">event: KeyboardEvent</span>) =&gt;</span> {
      bindings.forEach(<span class="hljs-function">(<span class="hljs-params">{ keys, callback }</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> normalizedKeys = keys.map(<span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> key.toLowerCase());
        <span class="hljs-keyword">const</span> pressedKeys = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>&lt;string&gt;();

        <span class="hljs-comment">// Track modifier keys explicitly</span>
        <span class="hljs-keyword">if</span> (event.ctrlKey) pressedKeys.add(<span class="hljs-string">"control"</span>);
        <span class="hljs-keyword">if</span> (event.shiftKey) pressedKeys.add(<span class="hljs-string">"shift"</span>);
        <span class="hljs-keyword">if</span> (event.altKey) pressedKeys.add(<span class="hljs-string">"alt"</span>);
        <span class="hljs-keyword">if</span> (event.metaKey) pressedKeys.add(<span class="hljs-string">"meta"</span>);

        <span class="hljs-comment">// Add the actual key pressed</span>
        <span class="hljs-keyword">if</span> (event.key) pressedKeys.add(event.key.toLowerCase());

        <span class="hljs-comment">// Match exactly: pressed keys must match the defined keys</span>
        <span class="hljs-keyword">const</span> isExactMatch =
          pressedKeys.size === normalizedKeys.length &amp;&amp;
          normalizedKeys.every(<span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> pressedKeys.has(key));

        <span class="hljs-keyword">if</span> (isExactMatch) {
          event.preventDefault(); <span class="hljs-comment">// Prevent default behavior</span>
          callback(); <span class="hljs-comment">// Execute the callback</span>
        }
      });
    };

    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);
    };
  }, [bindings]);
}
</code></pre>
<p>Here’s how you can use this hook in your <code>App</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useKeyBindings } <span class="hljs-keyword">from</span> <span class="hljs-string">"./hooks/useKeyBindings"</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">const</span> [isOpen, setIsOpen] = useState&lt;boolean&gt;(<span class="hljs-literal">false</span>);

  useKeyBindings([
    {
      <span class="hljs-attr">keys</span>: [<span class="hljs-string">"Control"</span>, <span class="hljs-string">"k"</span>], <span class="hljs-comment">// Listen for "Ctrl + K"</span>
      <span class="hljs-attr">callback</span>: <span class="hljs-function">() =&gt;</span> setIsOpen(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> !prev), <span class="hljs-comment">// Toggle the search box</span>
    },
    {
      <span class="hljs-attr">keys</span>: [<span class="hljs-string">"Escape"</span>], <span class="hljs-comment">// Listen for "Escape"</span>
      <span class="hljs-attr">callback</span>: <span class="hljs-function">() =&gt;</span> setIsOpen(<span class="hljs-literal">false</span>), <span class="hljs-comment">// Close the search box</span>
    },
  ]);
</code></pre>
<p>Which gives the following result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733863013736/620e7362-33fa-45d3-b2e0-4c8afc873cfd.gif" alt="620e7362-33fa-45d3-b2e0-4c8afc873cfd" class="image--center mx-auto" width="800" height="373" loading="lazy"></p>
<p>With this approach, you can even add multiple shortcuts to trigger the search component’s visibility.</p>
<pre><code class="lang-javascript">useKeyBindings([
    {
      <span class="hljs-attr">keys</span>: [<span class="hljs-string">"Control"</span>, <span class="hljs-string">"k"</span>], <span class="hljs-comment">// Listen for "Ctrl + K"</span>
      <span class="hljs-attr">callback</span>: <span class="hljs-function">() =&gt;</span> setIsOpen(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> !prev), <span class="hljs-comment">// Toggle the search box</span>
    },
    {
      <span class="hljs-attr">keys</span>: [<span class="hljs-string">"Control"</span>, <span class="hljs-string">"d"</span>], <span class="hljs-comment">// Listen for "Ctrl + D"</span>
      <span class="hljs-attr">callback</span>: <span class="hljs-function">() =&gt;</span> setIsOpen(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> !prev), <span class="hljs-comment">// Toggle the search box</span>
    },
    {
      <span class="hljs-attr">keys</span>: [<span class="hljs-string">"Escape"</span>], <span class="hljs-comment">// Listen for "Escape"</span>
      <span class="hljs-attr">callback</span>: <span class="hljs-function">() =&gt;</span> setIsOpen(<span class="hljs-literal">false</span>), <span class="hljs-comment">// Close the search box</span>
    },
  ]);
</code></pre>
<p>Here are links to all the resources you may need for this article:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/Daiveedjay/KSL-Component/tree/starter">Starter files</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/Daiveedjay/KSL-Component/tree/main">Final</a></p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope this article felt like a well-timed shortcut, getting you to the heart of building reusable keyboard shortcut components. With every keypress and animation, you can now turn ordinary web experiences extraordinary ones.</p>
<p>I hope your shortcuts help you create apps that click with your users. After all, the best journeys often start with just the right combination.</p>
<h3 id="heading-like-my-articles">Like my articles?</h3>
<p>Feel free to <a target="_blank" href="https://www.buymeacoffee.com/JajaDavid">buy me a coffee here</a>, to keep my brain chugging and provide more articles like this.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdVWaSayW-ZONciTLakWFfSKvOKoaQR3MTpyGmLR77hl58lDorTCRNfOZfP-dMf-2WcIwfSWZE_psVHr-4qU1CIy28hsLj755zJdEcsLp3blw6l1Wtu4EUxTZ8mSF--dCk6mEQRWg?key=ypBQIzv1TD8iWEKblpAC4CZM" alt="coffee-tom" width="620" height="461" loading="lazy"></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><p>Twitter / X: <a target="_blank" href="https://twitter.com/JajaDavid8">@jajadavid8</a></p>
</li>
<li><p>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/david-jaja-8084251b4/">David Jaja</a></p>
</li>
<li><p>Email: <a target="_blank" href="http://Jajadavidjid@gmail.com">Jajadavidjid@gmail.com</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Write Better Names for Your Variables, Functions, and Classes – With Examples ]]>
                </title>
                <description>
                    <![CDATA[ Naming is one of the most important and challenging parts of writing clean, maintainable, and scalable code. A well-thought-out variable name, for example, can act as self-documenting code, saving time and effort in understanding the logic. But poorl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-write-better-variable-names/</link>
                <guid isPermaLink="false">6750922cfe6f3f72caf2624a</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ variables ]]>
                    </category>
                
                    <category>
                        <![CDATA[ naming ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Asfak Ahmed ]]>
                </dc:creator>
                <pubDate>Wed, 04 Dec 2024 17:32:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733325693047/f3dff206-f0cf-47b0-9345-991b4d980d71.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Naming is one of the most important and challenging parts of writing clean, maintainable, and scalable code. A well-thought-out variable name, for example, can act as self-documenting code, saving time and effort in understanding the logic. But poorly chosen names, on the other hand, can lead to confusion and bugs.</p>
<p>This article will serve as a comprehensive guide on how to come up with meaningful names for class names, variables, and functions with examples and best practices.</p>
<h2 id="heading-why-does-naming-matter"><strong>Why Does Naming Matter?</strong></h2>
<ul>
<li><p><strong>Readability:</strong> Good names make your code intuitive and reduce the learning curve for others.</p>
</li>
<li><p><strong>Maintainability:</strong> It is easier to refactor or debug well-named code.</p>
</li>
<li><p><strong>Collaboration:</strong> Clear names improve team communication and productivity.</p>
</li>
<li><p><strong>Scalability:</strong> Meaningful names help keep large projects manageable.</p>
</li>
</ul>
<h2 id="heading-different-naming-convention-styles">Different Naming Convention Styles</h2>
<p>Different naming convention styles are crucial in improving code readability and maintainability across various programming languages.</p>
<p>Styles like camelCase, PascalCase, snake_case, and kebab-case are tailored to specific contexts and practices.</p>
<p><strong>camelCase</strong> is widely used for variables and functions, while <strong>PascalCase</strong> is preferred for classes. <strong>snake_case</strong> is a favorite in Python for its clarity, and <strong>kebab-case</strong> dominates CSS for HTML element styling.</p>
<p>Each style ensures consistency, making code intuitive for teams and future developers. Here’s a quick summary table of some popular naming conventions along with their use cases and examples:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Style</strong></td><td><strong>Example</strong></td><td><strong>Common Usage</strong></td></tr>
</thead>
<tbody>
<tr>
<td>camelCase</td><td><code>userName</code></td><td>Variables, functions, object properties</td></tr>
<tr>
<td>PascalCase</td><td><code>UserName</code></td><td>Classes, components, constructors</td></tr>
<tr>
<td>kebab-case</td><td><code>primary-button</code></td><td>CSS classes, HTML IDs, file names</td></tr>
<tr>
<td>snake_case</td><td><code>user_name</code></td><td>Variables, function names in Python</td></tr>
<tr>
<td>SCREAMING_SNAKE_CASE</td><td><code>MAX_CONNECTIONS</code></td><td>Constants</td></tr>
<tr>
<td>dot.case</td><td><code>config.file.path</code></td><td>Configurations, keys</td></tr>
<tr>
<td>Train-Case</td><td><code>Primary-Button</code></td><td>Titles rarely used</td></tr>
<tr>
<td>Hungarian Notation</td><td><code>bIsActive</code></td><td>Legacy code</td></tr>
<tr>
<td>UPPERCASE with Spaces</td><td><code>USER ACCOUNT DETAILS</code></td><td>Rare, mostly for old-style documentation</td></tr>
<tr>
<td>Flatcase</td><td><code>username</code></td><td>Minimalist, filenames, identifiers</td></tr>
</tbody>
</table>
</div><h3 id="heading-how-to-choose-the-right-style"><strong>How to Choose the Right Style</strong></h3>
<ol>
<li><p><strong>Language-Specific:</strong> Follow the conventions of your programming language or framework. For example:</p>
<ul>
<li><p>JavaScript: <code>camelCase</code> for variables and functions, <code>PascalCase</code> for components.</p>
</li>
<li><p>Python: <code>snake_case</code> for variables and functions.</p>
</li>
<li><p>CSS/HTML: <code>kebab-case</code> for class names and IDs.</p>
</li>
</ul>
</li>
<li><p><strong>Team or Project Standards:</strong> Consistency is key. Use the agreed style for your team/project.</p>
</li>
<li><p><strong>Purpose-Specific:</strong> Use naming styles that best represent the entity being named (for example, constants in <code>SCREAMING_SNAKE_CASE</code>).</p>
</li>
</ol>
<h2 id="heading-general-naming-guidelines"><strong>General Naming Guidelines</strong></h2>
<p>Before diving into specific naming conventions for class names, variables, and functions, let’s explore some universal principles:</p>
<ol>
<li><p><strong>Be descriptive and concise:</strong> Names should convey the purpose or role of the variable/function/etc:</p>
<pre><code class="lang-javascript"> <span class="hljs-comment">// Bad</span>
 <span class="hljs-keyword">let</span> x = <span class="hljs-number">10</span>;

 <span class="hljs-comment">// Good</span>
 <span class="hljs-keyword">let</span> maxUsersAllowed = <span class="hljs-number">10</span>;
</code></pre>
</li>
<li><p><strong>Avoid cryptic abbreviations</strong> that might be hard for other devs to understand (or even your future self):</p>
<pre><code class="lang-javascript"> <span class="hljs-comment">// Bad</span>
 <span class="hljs-keyword">let</span> usrNm = <span class="hljs-string">"John"</span>;

 <span class="hljs-comment">// Good</span>
 <span class="hljs-keyword">let</span> userName = <span class="hljs-string">"John"</span>;
</code></pre>
</li>
<li><p><strong>Use consistent naming conventions:</strong> Choose a naming style (camelCase, PascalCase, kebab-case, snake_case) and stick with it throughout your project.</p>
</li>
<li><p><strong>Avoid reserved keywords or confusing names:</strong></p>
<pre><code class="lang-javascript"> <span class="hljs-comment">// Bad</span>
 <span class="hljs-keyword">let</span> <span class="hljs-keyword">let</span> = <span class="hljs-number">5</span>;

 <span class="hljs-comment">// Good</span>
 <span class="hljs-keyword">let</span> variableName = <span class="hljs-number">5</span>;
</code></pre>
</li>
</ol>
<p>Alright, now that we’ve covered the basis, lets dig deeper into some helpful naming conventions.</p>
<h2 id="heading-how-to-create-good-class-names"><strong>How to Create Good Class Names</strong></h2>
<p>Class names define the visual or structural behavior of elements in your application. Writing clear class names ensures your HTML and CSS are easy to understand and maintain.</p>
<h3 id="heading-1-use-descriptive-names"><strong>1. Use Descriptive Names</strong></h3>
<p>Class names should describe the purpose of the element, not its appearance.</p>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- Bad --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"red-button"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-comment">&lt;!-- Good --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"primary-button"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-2-follow-the-bem-block-element-modifier-methodology"><strong>2. Follow the BEM (Block-Element-Modifier) Methodology</strong></h3>
<p>BEM is a popular convention for writing scalable and maintainable CSS. It separates components into:</p>
<ul>
<li><p><strong>Block:</strong> Represents the component (for example, <code>card</code>).</p>
</li>
<li><p><strong>Element:</strong> Represents child elements of the block (for example, <code>card__title</code>).</p>
</li>
<li><p><strong>Modifier:</strong> Represents variations of the block or element (for example, <code>card__title--highlighted</code>).</p>
</li>
</ul>
<p><strong>Example:</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__title card__title--highlighted"</span>&gt;</span>Welcome<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__description"</span>&gt;</span>This is a card component.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-3-use-kebab-case"><strong>3. Use kebab-case</strong></h3>
<p>CSS class names are traditionally written in kebab-case for better readability.</p>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- Bad --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"primaryButton"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-comment">&lt;!-- Good --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"primary-button"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h2 id="heading-how-to-create-good-variable-names"><strong>How to Create Good Variable Names</strong></h2>
<p>Variables hold data and should have meaningful names that describe what they represent.</p>
<h3 id="heading-1-use-nouns-for-variables"><strong>1. Use Nouns for Variables</strong></h3>
<p>Variables are typically nouns because they represent entities or data.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Bad</span>
<span class="hljs-keyword">let</span> a = <span class="hljs-string">"John"</span>;

<span class="hljs-comment">// Good</span>
<span class="hljs-keyword">let</span> userName = <span class="hljs-string">"John"</span>;
</code></pre>
<h3 id="heading-2-use-prefixes-to-add-context"><strong>2. Use Prefixes to Add Context</strong></h3>
<p>Adding prefixes helps clarify the type or purpose of a variable:</p>
<ul>
<li><p><strong>Boolean:</strong> <code>is</code>, <code>has</code>, <code>can</code></p>
</li>
<li><p><strong>Numbers:</strong> <code>max</code>, <code>min</code>, <code>total</code></p>
</li>
<li><p><strong>Arrays:</strong> Use plural forms (for example, <code>users</code>, <code>items</code>).</p>
</li>
</ul>
<p><strong>Example:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isUserLoggedIn = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">const</span> maxUploadLimit = <span class="hljs-number">5</span>; <span class="hljs-comment">// MB</span>
<span class="hljs-keyword">const</span> usersList = [<span class="hljs-string">"John"</span>, <span class="hljs-string">"Jane"</span>];
</code></pre>
<h3 id="heading-3-avoid-generic-names"><strong>3. Avoid Generic Names</strong></h3>
<p>Avoid names like <code>data</code>, <code>value</code>, or <code>item</code> unless they’re necessary.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Bad</span>
<span class="hljs-keyword">let</span> data = <span class="hljs-number">42</span>;

<span class="hljs-comment">// Good</span>
<span class="hljs-keyword">let</span> userAge = <span class="hljs-number">42</span>;
</code></pre>
<h2 id="heading-how-to-create-good-function-names"><strong>How to Create Good Function Names</strong></h2>
<p>Functions perform actions, so their names should reflect the operation or process they execute.</p>
<h3 id="heading-1-use-verbs-for-functions"><strong>1. Use Verbs for Functions</strong></h3>
<p>Functions are action-oriented, so their names should begin with a verb:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Bad</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">userData</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// Good</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchUserData</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<h3 id="heading-2-be-specific-about-functionality"><strong>2. Be Specific About Functionality</strong></h3>
<p>Function names should indicate what they do.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Bad</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handle</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// Good</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleFormSubmit</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<h3 id="heading-3-use-prefixes-for-intent"><strong>3. Use Prefixes for Intent</strong></h3>
<ul>
<li><p>For event handlers: <code>handle</code>, <code>on</code></p>
</li>
<li><p>For utilities: <code>calculate</code>, <code>convert</code>, <code>format</code></p>
</li>
<li><p>For fetch operations: <code>fetch</code>, <code>get</code>, <code>load</code></p>
</li>
<li><p>For setters and getters: <code>set</code>, <code>get</code></p>
</li>
</ul>
<p><strong>Example:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleButtonClick</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Button clicked!"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateDiscount</span>(<span class="hljs-params">price, discountPercentage</span>) </span>{
  <span class="hljs-keyword">return</span> price * (discountPercentage / <span class="hljs-number">100</span>);
}
</code></pre>
<h2 id="heading-how-to-know-if-a-name-is-good-for-a-variable-function-or-class">How to Know if a Na<strong>me is Good for a Variable, Function, or Class</strong></h2>
<p>To understand if a name is good for a variable, function, or class, evaluating it using several key principles is important. Here’s a guide to help you decide whether a name is appropriate and meaningful in your programming context:</p>
<h3 id="heading-1-does-it-represent-the-purpose">1. <strong>Does It Represent the Purpose?</strong></h3>
<p><strong>Purpose-driven names</strong> are the most important characteristic of good naming. A name should immediately tell you what the variable, function, or class represents or does without needing to read additional comments or documentation.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask yourself: "When I read this name, can I immediately understand its purpose?"</p>
<p><strong>Example:</strong></p>
<ul>
<li><code>userAge</code> is better than <code>a</code> because <code>userAge</code> tells you what the variable represents, whereas <code>a</code> is too ambiguous.</li>
</ul>
<h3 id="heading-2-is-it-specific-enough">2. <strong>Is It Specific Enough?</strong></h3>
<p>The name should be <strong>specific enough</strong> to reflect the exact role of the entity in your code. Overly generic names like <code>data</code> or <code>temp</code> can be confusing because they don’t provide enough context.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Is this name specific to what this variable, function, or class represents in my application?"</p>
<p><strong>Example:</strong></p>
<ul>
<li><code>calculateTaxAmount()</code> is better than <code>calculate()</code> because it’s clear what the function is calculating.</li>
</ul>
<h3 id="heading-3-does-it-follow-a-consistent-naming-convention">3. <strong>Does It Follow a Consistent Naming Convention?</strong></h3>
<p><strong>Consistency</strong> in naming conventions is vital. When all team members follow the same conventions, the code is easier to understand and navigate.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Is this name consistent with the naming conventions used in the rest of the project?" Follow project guidelines such as:</p>
<ul>
<li><p><code>camelCase</code> for variables and functions (e.g., <code>userAge</code>)</p>
</li>
<li><p><code>PascalCase</code> for classes (e.g., <code>UserProfile</code>)</p>
</li>
<li><p><code>UPPERCASE_SNAKE_CASE</code> for constants (e.g., <code>MAX_USERS</code>)</p>
</li>
</ul>
<p><strong>Example:</strong></p>
<ul>
<li>If your team follows <code>camelCase</code>, <code>userData</code> is better than <code>UserData</code>.</li>
</ul>
<h3 id="heading-4-does-it-avoid-ambiguity">4. <strong>Does it Avoid Ambiguity?</strong></h3>
<p>A good name <strong>eliminates ambiguity</strong>. It should not be open to multiple interpretations. If it can mean different things in different contexts, it will lead to confusion.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Could someone unfamiliar with the codebase misinterpret what this name refers to?"</p>
<p><strong>Example:</strong></p>
<ul>
<li>Instead of naming a boolean <code>isValid</code>, use <code>isUserLoggedIn</code> or <code>isEmailVerified</code> to make it clearer what is being checked.</li>
</ul>
<h3 id="heading-5-is-it-easy-to-read-and-pronounce">5. <strong>Is It Easy to Read and Pronounce?</strong></h3>
<p>While not strictly necessary, <strong>ease of reading and pronunciation</strong> can improve the overall readability and maintainability of your code.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Is this name easy to read aloud, and can I understand it at a glance?"</p>
<p>Avoid long names, and use common abbreviations only when they are widely accepted.</p>
<p><strong>Example:</strong></p>
<ul>
<li><code>maxRetries</code> is better than <code>maximumNumberOfAttemptsToReconnect</code>.</li>
</ul>
<h3 id="heading-6-does-it-avoid-redundancy">6. <strong>Does It Avoid Redundancy?</strong></h3>
<p><strong>Avoid redundancy</strong> in names. Don’t repeat information that is already implied or described by the context.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Am I repeating information that is already clear from the surrounding context?"</p>
<p><strong>Example:</strong></p>
<ul>
<li>If you have a class named <code>User</code>, naming a method <code>userGetData()</code> is redundant. Instead, use <code>getData()</code>.</li>
</ul>
<h3 id="heading-7-is-it-self-documenting">7. <strong>Is It Self-Documenting?</strong></h3>
<p>The best names <strong>document themselves</strong>. Good names reduce the need for additional comments or explanations.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Does this name fully describe the variable, function, or class without requiring a comment to explain what it does?"</p>
<p><strong>Example:</strong></p>
<ul>
<li><code>calculateTotalPrice</code> is self-explanatory, so there’s no need for an additional comment like “This function calculates the total price after discount.”</li>
</ul>
<h3 id="heading-8-is-it-contextual-and-relevant-to-the-domain">8. <strong>Is It Contextual and Relevant to the Domain?</strong></h3>
<p>The name should fit within the context of your project and its domain. For example, naming conventions for a web application may differ from those for a mobile app or a machine learning model.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Is this name aligned with the domain and context of my project?"</p>
<p>If you’re working in a specific domain (for example, finance, health, gaming), use domain-specific terms that are easily recognizable.</p>
<p><strong>Example:</strong></p>
<ul>
<li>In a gaming app, <code>healthPoints</code> is more appropriate than <code>hp</code>, as it reflects its meaning.</li>
</ul>
<h3 id="heading-9-is-it-future-proof">9. <strong>Is It Future-Proof?</strong></h3>
<p>Think about how your code will evolve. Names should be flexible enough to accommodate future changes without requiring refactoring.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Will this name still make sense if the functionality changes or the project grows?"</p>
<p><strong>Example:</strong></p>
<ul>
<li><code>userInfo</code> could become outdated if the data structure changes. It’s better to use <code>userProfile</code> if you expect more fields to be added.</li>
</ul>
<h3 id="heading-10-does-it-avoid-magic-numbers-and-hard-coded-values">10. <strong>Does It Avoid Magic Numbers and Hard-Coded Values?</strong></h3>
<p><strong>Magic numbers</strong> (numbers with unclear meaning) should be avoided in favor of named constants.</p>
<p><strong>How to Assess:</strong></p>
<p>Ask: "Does this name represent a meaningful constant, or is it just a raw number?"</p>
<p><strong>Example:</strong></p>
<ul>
<li>Instead of using <code>1000</code>, use a constant like <code>MAX_FILE_SIZE</code> to explain the meaning behind the number.</li>
</ul>
<h2 id="heading-practical-examples"><strong>Practical Examples</strong></h2>
<h3 id="heading-css-example"><strong>CSS Example</strong></h3>
<p>The following CSS example demonstrates how to apply <strong>BEM (Block-Element-Modifier)</strong> naming conventions to maintain a structured and scalable class hierarchy in your stylesheet:</p>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- HTML --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar__list"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar__item navbar__item--active"</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">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar__item"</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">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar__item"</span>&gt;</span>Contact<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>
</code></pre>
<pre><code class="lang-css"><span class="hljs-comment">/* CSS */</span>
<span class="hljs-selector-class">.navbar</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#333</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
}

<span class="hljs-selector-class">.navbar__list</span> {
  <span class="hljs-attribute">list-style</span>: none;
}

<span class="hljs-selector-class">.navbar__item</span> {
  <span class="hljs-attribute">display</span>: inline-block;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
}

<span class="hljs-selector-class">.navbar__item--active</span> {
  <span class="hljs-attribute">color</span>: orange;
}
</code></pre>
<p>Here’s what’s going on in this code:</p>
<ul>
<li><p><strong>BEM Naming</strong>: <code>navbar</code> is the <strong>Block</strong>, representing the main navigation component.</p>
</li>
<li><p><code>navbar__list</code> is the <strong>Element</strong>, a child of the block, representing the list of navigation items.</p>
</li>
<li><p><code>navbar__item</code> is another <strong>Element</strong> representing individual list items.</p>
</li>
<li><p><code>navbar__item--active</code> is a <strong>Modifier</strong>, used to highlight the active menu item.<br>  This approach makes it easy to understand relationships and roles within the HTML and CSS, supporting modular and reusable styles.</p>
</li>
</ul>
<h3 id="heading-javascript-example"><strong>JavaScript Example</strong></h3>
<p>This JavaScript example shows how to use meaningful and consistent naming conventions for variables and functions to make the code self-explanatory:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Variables</span>
<span class="hljs-keyword">let</span> isUserLoggedIn = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">const</span> maxAllowedItems = <span class="hljs-number">10</span>;

<span class="hljs-comment">// Functions</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchUserDetails</span>(<span class="hljs-params">userId</span>) </span>{
  <span class="hljs-comment">// Fetch user data from the API</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateTotalPrice</span>(<span class="hljs-params">cartItems</span>) </span>{
  <span class="hljs-keyword">return</span> cartItems.reduce(<span class="hljs-function">(<span class="hljs-params">total, item</span>) =&gt;</span> total + item.price, <span class="hljs-number">0</span>);
}
</code></pre>
<p>Here’s what’s going on in the code:</p>
<ul>
<li><p><strong>Variables</strong>:</p>
<ul>
<li><p><code>isUserLoggedIn</code>: A boolean variable named to clearly indicate its purpose. Prefixing with <code>is</code> helps identify it as a boolean.</p>
</li>
<li><p><code>maxAllowedItems</code>: A constant with an uppercase <code>max</code> prefix shows it's a limit, making its intent clear.</p>
</li>
</ul>
</li>
<li><p><strong>Functions</strong>:</p>
<ul>
<li><p><code>fetchUserDetails(userId)</code>: The name reflects the purpose of the function, that is retrieving user details. The parameter <code>userId</code> is descriptive and avoids ambiguity.</p>
</li>
<li><p><code>calculateTotalPrice(cartItems)</code>: The function name explicitly states the action performed. The <code>cartItems</code> parameter is contextually relevant to the e-commerce domain.</p>
</li>
</ul>
</li>
</ul>
<p><strong>Why It’s Good</strong>: These conventions ensure the code is readable and intuitive, reducing the cognitive load for other developers working on the same project.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Meaningful naming is both an important convention and an art form that significantly impacts your code's readability and maintainability.</p>
<p>Try to follow these basic principles:</p>
<ul>
<li><p>Use descriptive, concise names.</p>
</li>
<li><p>Adhere to consistent conventions like BEM for class names and camelCase for variables and functions.</p>
</li>
<li><p>Use prefixes to add context and clarity.</p>
</li>
</ul>
<p>These and the other tips we’ve discussed here will make your code a joy to work with, whether you revisit it months later or collaborate with a team. Start applying these tips today, and watch your code quality soar.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Zig-Zag CSS Loaders Using One Element ]]>
                </title>
                <description>
                    <![CDATA[ In a previous article, I showed you how to create filling CSS loaders collection where each loader was built using a single HTML element. Here, you’ll learn more about loaders by creating the Zig-Zag collection. Here is an overview of what you’ll be ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/zig-zag-css-loaders/</link>
                <guid isPermaLink="false">673e738139346c3d3174676e</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ animations ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Temani Afif ]]>
                </dc:creator>
                <pubDate>Wed, 20 Nov 2024 23:40:49 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732045303831/af9240a9-6a25-4b13-a397-102ee098db78.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In <a target="_blank" href="https://www.freecodecamp.org/news/filling-css-loaders">a previous article</a>, I showed you how to create filling CSS loaders collection where each loader was built using a single HTML element. Here, you’ll learn more about loaders by creating <a target="_blank" href="https://css-loaders.com/zig-zag/">the Zig-Zag collection</a>.</p>
<p>Here is an overview of what you’ll be building:</p>
<div class="embed-wrapper"><iframe height="500" style="width:100%;height:500px" src="https://codepen.io/t_afif/embed/preview/RwXdvKj/83804c95907793e888c3036d7dd29251?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/RwXdvKj/83804c95907793e888c3036d7dd29251">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>You can also check <a target="_blank" href="https://css-loaders.com/zig-zag/">my online collection</a> to see up to 20 variations using a zig-zag shape.</p>
<p>We won’t study all the variations but I will show you a few tricks that’ll help you create as many variations as you want.</p>
<h2 id="heading-how-to-create-a-zig-zag-shape">How to Create a Zig-Zag Shape</h2>
<p>The first step is to create a zig-zag shape. For this, you can grab the code from my CSS shape website: <a target="_blank" href="https://css-shape.com/zig-zag-line/">https://css-shape.com/zig-zag-line/</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731707755150/f1782db9-fa7f-472e-b771-cfc1c2046e0c.png" alt="Zig-Zag shape from css-shape.com" class="image--center mx-auto" width="1150" height="456" loading="lazy"></p>
<p>You can adjust the different variables to get the zig-zag you want. In our case, I will use an easier version with no variables.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">47px</span>; <span class="hljs-comment">/* control the size */</span>
  <span class="hljs-attribute">aspect-ratio</span>: <span class="hljs-number">5</span>;
  <span class="hljs-attribute">background</span>:
   <span class="hljs-built_in">conic-gradient</span>(from <span class="hljs-number">135deg</span> at top,#<span class="hljs-number">000</span> <span class="hljs-number">90deg</span>,#<span class="hljs-number">0000</span> <span class="hljs-number">0</span>) top,
   <span class="hljs-built_in">conic-gradient</span>(from <span class="hljs-number">135deg</span> at top,#<span class="hljs-number">0000</span> <span class="hljs-number">90deg</span>,#<span class="hljs-number">000</span> <span class="hljs-number">0</span>) bottom;
  <span class="hljs-attribute">background-size</span>: <span class="hljs-number">20%</span> <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background-repeat</span>: repeat-x;
}
</code></pre>
<p>And here is a figure to illustrate how those gradients create the shape:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731708477342/bbe3e0b6-24a2-498d-992b-4ee152b0d74c.png" alt="Color gradients created by the code" class="image--center mx-auto" width="838" height="292" loading="lazy"></p>
<p>The first gradient created the red part while the second one created the green part. We have two triangle shapes that repeat horizontally.</p>
<p>Since we have five repetitions, I used <code>aspect-ratio: 5</code> and <code>20% (100%/5)</code> in the <code>background-size</code>. You can make it more generic by introducing a variable to control the number of repetitions but as I said previously, I am going to keep things simple.</p>
<p>I want to point out that when using gradients, you can achieve the same result by using different syntaxes. For example, I can update the previous code with the following:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">47px</span>; <span class="hljs-comment">/* control the size */</span>
  <span class="hljs-attribute">aspect-ratio</span>: <span class="hljs-number">5</span>;
  <span class="hljs-attribute">background</span>:
   <span class="hljs-built_in">conic-gradient</span>(from <span class="hljs-number">135deg</span> at top   ,#<span class="hljs-number">000</span> <span class="hljs-number">90deg</span>,#<span class="hljs-number">0000</span> <span class="hljs-number">0</span>),
   <span class="hljs-built_in">conic-gradient</span>(from -<span class="hljs-number">45deg</span> at bottom,#<span class="hljs-number">000</span> <span class="hljs-number">90deg</span>,#<span class="hljs-number">0000</span> <span class="hljs-number">0</span>) <span class="hljs-number">12.5%</span> <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background-size</span>: <span class="hljs-number">20%</span> <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background-repeat</span>: repeat-x;
}
</code></pre>
<p>It’s still the same output but with a different syntax for the second gradient. Did you notice the repeated part within the gradients? That part controls the coloration and we can define it as a variable to avoid repetition and be able to update the color only once in the code.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">47px</span>; <span class="hljs-comment">/* control the size */</span>
  <span class="hljs-attribute">aspect-ratio</span>: <span class="hljs-number">5</span>;
  <span class="hljs-attribute">--c</span>:<span class="hljs-number">#000</span> <span class="hljs-comment">/* the color */</span> <span class="hljs-number">90deg</span>,<span class="hljs-number">#0000</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background</span>:
   <span class="hljs-built_in">conic-gradient</span>(from <span class="hljs-number">135deg</span> at top   ,var(--c)),
   <span class="hljs-built_in">conic-gradient</span>(from -<span class="hljs-number">45deg</span> at bottom,var(--c)) <span class="hljs-number">12.5%</span> <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background-size</span>: <span class="hljs-number">20%</span> <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background-repeat</span>: repeat-x;
}
</code></pre>
<p>Now we have our zig-zag shape and we are ready to animate it.</p>
<h2 id="heading-how-to-animate-the-zig-zag-shape">How to Animate the Zig-Zag Shape</h2>
<p>Since we’re using a background, we’ll animate the <code>background-position</code> to get our first loader. The idea is to move the gradients horizontally and create an infinite movement.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">47px</span>; <span class="hljs-comment">/* control the size */</span>
  <span class="hljs-attribute">aspect-ratio</span>: <span class="hljs-number">5</span>;
  <span class="hljs-attribute">--c</span>:<span class="hljs-number">#000</span> <span class="hljs-comment">/* the color */</span> <span class="hljs-number">90deg</span>,<span class="hljs-number">#0000</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background</span>:
   <span class="hljs-built_in">conic-gradient</span>(from <span class="hljs-number">135deg</span> at top   ,var(--c)),
   <span class="hljs-built_in">conic-gradient</span>(from -<span class="hljs-number">45deg</span> at bottom,var(--c)) <span class="hljs-number">12.5%</span> <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background-size</span>: <span class="hljs-number">20%</span> <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background-repeat</span>: repeat-x;
  <span class="hljs-attribute">animation</span>: loading .<span class="hljs-number">8s</span> infinite linear;
}

<span class="hljs-keyword">@keyframes</span> loading {
  0%   {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0</span>   <span class="hljs-number">0</span>,<span class="hljs-number">12.5%</span> <span class="hljs-number">100%</span>}
  100% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">25%</span> <span class="hljs-number">0</span>,<span class="hljs-number">37.5%</span> <span class="hljs-number">100%</span>}
}
</code></pre>
<p>Note how we increased the X value of the <code>background-position</code> by <code>25%</code>. In case you are wondering what the logic behind that value is, here is the formula:</p>
<p><code>0.2 / (1 - 0.2) = .25 = 25%</code></p>
<p><code>.2</code> corresponds to the <code>20%</code> used inside the <code>background-size</code>.</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/poMBgQO/5ddc67ad2324e68680f9d1071e46dc96?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/poMBgQO/5ddc67ad2324e68680f9d1071e46dc96">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>We have our first loader! Actually, two loaders because we can easily change the direction of the movement by adding <code>animation-direction: reverse</code>.</p>
<p>Let’s try a different animation: using <code>clip-path</code> and the <code>inset()</code> value. We can easily adjust this technique to create many variations.</p>
<p>Let’s start with a basic example:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-comment">/* same code as previously */</span>
  <span class="hljs-attribute">animation</span>: loading .<span class="hljs-number">8s</span> infinite linear;
}
<span class="hljs-keyword">@keyframes</span> loading {
  0%   {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">100%</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>)}
  100% {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span>    <span class="hljs-number">0</span> <span class="hljs-number">0</span>)}
}
</code></pre>
<p>The <code>inset()</code> value creates a rectangle where only the part inside it will be visible. For this, we define a distance from each side of the element (top, right, bottom, left).</p>
<p>Logically, <code>inset(0 0 0 0)</code> shows the whole element since all the distances are equal to 0, but <code>inset(0 100% 0 0)</code> completely hides the element since the right value is equal to 100%. So it will touch the opposite edge, creating an empty rectangle.</p>
<p>By animating that right value from <code>100%</code> to <code>0</code> we create a reveal animation. Another loader variation!</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/WNVWrVy/f6214a24e77a0ad6694d3a5bf93d2a23?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/WNVWrVy/f6214a24e77a0ad6694d3a5bf93d2a23">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>If you inspect the code of the second animation, you will see that I did the same thing but with the left side.</p>
<p>We can also have a sliding effect if we animate both the left and right values while keeping their difference constant.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-comment">/* same code as previously */</span>
  <span class="hljs-attribute">animation</span>: loading .<span class="hljs-number">8s</span> infinite linear;
}
<span class="hljs-keyword">@keyframes</span> loading {
  0%   {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">60%</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>  )}
  100% {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span>   <span class="hljs-number">0</span> <span class="hljs-number">60%</span>)}
}
</code></pre>
<p>The right value animates from <code>60%</code> to <code>0</code> and the left one from <code>0</code> to <code>60%</code>, so we have a constant difference equal to <code>60%</code> which will create the illusion of a sliding rectangle. Another cool loader!</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/wvVZGwy/71a0f6a7bf177c51252230d7e272fb57?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/wvVZGwy/71a0f6a7bf177c51252230d7e272fb57">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>By trying different combinations of <code>inset()</code> values, you can get a lot of CSS loaders. Give it a try! You can also check <a target="_blank" href="https://css-loaders.com/zig-zag/">my online collection</a> and try to identify the variations that use <code>clip-path: inset()</code>.</p>
<h2 id="heading-how-to-create-a-discrete-animation">How to Create a Discrete Animation</h2>
<p>To achieve a discrete animation, you can use the <code>steps()</code> timing function instead of <code>linear</code>. Let’s start with the first example using <code>steps(2)</code>.</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/YzmbzGL/28874aa2a6066deb4d06fdbefaaade62?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/YzmbzGL/28874aa2a6066deb4d06fdbefaaade62">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>We can do the same with almost all the variations. Let’s try with the ones that use <code>clip-path: inset()</code>.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-comment">/* same code as previously */</span>
  <span class="hljs-attribute">animation</span>: loading .<span class="hljs-number">8s</span> infinite <span class="hljs-built_in">steps</span>(<span class="hljs-number">5</span>);
}
<span class="hljs-keyword">@keyframes</span> loading {
  0%   {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">100%</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>)}
  100% {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span>    <span class="hljs-number">0</span> <span class="hljs-number">0</span>)}
}
</code></pre>
<p>We have five repetitions so let’s see what we’ll get with <code>steps(5)</code>.</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/JjgqjNr/9a6b43cda41ed9ec9cb49ea9bdaabb56?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/JjgqjNr/9a6b43cda41ed9ec9cb49ea9bdaabb56">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>At the moment, it’s not good because we don’t see all the repetition. The animation stops at 4 repetitions, but we need to see the whole element (5 repetitions). The count starts from 0 so what we really need is 6 steps instead of 5 to show all the repetitions.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-comment">/* same code as previously */</span>
  <span class="hljs-attribute">animation</span>: loading .<span class="hljs-number">8s</span> infinite <span class="hljs-built_in">steps</span>(<span class="hljs-number">6</span>);
}
<span class="hljs-keyword">@keyframes</span> loading {
  0%   {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">100%</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>)}
  100% {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span>    <span class="hljs-number">0</span> <span class="hljs-number">0</span>)}
}
</code></pre>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/RwXmKje/df2744eb1e707246a628b22ce96c7e4c?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/RwXmKje/df2744eb1e707246a628b22ce96c7e4c">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>Even with 6 steps, the result is still not good but don’t worry, it’s not a bug. The default behavior of <code>steps()</code> gives us that output but we can update it to get the expected output:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-comment">/* same code as previously */</span>
  <span class="hljs-attribute">animation</span>: loading .<span class="hljs-number">8s</span> infinite <span class="hljs-built_in">steps</span>(<span class="hljs-number">6</span>,jump-none);
}
<span class="hljs-keyword">@keyframes</span> loading {
  0%   {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">100%</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>)}
  100% {<span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">inset</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span>    <span class="hljs-number">0</span> <span class="hljs-number">0</span>)}
}
</code></pre>
<p>If you’re not familiar with <code>jump-none</code>, it’s a value that can fix most of your issues when working with <code>steps()</code>. I wrote a short article about it if you want more details: “<a target="_blank" href="https://css-tip.com/steps/">How to correctly use steps() with animations</a>“</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/JjgqEpO/5433bef4c1b86de39837108b68ca8eba?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/JjgqEpO/5433bef4c1b86de39837108b68ca8eba">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>Our animation looks perfect now! We can also make it an 11-step animation (<code>5×2 + 1</code>) and get another cool loader.</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/vYowgRV/47df83689104665da6997c41a5825efb?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/vYowgRV/47df83689104665da6997c41a5825efb">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>Even the sliding effect can have its discrete variation.</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/t_afif/embed/preview/bGXyZpO/799d03f2d573655e6522476418c6006a?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/bGXyZpO/799d03f2d573655e6522476418c6006a">
  Untitled</a> by Temani Afif (<a href="https://codepen.io/t_afif">@t_afif</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>Can you figure out why I am using 4 and 7 steps? I’ll let you do the calculation as a small exercise.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article showed you how to create zig-zag shapes, how to animate them using <code>clip-path</code>, and how to make a discrete animations. You can also consider more tricks like using both pseudo-elements to have two shapes.</p>
<p>I didn’t explore all the variations but you now have the recipe to create most of them!</p>
<p>You can explore <a target="_blank" href="https://css-loaders.com/zig-zag/">my Zig-Zag loaders collection</a> to study other variations and try to create your own loader. It’s a good opportunity to practice what you have learned from this article.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Host a Website on AWS EC2 Using a CSS Template ]]>
                </title>
                <description>
                    <![CDATA[ Are you ready to take your web hosting skills to the next level by using a CSS template? Hosting a professional looking website doesn’t have to be complicated, and with AWS EC2, you can have your website live in no time! In this guide, I’ll show you ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/host-a-website-on-aws-ec2-using-a-css-template/</link>
                <guid isPermaLink="false">672e5a06d7a5828f344ec242</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ec2 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kedar Makode ]]>
                </dc:creator>
                <pubDate>Fri, 08 Nov 2024 18:35:50 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731103973241/e1277a4c-3456-4f11-b809-24caf56ae13a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you ready to take your web hosting skills to the next level by using a CSS template? Hosting a professional looking website doesn’t have to be complicated, and with AWS EC2, you can have your website live in no time!</p>
<p>In this guide, I’ll show you how to host a website using a pre-designed template from <a target="_blank" href="https://www.free-css.com/free-css-templates"><strong>CSS templates</strong></a> directly on your EC2 instance.</p>
<p>Before we dive into this guide, make sure you’ve gone through my <a target="_blank" href="https://www.freecodecamp.org/news/how-to-launch-an-ec2-instance-and-a-web-server-using-httpd/"><strong>previous blog</strong></a> on how to launch and connect to an EC2 instance. If you haven’t set up an EC2 instance yet, head over to that post first to get your instance up and running. Once that’s done, you’re all set to proceed!</p>
<h3 id="heading-step-1-download-the-built-better-template">Step 1: Download the "Built Better" Template</h3>
<p>For this tutorial, we’ll use the Built Better template, which is free and easy to set up.</p>
<p>Head over to <a target="_blank" href="https://www.free-css.com/free-css-templates/page284/built-better">this link</a> and download the template.</p>
<p>Right-click on the download button and select "Copy clean link". We’ll use this link to download the template directly into your EC2 instance.</p>
<h3 id="heading-step-2-download-the-template-directly-to-your-ec2-instance">Step 2: Download the Template Directly to Your EC2 Instance</h3>
<p>Now that you have the link to the template, let’s download it straight to your EC2 instance using <code>wget</code>.</p>
<p>Log in to your EC2 instance via SSH or MobaXterm (as covered in my <a target="_blank" href="https://www.freecodecamp.org/news/connect-to-your-ec2-instance-using-mobaxterm/"><strong>previous blog</strong></a>) and navigate to the <code>/var/www/html</code> directory where your website files will be stored:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /var/www/html
</code></pre>
<p>Use the <code>wget</code> command followed by the copied link to download the "Built Better" template directly into your EC2 instance:</p>
<pre><code class="lang-bash">sudo wget https://www.free-css.com/assets/files/free-css-templates/download/page284/built-better.zip
</code></pre>
<p><strong>Note:</strong> After downloading, it's a good idea to check the file name to ensure it matches the file used in the subsequent commands. You can do this by running the <code>ls</code> command:</p>
<pre><code class="lang-bash">ls
</code></pre>
<h3 id="heading-step-3-unzip-the-template-files">Step 3: Unzip the Template Files</h3>
<p>Now that the template has been downloaded, it’s time to extract it. Install the <code>unzip</code> utility if it’s not already installed:</p>
<pre><code class="lang-bash">sudo dnf install unzip -y
</code></pre>
<p>Then unzip the template:</p>
<pre><code class="lang-bash">sudo unzip built-better.zip -d /var/www/html/
</code></pre>
<p>After unzipping, make sure to check the folder name where the files were extracted from. You can do this by listing the contents of the <code>/var/www/html</code> directory:</p>
<pre><code class="lang-bash">ls /var/www/html/
</code></pre>
<p>In this case, the unzipped contents are located inside a folder named <code>html</code>. This folder contains all the template files. If the folder name is different in your case, adjust the following steps accordingly.</p>
<p>First, move the files from the <code>html</code> folder to the root <code>/var/www/html/</code> directory:</p>
<pre><code class="lang-bash">sudo mv /var/www/html/html/* /var/www/html/
</code></pre>
<p>Then remove the unnecessary folder:</p>
<pre><code class="lang-bash">sudo rm -r /var/www/html/html
</code></pre>
<p>Lastly, remove the ZIP file:</p>
<pre><code class="lang-bash">sudo rm built-better.zip
</code></pre>
<h3 id="heading-step-4-set-up-the-web-server-to-host-your-template">Step 4: Set Up the Web Server to Host Your Template</h3>
<p>If you haven’t already, make sure your Apache HTTPD web server is installed and running. You can follow these steps to ensure your server is ready:</p>
<p>Install Apache (if not installed):</p>
<pre><code class="lang-bash">sudo yum install httpd -y
</code></pre>
<p>Start the Apache service:</p>
<pre><code class="lang-bash">sudo systemctl start httpd
</code></pre>
<p>Enable Apache to start on boot:</p>
<pre><code class="lang-bash">sudo systemctl <span class="hljs-built_in">enable</span> httpd
</code></pre>
<p>Now your web server should be up and running, ready to serve your template.</p>
<h3 id="heading-step-5-test-your-website">Step 5: Test Your Website</h3>
<p>Now for the exciting part seeing your site live! Open a browser and navigate to your EC2 instance’s public IP address. You should now see the Built Better template live and ready to go.</p>
<p>Here’s how to check:</p>
<ul>
<li><p>Find your EC2 instance’s public IP from the AWS EC2 dashboard.</p>
</li>
<li><p>Enter the IP in your browser, like so: <a target="_blank" href="http://your-ec2-public-ip"><code>http://your-ec2-public-ip</code></a></p>
</li>
<li><p>Your website should now be live with the Built Better template! 🎉</p>
</li>
</ul>
<h3 id="heading-wrapping-up">Wrapping Up</h3>
<p>Congratulations! You’ve successfully hosted a professional-looking website using the Built Better CSS template on your EC2 instance.</p>
<p>With just a few steps, you’ve moved from launching an EC2 instance to hosting a fully styled website, all using the AWS powerful cloud infrastructure.</p>
<p>You can follow me on</p>
<ul>
<li><p><a target="_blank" href="https://twitter.com/Kedar__98">Twitter</a></p>
</li>
<li><p><a target="_blank" href="https://www.linkedin.com/in/kedar-makode-9833321ab/?originalSubdomain=in">LinkedIn</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
