<?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>Sat, 23 May 2026 22:20:20 +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 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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>
        
            <item>
                <title>
                    <![CDATA[ How to Create Filling CSS Loaders Using One Element ]]>
                </title>
                <description>
                    <![CDATA[ In a previous article, I showed you how to create two types of CSS loaders: a spinner and a progress bar. In this article, you’ll learn about another variation called a filling CSS loader. I think a demo is worth thousands of words, so check out this... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/filling-css-loaders/</link>
                <guid isPermaLink="false">6719336f56c24fb7d4fc62b3</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ loaders ]]>
                    </category>
                
                    <category>
                        <![CDATA[ animations ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Temani Afif ]]>
                </dc:creator>
                <pubDate>Wed, 23 Oct 2024 17:33:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729637821745/50ff0461-350c-441c-9726-b838d3ef0a5c.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-a-css-only-loader">a previous article</a>, I showed you how to create two types of CSS loaders: a spinner and a progress bar. In this article, you’ll learn about another variation called a filling CSS loader.</p>
<p>I think a demo is worth thousands of words, so check out this Codepen:</p>
<div class="embed-wrapper"><iframe height="450" style="width:100%;height:450px" src="https://codepen.io/t_afif/embed/preview/ExqvRXO/77e4af243170fc28a0c8193a55b7ffe5?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/ExqvRXO/77e4af243170fc28a0c8193a55b7ffe5">
  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>In the above Pen, I’m showing you four different CSS filler-style loaders – but we can make even more. You can check out <a target="_blank" href="https://css-loaders.com/filling/">this collection</a> I created to see more than 20 different loaders.</p>
<p>You might think the article is going to be super long – I mean, how long will it take to explain how to create 20 different CSS loaders?</p>
<p>Well don’t worry – this tutorial will be super quick, because I’ll show a few CSS tricks that help you create as many variation as you want. The loaders look different, but all of them rely on the same techniques. By simply adjusting a few setting you can get a whole new loader.</p>
<h2 id="heading-the-initial-loader-configuration">The Initial Loader Configuration</h2>
<p>Like all <a target="_blank" href="https://css-loaders.com/">the CSS Loaders</a> I create, the HTML code is a simple as a single element. Nothing more! Here’s what it looks like:</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">"loader"</span>&gt;</span>Loading<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Then we apply the following CSS:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">text-transform</span>: uppercase;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#0000</span>; <span class="hljs-comment">/* or transparent */</span>
  <span class="hljs-attribute">-webkit-text-stroke</span>: <span class="hljs-number">1px</span> <span class="hljs-number">#000</span>;
}
</code></pre>
<p>Nothing fancy so far. We make the text transparent and we add a black stroke to it. Here’s what that looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729506785632/fe01d18e-6d7d-4d49-a5d0-ef6766c51241.png" alt="The &quot;loading&quot; text with a black stroke and transparent color" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The <code>-webkit-text-stroke</code> is still tagged as experimental, but it has <a target="_blank" href="https://caniuse.com/mdn-css_properties_-webkit-text-stroke">good browser support</a> so you should be able to use it without any issues. This said, it’s always good to test your code in different browsers to make sure everything works fine.</p>
<h2 id="heading-how-to-fill-the-text-with-colors">How to Fill the Text with Colors</h2>
<p>Now it’s time to fill our text (that’s why this technique is called the Filling CSS loaders!). To do this, we are going to rely on gradients and <code>background-clip: text</code>. Here’s the code:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">conic-gradient</span>(#<span class="hljs-number">000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>);
  <span class="hljs-attribute">background-position</span>: left;
  <span class="hljs-attribute">background-size</span>: <span class="hljs-number">40%</span> <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background-repeat</span>: no-repeat;
  <span class="hljs-attribute">background-clip</span>: text;
}
</code></pre>
<p>Or the shorthand version if you prefer more compact code:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.loader</span> {
  <span class="hljs-attribute">background</span>: 
    <span class="hljs-built_in">conic-gradient</span>(#<span class="hljs-number">000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>) text
    left/<span class="hljs-number">40%</span> <span class="hljs-number">100%</span> no-repeat;
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729507914013/b8991ffe-0b81-4ae0-a356-c102023c2f6c.png" alt="the difference between with and without background-clip: text" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The above figure illustrates the difference between using or not using <code>background-clip: text</code>. It’s pretty clear that the left result is what we are aiming for. We are limiting the background coloration to only the text instead of the whole element.</p>
<p>The <code>conic-gradient(#000 0 0)</code> looks strange, right? It lets you have a one-color gradient. I wrote a small tip about it that I invite you to read to understand why we’re using that particular syntax in this article, “<a target="_blank" href="https://css-tip.com/one-color-gradient/">How to correctly define a one-color gradient</a>“.</p>
<h2 id="heading-how-to-create-the-filling-loaders">How to Create the Filling Loaders</h2>
<p>Believe it or not, we’re almost done because we have everything we need to make the CSS loaders. For the first loader, we simply animate the <code>background-size</code> as follows:</p>
<pre><code class="lang-css"><span class="hljs-selector-id">#l1</span> {
  <span class="hljs-attribute">animation</span>: l1 <span class="hljs-number">1s</span> linear infinite;
}
<span class="hljs-keyword">@keyframes</span> l1 {  <span class="hljs-comment">/*  width  height */</span>
  0% {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">0%</span>   <span class="hljs-number">100%</span>}
  <span class="hljs-selector-tag">to</span> {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">120%</span> <span class="hljs-number">100%</span>}
}
</code></pre>
<p>We start with a width equal to <code>0%</code> until we reach a width equal to <code>120%</code>. I could have used <code>100%</code>, but I want the full coloration to stay longer so I am using a value bigger than <code>100%</code>. As for the height (the second value of the <code>background-size</code>), it remains at <code>100%</code>.</p>
<p>The second loader uses the same animation, but instead of a linear timing function, we use <code>steps()</code> to have a discrete animation.</p>
<pre><code class="lang-css"><span class="hljs-selector-id">#l2</span> {
  <span class="hljs-attribute">font-family</span>: monospace;
  <span class="hljs-attribute">animation</span>: l2 <span class="hljs-number">2s</span> <span class="hljs-built_in">steps</span>(<span class="hljs-number">8</span>, jump-none) infinite;
}
<span class="hljs-keyword">@keyframes</span> l2 {
  0% {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">0%</span>           <span class="hljs-number">100%</span>}
  <span class="hljs-selector-tag">to</span> {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">100%</span> <span class="hljs-number">100%</span>}
}
</code></pre>
<p>The text contains 7 characters so we use 8 steps (<code>N + 1</code>). I am also using a monospace font to make sure all the characters have the same width. In case you are wondering about the <code>jump-none</code> value, read the following: <a target="_blank" href="https://css-tip.com/steps/">How to correctly use steps() with animations</a>.</p>
<p><a target="_blank" href="https://css-tip.com/steps/">That’s basically the main trick. By animat</a>ing the background properties, we create different kinds of loaders. It’s either the <code>background-size</code> like the previous ones or the <code>background-position</code> like the below:</p>
<div class="embed-wrapper"><iframe height="400" style="width:100%;height:400px" src="https://codepen.io/t_afif/embed/preview/bGXogOx/59e12d693e164ac69804a554bbac8588?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/bGXogOx/59e12d693e164ac69804a554bbac8588">
  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 how they work before checking my code? This will be your first homework!</p>
<h2 id="heading-how-to-use-multiple-gradients">How to Use Multiple Gradients</h2>
<p>Using one gradient is enough to create a lot of variations – but we can do even more if we introduce multiple gradients. If you check the fourth loader of the first demo, you’ll see that I’m using seven gradients – one gradient per character.</p>
<pre><code class="lang-css"><span class="hljs-selector-id">#l4</span> {
  <span class="hljs-attribute">font-family</span>: monospace;
  <span class="hljs-attribute">--g</span>: <span class="hljs-built_in">conic-gradient</span>(#<span class="hljs-number">000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>) no-repeat text;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">var</span>(--g) <span class="hljs-number">0</span>,<span class="hljs-built_in">var</span>(--g) <span class="hljs-number">1ch</span>,<span class="hljs-built_in">var</span>(--g) <span class="hljs-number">2ch</span>,<span class="hljs-built_in">var</span>(--g) <span class="hljs-number">3ch</span>,<span class="hljs-built_in">var</span>(--g) <span class="hljs-number">4ch</span>,<span class="hljs-built_in">var</span>(--g) <span class="hljs-number">5ch</span>,<span class="hljs-built_in">var</span>(--g) <span class="hljs-number">6ch</span>;
  <span class="hljs-attribute">background-position-y</span>: bottom;
  <span class="hljs-attribute">animation</span>: l4 <span class="hljs-number">3s</span> infinite;
}
<span class="hljs-keyword">@keyframes</span> l4 {
  0%     {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   }
  14<span class="hljs-selector-class">.28</span>% {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   }
  28<span class="hljs-selector-class">.57</span>% {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   }
  42<span class="hljs-selector-class">.85</span>% {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   }
  57<span class="hljs-selector-class">.14</span>% {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   }
  71<span class="hljs-selector-class">.43</span>% {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   ,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   }
  85<span class="hljs-selector-class">.71</span>% {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">0</span>   }
  100%   {<span class="hljs-attribute">background-size</span>: <span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>,<span class="hljs-number">1ch</span> <span class="hljs-number">100%</span>}
}
</code></pre>
<p>I’m using the same gradient, so we consider a CSS variable <code>--g</code> to avoid repetition. Then, I call that variable 7 times inside the background property. All the gradients have the same Y position (<code>bottom</code>) but a different X position. That’s why you see the <code>0, 1ch, 2ch, …,6ch</code>.</p>
<p>Now if you check the animation, I’m simply animating the height of each gradient individually. At <code>0%</code>, all of them have a height equal to <code>0</code>. Then I update their height one by one until all of them are at <code>100%</code>. The width doesn’t change – it’s always equal to <code>1ch</code> (the width of one character).</p>
<p>It may look difficult at first glance, but if you think about it one gradient at a time, it’s pretty simple.</p>
<p>What about the third loader, you might ask? For that one, I will rely on <a target="_blank" href="https://css-generators.com/wavy-shapes/">my online generator for wavy shapes</a> to generate the gradient configuration:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729635835522/2f8726a3-e6bb-4949-8846-8408dad56a64.png" alt="Screenshot of the wavy shape generator" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Then I animate the <code>background-position</code> like below:</p>
<pre><code class="lang-css"><span class="hljs-selector-id">#l3</span> {
  <span class="hljs-attribute">background</span>:
    <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">1.13em</span> at <span class="hljs-number">50%</span> <span class="hljs-number">1.6em</span>,#<span class="hljs-number">000</span> <span class="hljs-number">99%</span>,#<span class="hljs-number">0000</span> <span class="hljs-number">101%</span>) <span class="hljs-built_in">calc</span>(<span class="hljs-number">50%</span> - <span class="hljs-number">1.6em</span>) <span class="hljs-number">0</span>/<span class="hljs-number">3.2em</span> <span class="hljs-number">100%</span>,
    <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">1.13em</span> at <span class="hljs-number">50%</span> -<span class="hljs-number">0.8em</span>,#<span class="hljs-number">0000</span> <span class="hljs-number">99%</span>,#<span class="hljs-number">000</span> <span class="hljs-number">101%</span>) <span class="hljs-number">50%</span> .<span class="hljs-number">8em</span>/<span class="hljs-number">3.2em</span> <span class="hljs-number">100%</span> repeat-x;
  <span class="hljs-attribute">background-clip</span>: text;
  <span class="hljs-attribute">animation</span>: l3 <span class="hljs-number">2s</span> linear infinite;
}
<span class="hljs-keyword">@keyframes</span> l3 {
  0% {<span class="hljs-attribute">background-position</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">50%</span> - <span class="hljs-number">1.6em</span>) <span class="hljs-number">0</span>,     <span class="hljs-number">50%</span>          .<span class="hljs-number">8em</span>}
  <span class="hljs-selector-tag">to</span> {<span class="hljs-attribute">background-position</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">50%</span> + <span class="hljs-number">1.6em</span>) <span class="hljs-number">0</span>,<span class="hljs-built_in">calc</span>(<span class="hljs-number">50%</span> + <span class="hljs-number">3.2em</span>) .<span class="hljs-number">8em</span>}
}
</code></pre>
<p>This one is probably a bit trickier, but it’s another example to illustrate all the possibilities. From the simple gradient configuration to the most complex one, we can create as many loaders as we want.</p>
<p>What about creating your own CSS loader? You can use what you have learned from the article and try to create a loader that is not part of <a target="_blank" href="https://css-loaders.com/filling/">my collection</a>. The best way to learn is to practice – so give it a try!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By creating some cool loaders, we went through a bunch of CSS tricks related to gradients and backgrounds. Even if creating loaders is not your goal, you can always re-use the same tricks to do something else.</p>
<p>Don’t forget to check my <a target="_blank" href="https://css-tip.com/">CSS Tip blog</a> where I am sharing cool CSS tricks and demos.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Curved-Edge and Rounded-Edge Shapes Using CSS ]]>
                </title>
                <description>
                    <![CDATA[ In a previous article, I showed you how to create some fancy shapes that you can use as section dividers on your websites (a slanted divider, an arrow divider, and others). In this article, we will study and learn how to make more CSS shapes using th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/rounded-and-curved-edge-css-shapes/</link>
                <guid isPermaLink="false">670d963aff55e287d6962c67</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Temani Afif ]]>
                </dc:creator>
                <pubDate>Mon, 14 Oct 2024 22:07:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728902609915/17a0da8c-143b-4160-8133-bc865c1b535b.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In <a target="_blank" href="https://www.freecodecamp.org/news/section-divider-using-css/">a previous article</a>, I showed you how to create some fancy shapes that you can use as section dividers on your websites (a slanted divider, an arrow divider, and others). In this article, we will study and learn how to make more CSS shapes using the same technique.</p>
<p>Here is an overview of the shapes we’ll look at here, applied to the header of my freeCodeCamp profile:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728811438184/24597334-f2be-4bb1-83f2-11045bc8cacc.png" alt="CSS Shapes: Rounded &amp; Curved edges " class="image--center mx-auto" width="1144" height="448" loading="lazy"></p>
<p>Cool right? Both designs are commonly used as section dividers. We will learn together how to create such shapes with some simple code.</p>
<p>Before we start, you can find the code of the shapes we are making (and more!) within <a target="_blank" href="https://css-shape.com/">my online collection</a>. You can easily copy the code from there – but don’t go away right now! Understanding the logic behind the code is also important and will help you customize it to fit your needs.</p>
<h2 id="heading-how-to-create-a-rounded-edge-using-clip-path">How to Create a Rounded Edge using <code>clip-path</code></h2>
<p>Let’s start with the first shape: <a target="_blank" href="https://css-shape.com/rounded-edge/">The rounded edge</a>. It may sound surprising, but the code to create such a shape is as simple as one CSS declaration:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.rounded-edge</span> {
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(<span class="hljs-number">85%</span> <span class="hljs-number">100%</span> at top);
}
</code></pre>
<p>Let’s draw a figure to understand how an “ellipse” creates a rounded edge shape.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728813578343/a4560ca7-c2db-41b6-a3ed-9661ab15ef0b.png" alt="Illustrating the ellipse value of the clip-path" class="image--center mx-auto" width="1106" height="379" loading="lazy"></p>
<p>We start with a rectangle element with no <code>clip-path</code> applied to it. Then, we add <code>clip-path: ellipse(50% 100% at top)</code>. As you can see, we have the ellipse shape. Its center is at the “top center” of the element and its radii are equal to <code>50%</code> horizontally and <code>100%</code> vertically. The shape is overflowing the element boundaries, which is why we only see the bottom half part of it. The top half is clipping nothing.</p>
<p>So you might be wondering: I said “top center”, but why in the code do we only have “top”?</p>
<p>By default, if we don’t specify the position it will be the center of the element. This is equivalent to “<code>center</code>”, “<code>center center</code>”, “<code>50%</code>” or “<code>50% 50%</code>”. Note how we can either define one value or two values. If the second value is omitted, it will be equal to “<code>center</code>” so defining “<code>top</code>” is the same as “<code>top center</code>”.</p>
<p>You don’t need to remember all the cases. Using the keywords such as "<code>top</code>”, “<code>left</code>”, and so on is, most of the time, enough – unless you need to create a custom shape (we will see this later on).</p>
<p>Let’s get back to the previous figure. If we increase the horizontal radius and make it bigger than 50% (85% for example), the ellipse will get bigger and will logically cover a bigger area. You start to see the trick, right? In the end, it’s only a portion at the bottom of the ellipse that is visible – the rounded edge we want!</p>
<p>As a bonus, it’s responsive, since we are relying on percentage values. The horizontal radius is relative to the width while the vertical one is relative to the height. This said, you can also rely on pixel values if you want a fixed size for your ellipse. In some cases, it can give a better result.</p>
<p>Here is a demo showing both cases. Resize the screen to notice the difference:</p>
<div class="embed-wrapper"><iframe height="500" style="width:100%;height:500px" src="https://codepen.io/t_afif/embed/preview/mdNRvEB/8d8fdd895914c59ab86257ed94f9f71a?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/mdNRvEB/8d8fdd895914c59ab86257ed94f9f71a">
  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>So what about the other variation? We just have to change “top” with “bottom”, right?</p>
<p>Exactly! By changing the center of the ellipse, you change the placement of the rounded edge and easily get the four directions.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728817085669/307215e8-f40a-4999-a36f-db6a70d3d1b7.png" alt="Four variations of the rounded edges" class="image--center mx-auto" width="746" height="498" loading="lazy"></p>
<p>You can also get a more custom shape if you adjust the center of the ellipse using percentage values. Like for example using <code>30% 0%</code> to get a rounded edge shifted to the left (note that <code>top</code> is equivalent to <code>50% 0%</code>).</p>
<div class="embed-wrapper"><iframe height="350" style="width:100%;height:350px" src="https://codepen.io/t_afif/embed/preview/vYogbZe/eee85304e8d8c22b625e81b0ee6e8a33?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/vYogbZe/eee85304e8d8c22b625e81b0ee6e8a33">
  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>A lot of possibilities with only one line of code!</p>
<p>Go check <a target="_blank" href="https://css-shape.com/rounded-edge/">the online version</a> where you can find more examples and easily customize the shape by adjusting the existing code.</p>
<h2 id="heading-how-to-create-a-curved-edge-using-mask">How to Create a Curved Edge using <code>mask</code></h2>
<p>Let’s move to the second shape: <a target="_blank" href="https://css-shape.com/curved-edge/">the curved edge</a>. This one is also pretty easy to create as it requires one line of code as well.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-edge</span> {
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">60%</span> <span class="hljs-number">70px</span> at bottom,#<span class="hljs-number">0000</span> <span class="hljs-number">100%</span>,#<span class="hljs-number">000</span>);
}
</code></pre>
<p>This time, we are going to rely on <code>mask</code> instead of <code>clip-path</code> but the logic is the same. We will hide some parts of the element and keep the remaining visible. When using mask, the hidden part is the transparent color of the gradient (the <code>#0000</code>) while the visible part is the opaque color of the gradient (the <code>#000</code>).</p>
<p>It doesn’t really matter which color you use, only the transparency of the color matters. So you are free to use any color syntax you want. Here’s an example using the <code>rgb()</code> syntax:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-edge</span> {
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">60%</span> <span class="hljs-number">70px</span> at bottom,rgb(<span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>/<span class="hljs-number">0%</span>) <span class="hljs-number">100%</span>,<span class="hljs-built_in">rgb</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>/<span class="hljs-number">100%</span>));
}
</code></pre>
<p>Or color names:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-edge</span> {
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">60%</span> <span class="hljs-number">70px</span> at bottom,transparent <span class="hljs-number">100%</span>,black);
}
</code></pre>
<p>And here is a figure to illustrate how it works.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728830604961/6468c949-d783-43b7-8d0a-81fb39ecf2c7.png" alt="Overview of the radial-gradient" class="image--center mx-auto" width="1117" height="457" loading="lazy"></p>
<p>Similar to the ellipse function of the <code>clip-path</code>, the <code>radial-gradient()</code> will also create an ellipse shape. The only difference is that this time we will hide the inside part of the ellipse and show the outside part. And thanks to the overflowing part, we get the curved shape we want.</p>
<p>I think you know the rest of the story now. By adjusting the radii and the position of the center of the ellipse, we get the different variations. As a small homework assignment, try to update the previous code to get the top, left, and right directions. You can compare what you have found with <a target="_blank" href="https://css-shape.com/curved-edge/">my implementation</a>.</p>
<p>When using this method, make sure you have enough space at the bottom. Unlike the rounded edge, the curved edge may hide some of your content at the bottom so it’s always good to include a padding equal to the vertical radius.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-edge</span> {
  <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">70px</span>;
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">60%</span> <span class="hljs-number">70px</span> at bottom,#<span class="hljs-number">0000</span> <span class="hljs-number">100%</span>,#<span class="hljs-number">000</span>);
}
</code></pre>
<h2 id="heading-how-to-combine-both-of-these-css-shapes">How to Combine Both of These CSS Shapes</h2>
<p>What about having both curves so you can create <a target="_blank" href="https://css-shape.com/curved-rectangle/">a curved rectangle shape</a>? It’s possible by simply combining both bits of code like this:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-rectangle</span> {
  <span class="hljs-comment">/* curved edge at the bottom */</span>
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">60%</span> <span class="hljs-number">70px</span> at bottom,#<span class="hljs-number">0000</span> <span class="hljs-number">100%</span>,#<span class="hljs-number">000</span>);
  <span class="hljs-comment">/* rounded edge at the top */</span>
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(<span class="hljs-number">80%</span> <span class="hljs-number">100%</span> at bottom);
}
</code></pre>
<p>See it in play:</p>
<div class="embed-wrapper"><iframe height="400" style="width:100%;height:450px" src="https://codepen.io/t_afif/embed/preview/VwoPOwm/8f71a34ba272ac1cb7d96ff7d494239b?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/VwoPOwm/8f71a34ba272ac1cb7d96ff7d494239b">
  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>And the opposite effect by changing <code>bottom</code> with <code>top</code>:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-rectangle</span> {
  <span class="hljs-comment">/* curved edge at the top */</span>
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">60%</span> <span class="hljs-number">70px</span> at top,#<span class="hljs-number">0000</span> <span class="hljs-number">100%</span>,#<span class="hljs-number">000</span>);
  <span class="hljs-comment">/* rounded edge at the bottom */</span>
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(<span class="hljs-number">80%</span> <span class="hljs-number">100%</span> at top);
}
</code></pre>
<div class="embed-wrapper"><iframe height="450" style="width:100%;height:450px" src="https://codepen.io/t_afif/embed/preview/ZEgLNEZ/56d77b755720ce445225af72a32e7941?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/ZEgLNEZ/56d77b755720ce445225af72a32e7941">
  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 will notice that I am either using <code>top</code> everywhere or <code>bottom</code> everywhere which makes both pieces of code easy to remember.</p>
<p>Now you might be wondering – how can we have the same curves and the top and bottom?</p>
<p>As you may have noticed, both curves don’t match, which makes the whole shape kind of broken. But we can fix this. We need to make sure both parts of the code create the same ellipse shape by setting the same radii.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-header</span> {
  <span class="hljs-comment">/* curved edge at the top */</span>
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">80%</span> <span class="hljs-number">100%</span> at top,#<span class="hljs-number">0000</span> <span class="hljs-number">100%</span>,#<span class="hljs-number">000</span>);
  <span class="hljs-comment">/* rounded edge at the bottom */</span>
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(<span class="hljs-number">80%</span> <span class="hljs-number">100%</span> at top);
}
</code></pre>
<p>Note the “<code>80% 100% at top</code>” which is the same in both declarations – but nothing will be visible if we use this code. Don’t forget that the <code>clip-path</code> will hide the outside part of the ellipse while the gradient will hide the inside part. So if both ellipses are the same, everything will be hidden.</p>
<p>To fix this, we have to offset the gradient and move it to the top to get the following:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-rectangle</span> {
  <span class="hljs-comment">/* curved edge at the top */</span>
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">80%</span> <span class="hljs-number">100%</span> at <span class="hljs-number">50%</span> -<span class="hljs-number">78%</span> /* instead of <span class="hljs-number">50%</span> <span class="hljs-number">0%</span> */,#<span class="hljs-number">0000</span> <span class="hljs-number">100%</span>,#<span class="hljs-number">000</span>);
  <span class="hljs-comment">/* rounded edge at the bottom */</span>
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(<span class="hljs-number">80%</span> <span class="hljs-number">100%</span> at top);
}
</code></pre>
<p>The shape is now perfect and both curves are aligned.</p>
<div class="embed-wrapper"><iframe height="450" style="width:100%;height:450px" src="https://codepen.io/t_afif/embed/preview/GRVWKZM/32ab225612d6ffade20cbef6c43100fa?default-tab=result" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/t_afif/pen/GRVWKZM/32ab225612d6ffade20cbef6c43100fa">
  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 don’t like to use magic numbers like the “-78%”, we can consider some math to get accurate results:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-rectangle</span> {
  <span class="hljs-attribute">--c</span>: <span class="hljs-number">80</span>; <span class="hljs-comment">/* control the curve */</span>

  <span class="hljs-attribute">mask</span>: 
    <span class="hljs-built_in">radial-gradient</span>(calc(var(--c)*<span class="hljs-number">1%</span>) <span class="hljs-number">100%</span> 
     at <span class="hljs-number">50%</span> <span class="hljs-built_in">calc</span>(-<span class="hljs-number">100%</span>*cos(asin(<span class="hljs-number">50</span>/var(--c)))),
     <span class="hljs-number">#0000</span> <span class="hljs-number">100%</span>,<span class="hljs-number">#000</span>);
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(calc(var(--c)*<span class="hljs-number">1%</span>) <span class="hljs-number">100%</span> at top);
}
</code></pre>
<p>The code looks more complex (I will skip the boring geometry explanation) but you can easily control the curve by adjusting a single value.</p>
<p>So what about the bottom version? We update the code like the below:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-rectangle</span> {
  <span class="hljs-attribute">--c</span>: <span class="hljs-number">80</span>; <span class="hljs-comment">/* control the curve */</span>

  <span class="hljs-attribute">mask</span>: 
    <span class="hljs-built_in">radial-gradient</span>(calc(var(--c)*<span class="hljs-number">1%</span>) <span class="hljs-number">100%</span> 
     at <span class="hljs-number">50%</span> <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> + <span class="hljs-number">100%</span>*cos(asin(<span class="hljs-number">50</span>/var(--c)))),
     <span class="hljs-number">#0000</span> <span class="hljs-number">100%</span>,<span class="hljs-number">#000</span>);
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(calc(var(--c)*<span class="hljs-number">1%</span>) <span class="hljs-number">100%</span> at bottom);
}
</code></pre>
<p>The <code>top</code> of the <code>clip-path</code> becomes <code>bottom</code> and inside the gradient, we use <code>100% + X</code> instead of <code>-X</code> where <code>X</code> is the offset. You can always find all the code within <a target="_blank" href="https://css-shape.com/curved-rectangle/">my online collection</a>.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>How many lines of code do you have to remember? Only two lines of code – that’s all! You can create a rounded edge using <code>clip-path</code>:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.rounded-edge</span> {
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(<span class="hljs-number">85%</span> <span class="hljs-number">100%</span> at top);
}
</code></pre>
<p>And you can create a curved edge using <code>mask</code>:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-edge</span> {
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">60%</span> <span class="hljs-number">70px</span> at top,#<span class="hljs-number">0000</span> <span class="hljs-number">100%</span>,#<span class="hljs-number">000</span>);
}
</code></pre>
<p>And by combining both, you get a curved rectangle:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.curved-rectangle</span> {
  <span class="hljs-attribute">mask</span>: <span class="hljs-built_in">radial-gradient</span>(<span class="hljs-number">80%</span> <span class="hljs-number">100%</span> at <span class="hljs-number">50%</span> -<span class="hljs-number">78%</span>,#<span class="hljs-number">0000</span> <span class="hljs-number">100%</span>,#<span class="hljs-number">000</span>);
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">ellipse</span>(<span class="hljs-number">80%</span> <span class="hljs-number">100%</span> at top);
}
</code></pre>
<p>You don’t have to remember the verbose version where I am using math. Most of the time, you don’t really need to have accurate values and you can manually adjust the position until you get it right.</p>
<p>Don’t forget to bookmark <a target="_blank" href="https://css-shape.com/">my online collection of CSS shapes</a> if you want to easily copy the code of any shape. I also recommend reading my “<a target="_blank" href="https://www.smashingmagazine.com/2024/05/modern-guide-making-css-shapes/">Modern guide for making CSS shapes</a>” to know the secret behind creating more CSS shapes.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a CSS Component Library and Improve Your Web Development Skills ]]>
                </title>
                <description>
                    <![CDATA[ Application development is a complex, multi-stage process, and it all begins with UI/UX design. Once the design phase is complete, the focus shifts to UI development, where tools like HTML, CSS, and JavaScript come into play. At a higher level, libra... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-css-component-library-step-by-step/</link>
                <guid isPermaLink="false">6709799c031bde0105c6130f</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ components ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Prankur Pandey ]]>
                </dc:creator>
                <pubDate>Fri, 11 Oct 2024 19:16:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728400677433/de06f432-0861-4ba4-ad7c-6e10157e2822.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Application development is a complex, multi-stage process, and it all begins with UI/UX design.</p>
<p>Once the design phase is complete, the focus shifts to <strong>UI development</strong>, where tools like <strong>HTML</strong>, <strong>CSS</strong>, and <strong>JavaScript</strong> come into play. At a higher level, libraries like <strong>React</strong> and <strong>Vue</strong> streamline the development process.</p>
<p>Regardless of the application type, your code can almost always be broken down into <strong>components</strong>.</p>
<p><em>“Repeated components are a nightmare to manage.”</em> – Every frustrated UI developer</p>
<p>Imagine having a component library where all commonly used elements are pre-built and responsive—how much easier and faster would that make development?</p>
<p>In this article, I’ll show you how to build your own component library, using a minimal tech stack, and then use it to build an application.</p>
<h3 id="heading-what-well-cover">What We’ll Cover</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-build-a-component-library">Why Build a Component Library</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-component-library">How to Build the Component Library</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-build-the-library">Let’s Build the Library</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-design-the-layout-using-pen-and-paper">Step 1: Design the Layout using Pen and Paper</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-design-the-components-in-html-and-css">Step 2: Design the Components in HTML and CSS</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-hosting-the-project-css-file">Step 3: Hosting the Project CSS File</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-faq">FAQ</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p><strong>Proficiency in HTML, CSS, and JavaScript</strong>: A solid understanding of front-end development fundamentals is essential.</p>
</li>
<li><p><strong>Basic Deployment Skills</strong>: Familiarity with deploying applications on platforms like <strong>Netlify</strong> or <strong>Vercel</strong> is required.</p>
</li>
<li><p><strong>Git/GitHub Knowledge</strong>: You should be comfortable with version control, including basic Git commands and managing repositories on GitHub.</p>
</li>
</ul>
<h2 id="heading-why-build-a-component-library">Why Build a Component Library?</h2>
<p>Every website is built from components, which are structured with <strong>HTML</strong> and styled using <strong>CSS</strong>.</p>
<p><strong>HTML</strong> and <strong>CSS</strong> are fundamental technologies for creating visually appealing web pages. However, mastering them can be challenging due to the wide array of HTML tags and CSS properties.</p>
<p>To simplify the process, developers often use <strong>component libraries</strong>, which deliver various benefits:</p>
<ol>
<li><p><strong>Faster Development</strong>: Pre-built components and responsive design features accelerate the development process.</p>
</li>
<li><p><strong>Consistency</strong>: Ensures uniform styling and cross-browser compatibility across the application.</p>
</li>
<li><p><strong>Maintainability</strong>: Encourages structured, modular code, making it easier to maintain and scale.</p>
</li>
<li><p><strong>Community Support</strong>: extensive documentation, plugins, and a strong community provide valuable resources.</p>
</li>
<li><p><strong>Customization and Accessibility</strong>: offers easy customization and accessibility-focused components for inclusive designs.</p>
</li>
</ol>
<h2 id="heading-how-to-build-the-component-library">How to Build the Component Library</h2>
<p>Building a component library involves several key steps. First, I’ll give you an overview of each step we’ll take to create the component library. After that, we’ll build it together.</p>
<h3 id="heading-1-design-the-layout-of-the-components">1. Design the Layout of the Components</h3>
<p>Before writing any code, it’s crucial to have a clear vision of what you want to build. Start by sketching the layout of your components on paper, or use design tools like <strong>Figma</strong> or <strong>Canva</strong> to create a visual representation.</p>
<p>Having a visual guide will streamline your coding process and help you stay focused as you translate designs into code.</p>
<h3 id="heading-2-write-the-component-structure-in-html">2. Write the Component Structure in HTML</h3>
<p>Once the design is ready, the next step is to structure your components in <strong>HTML</strong>. This creates the foundation of your webpage, as HTML is the backbone of any web project.</p>
<p><strong>Pro-tip</strong>: Use <strong>semantic HTML</strong> to improve user accessibility and SEO. For example, use <code>&lt;article&gt;</code>, <code>&lt;section&gt;</code>, or <code>&lt;header&gt;</code> tags instead of generic <code>&lt;div&gt;</code> elements when appropriate.</p>
<h3 id="heading-3-style-the-components-with-css">3. Style the Components with CSS</h3>
<p>With the HTML structure in place, you can begin styling the components using <strong>CSS</strong>. Apply styles like <strong>background colours</strong>, <strong>font sizes</strong>, <strong>link decorations</strong>, and <strong>button styles</strong> using <strong>CSS classes</strong> and <strong>IDs</strong>.</p>
<p>CSS is a powerful tool—you can even add beautiful animations. But in this tutorial, we’ll focus on utilizing the essential properties of CSS to create clean, functional designs.</p>
<h3 id="heading-4-host-the-projects-css-file">4. Host the Project's CSS File</h3>
<p>Once your component library is ready, you’ll want to make it accessible for future projects. Hosting your <strong>CSS file</strong> on platforms like <strong>Netlify</strong>, <strong>GitHub Pages</strong>, or <strong>Vercel</strong> allows you to use the components across different projects by simply linking to the global CSS file.</p>
<p>By following these four steps, you’ll create a reusable component library that helps you build beautiful websites efficiently and effectively.</p>
<h2 id="heading-lets-build-the-library">Let’s Build the Library</h2>
<p>I began my journey as a software developer by diving into <strong>HTML</strong> and <strong>CSS</strong> to design webpages. These foundational technologies are essential for any web developer, but mastering them can be challenging—HTML boasts 152 tags, while CSS has over 200 properties.</p>
<p>While you won’t need to use every single HTML tag or CSS property, knowing the core concepts requires significant time and effort. .</p>
<p>Now, let’s consider a scenario: if I asked you to create a small website or a landing page without using any component library, how would you approach it? My goal is to minimize the time spent on designing.</p>
<p>Imagine if there was a way to automate the design process, allowing you to achieve beautiful results without sacrificing flexibility. This is where a <strong>component library</strong> comes into play. By writing your components in pure vanilla CSS once, you can reuse them across any project.</p>
<p>I encourage you to pursue this approach because it will provide real-time experience with HTML and CSS while helping you learn a multitude of concepts simultaneously.</p>
<p>I developed a small library consisting of 10+ beautiful components, which you can explore here: <a class="post-section-overview" href="#"><strong>SlateUi</strong></a>. This library has helped deepen my understanding of web technologies.</p>
<p>My goal was to understand HTML and CSS thoroughly. After completing one project, I wanted to feel confident in all the critical aspects of web design, from UI to code.</p>
<p>By designing and developing these components, I gained greater control and customization options tailored to specific requirements.</p>
<p>The learning process was also incredibly rewarding. Creating each component took considerable time, but the exposure I gained to these two technologies significantly boosted my confidence.</p>
<p>Additionally, this approach helps avoid the redundancy of writing repetitive CSS code for similar elements.</p>
<h3 id="heading-step-1-design-the-layout-using-pen-and-paper">Step 1: Design the Layout using Pen and Paper</h3>
<p>First, you’ll want to create a basic layout for the webpage. This is just initial sketching so that you know what you have to build in your project</p>
<p>The layout consists of three key elements:</p>
<p>a) <strong>Header</strong><br>b) <strong>Card(s)</strong><br>c) <strong>Footer</strong></p>
<p>Each component includes distinct colours, text, and additional elements. Here’s what it looks like in our example:</p>
<p><a target="_blank" href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrcqxm4mrnyx778sedui.png"><img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrcqxm4mrnyx778sedui.png" alt="Illustration of the main components of the layout: header, card, footer" width="800" height="400" loading="lazy"></a></p>
<p>In an actual project, you typically start with prepared design models created in tools like <strong>Figma</strong> or other UI design software.</p>
<p>For this project, I used <strong>Canva</strong> to design the layout. This initial design phase is crucial as it lays the foundation for core feature development.</p>
<p>Then, you’ll write the structure of the component in HTML. At this level, I will simply put our HTML elements in such a way so that we can prepare a basic skeleton of the webpage as we have designed it. In the header I have a logo and some navigation links, in the cards I have a button and an image; and in the footer, I have some more links.</p>
<h3 id="heading-step-2-design-the-components-in-html-and-css">Step 2: Design the Components in HTML and CSS</h3>
<p>At this point<strong>,</strong> we’ll enhance the components we created with HTML by applying CSS properties to beautify them. This stage involves using CSS to set background colours, primary colours, link decorations, button styles, and more. We’ll do this by utilizing CSS classes and IDs.</p>
<p>So far, we’ve built three components:</p>
<p>a) <strong>Header</strong> with navigation links<br>b) <strong>Footer</strong><br>c) <strong>Horizontal Cards</strong> with action buttons</p>
<p>Now, let’s begin by building the first component: the <strong>Header</strong>.</p>
<h4 id="heading-the-header-component">The header component:</h4>
<p>The header is at the top of a page or a section. It usually contains a logo, search bar, navigational links, and so on.</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/iprankurpandey/embed/eYVzNPR?default-tab=html" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/iprankurpandey/pen/eYVzNPR">
  Header</a> by PRANKUR PANDEY (<a href="https://codepen.io/iprankurpandey">@iprankurpandey</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<h4 id="heading-the-footer-component">The footer component:</h4>
<p>The Footer defines the footer or bottom of a web page or a section. Usually, it contains copyright information, contact details, navigation links, and so on.</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/iprankurpandey/embed/poabJqN?default-tab=html" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/iprankurpandey/pen/poabJqN">
  Footer</a> by PRANKUR PANDEY (<a href="https://codepen.io/iprankurpandey">@iprankurpandey</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<h4 id="heading-the-card-component">The card component:</h4>
<p>The <strong>card component</strong> can house various types of content, including a heading, image, main content, and a footer that features a call-to-action button.</p>
<p>Cards are designed to serve specific purposes, such as showcasing e-commerce products, displaying news items, or serving multiple other functions across different contexts.</p>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/iprankurpandey/embed/KKQMpvd?default-tab=html" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/iprankurpandey/pen/KKQMpvd">
  Horizontal-Cards</a> by PRANKUR PANDEY (<a href="https://codepen.io/iprankurpandey">@iprankurpandey</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<h4 id="heading-combining-it-all-together">Combining it all together:</h4>
<div class="embed-wrapper"><iframe height="300" style="width:100%" src="https://codepen.io/iprankurpandey/embed/NWyrjGm?default-tab=html" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/iprankurpandey/pen/NWyrjGm">
  Demo-API</a> by PRANKUR PANDEY (<a href="https://codepen.io/iprankurpandey">@iprankurpandey</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<p>By clicking the button, the card data will update with random cat facts, along with the length of each fact.</p>
<p><strong>Note</strong>: I used an open-source API for this project. If the content does not update on the card, it may be due to an API outage.</p>
<p>Now, have you checked the CSS code?</p>
<p>You might be wondering why I’ve imported just a single line of code into my CSS file. Well, I have combined the CSS files of all three parts of the layout (<strong>header, footer, &amp; cards</strong> components) into one CSS file and hosted the file on Netlify. This is the URL that is holding all three CSS files:</p>
<p><code>@import url("</code><a target="_blank" href="https://hashnodeblogchallenge.netlify.app/index.css"><code>https://hashnodeblogchallenge.netlify.app/index.css</code></a><code>");</code></p>
<p>It serves the CSS in all three components and maintains the styles for all three components.</p>
<h3 id="heading-step-3-hosting-the-project-css-file">Step 3: Hosting the Project CSS File</h3>
<p>Finally, I’ve arrived at the most crucial part of this project, where all the magic happens.</p>
<p>Currently, I have three CSS files for each of the web components: <strong>header</strong>, <strong>cards</strong>, and <strong>footer</strong>.</p>
<p>Since our project is small, I will combine all CSS file code into one CSS file to get one CSS file which will work as a universal CSS file.</p>
<p>The process of hosting the CSS file is straightforward. Here's a detailed breakdown of what you need to do:</p>
<h4 id="heading-1-push-your-code-to-github">1. <strong>Push Your Code to GitHub</strong></h4>
<p>You need to push (upload) your project files, including the HTML, CSS, and other assets, to a GitHub repository. Here’s how you can do that:</p>
<ol>
<li><p>Initialize a Git repository in your project directory using <code>git init</code>.</p>
</li>
<li><p>Add all your files using <code>git add .</code>.</p>
</li>
<li><p>Commit the files with <code>git commit -m "Initial commit"</code>.</p>
</li>
<li><p>Link to a GitHub repository you’ve created using <code>git remote add origin &lt;repo-url&gt;</code>.</p>
</li>
<li><p>Finally, push your code using <code>git push -u origin main</code>.</p>
</li>
<li><p><strong>Result</strong>: Your project files will now be hosted in your GitHub repository.</p>
</li>
</ol>
<h4 id="heading-2-open-netlify-and-log-in-or-sign-up">2. <strong>Open Netlify and Log In or Sign Up</strong></h4>
<p>Then, visit <a target="_blank" href="https://www.netlify.com">Netlify</a> and either sign in if you already have an account or create a new one.</p>
<p>You can sign up using your GitHub credentials or a separate email. This step gives you access to Netlify's web hosting services, which will allow you to deploy your project directly from GitHub.</p>
<h4 id="heading-3-connect-your-github-repository-that-contains-your-code">3. <strong>Connect Your GitHub Repository That Contains Your Code</strong></h4>
<p>Once logged in, you'll connect your GitHub repository to Netlify.</p>
<ol>
<li><p>On Netlify, click on "New site from Git".</p>
</li>
<li><p>Choose <strong>GitHub</strong> as the source.</p>
</li>
<li><p>Authorize Netlify to access your GitHub account.</p>
</li>
<li><p>Select the repository that contains your project from the list.</p>
</li>
<li><p>Configure build settings if necessary (though for simple static sites, Netlify automatically detects them).</p>
</li>
</ol>
<h4 id="heading-4-click-deploy">4. <strong>Click "Deploy"</strong></h4>
<p>After connecting your repository, Netlify will display a "Deploy" button.</p>
<ol>
<li><p>Click "Deploy" to trigger the build and deployment process.</p>
</li>
<li><p>Netlify will pull your code from GitHub, build the site (if needed), and deploy it to a live URL.</p>
</li>
</ol>
<p>Your project is now live on the web, and you’ll be provided with a URL where you can access the deployed site.</p>
<h4 id="heading-5-access-the-deployed-url-and-append-the-css-file-url">5. <strong>Access the Deployed URL and Append the CSS File URL</strong></h4>
<p>You’ll access the deployed site by visiting the URL provided by Netlify and directly referencing the CSS file you uploaded.</p>
<ol>
<li><p>Once your site is deployed, note down the provided URL (for example, <a target="_blank" href="https://example.netlify.app"><code>https://example.netlify.app</code></a>).</p>
</li>
<li><p>To access a specific CSS file, append the file name to the URL, for example: <a target="_blank" href="https://example.netlify.app/styles.css"><code>https://example.netlify.app/styles.css</code></a>.</p>
</li>
<li><p>Here, <code>styles.css</code> is the name of your CSS file that you uploaded to GitHub and deployed via Netlify.</p>
</li>
</ol>
<p>This will allow you to view or reference the CSS file directly through a public URL.</p>
<p>This process essentially helps you host your project and its assets on Netlify, allowing easy access to any file (like <code>filename.css</code>) that you uploaded to GitHub. You can use these public links in your projects or share them.</p>
<p><strong>And that’s it! Link this URL to your project's CSS file.</strong></p>
<p>I have hosted the main CSS file on the Netlify app so that it can be accessed anywhere simply by importing it into your project. Here is the URL of my hosted CSS file: <code>https://hashnodeblogchallenge.netlify.app/index.css</code>.</p>
<p>The beauty of component libraries is that they allow you to focus on development rather than design.</p>
<h2 id="heading-faq">FAQ</h2>
<h3 id="heading-what-benefits-have-we-gained-from-this">What Benefits Have We Gained from This?</h3>
<p>Now, you simply need to copy the HTML code and import the CSS file into your project. Here are the key benefits:</p>
<ul>
<li><p><strong>Reduces time</strong> spent on repetitive CSS coding.</p>
</li>
<li><p>Provides <strong>greater control</strong> over components, allowing for customization based on your needs.</p>
</li>
<li><p>Offers <strong>real-time experience</strong> with HTML and CSS, enabling you to learn core concepts effectively.</p>
</li>
</ul>
<h3 id="heading-what-if-i-want-to-change-something">What if I Want to Change Something?</h3>
<p>This is straightforward. Just edit your CSS file and update the header colour from black to blue where you have declared that header class or ID.</p>
<h3 id="heading-what-if-i-want-to-create-more-components">What if I Want to Create More Components?</h3>
<p>You can create as many components as you need! Just store the style code in the same hosted CSS file, and everything will work seamlessly.</p>
<h3 id="heading-how-does-this-save-me-time">How Does This Save Me Time?</h3>
<p>Imagine you need to create 5 websites, each with 5 pages (that’s a total of 25 pages). If you identify common elements, such as headers and footers, that will be used across all 25 pages, you can avoid writing 25 separate components. Instead, you can simply use the components from your library—just copy and paste the HTML and add the CSS file.</p>
<h3 id="heading-what-lets-the-entire-app-function-with-just-one-line-of-css">What Lets the Entire App Function with Just One Line of CSS?</h3>
<p>The concept is quite simple and can be broken down into the following steps:</p>
<ol>
<li><p><strong>Create components</strong> based on your requirements.</p>
</li>
<li><p><strong>Control their design</strong> with CSS and apply the necessary properties.</p>
</li>
<li><p><strong>Host your main CSS file</strong> somewhere to obtain a new URL, which you can use to import your CSS styles in HTML.</p>
</li>
</ol>
<p><strong>Now you can calculate how much time you have saved.</strong></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Creating a component library using <strong>HTML</strong> and <strong>CSS</strong> allows you to build beautiful web pages quickly and effectively.</p>
<p>It also provides you with a deep understanding of HTML and CSS, which are essential skills for a successful software development career. With these skills, you'll be able to create engaging layouts without spending excessive time coding from scratch.</p>
<p>To help you get started, here’s an example of a component library I developed, which includes 10+ beautiful components: <a target="_blank" href="https://slateui.netlify.app">SlateUI</a>.</p>
<p>Now, you simply need to copy the HTML code and paste it where you want to display your components, along with importing your CSS file URL into that HTML file.</p>
<p>So guys this is the end from my side. If you find this article useful, then do share it and connect with me – I am open to opportunities:</p>
<ul>
<li><p>Follow Me on X: <a target="_blank" href="https://x.com/prankurpandeyy">Prankur's Twitter</a></p>
</li>
<li><p>Follow me on LinkedIn: <a target="_blank" href="https://linkedin.com/in/prankurpandeyy">Prankur's Linkedin</a></p>
</li>
<li><p>Look at my Portfolio here: <a target="_blank" href="https://prankurpandeyy.netlify.app/">Prankur's Portfolio</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use CSS to Improve Web Accessibility ]]>
                </title>
                <description>
                    <![CDATA[ Did you know that CSS can play a significant role in web accessibility? While CSS primarily handles the visual presentation of a webpage, when you use it properly it can enhance the user’s experience and improve accessibility. In this article, I'll s... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-css-to-improve-web-accessibility/</link>
                <guid isPermaLink="false">66eb0a282e5bd4267b92ca10</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ a11y ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webdev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elizabeth Lola ]]>
                </dc:creator>
                <pubDate>Wed, 18 Sep 2024 17:13:12 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726577970240/02631676-6492-4b83-a057-b9c2048709ee.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Did you know that CSS can play a significant role in web accessibility? While CSS primarily handles the visual presentation of a webpage, when you use it properly it can enhance the user’s experience and improve accessibility.</p>
<p>In this article, I'll share some ways CSS can support accessibility so you can start using these techniques in your own projects.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>To follow along with this tutorial, you should have a basic understanding of HTML, CSS, and a little bit of Javascript.</p>
<h2 id="heading-update-focus-styles">Update Focus Styles</h2>
<p>The browser provides default focus styles for interactive elements like buttons or input fields. But sometimes these default focus styles might not be ideal for your design system – especially if the colors used in your design are too close to the default colors. This might make it difficult to notice.</p>
<p>Also, different browsers have different default focus styles and you might want to standardize the focus styles to ensure uniformity.</p>
<p>You can change the default focus style of an element in CSS using the <code>:focus</code> pseudo-class. For example, the default focus style for an input element is a blue outline in Chrome and a blue outline with outline offset in Firefox, to update the default focus styles of an input element you can do this:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span> {
  <span class="hljs-attribute">outline</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#007BFF</span>;
  <span class="hljs-attribute">outline-offset</span>: <span class="hljs-number">2px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">1rem</span>;
}
</code></pre>
<h2 id="heading-avoid-content-shifts">Avoid Content Shifts</h2>
<p>Content shifts can happen when you’re lazy loading images, where images load progressively as the user scrolls down the page. Sometimes the image pushes the content around it downwards, shifting the text you're reading out of place.</p>
<p>Content shifts can also happen during dynamic content fetching, especially when new content like text, images, or ads is added to the page without reserving space for it in advance.</p>
<p>Content shifts can be frustrating, especially for users:</p>
<ul>
<li><p>With cognitive disabilities who may lose track of where they are in the content.</p>
</li>
<li><p>Using screen magnifiers, where the shift can cause them to lose their zoomed-in focus.</p>
</li>
<li><p>Navigating with a keyboard, as it can mess up the natural tab order and make navigation confusing.</p>
</li>
</ul>
<p>You can pre-allocate space for content to prevent shifts by using the <code>min-height</code> or <code>aspect-ratio</code> properties. Here's how you can allocate space for an image to prevent content shift before the image has fully loaded.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">height</span>: auto;
    <span class="hljs-attribute">aspect-ratio</span>: <span class="hljs-number">16</span>/<span class="hljs-number">9</span>;
    <span class="hljs-attribute">object-fit</span>: cover; <span class="hljs-comment">/* Ensures the image fits well within the allocated space */</span>
}
</code></pre>
<p>You can also use animations or transitions when dynamically loading content to add smooth transitions for new content. So, instead of a sudden shift, the content slides in gracefully, reducing the perception of disruption.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.new-content</span> {
    <span class="hljs-attribute">transition</span>: margin <span class="hljs-number">0.3s</span> ease-in-out, opacity <span class="hljs-number">0.3s</span> ease-in-out;
}
</code></pre>
<h2 id="heading-reduce-motion">Reduce Motion</h2>
<p>Rapid animations or really complex transitions can be disorienting for users with motion sensitivity, which could lead to discomfort like headaches, dizziness, or vertigo (for users with vestibular disorders).</p>
<p>You can use CSS’s <code>prefers-reduced-motion</code> media query to reduce or disable animations for users.</p>
<p>Personally, instead of disabling animations completely, I replace complex, distracting animations with more subtle ones to maintain functionality while respecting user preferences.</p>
<p>Here's how to use <code>prefers-reduced-motion</code> to create a simpler animation:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Default animation */</span>
<span class="hljs-keyword">@keyframes</span> complexAnimation {
    0% { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(<span class="hljs-number">0</span>); <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>; }
    50% { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(<span class="hljs-number">100px</span>); <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0.5</span>; }
    100% { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(<span class="hljs-number">0</span>); <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>; }
}

<span class="hljs-selector-class">.element</span> {
    <span class="hljs-attribute">animation</span>: complexAnimation <span class="hljs-number">2s</span> ease-in-out;
}

<span class="hljs-comment">/* Simpler animation for reduced motion preference */</span>
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-reduced-motion:</span> reduce) {
    <span class="hljs-keyword">@keyframes</span> simpleAnimation {
        0% { <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>; }
        100% { <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>; }
    }

    <span class="hljs-selector-class">.element</span> {
        <span class="hljs-attribute">animation</span>: simpleAnimation <span class="hljs-number">1s</span> ease-in-out;
    }
}
</code></pre>
<p>Here’s an example from the code above. If you have reduced motion enabled you’ll see a fading ball instead of a moving ball:</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/leezee/embed/preview/PorrrQW?default-tab=result&amp;editable=true" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<p><strong>Note</strong>: If you want to see the reduced motion in action, you can enable it in the <a target="_blank" href="https://developer.chrome.com/docs/devtools/rendering">rendering tab on Google Chrome</a>.</p>
<h2 id="heading-focus-within-for-nested-elements">Focus Within for Nested Elements</h2>
<p>You can highlight or style a parent element when any of its child elements receive focus to make it clear which group (like form inputs or dropdown menus) is currently being interacted with.</p>
<p>To do this, you can use CSS’s <code>:focus-within</code> pseudo-class which is used to style an element when any of its descendants receive focus either through keyboard navigation or user interaction.</p>
<p>For example, to highlight a fieldset when any item in the group is focused in a grouped control, you can do this:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
 <span class="hljs-selector-tag">fieldset</span> {
   <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
   <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
 }

 <span class="hljs-selector-tag">fieldset</span><span class="hljs-selector-pseudo">:focus-within</span> {
   <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#007BFF</span>; <span class="hljs-comment">/* highlight the fieldset when a user focuses on any input */</span>
 }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">fieldset</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">legend</span>&gt;</span>Choose a color:<span class="hljs-tag">&lt;/<span class="hljs-name">legend</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"radio"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"color"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"red"</span>&gt;</span> Red<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"radio"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"color"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"green"</span>&gt;</span> Green<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"radio"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"color"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"blue"</span>&gt;</span> Blue<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">fieldset</span>&gt;</span>
</code></pre>
<h2 id="heading-customize-contrast-options">Customize Contrast Options</h2>
<p>Sometimes you may be working on a design that uses lots of colors and might not maintain high contrast between text and background to fit an aesthetic. Or perhaps you're working on a design with lots of bright colors. In these cases, you should consider how your application renders for different users.</p>
<p>Some users with low vision or certain types of color blindness might need high contrast mode to differentiate text from the background more clearly. Other users sensitive to bright colors might prefer a softer, less jarring visual experience.</p>
<p>Some of these users might have their systems set to high or low contrast to help improve their experience. To customize their experience, you can use the CSS <code>prefers-contrast</code> media query.</p>
<p>The <code>prefers-contrast</code> media query allows you to tailor the contrast of your website or application based on the user's system settings.</p>
<p>Here's an example of using <code>prefers-contrast</code>:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* default styling preference */</span>
<span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background-color</span>: white;
    <span class="hljs-attribute">color</span>: black;
}

<span class="hljs-comment">/* high contrast preference */</span>
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-contrast:</span> more) {
    <span class="hljs-selector-tag">body</span> {
        <span class="hljs-attribute">background-color</span>: black;
        <span class="hljs-attribute">color</span>: white;
    }
    <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">color</span>: yellow;
    }
}
<span class="hljs-comment">/* low contrast preference */</span>

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-contrast:</span> less) {
    <span class="hljs-selector-tag">body</span> {
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f0f0f0</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
    }
    <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#555</span>;
    }
}
</code></pre>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/leezee/embed/preview/dyBBxgV?default-tab=result&amp;editable=true" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<p>In the example above, the <code>prefers-contrast: more</code> option ensures that when a user prefers high contrast, the background is black and the text is white, with yellow links for better visibility.</p>
<p>The <code>prefers-contrast: less</code> adjusts the color scheme to a softer color for users who prefer less contrast. The default style is used if the user has no specific contrast preference or if their preference is not detected.</p>
<p><strong>Note</strong>: If your design uses minimal colors and maintains high contrast between text and background or you're working with a design where text is minimal and the focus is on visual content (like image galleries or video players), you might not need <code>prefers-contrast</code> as much. But it's still good practice to consider contrasts.</p>
<h2 id="heading-enable-dark-mode">Enable Dark Mode</h2>
<p>You can use CSS to accommodate users’ preferences for dark or light modes. You can achieve this through the CSS <code>prefers-color-scheme</code> media query. The browser can detect the user's color preference and apply the style if provided in CSS.</p>
<p>Here's an example of how you can add a dark mode style to your site using CSS variables:</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--background-color</span>: <span class="hljs-number">#ffffff</span>;
  <span class="hljs-attribute">--text-color</span>: <span class="hljs-number">#000000</span>;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-color-scheme:</span> dark) {
  <span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attribute">--background-color</span>: <span class="hljs-number">#000000</span>;
    <span class="hljs-attribute">--text-color</span>: <span class="hljs-number">#ffffff</span>;
  }
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--background-color);
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--text-color);
}
</code></pre>
<p>In the example above, the variables get updated if the browser detects a dark color scheme preference.</p>
<p>If you want to allow users to toggle between modes manually, you can use JavaScript for this:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
 <span class="hljs-comment">/* Default light mode styles */</span>
  <span class="hljs-selector-tag">body</span> {
   <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#ffffff</span>;
   <span class="hljs-attribute">color</span>: <span class="hljs-number">#000000</span>;
  }
 <span class="hljs-comment">/* Dark mode styles */</span>
  <span class="hljs-selector-tag">body</span><span class="hljs-selector-class">.dark-mode</span> {
   <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#000000</span>;
   <span class="hljs-attribute">color</span>: <span class="hljs-number">#ffffff</span>;
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"toggle-theme"</span>&gt;</span>Toggle Theme<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">const</span> toggleButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'toggle-theme'</span>);
  toggleButton.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> {
   <span class="hljs-built_in">document</span>.body.classList.toggle(<span class="hljs-string">'dark-mode'</span>);
  });
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h2 id="heading-use-rem-units-for-responsive-typography">Use <code>rem</code> Units for Responsive Typography</h2>
<p>Using <code>rem</code> units for responsive typography can help enhance accessibility to adapt more dynamically to a user's preference. Since <code>rem</code> is relative to the root font size (typically set by the browser or user), it scales with changes in the base font size. This helps ensure that text remains readable without breaking layouts.</p>
<p>Users can set a preferred font size in their browser or operating system for better readability. When you use <code>rem</code>, the website content scales according to this setting which ensures that the text is not too small or too large for the users (which can happen when using fixed units like <code>px</code>).</p>
<p>When users zoom in using browser settings or increase their preferred text size, the <code>rem</code>-based text will scale appropriately.</p>
<p>The default root font size (usually 16px) is typically inherited from the browser, but you can set it explicitly if needed:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">100%</span>; <span class="hljs-comment">/* Default 16px */</span>
}
</code></pre>
<p>After setting the root font size, you can use <code>rem</code> unit for the rest of your content. For example:</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-comment">/* Equivalent to 40px if root is 16px */</span>
}

<span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>; <span class="hljs-comment">/* Equivalent to 16px */</span>
}
</code></pre>
<h2 id="heading-use-animations-to-enhance-ux">Use Animations to Enhance UX</h2>
<p>CSS animations can enhance accessibility when used thoughtfully. They can help create an engaging and understandable experience for users.</p>
<p>Here are some ways that animations can help improve accessibility:</p>
<ul>
<li><p>You can use animations to indicate loading state to visually communicate to users that the system is working on a task.</p>
</li>
<li><p>Using animated text effects, like fades or scaling on headlines or important sections, can help guide users' eyes to important content. This can be useful for people with cognitive disabilities who benefit from clear visual hierarchies.</p>
</li>
<li><p>Subtle transitions for state change instead of having abrupt changes (like a modal popping up instantly) can create smoother transitions between different interface states.</p>
</li>
<li><p>Using animated highlights or shaking effects on form fields can provide visual feedback to users about input errors. You should pair these animations with labels or ARIA attributes to make it clear what the user needs to correct.</p>
</li>
<li><p>Animations can help users track focus, especially keyboard users or those with visual impairments. CSS transitions that highlight focused elements (for example by enlarging buttons or changing the border) assist users in understanding where they are within the page.</p>
</li>
</ul>
<h3 id="heading-best-practices">Best Practices:</h3>
<ul>
<li><p>Ensure animations are used purposefully, not just for aesthetic reasons.</p>
</li>
<li><p>Avoid overly long or continuous animations that can distract or annoy users.</p>
</li>
<li><p>Combine animations with other accessible features, such as screen reader announcements, to ensure all users understand content changes.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>When considering accessibility, well-structured HTML forms the foundation of an accessible page – but CSS also plays a vital role in enhancing that structure.</p>
<p>CSS alone cannot fix poorly structured HTML. But when it’s applied thoughtfully to a solid foundation, it ensures a more inclusive and engaging experience by improving visual hierarchy, readability, and interaction for users of all abilities.</p>
<p>Combining accessible HTML with CSS not only improves the user interface but also provides support for assistive technologies.</p>
<p>Thank you so much for reading this article. If you found it helpful, consider sharing. Happy coding!</p>
<p>You can connect with me on <a target="_blank" href="https://www.linkedin.com/in/elizabeth-meshioye/">LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
