<?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[ UI - 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[ UI - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 27 May 2026 20:46:38 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/ui/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Apply Academic Theories to Human-Centered Web Design [Full Handbook] ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever abandoned an app right at the sign‑up page? Or felt uneasy navigating a website because the buttons were scattered randomly, the colors clashed, and the layout felt confusing and unneces ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-apply-academic-theories-to-human-centered-web-design-handbook/</link>
                <guid isPermaLink="false">69fe29e9f239332df4f7cd02</guid>
                
                    <category>
                        <![CDATA[ Frontend Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UX ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ux design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MathJax ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Great John ]]>
                </dc:creator>
                <pubDate>Fri, 08 May 2026 18:22:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/d7621fda-83a6-460e-aa38-bce970d4a655.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever abandoned an app right at the sign‑up page? Or felt uneasy navigating a website because the buttons were scattered randomly, the colors clashed, and the layout felt confusing and unnecessarily complex?</p>
<p>Maybe you were asked to complete twenty fields in one go. You carefully filled everything out, hit Submit — and only then were you told that your password didn't meet some hidden, unspoken requirement. A requirement that was never communicated upfront.</p>
<p>Instead of helpful guidance, you were met with a vague message: “Invalid input." Invalid how, you wonder?</p>
<p>Required fields weren’t marked. There was no real‑time validation. No helpful red outline showing which field was wrong. Just a generic prompt telling you to “go back and correct missing information,” as if you’re supposed to magically know what the system wants.</p>
<p>So you scroll.</p>
<p>You search.</p>
<p>You guess.</p>
<p>And you're now getting frustrated.</p>
<p>The reason you're frustrated is simple: no one enjoys repeating a task they thought they had already completed — especially when the mistakes could've been prevented with clear guidance along the way.</p>
<p>You manage to fill in the form and you tap the Submit button.</p>
<p>Nothing happens.</p>
<p>No loading spinner.</p>
<p>No subtle animation.</p>
<p>No confirmation message.</p>
<p>No success screen.</p>
<p>Just silence. For a brief moment, you’re left wondering: Did it go through? So you tap again. And maybe… one more time.</p>
<p>At this point, you become fed up and you either postpone the signup process to when you have the time, or you may not ever return.</p>
<p>Even if you haven’t experienced this exact scenario, you’ve almost certainly felt the same kind of friction: that moment when a digital interface makes you pause, hesitate, or wonder what you’re supposed to do next.</p>
<p>These frustrations often arise because frontend developers either overlook or are unaware of the essential design principles and theories that underpin a smooth, intuitive user experience.</p>
<p>As a frontend developer, your interface should minimise cognitive load, provide immediate clarity, and guide users effortlessly through every task.</p>
<p>In this handbook, I'll introduce the academic theories that should inform and elevate your frontend decisions.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-10-fittss-law">1.0 Fitts’s Law:</a></p>
<ul>
<li><p><a href="#heading-11-use-padding-wisely">1.1 Use padding wisely</a></p>
</li>
<li><p><a href="#heading-12-use-infinite-targets">1.2 Use infinite targets</a></p>
</li>
<li><p><a href="#heading-design-takeaway-from-fitts-law">Design Takeaway from Fitts Law:</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-20-hicks-law">2.0 Hick's Law:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-hicks-law">Design Takeaway from Hick's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-30-gestalt-principles">3.0 Gestalt Principles:</a></p>
<ul>
<li><p><a href="#heading-key-gestalt-principles">Key Gestalt Principles:</a></p>
</li>
<li><p><a href="#heading-31-proximity">3.1 Proximity</a></p>
</li>
<li><p><a href="#heading-32-similarity">3.2 Similarity</a></p>
</li>
<li><p><a href="#heading-33-continuity">3.3 Continuity</a></p>
</li>
<li><p><a href="#heading-34-closure">3.4 Closure</a></p>
</li>
<li><p><a href="#heading-35-figureground">3.5 Figure/Ground</a></p>
</li>
<li><p><a href="#heading-36-common-fate">3.6 Common Fate</a></p>
</li>
<li><p><a href="#heading-37-focal-point">3.7 Focal Point</a></p>
</li>
<li><p><a href="#heading-design-takeaways-from-the-gestalt-principles">Design Takeaways from the Gestalt Principles</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-40-von-restorff-effect-the-isolation-effect">4.0 Von Restorff Effect (The Isolation Effect):</a></p>
<ul>
<li><a href="#heading-design-takeway-from-von-restorff">Design takeway from Von Restorff</a></li>
</ul>
</li>
<li><p><a href="#heading-50-jakobs-law">5.0 Jakob’s Law</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-jakobs-law">Design Takeaway from Jakob's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-60-millers-law">6.0 Miller’s Law</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-millers-law">Design Takeaway from Miller's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-70-the-goal-gradient-hypothesis">7.0 The Goal-Gradient Hypothesis</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-goal-gradient-hypothesis">Design Takeaway from Goal-Gradient Hypothesis</a></li>
</ul>
</li>
<li><p><a href="#heading-80-zeigarnik-effect">8.0 Zeigarnik Effect</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-zeigarnik-effect">Design Takeaway from Zeigarnik Effect</a></li>
</ul>
</li>
<li><p><a href="#heading-90-teslas-law">9.0 Tesla’s Law:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-teslas-law">Design Takeaway from Tesla's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-100-peak-end-rule">10.0 Peak End Rule:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-peak-end-rule">Design takeaway from Peak End Rule</a></li>
</ul>
</li>
<li><p><a href="#heading-110-postels-law">11.0 Postel’s Law:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-postels-law">Design Takeaway from Postel's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-120-doherty-threshold">12.0 Doherty Threshold:</a></p>
<ul>
<li><a href="#heading-design-takeaways-from-doherty-threshold">Design Takeaways from Doherty Threshold</a></li>
</ul>
</li>
<li><p><a href="#heading-130-serial-position-effect-primacy-and-recency">13.0 Serial Position Effect (Primacy and Recency):</a></p>
<ul>
<li><a href="#heading-design-takeaways-serial-position-effect">Design Takeaways Serial Position Effect</a></li>
</ul>
</li>
<li><p><a href="#heading-140-occams-razor">14.0 Occam’s Razor:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-occams-razor">Design Takeaway from Occam's Razor</a></li>
</ul>
</li>
<li><p><a href="#heading-150-parkinsons-law">15.0 Parkinson's Law</a></p>
<ul>
<li><a href="#heading-design-takeaway-for-parkinsons-law">Design Takeaway for Parkinson's law</a></li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a href="#heading-references">References</a></p>
</li>
</ul>
<p>You might wonder what academic theories have to do with frontend development.</p>
<p>The answer is simple. Academic theories aren't abstract ideas. There are the result of rigorous scientific investigation — controlled experiments, validated models, and decades of research into how humans think, learn, perceive, and interact with information.</p>
<p>Because these theories are grounded in evidence rather than opinion, they offer reliable guidance for building interfaces that align with how the human brain actually processes information.</p>
<p>Applying them to frontend development means you're not designing by guesswork or personal preference. Instead, you're applying tested, scientific insights to create clearer, faster, more humane user experiences.</p>
<p>In other words, when you build with academic theory in mind, your frontend becomes more than just visually appealing — it becomes cognitively efficient, behaviourally aligned, and measurably easier for users to navigate.</p>
<p>You can use the following laws and principles to guide your development work. Let’s start by looking at Fitt’s law.</p>
<h2 id="heading-10-fittss-law"><strong>1.0 Fitts’s Law:</strong></h2>
<p>Fitts’s law is the brainchild of Paul Fitts. He was among the early psychologists who recognised that many human errors result from flawed design rather than simple human weakness.</p>
<p>During World War II, he studied airplane cockpit layouts and concluded that numerous incidents attributed to pilot error were actually caused by poor design decisions (Hall, 2023; Budiu, 2022).</p>
<p>Here's the formula:</p>
<p>$$T = a + b \cdot \log_2\left(1 + \frac{D}{W}\right)$$</p>
<p>T = Movement Time</p>
<p>D = Distance to the target</p>
<p>W = Width (size) of the target</p>
<p>a, b = Empirically determined constants</p>
<p>Based on his findings, Fitts postulated that the time required to acquire/reach a target is determined by the distance to the target and the size of the target.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/7d2699bd-4878-4ab0-8669-21616cb8faf1.png" alt="7d2699bd-4878-4ab0-8669-21616cb8faf1" style="display:block;margin:0 auto" width="691" height="244" loading="lazy">

<p><em>Fig 1.0: Illustration of Fitts Law.</em></p>
<p>From the above, between Target B and Target C, it will be faster to interact with Target C than Target B simply because of the distance (Target B is farther away). Interestingly, though Target A and Target C are at the same distance, Target C will still be faster to interact with and less error-prone because of its larger size.</p>
<p>In simple terms, Fitt’s Law tells us that the time required to move to a target depends on two main factors: the distance to the target and the size of the target. The farther away an element is, the longer it takes to reach. The smaller it is, the more precision it demands, which increases the interaction time and the likelihood of errors.</p>
<p>Conversely, closer and larger targets reduce cognitive load, motor effort, and frustration.</p>
<p>In a nutshell, Fitts’s main message to developers is to reduce the distance users must travel on the screen and to make important buttons large and visually dominant.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/bc293949-cadc-46b2-892d-bdfa1dfc6db3.jpg" alt="bc293949-cadc-46b2-892d-bdfa1dfc6db3" style="display:block;margin:0 auto" width="928" height="664" loading="lazy">

<p><em>Fig 1.1: Showing Call-to-Action buttons are the largest and most visually prominent elements on each screen.</em></p>
<p>From the image above, you can see that the Call-to-Action buttons on each of the screens are the most visually dominant button and largest in size. They're also placed within the natural region. This makes them faster/easier to interact with.</p>
<p>You should also place your Call-to-Action button within the natural zone. This is a zone on a mobile phone where it's easy to reach with the thumb (as most people use their thumbs to select things on a phone screen). Here's a diagram showing the "natural zone" on a typical smartphone. It's much faster for a user to interact within the "natural zone" than the "hard zone" (see figure).</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/e98d8d91-b4e6-4b4a-96e5-2524a6e09028.png" alt="e98d8d91-b4e6-4b4a-96e5-2524a6e09028" style="display:block;margin:0 auto" width="1536" height="1024" loading="lazy">

<p><em>Fig 1.2: Showing three different zones for buttons placement (natural, stretching and hard region)</em></p>
<h3 id="heading-11-use-padding-wisely">1.1 Use Padding Wisely</h3>
<p>Fitts' law can be applied to your development by increasing padding wisely. You can also use padding to increase the interactive area. By doing this, you're increasing the size of the targets.</p>
<p>This is important, because imagine a menu that disappears the moment your cursor drifts a few inches away. You’re weren't trying to close it — you simply moved slightly, and suddenly the entire menu collapses. That tiny slip forces you to start the interaction all over again. It’s a small mistake, but it creates a disproportionately frustrating experience.</p>
<p>This happens because the interactive area is too narrow.</p>
<p>That’s why effective padding — or more broadly, generous interactive zones — is essential. By increasing the clickable or hoverable area around a menu, you are increasing the size of the targets, which makes the interaction more stable, more forgiving, and far less cognitively demanding.</p>
<p>This ensures users can move naturally without fear of accidentally “falling off” the target.</p>
<h3 id="heading-12-use-infinite-targets">1.2 Use Infinite Targets</h3>
<p>Another fundamental principle that emerges from Fitt’s Law is the idea of infinite targets. When an interface element is placed at the very edge or corner of a screen, it becomes effectively “infinite” because the cursor can't move beyond the screen boundary. The edge acts as a physical barrier, allowing the user to fling the mouse in that direction without precision or careful aiming.</p>
<p>As a result, corners and edges become the fastest, easiest, and most reliable places for users to access important controls.</p>
<p>This is why operating systems such as Apple’s macOS and Microsoft Windows position their most essential menus and buttons at these locations. The macOS Apple Menu sits in the top‑left corner, Windows historically placed the Start button in the bottom‑left corner, and both systems anchor taskbars, docks, and notification areas along screen edges.</p>
<p>These placements reduce cognitive load, minimise motor effort, and increase interaction speed because users do not need to slow down or correct their cursor movement. The screen itself “catches” the pointer.</p>
<p>In essence, infinite targets transform small interface elements into large, easy‑to‑hit zones simply by leveraging the geometry of the screen.</p>
<p>What this means for you: place your most important and frequently used actions where users can reach them with the least effort. Screen edges and corners act as natural stopping points, meaning users can't overshoot them.</p>
<h3 id="heading-design-takeaways-from-fitts-law">Design Takeaways from Fitts Law:</h3>
<p><strong>Place Primary Actions Where the Task Ends:</strong><br>Placing a submit button at the top‑right forces users to travel all the way back after completing a long form. This increases interaction cost and breaks flow. The best place for a submit button is at the bottom of the form — exactly where the user finishes the task. This aligns with natural reading and interaction patterns.</p>
<p><strong>Keep Related Actions Physically Close:</strong><br>Separating “Add to Cart” and “Check Out” across opposite sides of the screen forces unnecessary thumb movement. Group related actions to reduce effort and speed up decisions.</p>
<p><strong>Make Primary Targets Large and Visually Dominant:</strong><br>Your main CTAs (“Subscribe Now,” “Pay Now,” “Create Account,” “Sign Up”) should be the most recognisable elements on the screen. Large, high‑contrast targets reduce errors and improve speed.</p>
<p><strong>Place High‑Value Actions at Screen Edges and Corners:</strong><br>Edges and corners act as “infinite targets” because the cursor can’t overshoot them. This makes them the fastest, easiest, and most reliable places for critical controls.</p>
<p>A tiny icon in the middle of the screen is hard to hit. The same icon placed at an edge becomes effectively huge because the boundary “catches” the pointer. Also, actions like navigation, primary CTAs, or global controls should live where users can reach them with minimal effort. Avoid burying important actions in the centre of the screen.</p>
<p><strong>Increase Target Size With Generous Padding:</strong><br>Small interactive zones force users to aim with pixel‑level precision. Adding padding expands the clickable or hoverable area, making interactions easier, faster, and more forgiving.</p>
<p><strong>Prevent Accidental “Fall‑Off” With Larger Hit Areas:</strong><br>Menus that collapse the moment the cursor drifts slightly create frustration. A wider interactive zone keeps the menu open during natural mouse movement, reducing accidental resets.</p>
<p>Users don’t move perfectly. Interfaces should accommodate slight slips without punishing them. Larger targets reduce cognitive load and eliminate unnecessary frustration. so by increasing the effective size of buttons, menus, and controls, you create interactions that feel stable and predictable, and users can move confidently without fear of losing their place.</p>
<p><strong>To Sum Up:</strong> The farther away an element is, the longer it takes to reach. The smaller it is, the more precision it demands, which increases the interaction time and the likelihood of errors. Conversely, closer and larger targets reduce cognitive load, motor effort, and frustration.</p>
<h2 id="heading-20-hicks-law"><strong>2.0 Hick's Law</strong>:</h2>
<p>Hick’s Law is a psychological principle that describes the relationship between the number of choices presented to a user and the time it takes them to make a decision. It was formulated by William Edmund Hick in 1952 (Yablonski, 2022; Proctor &amp; Scheider, 2018).</p>
<p>The law states that as the number of options increases, the decision time increases logarithmically. In simple terms, more choices slow users down, while fewer choices speed up decision-making.</p>
<p>$$T = a + b \cdot \log_2(n + 1)$$</p>
<p>Where:</p>
<p>T = time to make a decision,</p>
<p>n = number of choices,</p>
<p>b= a constant that depends on the task and the individual</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/d2c45f4b-77e9-42dc-a9c3-165dfe5f7ce7.jpg" alt="d2c45f4b-77e9-42dc-a9c3-165dfe5f7ce7" style="display:block;margin:0 auto" width="1136" height="692" loading="lazy">

<p><em>Figure 2.0 illustrates the relationship between user experience, reaction time, and the number of actions.</em></p>
<p>This is how users feel, for example, when they encounter a form that asks for too much information upfront. The longer the form gets, the more frustrated they become.</p>
<p>Examples of this are overloading menus with too many items, presenting long, unorganised forms, giving too many calls-to-action on one screen, and building nested menus with excessive depth.</p>
<p>All of these create friction and can lead to cognitive overload.</p>
<h3 id="heading-design-takeaway-from-hicks-law">Design Takeaway from Hick's Law</h3>
<p><strong>Avoid Overloading Users With Too Many Actions:</strong><br>Too many buttons, menu items, or choices at once increases cognitive load and slows decision‑making. Users freeze when everything competes for attention.</p>
<p><strong>Keep Navigation Clean and Focused:</strong><br>Cluttered menus hurt both usability and SEO. Search engines struggle to track overly complex navigation structures, and users struggle to find what matters.</p>
<p><strong>Use Progressive Disclosure to Reduce Complexity:</strong><br>Hide advanced or rarely used options under “More” or expandable sections. Reveal complexity only when the user needs it.</p>
<p><strong>Break Complex Tasks Into Smaller, Manageable Steps:</strong><br>Progressive disclosure works beautifully for multi‑step forms and decision flows. Smaller steps reduce overwhelm and improve completion rates.</p>
<p><strong>Group Related Options Into Logical Categories:</strong><br>Organising actions into meaningful clusters helps users process information faster. For example, placing “Edit” and “Delete” together leverages natural mental grouping.</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/mbYIfRxSkHs" 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><em>Video 2.0: Video description of Progressive Disclosure.</em></p>
<p>From the video above, instead of showing all the menu details at once, it is better to hide them initially. As you can see, the additional information only appears when the arrow down button is pressed. This approach prevents overwhelming the user and keeps the interface clean and focused.</p>
<p>You should also reduce decision anxiety, as too many choices create doubt and friction (as they say, the more you ask from a user, the less you get).</p>
<p>Beyond this, try to use recommended labels, show brief descriptions, provide visual previews, and use comparison tables wisely to show comparison between products especially when they have many characteristics. An example of a comparison table is shown below:</p>
<p><a href="https://drive.google.com/file/d/1sY-tb9W1QDnyrH9dd3NSYsteP9WoMT27/view?usp=drive_link"><img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/5e52527a-3faf-4da1-97c2-a2a09ca7af8b.jpg" alt="Use of comparison table to compare products" style="display:block;margin:0 auto" width="1038" height="972" loading="lazy"></a></p>
<p><em>Figure 2.1: A comparison table being used to simplify complex information.</em></p>
<p>Also, rather than showing advanced configuration options by default, display only the most commonly used settings. Advanced options can be hidden under an expandable section like “Advanced” or “More Settings. This makes your interface less cluttered and more visually organized.</p>
<p>And speaking of visual organization, this is the perfect moment to introduce Gestalt principles — the psychological rules that explain how users naturally group and interpret what they see.</p>
<p><strong>To Sum Up</strong>: As the number of options increases, the decision time increases logarithmically.</p>
<h2 id="heading-30-gestalt-principles"><strong>3.0 Gestalt Principles</strong>:</h2>
<p>In the 1920s, a group of German psychologists – Max Wertheimer, Kurt Koffka, and Wolfgang Köhlern – introduced what are now known as the Gestalt Principles. Their work sought to understand how humans perceive and interpret visual information (Bustamante, 2023).</p>
<p>The word “Gestalt” is German for “unified whole,” reflecting the core idea behind the theory: people naturally perceive objects as organised patterns and complete forms rather than as separate, disconnected parts.</p>
<p>These principles explain how the human mind structures visual elements to make sense of the world. Over time, they have become highly influential in fields such as design, user experience (UX), psychology, and data visualization, where understanding perception is critical.</p>
<h3 id="heading-key-gestalt-principles">Key Gestalt Principles:</h3>
<h3 id="heading-31-proximity">3.1 Proximity</h3>
<p>Elements that are placed close to each other are perceived as a group, while those spaced far apart are seen as separate. This is why labels are placed directly next to their corresponding input fields.</p>
<p>For example: In a blog feed, the "Title," "Author," and "Date" should have small margins between them (8px), while the space between one blog post card and the next should be much larger (40px). This tells the user's brain: "These three text strings belong to this specific post."</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/b68ab7f4-1b2e-47f1-8e8c-8ae8f32198c1.jpg" alt="b68ab7f4-1b2e-47f1-8e8c-8ae8f32198c1" style="display:block;margin:0 auto" width="1046" height="956" loading="lazy">

<p><em>Fig 3:0 Illustration of proximity (Gestalt Principle)</em></p>
<p>From the fig above, the spacing within the blog feed plays a powerful role in how effortlessly users interpret what they see. When elements sit close together, the brain instinctively treats them as belonging to the same unit. This is why placing the author credit just 8px beneath the title creates an immediate mental link. The viewer doesn’t need to pause or decode who wrote which article; proximity does the cognitive work automatically, forming a tight, intuitive grouping.</p>
<p>Equally important is the generous 40px gap between individual cards. This larger spacing introduces “visual breathing room.” Without it, a feed can quickly collapse into a dense wall of text, overwhelming the user and discouraging exploration. The wider margin establishes a clear boundary—a natural stop-and-start rhythm—that makes each card feel distinct and the entire layout more scannable.</p>
<p>Finally, subtle spacing differences can guide behaviour, not just perception. The slightly larger 12px margin above the read‑more link separates it from the passive information above it. This spacing cues the user that the link represents an action rather than another piece of descriptive text. It’s a small adjustment, but it shifts the element’s role from informational to interactive, helping users understand what they can <em>do</em> next.</p>
<p>Together, these spacing decisions transform a simple list of posts into a structured, intuitive, and behaviourally clear interface—one where the user never has to think about the layout, because the layout is already thinking for them.</p>
<p>Proximity controls meaning: move elements closer to show connection, separate them to show difference.</p>
<h3 id="heading-32-similarity">3.2 Similarity</h3>
<p>We naturally group elements that share similar visual characteristics, such as color, shape, size, or orientation.</p>
<p>For example, even if buttons are spread across a page, if they're all the same shade of blue, the user understands they perform similar functions.</p>
<p>If your primary "Submit" button is blue with rounded corners, every other primary action on your site should look exactly the same. If you suddenly use a square red button for a primary action, the user will be confused because the "similarity" is broken.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/151d9658-5a4b-4d1d-b885-02c73c53cdbd.jpg" alt="151d9658-5a4b-4d1d-b885-02c73c53cdbd" style="display:block;margin:0 auto" width="1618" height="2170" loading="lazy">

<p><em>Fig 3:1 : illustration of similarity (Gestalt principle)</em></p>
<p>As you can see from above, the layout clearly demonstrates how the Gestalt Principle of Similarity works by showing two different visual situations: one where everything matches, and one where a single element breaks the pattern.</p>
<p>All three product cards share the same visual characteristics:</p>
<ul>
<li><p>Same card shape</p>
</li>
<li><p>Same border and shadow</p>
</li>
<li><p>Same image size and placement</p>
</li>
<li><p>Same blue “Add to Cart” button</p>
</li>
<li><p>Same font style and spacing</p>
</li>
</ul>
<p>Because these elements look alike, your brain automatically groups them as one category — “products that belong together.”<br>You don’t have to think about it; the similarity creates instant visual unity.</p>
<p>This is the Gestalt Principle of Similarity in action.</p>
<p>In the second row, everything is still similar except one button:</p>
<ul>
<li><p>The middle product’s button is orange, not blue</p>
</li>
<li><p>It has square corners, not rounded</p>
</li>
<li><p>The text is italic, not regular</p>
</li>
<li><p>The label changes to “Quick Buy”</p>
</li>
</ul>
<p>Because this button breaks the shared pattern, your brain immediately notices it and treats it as different or special.</p>
<p>Developers can use broken similarity to intentionally highlight featured items, promotions, or urgent actions.</p>
<p>When similarity is broken, the different element stands out and draws attention.</p>
<h3 id="heading-33-continuity">3.3 Continuity</h3>
<p>The human eye prefers to follow a continuous path or curve rather than jagged or broken lines. We perceive items aligned on a line or curve as being related. This is often used in navigation menus or horizontal carousels to guide the user's gaze.</p>
<p>For example, you might have a horizontal carousel where the last visible card is slightly "cut off" at the edge of the screen. This visual break creates a path that encourages the user to keep scrolling as their eyes follow the line of cards.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/e44556e8-4174-4073-9c05-63b5df9a1278.jpg" alt="e44556e8-4174-4073-9c05-63b5df9a1278" style="display:block;margin:0 auto" width="1650" height="214" loading="lazy">

<p><em>Fig 3:2: illustration of continuity (Gestalt principle)</em></p>
<p>As you can see, all four form fields — <em>First Name</em>, <em>Last Name</em>, <em>Email Address</em>, and <em>Phone Number</em> — are perfectly aligned along one continuous horizontal path. Because the human eye naturally prefers to follow an unbroken line, your gaze moves smoothly from left to right across the fields without effort.</p>
<p>The final field is slightly cut off at the edge, which creates a subtle visual cue that the line continues beyond the visible area. This encourages the user to keep scrolling or swiping, because their eyes are already following the direction of the form.<br>when elements are arranged along a straight path, curve, or flow, the brain automatically treats them as connected and expects the pattern to continue.</p>
<p>Another example is Instagram Stories, which are arranged in a smooth horizontal line at the top of the app. Instagram reinforces this by slightly revealing the next story circle at the edge of the screen. That tiny “peek” acts as a continuation cue — your eyes expect the line to keep going, so your finger follows.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/bb019a6d-33c9-4509-b1c7-191b5a453c46.jpg" alt="bb019a6d-33c9-4509-b1c7-191b5a453c46" style="display:block;margin:0 auto" width="1320" height="520" loading="lazy">

<p><em>Fig 3:3: illustration of continuity (Gestalt principle)</em></p>
<p>As you can see from above, all the circular story icons are arranged in a straight horizontal line, and your visual system instinctively follows that line from left to right without effort.</p>
<p>The slight visibility of the next story at the edge of the screen strengthens this effect, signaling that the sequence continues beyond what's currently shown. Also, because the icons share the same size, spacing, and shape, there are no visual interruptions, allowing your eyes to glide across them in one continuous motion.</p>
<p>This seamless flow is exactly what continuity describes: the tendency of the human eye to follow the direction of a line or pattern, assuming it continues even when part of it is out of view.</p>
<p>Continuity is the tendency of the human eye to follow the direction of a line or pattern, assuming it continues even when part of it is out of view.</p>
<h3 id="heading-34-closure">3.4 Closure</h3>
<p>Closure refers to the mind’s ability to perceive a complete, unified form even when parts of that form are missing. Rather than requiring every boundary, line, or shape to be explicitly drawn, the brain instinctively fills in the gaps. When used intentionally, closure allows interfaces to feel cleaner, more elegant, and more cognitively efficient.</p>
<p>When we look at a complex arrangement of visual elements, we tend to look for a single, recognisable pattern. If an image is missing parts, our brains fill in the gaps to "close" the shape.</p>
<p>One of the most celebrated examples of closure in visual identity design is the panda symbol used by the World Wide Fund for Nature (WWF). This logo demonstrates how strategic omission can produce a memorable, emotionally resonant, and universally recognisable mark.</p>
<p>At first glance, the panda illustration appears simple, composed of a few bold black shapes arranged against a white background.</p>
<p>Yet a closer look reveals that the panda is not fully drawn. There are no outlines defining the body, no complete contours around the head, and no explicit boundaries separating limbs from background. Instead, the designer uses a series of carefully placed shapes (ears, eye patches, nose, and partial limbs) to imply the rest of the animal. The viewer’s mind fills in the missing information, completing the silhouette effortlessly.</p>
<p>This is closure at its most effective: the brain constructs a whole from fragments, creating a sense of completeness without visual overload.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/0c40effd-e4d2-4669-af1d-56faac976b4a.jpg" alt="0c40effd-e4d2-4669-af1d-56faac976b4a" style="display:block;margin:0 auto" width="522" height="784" loading="lazy">

<p><em>Fig 3:4: illustration of closure (Gestalt principle)</em></p>
<p>For example, a "hamburger menu" (three lines) isn't a literal drawer, but our brains "close" the shape to understand it represents a menu.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/b67253cd-2a2c-4565-9818-f5586dad6d36.jpg" alt="b67253cd-2a2c-4565-9818-f5586dad6d36" style="display:block;margin:0 auto" width="1244" height="236" loading="lazy">

<p><em>Fig 3:5: illustration of closure (Gestalt principle)</em></p>
<p>An example of closure in practice can be seen in step indicators commonly used in checkout flows. These components often rely on partial shapes, implied boundaries, and incomplete outlines to guide the user through a sequence of actions.</p>
<p>For instance, upcoming steps may be represented by dashed circles. Although the circles aren't fully drawn, the viewer immediately recognises them as complete shapes. The brain resolves the missing segments, allowing the interface to communicate progression without heavy borders or fully rendered icons. This subtle use of closure reduces visual clutter while preserving clarity.</p>
<p>Closure refers to the mind’s ability to perceive a complete, unified form even when parts of that form are missing.</p>
<h3 id="heading-35-figureground">3.5 Figure/Ground</h3>
<p>This principle describes the mind's tendency to separate an object (the figure) from its surrounding area (the ground or background). In web design, using a "modal" or "pop-up" relies on this: by blurring the background, you force the user to see the pop-up as the focal figure.</p>
<p>When a user clicks "Login" on a modal/lightbox, the background site often dims (the "Ground") while the login box stays bright and centered (the "Figure"). This immediate depth change tells the user exactly where their attention belongs.</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/UCdjymjASOU" 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><em>Video 3.5.0 Video description of Figure/Ground (Gestalt Principle)</em></p>
<p>From the video above, you can see that when the Quick View button is clicked, the selected figure stands out while the background darkens. This contrast guides the user’s attention and helps them focus on the figure. Developers can use this technique to direct users’ attention to what matters most or to what they want users to notice.</p>
<p>This principle describes the mind's tendency to separate an object (the figure) from its surrounding area (the ground or background).</p>
<h3 id="heading-36-common-fate">3.6 Common Fate</h3>
<p>Elements that move in the same direction are perceived as more related than elements that are stationary or move in different directions. Think of a dropdown menu: when all sub-items slide down together, they are clearly part of the same "unit."</p>
<p>For example, when you click a FAQ header and five sub-items slide down at the exact same speed and direction, the "Common Fate" tells the user that all those items belong to that specific category. If they flew in from different directions, the relationship would be lost.</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/T10xmlne6E4" 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><em>Video 3.6.1 Video description of common fate (Gestalt Principle)</em></p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/5FncGLRy0bM" 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><em>Video 3.6.2 Video description of common fate (Gestalt Principle)</em></p>
<p>From the video shown above, the e‑commerce animation example demonstrates these principles clearly by using two distinct motion patterns: a group of regular products that move upward together, and a pair of special‑category items that enter dramatically from the left. Through these contrasting movements, the interface communicates category differences without relying on text labels or explicit instructions.</p>
<p>Therefore, developers can use this motion‑based differentiation as a design strategy to guide users’ perception—allowing the interface to signal hierarchy, category structure, and product importance purely through animated behaviour rather than through static visual labels.</p>
<p>Elements that move in the same direction are perceived as more related than elements that are stationary or move in different directions.</p>
<h3 id="heading-37-focal-point">3.7 Focal Point</h3>
<p>Whatever stands out visually will capture and hold the viewer’s attention first. This is essentially the principle of emphasis. A bright "Sign Up" button in a sea of gray text acts as the focal point, directing the user's primary action.</p>
<p>For example, an alert banner or a pricing table should stand out from its surroundings. Beyond this, in a three-tier pricing table (Basic, Pro, Enterprise), the "Pro" column is often slightly larger or a different color. This creates a focal point that draws the eye to the "recommended" option immediately.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/711c9ee9-b546-4041-bf86-30c80154bd47.jpg" alt="711c9ee9-b546-4041-bf86-30c80154bd47" style="display:block;margin:0 auto" width="1784" height="646" loading="lazy">

<p><em>Fig 3:7: illustration of closure (Gestalt principle)</em></p>
<p>In visual interface design, the Gestalt principle of Focal Point plays a crucial role in directing user attention toward the most important element on a screen.</p>
<p>A focal point is created when one element breaks the established pattern of surrounding elements, making it stand out immediately.</p>
<p>In e‑commerce interfaces, this principle is often applied to highlight primary actions such as purchasing, subscribing, or upgrading. The “Buy Now” button provides a clear and practical example of how focal points function within a layout.</p>
<p>From the example above, the first two buttons share the same visual characteristics: neutral colours, and regular weight text. This repetition establishes a visual pattern that the user quickly becomes familiar with.</p>
<p>But the “Buy Now” button intentionally disrupts this pattern. It uses a bright colour, which contrasts sharply with the muted tones of the other buttons. This colour difference alone is enough to draw the eye, as humans are naturally sensitive to changes in hue and saturation within a uniform environment.</p>
<p>The Focal Point may sound like it's similar to the principle of Similarity, but the two operate in completely opposite ways within perceptual psychology.</p>
<p>Similarity explains how the mind naturally groups elements that share visual characteristics –&nbsp;such as colour, shape, or size – into coherent units. Once this grouping is established, the interface gains structure and predictability.</p>
<p>Focal Point, on the other hand, works by intentionally breaking that structure. Instead of reinforcing uniformity, it introduces a deliberate contrast – through colour, scale, brightness, or motion –&nbsp;to draw the viewer’s attention to one specific element.</p>
<p>In other words, Similarity creates the background pattern, while Focal Point identifies the one element that must stand out against that pattern.</p>
<p>Whatever stands out visually will capture and hold the viewer’s attention first.</p>
<h3 id="heading-design-takeaways-from-the-gestalt-principles">Design Takeaways from the Gestalt Principles</h3>
<p><strong>Use Spacing as Your Primary Grouping Tool:</strong><br>Elements that belong together should sit closer to each other than to anything else. Spacing communicates structure faster than borders or boxes. Use tight internal spacing (6–12px) for related items and wide external spacing (24–48px) to separate groups.</p>
<p><strong>Build a Strict, Consistent Visual System — and Stick to It:</strong><br>Define clear rules for button types, text styles, icon sizes, and alignment patterns. Consistent left‑aligned text blocks, predictable carousel lines, and stable flow patterns reduce cognitive load and make interfaces feel trustworthy.</p>
<p><strong>Guide the Brain With Spacing, Alignment, Consistency, Contrast, and Motion:</strong><br>The human brain is always trying to group, follow, and prioritise what it sees. Your job is to guide that instinct through intentional layout decisions, not fight against it.</p>
<h2 id="heading-40-von-restorff-effect-the-isolation-effect"><strong>4.0 Von Restorff Effect (The Isolation Effect)</strong>:</h2>
<p>This is the brainchild of Hedwig von Restorff, posited in 1933. In principle it states: An item that stands out is more noticable and more likely to be remembered than other items (Hunt, 1995).</p>
<p>So unique or visually distinct elements grab attention and are more memorable – in other words, distinctiveness dictates memory. When a user interacts with an interface, their brain naturally seeks patterns to minimize cognitive effort.</p>
<p>While consistency is generally a virtue in design, a perfectly uniform layout can lead to "banner blindness" or habituation, where the user stops noticing details.</p>
<p>By strategically breaking a pattern through changes in color, size, shape, or spacing, the developer can "isolate" an element, triggering a biological response that flags the item as high-priority.</p>
<p>Note that although the Focal Point principle may initially seem similar to the Von Restorff Effect, they describe two different psychological processes.</p>
<p>Focal Point is a Gestalt visual principle that explains how one element becomes the centre of attention within a composition because it carries the strongest visual contrast –&nbsp;through size, colour, brightness, position, or motion. Its purpose is to guide the viewer’s eye toward the most important element in the layout.</p>
<p>The Von Restorff Effect comes from cognitive psychology, not Gestalt theory. It states that an item that is noticeably different from a group of similar items is not only more attention‑grabbing but also more memorable.</p>
<p>So Focal Point is about where the eye goes first, while the Von Restorff Effect is about what the brain remembers later.</p>
<h3 id="heading-design-takeaways-from-von-restorff">Design takeaways from Von Restorff</h3>
<p><strong>Use Isolation to Make CTAs Impossible to Miss:</strong><br>On a page filled with neutral text and standard links, a single high‑contrast button (like a bold “Primary Blue” or “Emergency Red”) instantly becomes the standout element. This leverages the Von Restorff Effect to pull the user’s eye toward the most important action.</p>
<p><strong>Create a Visual “Hitch” in the Scan Path:</strong><br>A distinct CTA interrupts the user’s natural left‑to‑right, top‑to‑bottom scanning rhythm. This makes actions like “Buy Now” or “Sign Up” the first thing they notice and the last thing they forget.</p>
<p><strong>Make Critical Actions Visually Distinct:</strong><br>Because users naturally notice the one element that breaks a pattern, your most important actions should use deliberate contrast — color, size, shape, weight, or motion. Isolate key information instead of letting it blend into surrounding UI noise.</p>
<p><strong>Avoid Over‑Differentiation — or Nothing Stands Out:</strong> If every button is loud, animated, or uniquely styled, the interface becomes chaotic. The Von Restorff Effect only works when there is a clear, stable pattern — and you break it once, intentionally.</p>
<p><strong>To Sum Up:</strong> An item that stands out is more noticable and more likely to be remembered than other items.</p>
<h2 id="heading-50-jakobs-law"><strong>5.0 Jakob’s Law</strong></h2>
<p>Jakob’s Law states that users spend most of their time on other sites, so they expect your interface to behave like the ones they already know.</p>
<p>Familiar patterns — hamburger menus, top navigation, search icons, and clickable top‑left logos — reduce cognitive load because users don’t have to interpret anything new.</p>
<p>But while Jakob’s Law is foundational to UX, I think it can also unintentionally suppress innovation.</p>
<p>When developers over‑prioritise familiarity, they fall into a standardisation trap: endlessly optimising conventional patterns instead of exploring fundamentally better ones.</p>
<p>The Pie Menu is a perfect illustration of this. According to Fitts’s Law, the time required to reach a target depends on its distance and size. Linear menus place the last item much farther from the cursor than the first, creating uneven interaction costs.</p>
<p>Radial menus position every option at an equal distance from the centre, and their wedge‑shaped targets effectively grow larger as the pointer moves outward.</p>
<p>Mathematically, pie/radial menu are faster to interact with and more efficient — yet they remain rare in mainstream web design because they violate users’ expectations. In other words, Jakob’s Law keeps us locked into a familiar but suboptimal pattern simply because “that’s how it’s always been done.”</p>
<p>But the challenge is not choosing between familiarity and innovation, but balancing them.</p>
<p>This is where the Aesthetic–Usability Effect becomes powerful. Research shows that users perceive attractive interfaces as easier to use, and they are more forgiving of minor usability friction when the design is visually pleasing.</p>
<p>A beautifully crafted Pie Menu, for example, can encourage users to invest the small amount of learning required to use it. By applying aesthetic delight strategically, developers can introduce innovative patterns without overwhelming users.</p>
<p>The principle that emerges is simple: Be conventional where it matters, and innovative where it delights.</p>
<h3 id="heading-design-takeaway-from-jakobs-law">Design Takeaway from Jakob's Law</h3>
<p><strong>Keep Trust‑Critical Elements Predictable:</strong><br>Navigation, search, authentication, and other high‑stakes interactions must follow established conventions. Users rely on these patterns for speed, confidence, and safety — this is where Jakob’s Law should be respected without exception.</p>
<p><strong>Experiment Only in Low‑Risk, High‑Creativity Areas:</strong><br>In creative or productivity‑focused zones — like editing tools in a photo app — you can safely introduce new interaction models such as radial menus, gesture wheels, or context‑aware tool selectors. These areas invite exploration and benefit from efficiency‑driven innovation.</p>
<p><strong>To Sum Up:</strong> Be conventional where it matters, and innovative where it delights.</p>
<h2 id="heading-60-millers-law"><strong>6.0 Miller’s Law</strong></h2>
<p>Miller’s Law originates from George A. Miller’s classic paper “The Magical Number Seven, Plus or Minus Two.” It states that the average person can hold only about 7 (±2) chunks of information in working memory at any given moment (Miller, 1956).</p>
<p>Crucially, Miller emphasised that the brain doesn’t store isolated items — it groups them into meaningful units called chunks. Because working memory is so limited, developers must structure information in ways that respect this cognitive boundary.</p>
<p>This principle has direct implications for interface design. Long, unbroken strings of information overwhelm users, whereas chunked formats are far easier to process.</p>
<p>For example, instead of displaying a phone number as 1234567890, formatting it as 123‑456‑7890 transforms ten digits into three manageable chunks. The same logic applies to navigation: aim for five to nine primary menu items, and if you need more, group them into categories. Users remember the category as a single chunk rather than each individual link.</p>
<p>Miller’s Law also explains why long forms are so intimidating. When a user sees 30 fields on one page, their brain interprets it as a single, massive task — far beyond the 7±2 limit.</p>
<p>A progressive stepper solves this by breaking the form into smaller stages of 5–7 fields each. This reduces cognitive load, creates a sense of progress, and significantly lowers abandonment rates.</p>
<p>The same principle applies to product listings or search results. Expecting users to compare 50 items at once is unrealistic. Instead, provide strong filtering tools so users can narrow the set to a manageable size — ideally within the range their working memory can meaningfully evaluate.</p>
<p>In essence, Miller’s Law reminds developers that humans don’t process information in bulk. They process it in structured, meaningful chunks.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/3add2891-c6f8-4df1-a044-6cea6c5f1945.jpg" alt="Image of progressive stepper" style="display:block;margin:0 auto" width="818" height="960" loading="lazy">

<p><em>Fig 6.0: Illustrating progressive stepper</em></p>
<p>In the example above, the interface uses both a progress bar and a stepper to guide the user through multiple stages of a task. After completing the first page and selecting “Continue,” the user moves to the next step, and the progress bar updates accordingly. This creates a clear sense of forward movement and accomplishment.</p>
<p>By breaking the process into smaller segments, the interface prevents cognitive overload. If all the information were presented on a single page, users might feel overwhelmed, unsure where to begin, or discouraged by the sheer volume of work.</p>
<p>A step‑by‑step flow transforms a large task into a sequence of manageable actions, increasing the likelihood of completion.</p>
<h3 id="heading-design-takeaway-from-millers-law">Design Takeaway from Miller's Law</h3>
<p><strong>Respect the 7±2 Working‑Memory Limit:</strong><br>Users can only hold about seven chunks of information at once. Long, unbroken content overwhelms them, while chunked information is instantly easier to process.</p>
<p><strong>Chunk Information Into Meaningful Units:</strong><br>The brain doesn’t store isolated items — it groups them. Format data (like phone numbers), menus, and settings into clear, memorable chunks instead of long, flat lists.</p>
<p><strong>Keep Navigation Within 5–9 Primary Items:</strong><br>If you need more than nine options, group them into categories. Users remember the category as a single chunk, not each individual link.</p>
<p><strong>Break Long Forms Into Smaller Steps:</strong><br>A 30‑field form feels like one giant task. A progressive stepper with 5–7 fields per step keeps users below the cognitive overload threshold and dramatically reduces abandonment.</p>
<p><strong>Reduce Comparison Load With Strong Filters:</strong><br>Expecting users to compare 50 products at once is unrealistic. Provide filtering tools that shrink the decision set to something the working memory can actually handle.</p>
<p><strong>Design for Chunked Thinking, Not Bulk Processing:</strong><br>Humans don’t process information in bulk — they process structured, meaningful groups. Interfaces that respect this limitation feel lighter, faster, and more intuitive.</p>
<p><strong>To Sum Up:</strong> A step‑by‑step flow transforms a large task into a sequence of manageable actions, increasing the likelihood of completion.</p>
<h2 id="heading-70-the-goal-gradient-hypothesis">7.0 The Goal-Gradient Hypothesis</h2>
<p>This is the perfect moment to introduce the Goal‑Gradient Hypothesis, originally proposed by behaviorist Clark Hull in 1932 (Yablonski, 2022). The hypothesis states that people become more motivated as they get closer to achieving a goal. In other words, users naturally accelerate their engagement when they sense they are nearing completion.</p>
<p>This principle is incredibly powerful in UX design, especially for progress tracking, gamification, and reward systems.</p>
<p>The takeaway is straightforward: Because users are more motivated near the finish line, progress indicators should be prominent and meaningful.</p>
<p>Percentages, progress bars, and step counters reinforce momentum. Micro‑achievements — such as badges, checkmarks, or subtle confetti — amplify motivation by celebrating small wins.</p>
<p>Tasks should be broken into measurable milestones so users can see themselves advancing.</p>
<p>This is why e‑learning platforms display messages like “You’ve completed 8 of 10 lessons — almost there!” and why fitness apps highlight progress with prompts such as “3 km done, 2 km to go.” These cues leverage the goal‑gradient effect to keep users engaged, energized, and eager to finish.</p>
<p>By combining progressive steppers with clear progress feedback, developers create interfaces that feel lighter, more encouraging, and far more motivating — ultimately improving completion rates and overall user satisfaction.</p>
<p>But what happens when a goal isn't completed? Why do we sometimes feel uncomfortable leaving things unfinished? That discomfort is explained by another psychological principle called the Zeigarnik Effect — the tendency for people to remember and feel tension about incomplete tasks. We will look at this next.</p>
<h3 id="heading-design-takeaway-from-goal-gradient-hypothesis">Design Takeaway from Goal-Gradient Hypothesis</h3>
<p><strong>Make Progress Visible to Boost Motivation:</strong><br>According to the Goal‑Gradient Hypothesis, users naturally speed up as they sense they’re nearing completion. Prominent progress bars, percentages, and step counters tap into this instinct and keep momentum high.</p>
<p><strong>Celebrate Micro‑Achievements to Reinforce Engagement:</strong><br>Badges, checkmarks, subtle confetti, and “step completed” cues reward small wins. These micro‑rewards amplify motivation and make long tasks feel lighter and more achievable.</p>
<p><strong>Break Tasks Into Measurable Milestones:</strong><br>Users stay motivated when they can see themselves advancing. Divide complex flows into clear steps so progress feels tangible rather than overwhelming.</p>
<p><strong>Use Progress Feedback to Drive Completion:</strong><br>Messages like “8 of 10 lessons completed — almost there” or “3 km done, 2 km to go” leverage the goal‑gradient effect to energise users and pull them toward the finish line.</p>
<p><strong>Combine Steppers With Clear Feedback for Maximum Impact:</strong><br>Progressive steppers paired with strong visual feedback create interfaces that feel encouraging, structured, and motivating — dramatically improving completion rates.</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/-mDuFB3W2bg" 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><em>Video 8.0 : Video illustrating goal gradient</em></p>
<p><strong>To Sum Up:</strong> People become more motivated as they get closer to achieving a goal.</p>
<h2 id="heading-80-zeigarnik-effect"><strong>8.0 Zeigarnik Effect</strong></h2>
<p>The Zeigarnik Effect is a psychological principle stating that people remember unfinished or interrupted tasks better than completed ones (Cherry, 2024).</p>
<p>Memory begins with sensory input, which is processed into short-term memory. Unfinished tasks persist in our thoughts, leading to active recall. This ongoing engagement can turn them into long-term memories, enhancing recall until resolved. This increases engagement, encourages task completion, improves retention, and drives conversions.</p>
<p>So because people remember unfinished tasks better than completed ones (Zeigarnik Effect), developers use progress indicators to make users aware that something is incomplete and motivate them to finish it.</p>
<p>In your designs, you can break long forms into multi-step processes to encourage completion and display profile completion percentages (for example, 70% complete) to push users toward 100%.</p>
<p>This is the main reason why e-commerce platforms send abandoned cart reminders to bring users back to complete their purchases. It's also why apps use streak systems to encourage daily engagement and habit formation and learning platforms show course completion bars to motivate users to finish modules.</p>
<h3 id="heading-design-takeaway-from-zeigarnik-effect">Design Takeaway from Zeigarnik Effect</h3>
<p><strong>Unfinished Tasks Stay Active in Memory — Use That to Drive Completion:</strong><br>Because incomplete tasks linger in working memory (Zeigarnik Effect), users naturally keep thinking about what they haven’t finished. This tension boosts recall, engagement, and the likelihood of returning to complete the task.</p>
<p><strong>Make Incompleteness Visible With Progress Indicators:</strong><br>Progress bars, percentages, and step counters remind users that something is still unfinished. This gentle psychological pressure motivates them to continue until the task is complete.</p>
<p><strong>Break Long Flows Into Multi‑Step Processes:</strong><br>A massive form feels overwhelming, but a stepper with smaller chunks keeps users moving. Showing “70% complete” nudges them toward finishing the last stretch.</p>
<p><strong>Use Reminders to Re‑activate Unfinished Intent:</strong><br>Abandoned cart emails, streak reminders, and “continue your lesson” prompts work because the unfinished task is already active in the user’s mind. The reminder simply pulls them back into the loop.</p>
<p><strong>Celebrate Completion to Close the Cognitive Loop:</strong><br>Checkmarks, confirmations, and completion badges give users closure. This resolves the mental tension created by the unfinished task and reinforces positive behaviour.</p>
<p><strong>To Sum Up:</strong> Unfinished tasks persist in our thoughts, leading to active recall.</p>
<h2 id="heading-90-teslers-law"><strong>9.0 Tesler’s Law</strong>:</h2>
<p>This law was proposed by Lawrence Tesler. He was a computer scientist known for his work on human-computer interaction, and he contributed significantly to making software more user-friendly, including work on cut, copy, and paste functionality.</p>
<p>This law is otherwise known as the Law of Conservation of Complexity. The core Idea here is every process has a certain amount of “inherent complexity" that can't be removed. You can only decide who handles it: the user or the system.</p>
<p>Some examples of these inherent complexities might be:</p>
<ul>
<li><p>translating user actions into correct operations behind the scenes,</p>
</li>
<li><p>handling unreliable or slow network connections,</p>
</li>
<li><p>connecting with third-party APIs, services, or legacy systems,</p>
</li>
<li><p>sorting large datasets quickly,</p>
</li>
<li><p>performing complex search operations</p>
</li>
<li><p>managing version changes and compatibility issues,</p>
</li>
<li><p>managing state, interactions, and animations without confusing the user.</p>
</li>
</ul>
<p>All of these can be inherently complex, but it's the job of the developer to deal with the complexity.</p>
<p>As a developer, you should always try as much as possible to push complexity to the system. For example, instead of making a user type their full address manually, use an Auto-complete API (Google’s Places and Map is best for this). The complexity of finding and validating the address still exists, but the software handles the work for them.</p>
<p>Here's a practical example: let’s say you're designing a student platform that requires users to enter their university name. A practical approach would be to store an array of all universities in the UK in your codebase (This is the hard part Tesla hinted at).</p>
<p>As the user types, they don't need to enter the full name, and their full university name is shown (relating to what they have typed). For instance, if they intend to type “University of Sheffield,” simply typing “Sheff” should prompt the system to display the full university name, which they can then select.</p>
<p>In Dart, you can use a package like fuzzysearch to implement this kind of intelligent matching.</p>
<p>The advantage of this approach is greater than it first appears. It improves data consistency because users often enter the same information in different ways. For example, some users might type “Uni of Sheff,” others “Sheffield University,” and others “Uni of Sheffield,” while all are referring to “University of Sheffield.”</p>
<p>This is how messy data is created, and it creates more work for data analysts. Little wonder that data analysts spend up to 70% of their time cleaning data.</p>
<p>If developers invested more time in structuring how data is collected to ensure consistency, there would be far less work downstream for analysts. This same logic should be applied in how we collect date, time, and other information.</p>
<p>So apart from people's names and email addresses, you should try to standardize the data your app collects as much as possible. Use date and time pickers, stepper controls, input masks, checkboxes, dropdown menu and radio buttons, toggle switches. and so on.</p>
<p>The essence of removing complexity from the user is not only about improving usability, but also about ensuring that the data collected is standardised, structured, and consistent.</p>
<h3 id="heading-design-takeaway-from-teslers-law">Design Takeaway from Tesler's Law</h3>
<p><strong>Push Complexity to the System, Not the User:</strong><br>Every process contains unavoidable complexity. Your job is to handle it behind the scenes so the user experiences the simplest possible interaction.</p>
<p><strong>Automate Tasks Users Shouldn’t Have to Think About:</strong><br>Use tools like autocomplete, fuzzy search, intelligent defaults, and validation APIs to remove manual effort. The complexity still exists — but the system absorbs it instead of the user.</p>
<p><strong>Standardise Inputs to Prevent Messy Data:</strong><br>Users enter the same information in wildly different ways. Use pickers, dropdowns, input masks, radio buttons, and toggles to enforce consistent, structured data collection.</p>
<p><strong>Handle Inherent Technical Complexity Internally:</strong><br>Network issues, API quirks, large dataset sorting, search optimisation, state management, and animation logic are all developer responsibilities. Users should never feel this complexity.</p>
<p><strong>To Sum Up:</strong> Every process contains unavoidable complexity. Your job as a developer is to handle it behind the scenes so the user experiences the simplest possible interaction.</p>
<h2 id="heading-100-peak-end-rule"><strong>10.0 Peak End Rule</strong>:</h2>
<p>In 1993, Daniel Kahneman, Barbara Fredrickson, Charles Schreiber, and Donald Redelmeier invited volunteers into a lab for what sounded like a simple experiment. The task was straightforward: place a hand into a container of painfully cold water (Kahneman et al., 1993)</p>
<p>In the first round, participants kept their hand in 14°C water for 60 seconds. It was uncomfortable, sharp, and unpleasant but after one minute, it was over.</p>
<p>In the second round, they again endured 60 seconds in 14°C water. But this time, they were asked to keep their hand in for an extra 30 seconds. The temperature was raised slightly to 15°C. Still cold. Still unpleasant. Just slightly less intense.</p>
<p>Objectively, the second experience was worse. It lasted 90 seconds instead of 60. More total pain. More suffering.</p>
<p>Later, the researchers asked a simple question:</p>
<p>If you had to repeat one of the trials, which would you choose?” Surprisingly, most participants chose the longer one.</p>
<p>Why would anyone choose more pain?</p>
<p>The researchers realised something profound: people don’t remember experiences by calculating total discomfort. Instead, the mind summarizes the experience using just two key moments — the most intense point (the peak) and the final moment (the end).</p>
<p>In both trials, the peak pain was the same: 14°C. But the longer trial ended slightly better, at 15°C. That small improvement at the end reshaped how the entire episode was remembered. The participants’ “experiencing self” suffered more during the longer trial. But their “remembering self” preferred it because it ended on a less painful note.</p>
<p>From this, the researchers introduced what became known as the Peak–End Rule: we judge experiences largely by their most intense moment and how they finish, not by how long they last.</p>
<p>Since people largely judge an experience by how it ends, developers should focus on designing satisfying confirmation screens and smooth exit interactions. You should concentrate less on making every single moment perfect and instead prioritise optimising the peak and final moments.</p>
<p>A negative ending can overshadow an otherwise good experience, so carefully avoid frustrating final steps such as unexpected fees or confusing confirmations.</p>
<p>Emotional intensity strongly shapes memory, which is why many apps incorporate celebration animations, rewards, or success messages at key moments to leave a lasting positive impression.</p>
<h3 id="heading-design-takeaway-from-peak-end-rule">Design takeaway from Peak End Rule</h3>
<p><strong>People Judge Experiences by the Peak and the Ending — Not the Total Duration:</strong><br>Users don’t remember every moment. They remember the most intense point and how the experience ends. A slightly better ending can completely reshape how the entire interaction is remembered.</p>
<p><strong>Prioritise Strong, Positive Endings in Your UX Flows:</strong><br>A smooth final step, a clear confirmation, or a satisfying success screen leaves a disproportionately strong impression. A bad ending can overshadow an otherwise great experience.</p>
<p><strong>Design for Emotional Peaks at Key Moments:</strong><br>Celebration animations, rewards, checkmarks, and success messages create memorable emotional spikes. These peaks anchor the experience in the user’s memory.</p>
<p><strong>Don’t Try to Perfect Every Moment — Perfect the Right Moments:</strong><br>Optimise the peak and the end of the journey. These two moments define how users recall the entire interaction.</p>
<p><strong>Avoid Negative Surprises at the Finish Line:</strong><br>Unexpected fees, confusing confirmations, or friction at the last step can ruin the memory of the whole process. Protect the ending carefully.</p>
<p><strong>To Sum Up:</strong> We judge experiences largely by their most intense moment and how they finish, not by how long they last.</p>
<h2 id="heading-110-postels-law"><strong>11.0 Postel’s Law</strong>:</h2>
<p>Jon Postel’s famous principle – “Be conservative in what you send, be liberal in what you accept” –&nbsp; is a philosophy of kindness in software design. At its core, the principle argues that systems should be generous with what they accept from users, yet disciplined and predictable in what they output.</p>
<p>When developers follow this approach, users feel supported and understood. When they don’t, users feel punished for being human.</p>
<p>A user’s input is rarely perfect. People type quickly, make mistakes, follow their own habits, or rely on formats familiar to them. A robust system embraces this reality. It accepts messy, human input and quietly transforms it into clean, standardized data.</p>
<p>Real people don't think in strict formats. They write dates the way they learned in school, type phone numbers the way they say them aloud, and enter names and addresses in whatever structure feels natural to them.</p>
<p>A rigid system will reject anything that doesn’t match its narrow expectations, but a robust system, by contrast, adapts to the user.</p>
<p>Consider dates. A brittle interface might demand MM/DD/YYYY and reject everything else. A more humane system accepts a wide range of formats — “1 May 2024,” “2024‑05‑01,” “05/01/24,” or “May 1st, 2024” — and quietly converts them into a standard internal representation. This is where the complex handling described by Tesla's Law comes into play (Shifting complexity to the system, rather than the user).</p>
<p>Phone numbers follow the same pattern. People might enter (555) 123 4567, 555‑123‑4567, 5551234567, or +1 555 123 4567. A fragile system throws errors. A robust one parses all of them using libraries like libphonenumber and moves on.</p>
<p>Addresses are equally varied. “221B Baker St,” “221‑B Baker Street,” and “221 Baker St., Apt B” all refer to the same place. A forgiving system normalizes these instead of rejecting them.</p>
<p>Even names can be surprisingly complex. Hyphens, apostrophes, multiple words, and titles are all part of real human identity. Rejecting “O’Connor,” “Jean‑Luc,” or “Dr. Sarah Lee” is not just technically incorrect — it's disrespectful to the user.</p>
<p>Search bars offer another clear example. A strict search bar demands perfect spelling and exact phrasing. A robust one handles typos (“restuarant”), partial words (“resta”), synonyms (“food places”), and natural language (“where can I eat nearby”). It meets the user where they are instead of forcing them to think like a machine.</p>
<p>Currency should be normalized to a clear format such as GBP 5.00, no matter whether the user typed “£5,” “5 pounds,” or “5 GBP.”</p>
<p>Even file uploads benefit from standardization: whether the user uploads .jpeg, .jpg, .JPG, or .JPEG, the system should store everything as .jpg.</p>
<p>Error messages follow the same principle. Vague feedback like “Invalid password” leaves users confused and frustrated.</p>
<p>A clear, conservative message — “Incorrect password. Please try again.” — respects the user’s time. And instead of hiding password requirements, the system should state them upfront: minimum eight characters, at least one uppercase letter, at least one number.</p>
<p>Predictability reduces friction.</p>
<p>Because users inevitably make mistakes or enter data in unexpected ways, developers should design input fields that are tolerant rather than brittle. This means accepting flexible formats, offering autocorrect or intelligent parsing, and using forgiving validation rules that interpret the user’s intent instead of rejecting their effort.</p>
<p>Clear instructions, tooltips, and visible requirements should appear before submission so users understand what the system expects without trial and error.</p>
<p>When errors do occur, the interface should handle them gently—never crashing, and never forcing the user to start over.</p>
<p>Even simple variations, such as phone numbers typed with spaces, dashes, or parentheses, should be accepted and normalized behind the scenes.</p>
<p>By embracing flexibility on the input side and clarity on the output side, developers create systems that feel humane, resilient, and respectful of the way real people actually behave.</p>
<h3 id="heading-design-takeaway-from-postels-law">Design Takeaway from Postel's Law</h3>
<p><strong>Accept Messy Human Input, Output Clean Structured Data:</strong><br>Users type dates, names, phone numbers, and addresses in unpredictable ways. A humane system accepts this variability and quietly normalises it into a consistent internal format.</p>
<p>Rigid interfaces punish users for being human. Robust interfaces interpret intent — handling typos, partial matches, synonyms, and natural language without complaint.</p>
<p>Also accept variations in spacing, punctuation, casing, and structure. Let users type naturally — the system should handle the complexity, not them.</p>
<p><strong>Be Flexible With Input, Be Strict With Output:</strong><br>This is the heart of Postel’s Law. Let users express information naturally, but ensure your system stores and displays it in a predictable, standardised way.</p>
<p><strong>Use Intelligent Parsing and Autocorrection to Reduce Errors:</strong><br>Libraries like libphonenumber, fuzzy search, and natural‑language parsers allow systems to accept a wide range of formats while still producing clean, reliable data.</p>
<p><strong>Normalise Everything Behind the Scenes:</strong><br>Dates, phone numbers, currency, file extensions, and addresses should all be standardised internally. This prevents messy data and reduces downstream cleanup work.</p>
<p><strong>Provide Clear, Predictable Feedback:</strong><br>Error messages should be specific and helpful. Requirements should be visible upfront. Users should never be surprised, confused, or forced to start over.</p>
<p><strong>Combine Postel’s Law With Tesler’s Law:</strong><br>Shift complexity to the system. Intelligent handling of messy input reduces cognitive load, improves usability, and ensures consistent, high‑quality data.</p>
<p><strong>To Sum Up:</strong> A rigid system will reject anything that doesn’t match its narrow expectations, but a robust system, by contrast, adapts to the user.</p>
<h2 id="heading-120-doherty-threshold"><strong>12.0 Doherty Threshold</strong>:</h2>
<p>The Doherty Threshold is a principle in human–computer interaction which proposes that systems should respond quickly enough to keep users actively engaged (Mod 2024).</p>
<p>When response times stay below a certain limit, users remain focused and productive. But once performance already meets this optimal responsiveness level, making the system even faster or adding extra capability doesn't significantly enhance satisfaction or efficiency.</p>
<p>The idea was introduced by Walter J. Doherty in 1976 in his paper “A Comparison of Programming Systems and Doherty Threshold.” His research showed that maintaining rapid system feedback fast enough to sustain continuous interaction has a stronger impact on productivity than simply increasing system power or features beyond that point.</p>
<p>Doherty proposes that this shouldn't be greater than 400ms Rule: If the system responds within this window, the user feels in total control. If the response takes longer, the user's attention begins to wander, and their "train of thought" is broken.</p>
<p>The challenge, of course, is that not every operation can realistically complete within 400ms. Some tasks require heavy computation, large network calls, or complex rendering. This is where the concept of perceived performance becomes essential.</p>
<p>Even when the system can't finish the work quickly, it can feel fast by responding instantly at the UI level. Developers can achieve this illusion of speed through a combination of thoughtful design patterns and disciplined engineering practices.</p>
<p>On the technical side, performance begins with reducing unnecessary work. Keeping the number of HTML elements low helps the browser render faster. Rendering only the visible portion of long lists prevents the Document Oject Model (DOM) from becoming bloated. Splitting scripts and deferring non‑critical code ensures that essential interactions load first.</p>
<p>Using CSS transforms and opacity changes avoids expensive layout recalculations. Lazy‑loading images, videos, and scripts ensures that the interface becomes interactive long before all assets are downloaded.</p>
<p>These optimizations don’t just improve raw speed — they create the foundation for interfaces that feel responsive.</p>
<h3 id="heading-design-takeaways-from-doherty-threshold">Design Takeaways from Doherty Threshold</h3>
<p><strong>Instant Feedback</strong>: When a user clicks a button, provide a visual change (like a button press animation or a spinner) immediately, even if the background task takes longer.</p>
<p><strong>Skeleton Screens</strong>: Use placeholder blocks that mimic the layout of the page while data loads. This makes the app feel like it is responding instantly.</p>
<p><strong>Progressive Loading</strong>: Load text and basic structures first, then "pop in" high-resolution images later.</p>
<p><strong>Optimistic UI</strong>: When a user hits "Save," don't wait for the server. Update the UI instantly (Doherty) and handle the "messy" data formatting on the backend (Postel).</p>
<p><strong>Live Inline Validation</strong>: Show a green checkmark or a helpful error message as the user types. This keeps them below the 400ms "thought-break" limit.</p>
<p><strong>Debouncing</strong>: In search bars, start showing results after a few keystrokes so the user feels the app is "predicting" their needs.</p>
<p><strong>To Sum Up:</strong> When response times stay below a certain limit, users remain focused and productive. But once performance already meets this optimal responsiveness level, making the system even faster or adding extra capability doesn't significantly enhance satisfaction or efficiency.</p>
<h2 id="heading-130-serial-position-effect-primacy-and-recency"><strong>13.0 Serial Position Effect (Primacy and Recency)</strong>:</h2>
<p>Murdock’s study investigated how the position of a word in a list affects recall, known as the serial position effect. He presented 103 psychology students with lists of 10 to 40 words, one at a time, at either 1 or 2 seconds per word (McLeod, 2025).</p>
<p>Participants were divided into six groups, each experiencing a different combination of list length and presentation rate, and were asked to recall as many words as possible in any order.</p>
<p>The results showed that participants were most likely to remember words at the beginning of the list (primacy effect) and at the end of the list (recency effect), while words in the middle were recalled less often. The recency effect persisted even in longer lists, and the middle section of the recall curve formed a flat asymptote.</p>
<p>Murdock explained this using the multi-store model of memory: early words were rehearsed and transferred to long-term memory, last words remained in short-term memory, and middle words were neither sufficiently rehearsed nor retained, leading to poorer recall.</p>
<p>The experiment demonstrated that memory performance varies systematically with the position of information in a sequence.</p>
<p>This is the reason why the most important information or actions should never be buried in the middle.</p>
<p>As a developer, you should put your most critical navigation links (like "Home" or "Dashboard") at the far left or the top of a list. In a pricing table, put the most popular or recommended plan on the Place "Final Actions" (like "Log Out," "Cart," or "Support") at the end of a menu or the far right of a navigation bar.</p>
<p>In a long onboarding flow, put the most exciting benefit of the app on the very last slide so the user enters the app feeling motivated.</p>
<p>Avoid placing highly important buttons in the middle of a row. If you have a row of 7 buttons, the user is statistically likely to overlook the 4th one.</p>
<h3 id="heading-design-takeaways-serial-position-effect">Design Takeaways <strong>Serial Position Effect</strong></h3>
<p><strong>Place Critical Items at the Beginning or End — Never the Middle:</strong><br>Users reliably remember the first and last items in any sequence (primacy and recency). Anything placed in the middle is statistically more likely to be forgotten or ignored. Also, actions such as “Log Out,” “Cart,” “Support,” or “Checkout” should sit at the far right or bottom — the natural recency position.</p>
<p><strong>Put Essential Navigation Links at the Far Left or Top:</strong><br>Links like “Home,” “Dashboard,” or “Overview” should appear at the start of a menu, where recall and recognition are strongest.</p>
<p><strong>To Sum Up:</strong> The results showed that participants were most likely to remember words at the beginning of the list (primacy effect) and at the end of the list (recency effect), while words in the middle were recalled less often.</p>
<h2 id="heading-140-occams-razor"><strong>14.0 Occam’s Razor</strong>:</h2>
<p>Although first articulated in the 14th century by the Franciscan friar William of Ockham, Occam’s Razor remains one of the most indispensable principles in a developer’s toolkit. In fact, skipping this law while discussing other theories and principles would be like skipping the glue that holds the entire framework together.</p>
<p>At its core, Occam’s Razor states that “among competing explanations, the simplest one is usually the best.”</p>
<p>For example, if two user interfaces achieve the same goal, the one with fewer visual elements is typically superior because it requires less processing power.</p>
<p>The fundamental takeaway for modern developers regarding Occam’s Razor is that complexity is a tax on the user’s cognitive resources.</p>
<p>In an era of information density, the developer's primary role is no longer to provide "more" features – rather, it's to curate the most direct path to a solution.</p>
<p>In practice, Occam’s Razor becomes a reminder to keep things as simple as possible. This “less is more” mindset shapes everything from navigation to forms.</p>
<p>A good rule for navigation is the Rule of Five: aim for three to five main menu items instead of a long, overwhelming list. This keeps choices clear and prevents users from freezing up when they see too many options.</p>
<p>The same idea applies to data entry. When you ask only for the information that truly matters, you respect the user’s time and reduce the chance of “form fatigue,” which is one of the biggest reasons people abandon sign‑ups or checkout flows.</p>
<p>Simplicity isn’t just elegant — it’s practical, humane, and far more effective.</p>
<h3 id="heading-design-takeaway-from-occams-razor">Design Takeaway from Occam's Razor</h3>
<p><strong>Choose the Simplest Effective Solution:</strong><br>When two designs achieve the same goal, the one with fewer elements is almost always better. Simplicity reduces cognitive load and speeds up user decision‑making.</p>
<p><strong>Simplicity Is Not Just Aesthetic — It’s Humane:</strong><br>Clear, minimal interfaces respect the user’s time, reduce friction, and make the product feel effortless. Simplicity is both a design strategy and an act of empathy.</p>
<p><strong>To Sum Up:</strong> Simplicity isn’t just elegant — it’s practical, humane, and far more effective.</p>
<h2 id="heading-150-parkinsons-law">15.0 Parkinson's Law</h2>
<p>Occam’s Razor teaches us to prefer the simplest solution that works. But why do we so often end up with complex systems in the first place? That tendency is explained by another principle: Parkinson’s Law.</p>
<p>Parkinson’s Law states that "work expands to fill the time available for its completion". In design, this means projects often become overly complex or take longer than necessary if given too much time, resulting in inefficient, over-designed, or cluttered interfaces.</p>
<p>In design, this manifests as Feature Creep. If you give yourself three months to build an app, you will spend three months adding "nice-to-have" animations, extra settings toggles, and niche edge cases that nobody asked for and in reality, what you have added isn’t that important.</p>
<p>You just succeeded in adding layers of complexity that might ends up violating some of the laws we spoke about. Occam’s Razor reminds us that the simplest solution is often the most effective.</p>
<p>By being aware of Parkinson’s Law and the tendency for work to expand, developers can manage their time intentionally and focus only on what truly matters.</p>
<h3 id="heading-design-takeaway-for-parkinsons-law">Design Takeaway for Parkinson's law</h3>
<p><strong>Set Clear Constraints to Keep Designs Focused:</strong><br>Intentional time limits and scope boundaries prevent over‑designing. Constraints force clarity, prioritisation, and simplicity.</p>
<p><strong>Build Only What Truly Matters for the User:</strong><br>Parkinson’s Law reminds you to resist the urge to fill time with unnecessary features. Focus on the core experience, not the edge cases nobody asked for.</p>
<p><strong>Use Occam’s Razor to Counterbalance Parkinson’s Law:</strong><br>As work expands, complexity grows. Occam’s Razor pulls you back to the simplest effective solution. Together, the two principles prevent bloated, over‑engineered products.</p>
<p><strong>To Sum Up:</strong> Work expands to fill the time available for its completion</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Human-centered design is deeply influenced by a set of psychological principles that explain how users perceive, process, and interact with digital systems.</p>
<p>Among these, Fitts’s Law establishes that the time required to acquire a target depends on its size and distance. In practice, this means that larger and closer elements are easier and faster to interact with.</p>
<p>To apply this in practice, developers should make primary call-to-action elements prominent, large, and easily reachable –&nbsp;especially in mobile interfaces where thumb accessibility is critical.</p>
<p>Closely related to decision-making is Hick’s Law, which states that the more choices a user is presented with, the longer it takes to make a decision. Excessive options can overwhelm users and lead to decision fatigue.</p>
<p>To address this, developers should simplify interfaces, minimise unnecessary options, and guide users through processes step-by-step rather than presenting everything at once.</p>
<p>Another important cognitive principle discussed is Miller’s Law, which suggests that the average person can hold approximately seven (plus or minus two) items in working memory at a time. This limitation highlights the need to present information in manageable chunks.</p>
<p>By breaking content into smaller groups and avoiding information overload, developers can improve comprehension and usability.</p>
<p>User expectations are strongly shaped by Jakob’s Law, which says that people spend most of their time on other websites and therefore expect similar patterns across digital products.</p>
<p>Instead of reinventing basic interactions, developers should follow familiar conventions such as placing the logo in the top‑left, the shopping cart in the top‑right, and keeping scrolling behaviour predictable.</p>
<p>But innovation is still possible where it truly adds value. As we discussed with the Aesthetic‑Usability Effect, users are far more tolerant of new or unusual design patterns when the interface is visually appealing and thoughtfully crafted.</p>
<p>The Gestalt Principles provided additional insight into how users visually organise information. The principle of proximity suggests that objects placed close together are perceived as related, so grouping related elements improves clarity. Similarity indicates that elements with consistent colours, shapes, or styles are seen as belonging together, reinforcing visual hierarchy and function. Closure explains that users can perceive incomplete shapes as complete, allowing for minimalistic designs where the brain fills in missing details. Continuity highlights that users naturally follow smooth visual paths, meaning layouts should guide the eye logically through alignment and structure.</p>
<p>We also looked at The Von Restorff Effect which emphasizes that elements which stand out are more likely to be remembered. By using contrast in colour, size, or design, important features such as buttons or alerts can capture user attention.</p>
<p>Managing complexity was addressed by Tesler’s Law, which asserts that every system has inherent complexity that cannot be eliminated but only managed.</p>
<p>Developers must therefore shift complexity away from the user by simplifying interfaces while handling intricate processes behind the scenes.</p>
<p>The Zeigarnik Effect reveals that people remember unfinished tasks better than completed ones, creating a sense of mental tension. This can be leveraged by incorporating progress indicators, checklists, and reminders that encourage users to complete tasks.</p>
<p>Similarly, the Peak-End Rule suggests that users judge an experience based on its most intense moment and its conclusion. Developers should create memorable highlights and ensure a smooth, satisfying ending to user journeys.</p>
<p>We also discussed the Goal-Gradient Effect, which explains that users become more motivated as they approach the completion of a task. By showing progress –such as indicating that a process is “80% complete” –&nbsp;and breaking tasks into stages, developers can encourage users to finish what they have started.</p>
<p>In terms of system interaction, Postel’s Law advises developers to be flexible in accepting user input while maintaining strict standards for output. This means allowing different input formats while ensuring consistent and reliable system responses.</p>
<p>Performance is equally important, as highlighted by the Doherty Threshold, which shows that productivity increases when system response times stay under 400 milliseconds. Fast systems keep users engaged and create a sense of ease.</p>
<p>This means that developers should focus on building interfaces that feel instant, even when real processing takes longer, by combining smart engineering practices with thoughtful design patterns that maintain the illusion of speed.</p>
<p>Memory and attention are further explained by the Serial Position Effect, where users tend to remember the first and last items in a sequence more than those in the middle. Developers should position key information or actions at the beginning or end of lists.</p>
<p>Simplicity is reinforced by Occam’s Razor, which argues that the simplest solution is often the most effective. Eliminating unnecessary features reduces friction and enhances usability, and we further discussed about Parkinson’s Law, which suggests that tasks expand to fill the time available, indicating the importance of setting constraints such as deadlines or timers to encourage timely action.</p>
<p>These principles collectively highlight the importance of simplicity, clarity, performance, and user psychology in design. By applying them thoughtfully, developers can create intuitive, efficient, and engaging user experiences that align with both human behaviour and user expectations.</p>
<h2 id="heading-references">References</h2>
<p>Budiu, R. (2022). <em>Fitts’s Law and Its Applications in UX</em>. [online] Nielsen Norman Group. Available at: <a href="https://www.nngroup.com/articles/fitts-law/">https://www.nngroup.com/articles/fitts-law/</a>.</p>
<p>Bustamante, N. (2023). <em>Gestalt Psychology? Definition, Principles, &amp; Examples - Simply Psychology</em>. [online] <a href="http://www.simplypsychology.org">www.simplypsychology.org</a>. Available at: <a href="https://www.simplypsychology.org/what-is-gestalt-psychology.html">https://www.simplypsychology.org/what-is-gestalt-psychology.html</a>.</p>
<p>Cherry, K. (2024). <em>The Zeigarnik Effect Is Why You Keep Thinking of Unfinished Work</em>. [online] Verywell Mind. Available at: <a href="https://www.verywellmind.com/zeigarnik-effect-memory-overview-4175150">https://www.verywellmind.com/zeigarnik-effect-memory-overview-4175150</a>.</p>
<p>DO, A.M., RUPERT, A.V. and WOLFORD, G. (2008). Evaluations of pleasurable experiences: The peak-end rule. <em>Psychonomic Bulletin &amp; Review</em>, 15(1), pp.96–98. doi:<a href="https://doi.org/10.3758/pbr.15.1.96">https://doi.org/10.3758/pbr.15.1.96</a>.</p>
<p>GUPTA, S., GUPTA, S., MAHENDRA, A. and GUPTA, S. (2006). Inverse Halo Nevus. <em>Dermatologic Surgery</em>, 32(6), pp.871–872. doi:<a href="https://doi.org/10.1097/00042728-200606000-00025">https://doi.org/10.1097/00042728-200606000-00025</a>.</p>
<p>‌Hall, D. (2023). <em>Pilot Error, Chapanis and The Shape of Things to Come</em>. [online] UX Magazine. Available at: <a href="https://uxmag.com/articles/pilot-error-chapanis-and-the-shape-of-things-to-come">https://uxmag.com/articles/pilot-error-chapanis-and-the-shape-of-things-to-come</a>.</p>
<p>Hunt, R.R. (1995). The subtlety of distinctiveness: What von Restorff really did. <em>Psychonomic Bulletin &amp; Review</em>, 2(1), pp.105–112. doi:<a href="https://doi.org/10.3758/bf03214414">https://doi.org/10.3758/bf03214414</a>.</p>
<p>Kahneman, D., Fredrickson, B.L., Schreiber, C.A. and Redelmeier, D.A. (1993). When More Pain Is Preferred to Less: Adding a Better End. <em>Psychological Science</em>, 4(6), pp.401–405. doi:<a href="https://doi.org/10.1111/j.1467-9280.1993.tb00589.x">https://doi.org/10.1111/j.1467-9280.1993.tb00589.x</a>.</p>
<p>Mod, D. (2024). <em>Doherty Threshold: UX Law of Swift Interactions</em>. [online] Articles on everything UX: Research, Testing &amp; Design. Available at: <a href="https://blog.uxtweak.com/doherty-threshold/">https://blog.uxtweak.com/doherty-threshold/</a>.</p>
<p>Miller, G.A. (1956). The magical number seven, plus or minus two: Some limits on our capacity for processing information. <em>Psychological Review</em>, [online] 101(2), pp.343–352. doi:<a href="https://doi.org/10.1037/0033-295x.101.2.343">https://doi.org/10.1037/0033-295x.101.2.343</a>.</p>
<p>Proctor, R.W. and Schneider, D.W. (2018). Hick’s law for choice reaction time: A review. <em>Quarterly Journal of Experimental Psychology</em>, [online] 71(6), pp.1281–1299. doi:<a href="https://doi.org/10.1080/17470218.2017.1322622">https://doi.org/10.1080/17470218.2017.1322622</a>.</p>
<p>Yablonski, J. (2022). <em>Hick’s Law</em>. [online] Laws of UX. Available at: <a href="https://lawsofux.com/hicks-law/">https://lawsofux.com/hicks-law/</a>.</p>
<p>Yablonski, J. (2022). <em>Goal-Gradient Effect</em>. [online] Laws of UX. Available at: <a href="https://lawsofux.com/goal-gradient-effect/">https://lawsofux.com/goal-gradient-effect/</a>.</p>
<p>‌</p>
<p>‌</p>
<p>‌</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Admin Dashboard Sidebar with shadcn/ui and Base UI ]]>
                </title>
                <description>
                    <![CDATA[ Admin dashboards are one of the most common real-world UI components you will build as a React developer. At the heart of nearly every dashboard is a sidebar, a persistent navigation panel that organi ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-an-admin-dashboard-sidebar-with-shadcn-ui-and-base-ui/</link>
                <guid isPermaLink="false">69de6a6491716f3cfb542305</guid>
                
                    <category>
                        <![CDATA[ shadcn ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ baseui ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tailwind CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vaibhav Gupta ]]>
                </dc:creator>
                <pubDate>Tue, 14 Apr 2026 16:25:08 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/3ce152b1-9a34-4c72-85f0-cabf7d4f3460.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Admin dashboards are one of the most common real-world UI components you will build as a React developer. At the heart of nearly every dashboard is a sidebar, a persistent navigation panel that organizes pages, tools, and features into a clean, scannable structure.</p>
<p>Building a sidebar from scratch involves much more than an <code>&lt;nav&gt;</code> element. You need collapsible submenus, active state tracking across parent and child items, accessible keyboard navigation, a scroll area for long nav lists, and a consistent design system that holds together across screen sizes.</p>
<p>In this tutorial, you'll learn how to build a fully functional, accessible admin dashboard sidebar using shadcn/ui, a collection of beautifully designed, accessible React components, and a pre-built community block from Shadcn Space, which extends shadcn/ui with ready-to-use dashboard UI patterns.</p>
<p>By the end of this tutorial, you'll have a working sidebar that includes:</p>
<ul>
<li><p>Grouped navigation sections with uppercase labels</p>
</li>
<li><p>Collapsible parent menu items with child links</p>
</li>
<li><p>Active state tracking across both parent and child items</p>
</li>
<li><p>A floating sidebar with an independent scroll area</p>
</li>
<li><p>A promotional card pinned inside the sidebar footer</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-what-you-will-build">What You Will Build</a></p>
</li>
<li><p><a href="#heading-why-shadcnui">Why shadcn/ui?</a></p>
</li>
<li><p><a href="#heading-what-is-shadcn-space">What is Shadcn Space?</a></p>
</li>
<li><p><a href="#heading-how-to-install-the-sidebar-block">How to Install the Sidebar Block</a></p>
</li>
<li><p><a href="#heading-how-to-understand-the-folder-structure">How to Understand the Folder Structure</a></p>
</li>
<li><p><a href="#heading-how-to-build-the-page-layout">How to Build the Page Layout</a></p>
</li>
<li><p><a href="#heading-how-to-build-the-appsidebar-component">How to Build the AppSidebar Component</a></p>
</li>
<li><p><a href="#heading-how-to-define-the-navigation-data">How to Define the Navigation Data</a></p>
</li>
<li><p><a href="#heading-how-to-build-the-navmain-component">How to Build the NavMain Component</a></p>
</li>
<li><p><a href="#heading-how-to-handle-active-states-and-collapsible-menus">How to Handle Active States and Collapsible Menus</a></p>
</li>
<li><p><a href="#heading-how-to-style-the-sidebar">How to Style the Sidebar</a></p>
</li>
<li><p><a href="#heading-live-preview">Live Preview</a></p>
</li>
<li><p><a href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before you start, make sure you have the following:</p>
<ul>
<li><p>Node.js 18+ installed on your machine</p>
</li>
<li><p>Basic knowledge of React and TypeScript</p>
</li>
<li><p>Familiarity with Tailwind CSS utility classes</p>
</li>
<li><p>A package manager installed (npm, pnpm, yarn, or bun)</p>
</li>
</ul>
<p>You don't need prior experience with shadcn/ui, but it helps to have read through <a href="https://ui.shadcn.com/docs">the official docs</a> at least once.</p>
<h2 id="heading-what-you-will-build"><strong>What You Will Build</strong></h2>
<p>In this article, you'll build a fully functional admin dashboard sidebar with the following features:</p>
<ol>
<li><p><strong>Floating sidebar shell</strong>: a card-style sidebar with rounded corners, a drop shadow, and a configurable width</p>
</li>
<li><p><strong>Grouped navigation</strong>: navigation items organized under section labels like Dashboards, Pages, Apps, and Form Elements</p>
</li>
<li><p><strong>Collapsible submenus</strong>: parent items like Blogs and Shadcn Forms that expand on click to reveal child links</p>
</li>
<li><p><strong>Active state tracking</strong>: visual highlighting of the selected parent and child item at all times</p>
</li>
<li><p><strong>Sidebar toggle</strong>: a trigger button in the page header that opens and closes the sidebar</p>
</li>
<li><p><strong>Promotional card</strong>: a "Get Premium" card at the bottom of the sidebar scroll area</p>
</li>
</ol>
<h2 id="heading-why-shadcnui"><strong>Why shadcn/ui?</strong></h2>
<p><a href="https://ui.shadcn.com/"><strong>shadcn/ui</strong></a> is a collection of beautifully designed, accessible React components built on top of Radix UI and styled with Tailwind CSS.</p>
<p>Instead of installing a traditional component library as a dependency, you copy components directly into your project using a CLI. This gives you full ownership of the code structure and styling. You can read every line, change anything, and the components never break because of a library update you didn't control.</p>
<p>Some key benefits of shadcn/ui include:</p>
<ul>
<li><p>Accessible by default, built on Radix and Base UI primitives</p>
</li>
<li><p>Fully styled with Tailwind CSS utility classes</p>
</li>
<li><p>Zero lock-in: the code lives in your project, not inside <code>node_modules</code></p>
</li>
<li><p>Works with Next.js, React, Astro, Vite, and other frameworks</p>
</li>
<li><p>A growing ecosystem of community-built blocks and registries</p>
</li>
</ul>
<p>The <code>Sidebar, Collapsible, ScrollArea, Card, and Button</code> Components you'll use in this tutorial all come from shadcn/ui.</p>
<h2 id="heading-what-is-shadcn-space"><strong>What is Shadcn Space?</strong></h2>
<p><strong>Shadcn Space</strong> is an open-source library of pre-built <a href="https://shadcnspace.com/blocks"><strong>UI blocks</strong></a> built on top of shadcn/ui. It provides ready-to-use dashboard layouts, sidebars, tables, cards, and other common admin UI patterns so you don't have to assemble them from individual primitives every time.</p>
<p>Each block in Shadcn Space is installable directly into your project using the shadcn CLI. Once installed, the code is yours: you can read it, extend it, and adapt it to your design system without any runtime dependency on Shadcn Space itself.</p>
<p>For this tutorial, you'll use the <code>sidebar-06</code> block (it’s free to use), which is a floating admin sidebar with grouped navigation, collapsible submenus, and an integrated scroll area.</p>
<p>Shadcn Space also provides a companion <a href="https://www.figma.com/community/file/1597967874273587400/shadcn-space-figma-ui-kit"><strong>Figma UI Kit</strong></a> that matches the design system used in the blocks, which is useful if you do design work alongside development.</p>
<p>You can explore the full block library and the getting-started documentation in the <a href="https://shadcnspace.com/docs/getting-started/blocks"><strong>official Shadcn Space docs</strong></a>.</p>
<h3 id="heading-how-to-set-up-the-project">How to Set Up the Project</h3>
<p>Start by creating a new Next.js project if you don't already have one:</p>
<pre><code class="language-javascript">npx shadcn@latest init --preset b0 --base base --template next
</code></pre>
<p>This command:</p>
<ul>
<li><p>Creates a Next.js project</p>
</li>
<li><p>Configures Tailwind CSS</p>
</li>
<li><p>Sets up Base UI as the component foundation</p>
</li>
<li><p>Uses Nova style preset</p>
</li>
<li><p>Configures Lucide icons</p>
</li>
<li><p>Uses Inter font</p>
</li>
<li><p>Applies neutral theme tokens</p>
</li>
</ul>
<p>Follow the prompts to configure your base color, CSS variables, and component output directory. This sets up the <code>components/ui</code> directory and the required Tailwind configuration that all shadcn/ui components depend on.</p>
<p>Once the initialization is complete, your <code>components.json</code> project will be created at the root of your project. This file tells the shadcn CLI where to place components, what path aliases you're using, and which styling configuration to follow.</p>
<p>Add this in <code>components.json</code>:</p>
<pre><code class="language-javascript">{
  "registries": {
    "@shadcn-space": {
      "url": "https://shadcnspace.com/r/{name}.json",
    }
  }
}
</code></pre>
<h2 id="heading-how-to-install-the-sidebar-block"><strong>How to Install the Sidebar Block</strong></h2>
<p>With shadcn/ui initialized, you can now pull in the <code>sidebar-06</code> block from Shadcn Space. While Shadcn Space provides components for both Radix UI and Base UI, this tutorial uses the Base UI version. Run one of the following commands depending on your package manager:</p>
<p><strong>npm:</strong></p>
<pre><code class="language-javascript">npx shadcn@latest add @shadcn-space/sidebar-06
</code></pre>
<p><strong>pnpm:</strong></p>
<pre><code class="language-javascript">pnpm dlx shadcn@latest add @shadcn-space/sidebar-06
</code></pre>
<p><strong>yarn:</strong></p>
<pre><code class="language-javascript">yarn dlx shadcn@latest add @shadcn-space/sidebar-06
</code></pre>
<p><strong>bun:</strong></p>
<pre><code class="language-javascript">bunx --bun shadcn@latest add @shadcn-space/sidebar-06
</code></pre>
<p>This command fetches the block from the Shadcn Space registry and scaffolds all the required component files into your project automatically. It also installs any shadcn/ui primitives the block depends on (such as <code>Sidebar, ScrollArea, Card, Button,</code> and <code>Collapsible</code>) if they aren't already present in your components/ui directory.</p>
<p>You can preview the live block and find the installation command on their <a href="https://shadcnspace.com/blocks/dashboard-ui/sidebars"><strong>shadcn sidebar</strong></a> page.</p>
<h2 id="heading-how-to-understand-the-folder-structure"><strong>How to Understand the Folder Structure</strong></h2>
<p>After installation, your project will contain the following new files:</p>
<pre><code class="language-javascript">app/
  sidebar-06/
    page.tsx                  ← Route entry point
assets/
  logo/
    logo.tsx                  ← Logo component
components/
  shadcn-space/
    blocks/
      sidebar-06/
        app-sidebar.tsx       ← Main sidebar shell
        nav-main.tsx          ← Navigation logic and rendering
</code></pre>
<p>Each file has a clearly defined responsibility:</p>
<ul>
<li><p><code>app/sidebar-06/page.tsx</code>: the route entry point that wires the sidebar into a page layout using SidebarProvider</p>
</li>
<li><p><code>assets/logo/logo.tsx</code>: the logo component rendered in the sidebar header</p>
</li>
<li><p><code>components/shadcn-space/blocks/sidebar-06/app-sidebar.tsx</code>: the main sidebar shell, including the header, scroll area, nav data, and promotional card</p>
</li>
<li><p><code>components/shadcn-space/blocks/sidebar-06/nav-main.tsx</code>: all navigation rendering logic, including section labels, leaf items, collapsible parents, and active state management</p>
</li>
</ul>
<p>You'll work through each of these files in detail in the sections below.</p>
<h2 id="heading-how-to-build-the-page-layout"><strong>How to Build the Page Layout</strong></h2>
<p>Open <code>app/sidebar-06/page.tsx</code>. This file is the entry point for your dashboard page. It uses <code>SidebarProvider</code> to establish sidebar context across the page, and <code>SidebarTrigger</code> to render a toggle button inside the header.</p>
<pre><code class="language-javascript">import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/shadcn-space/blocks/sidebar-06/app-sidebar";

const Page = () =&gt; {
  return (
    &lt;SidebarProvider
      className="p-4 bg-muted"
      style={{ "--sidebar-width": "300px" } as React.CSSProperties}
    &gt;
      &lt;AppSidebar /&gt;

      {/* Main content area */}
      &lt;div className="flex flex-1 flex-col gap-4"&gt;
        &lt;header className="flex h-14 shrink-0 items-center gap-2 rounded-xl bg-background px-4 shadow-sm"&gt;
          &lt;SidebarTrigger className="cursor-pointer" /&gt;
        &lt;/header&gt;
        &lt;main className="flex-1 rounded-xl bg-background" /&gt;
      &lt;/div&gt;
    &lt;/SidebarProvider&gt;
  );
};

export default Page;
</code></pre>
<p>Let's break down the key parts of this layout:</p>
<p><strong>SidebarProvider</strong> wraps everything on the page. It manages the sidebar's open/closed state and passes it down to child components via React context. Any component that needs to read or change the sidebar state, including <code>SidebarTrigger</code> and <code>AppSidebar</code>, must be a descendant of <code>SidebarProvider</code>.</p>
<p><strong>The</strong> <code>--sidebar-width</code> <strong>CSS custom property</strong> controls the rendered width of the sidebar. It's set inline using a type assertion (<code>as React.CSSProperties</code>) because TypeScript doesn't know about this custom property by default. Setting it here rather than in a CSS file keeps the width configurable on a per-page basis.</p>
<p><code>SidebarTrigger</code> is a toggle button component that reads the sidebar open/closed state from the nearest <code>SidebarProvider</code> context and flips it on click. It renders in the header so users always have access to the toggle regardless of scroll position.</p>
<p><code>bg-muted</code> on <code>SidebarProvider</code> creates the light gray outer background that makes the floating sidebar card visually stand out from the page.</p>
<h2 id="heading-how-to-build-the-appsidebar-component"><strong>How to Build the AppSidebar Component</strong></h2>
<p>Open <code>components/shadcn-space/blocks/sidebar-06/app-sidebar.tsx</code>. This component is the main sidebar shell. It composes shadcn/ui's <code>Sidebar</code>, <code>SidebarHeader</code>, and <code>SidebarContent</code> layout primitives and wraps the scrollable navigation area in a <code>ScrollArea</code> component to handle overflow independently.</p>
<pre><code class="language-javascript">"use client";

import {
  Sidebar,
  SidebarContent,
  SidebarHeader,
  SidebarMenu,
  SidebarMenuItem,
} from "@/components/ui/sidebar";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import Logo from "@/assets/logo/logo";
import { NavItem, NavMain } from "@/components/shadcn-space/blocks/sidebar-06/nav-main";
import {
  AlignStartVertical,
  PieChart,
  CircleUserRound,
  ClipboardList,
  Notebook,
  NotepadText,
  Table,
  Languages,
  Ticket,
} from "lucide-react";
</code></pre>
<p>The <code>"use client"</code> directive at the top is required because this component uses React state (through <code>NavMain</code>) and event handlers, both of which require the component to run in the browser rather than being server-rendered by Next.js.</p>
<h2 id="heading-how-to-define-the-navigation-data"><strong>How to Define the Navigation Data</strong></h2>
<p>Inside <code>app-sidebar.tsx</code>the navigation structure is defined as a flat array of <code>NavItem</code> objects. Each item belongs to one of three categories:</p>
<ol>
<li><p><strong>A section label</strong> marked with <code>isSection: true</code> and a <code>label</code> string. Renders as an uppercase group heading.</p>
</li>
<li><p><strong>A leaf item</strong> has a <code>title, icon</code>, and <code>href</code>, but no <code>children</code>. Renders as a direct navigation link.</p>
</li>
<li><p><strong>A parent item</strong> has a <code>title, icon</code>, and a <code>children</code> array of sub-items. Renders as a collapsible trigger.</p>
</li>
</ol>
<pre><code class="language-javascript">
export const navData: NavItem[] = [
  // Dashboards Section
  { label: "Dashboards", isSection: true },
  { title: "Analytics", icon: PieChart, href: "#" },
  { title: "CRM Dashboard", icon: ClipboardList, href: "#" },

  // Pages Section
  { label: "Pages", isSection: true },
  { title: "Tables", icon: Table, href: "#" },
  { title: "Forms", icon: ClipboardList, href: "#" },
  { title: "User Profile", icon: CircleUserRound, href: "#" },

  // Apps Section
  { label: "Apps", isSection: true },
  { title: "Notes", icon: Notebook, href: "#" },
  { title: "Tickets", icon: Ticket, href: "#" },
  {
    title: "Blogs",
    icon: Languages,
    children: [
      { title: "Blog Post", href: "#" },
      { title: "Blog Detail", href: "#" },
      { title: "Blog Edit", href: "#" },
      { title: "Blog Create", href: "#" },
      { title: "Manage Blogs", href: "#" },
    ],
  },

  // Form Elements Section
  { label: "Form Elements", isSection: true },
  {
    title: "Shadcn Forms",
    icon: NotepadText,
    children: [
      { title: "Button", href: "#" },
      { title: "Input", href: "#" },
      { title: "Select", href: "#" },
      { title: "Checkbox", href: "#" },
      { title: "Radio", href: "#" },
    ],
  },
  {
    title: "Form layouts",
    icon: AlignStartVertical,
    children: [
      { title: "Forms Horizontal", href: "#" },
      { title: "Forms Vertical", href: "#" },
      { title: "Forms Validation", href: "#" },
      { title: "Forms Examples", href: "#" },
      { title: "Forms Wizard", href: "#" },
    ],
  },
];
</code></pre>
<p>This flat array approach is intentionally simple to maintain. You don't need a nested tree structure because the <code>NavMain</code> component handles the rendering logic for each item type by inspecting each item's shape. Adding a new section, item, or submenu is as straightforward as appending a new object to the array.</p>
<h2 id="heading-how-to-build-the-navmain-component"><strong>How to Build the NavMain Component</strong></h2>
<p>Open <code>components/shadcn-space/blocks/sidebar-06/nav-main.tsx</code>. This file contains all the navigation rendering logic. Start with the type definition and the top-level <code>NavMain</code> function:</p>
<pre><code class="language-javascript">"use client";

import * as React from "react";
import { ChevronRight, LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import {
  Collapsible,
  CollapsibleTrigger,
  CollapsibleContent,
} from "@/components/ui/collapsible";
import {
  SidebarGroup,
  SidebarGroupLabel,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarMenuSub,
  SidebarMenuSubItem,
  SidebarMenuSubButton,
} from "@/components/ui/sidebar";

export type NavItem = {
  label?: string;
  isSection?: boolean;
  title?: string;
  icon?: LucideIcon;
  href?: string;
  children?: NavItem[];
};

export function NavMain({ items }: { items: NavItem[] }) {
  const [activeParent, setActiveParent] = React.useState&lt;string | null&gt;(
    items.find((i) =&gt; !i.isSection)?.title || null
  );
  const [activeChild, setActiveChild] = React.useState&lt;string | null&gt;(null);

  return (
    &lt;&gt;
      {items.map((item, index) =&gt; (
        &lt;NavMainItem
          key={item.title || item.label || index}
          item={item}
          activeParent={activeParent}
          setActiveParent={setActiveParent}
          activeChild={activeChild}
          setActiveChild={setActiveChild}
        /&gt;
      ))}
    &lt;/&gt;
  );
}
</code></pre>
<p><code>activeParent</code> tracks which top-level nav item is currently selected. It initializes to the title of the first non-section item, so the sidebar always has a selection on first render, and you never show the sidebar with nothing highlighted. <code>activeChild</code> tracks which sub-item inside a collapsible menu is selected.</p>
<p>Both state values are passed down as props to each <code>NavMainItem</code>, so every item in the list can read the current selection and trigger updates to it.</p>
<h2 id="heading-how-to-handle-active-states-and-collapsible-menus"><strong>How to Handle Active States and Collapsible Menus</strong></h2>
<p>The <code>NavMainItem</code> function branches into one of three rendering paths based on the shape of the incoming item.</p>
<h3 id="heading-how-to-render-section-labels">How to Render Section Labels</h3>
<pre><code class="language-javascript">if (item.isSection &amp;&amp; item.label) {
  return (
    &lt;SidebarGroup className="p-0 pt-5 first:pt-0"&gt;
      &lt;SidebarGroupLabel className="p-0 text-xs font-medium uppercase text-sidebar-foreground"&gt;
        {item.label}
      &lt;/SidebarGroupLabel&gt;
    &lt;/SidebarGroup&gt;
  );
}
</code></pre>
<p>Section labels use <code>first:pt-0</code> to remove the top padding from the very first section, so the nav starts flush with the header.</p>
<h3 id="heading-how-to-render-collapsible-parent-items">How to Render Collapsible Parent Items</h3>
<pre><code class="language-javascript">if (hasChildren &amp;&amp; item.title) {
  return (
    &lt;SidebarGroup className="p-0"&gt;
      &lt;SidebarMenu&gt;
        &lt;Collapsible open={isOpen} onOpenChange={setIsOpen}&gt;
          &lt;SidebarMenuItem&gt;
            &lt;CollapsibleTrigger
              className="w-full"
              render={
                &lt;SidebarMenuButton
                  id={`nav-main-trigger-${item.title.toLowerCase().replace(/\s+/g, '-')}`}
                  tooltip={item.title}
                  isActive={isParentActive}
                  onClick={() =&gt; setActiveParent(item.title!)}
                  className={cn(
                    "rounded-md text-sm font-medium px-3 py-2 h-9 transition-colors cursor-pointer",
                    isParentActive ? "bg-primary! text-primary-foreground!" : ""
                  )}
                &gt;
                  {item.icon &amp;&amp; &lt;item.icon size={16} /&gt;}
                  &lt;span&gt;{item.title}&lt;/span&gt;
                  &lt;ChevronRight
                    className={cn(
                      "ml-auto transition-transform duration-200",
                      isOpen &amp;&amp; "rotate-90"
                    )}
                  /&gt;
                &lt;/SidebarMenuButton&gt;
              }
            /&gt;
            &lt;CollapsibleContent&gt;
              &lt;SidebarMenuSub className="me-0 pe-0"&gt;
                {item.children!.map((child, index) =&gt; (
                  &lt;NavMainSubItem
                    key={child.title || index}
                    item={child}
                    activeParent={activeParent}
                    setActiveParent={setActiveParent}
                    activeChild={activeChild}
                    setActiveChild={setActiveChild}
                    parentTitle={item.title}
                  /&gt;
                ))}
              &lt;/SidebarMenuSub&gt;
            &lt;/CollapsibleContent&gt;
          &lt;/SidebarMenuItem&gt;
        &lt;/Collapsible&gt;
      &lt;/SidebarMenu&gt;
    &lt;/SidebarGroup&gt;
  );
}
</code></pre>
<p>A <code>useEffect</code> inside <code>NavMainItem</code> syncs the local <code>isOpen</code> state with <code>activeParent</code> so that when a different parent is activated, the previously open collapsible stays open until the user explicitly closes it:</p>
<pre><code class="language-javascript">React.useEffect(() =&gt; {
  if (isParentActive) {
    setIsOpen(true);
  }
}, [isParentActive]);
</code></pre>
<p>The <code>ChevronRight</code> icon rotates 90 degrees when the submenu is open, using a Tailwind transition class:</p>
<pre><code class="language-javascript">&lt;ChevronRight
  className={cn(
    "ml-auto transition-transform duration-200",
    isOpen &amp;&amp; "rotate-90"
  )}
/&gt;
</code></pre>
<h3 id="heading-how-to-render-leaf-items">How to Render Leaf Items</h3>
<pre><code class="language-javascript">if (item.title) {
  return (
    &lt;SidebarGroup className="p-0"&gt;
      &lt;SidebarMenu&gt;
        &lt;SidebarMenuItem&gt;
          &lt;SidebarMenuButton
            id={`nav-main-button-${item.title.toLowerCase().replace(/\s+/g, '-')}`}
            tooltip={item.title}
            isActive={isParentActive}
            onClick={() =&gt; {
              setActiveParent(item.title!);
              setActiveChild(null);
            }}
            className={cn(
              "rounded-md text-sm font-medium px-3 py-2 h-9 transition-colors cursor-pointer",
              isParentActive ? "bg-primary! text-primary-foreground!" : ""
            )}
            render={&lt;a href={item.href} /&gt;}
          &gt;
            {item.icon &amp;&amp; &lt;item.icon /&gt;}
            {item.title}
          &lt;/SidebarMenuButton&gt;
        &lt;/SidebarMenuItem&gt;
      &lt;/SidebarMenu&gt;
    &lt;/SidebarGroup&gt;
  );
}
</code></pre>
<p>The <code>render</code> prop on <code>SidebarMenuButton</code> replaces the default button element with an <code>&lt;a&gt;</code> tag. This preserves correct anchor link semantics and accessibility while keeping the button's visual styling. When a leaf item is clicked, <code>activeChild</code> is reset to <code>null</code> since there is no child to track.</p>
<h3 id="heading-how-to-render-child-items-in-a-submenu">How to Render Child Items in a Submenu</h3>
<p>The <code>NavMainSubItem</code> function handles sub-items inside a collapsible. When a child is clicked, it sets both <code>activeChild</code> to itself and <code>activeParent</code> to its parent's title so the parent item remains visually highlighted:</p>
<pre><code class="language-javascript">if (item.title) {
  return (
    &lt;SidebarMenuSubItem className="w-full"&gt;
      &lt;SidebarMenuSubButton
        id={`nav-sub-button-${item.title.toLowerCase().replace(/\s+/g, '-')}`}
        className={cn(
          "w-full rounded-md transition-colors",
          activeChild === item.title ? "bg-muted! text-foreground!" : ""
        )}
        isActive={activeChild === item.title}
        onClick={() =&gt; {
          setActiveParent(parentTitle || "");
          setActiveChild(item.title!);
        }}
        render={&lt;a href={item.href}&gt;{item.title}&lt;/a&gt;}
      /&gt;
    &lt;/SidebarMenuSubItem&gt;
  );
}
</code></pre>
<p>The child uses a different active style (<code>bg-muted</code> with <code>text-foreground</code>) compared to the parent (<code>bg-primary</code> with <code>text-primary-foreground</code>). This visual distinction makes it easy to see both which section you are in and which specific page is currently active.</p>
<p>Sub-items also support nesting. If a child item itself has a <code>children</code> array, <code>NavMainSubItem</code> renders another <code>Collapsible</code> with a nested <code>SidebarMenuSub</code>, allowing you to build multi-level navigation trees without any changes to the data structure.</p>
<h2 id="heading-how-to-style-the-sidebar"><strong>How to Style the Sidebar</strong></h2>
<p>The full <code>AppSidebar</code> render function puts all of the pieces together:</p>
<pre><code class="language-javascript">export function AppSidebar() {
  return (
    &lt;Sidebar variant="floating" className="p-4 h-full [&amp;_[data-slot=sidebar-inner]]:h-full"&gt;
      &lt;div className="flex flex-col gap-6 overflow-hidden"&gt;

        {/* Header with Logo */}
        &lt;SidebarHeader className="px-4"&gt;
          &lt;SidebarMenu&gt;
            &lt;SidebarMenuItem&gt;
              &lt;a href="#" className="w-full h-full"&gt;
                &lt;Logo /&gt;
              &lt;/a&gt;
            &lt;/SidebarMenuItem&gt;
          &lt;/SidebarMenu&gt;
        &lt;/SidebarHeader&gt;

        {/* Scrollable Navigation Content */}
        &lt;SidebarContent className="overflow-hidden"&gt;
          &lt;ScrollArea className="h-[calc(100vh-100px)]"&gt;
            &lt;div className="px-4"&gt;
              &lt;NavMain items={navData} /&gt;
            &lt;/div&gt;

            {/* Promotional Card */}
            &lt;div className="pt-5 px-4"&gt;
              &lt;Card className="shadow-none ring-0 bg-secondary px-4 py-6"&gt;
                &lt;CardContent className="p-0 flex flex-col gap-3 items-center"&gt;
                  &lt;img
                    src="https://images.shadcnspace.com/assets/backgrounds/download-img.png"
                    alt="sidebar-img"
                    width={74}
                    height={74}
                    className="h-20 w-20"
                  /&gt;
                  &lt;div className="flex flex-col gap-4 items-center"&gt;
                    &lt;div&gt;
                      &lt;p className="text-base font-semibold text-card-foreground text-center"&gt;
                        Grab Pro Now
                      &lt;/p&gt;
                      &lt;p className="text-sm font-regular text-muted-foreground text-center"&gt;
                        Customize your admin
                      &lt;/p&gt;
                    &lt;/div&gt;
                    &lt;Button className="w-fit h-9 px-4 py-2 shadow-none cursor-pointer rounded-xl hover:bg-primary/80"&gt;
                      Get Premium
                    &lt;/Button&gt;
                  &lt;/div&gt;
                &lt;/CardContent&gt;
              &lt;/Card&gt;
            &lt;/div&gt;
          &lt;/ScrollArea&gt;
        &lt;/SidebarContent&gt;

      &lt;/div&gt;
    &lt;/Sidebar&gt;
  );
}
</code></pre>
<p>Let's walk through the key styling decisions:</p>
<p><code>variant="floating"</code> gives the sidebar a card-like appearance with rounded corners and a subtle drop shadow. It visually lifts the sidebar off the background rather than making it flush with the page edge like a standard sidebar would.</p>
<p><code>[&amp;_[data-slot=sidebar-inner]]:h-full</code> is an arbitrary Tailwind variant selector that targets shadcn/ui's internal sidebar slot element. Without this, the sidebar inner container doesn't fill the full available height, which breaks the layout. The <code>data-slot</code> attribute is how shadcn/ui identifies internal sub-elements of compound components.</p>
<p><code>h-[calc(100vh-100px)]</code> on <code>ScrollArea</code> makes the navigation list independently scrollable. The 100px offset accounts for the sidebar header and padding, so the scroll area doesn't overflow the viewport. The rest of the page layout remains static while the nav scrolls.</p>
<p>The <code>bg-secondary</code> card at the bottom of the scroll area is a common admin dashboard pattern, a soft prompt for an upgrade or onboarding action that lives passively in the sidebar without blocking navigation.</p>
<p>For more details on the Sidebar component's API, variants, and configuration options, refer to the official shadcn/ui sidebar docs.</p>
<h2 id="heading-live-preview"><strong>Live Preview</strong></h2>
<img src="https://cdn.hashnode.com/uploads/covers/68b53a3d851476bd2ce87f12/f1538441-fa73-4eb0-af91-04f5bf4fab08.png" alt="f1538441-fa73-4eb0-af91-04f5bf4fab08" style="display:block;margin:0 auto" width="1440" height="892" loading="lazy">

<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>Congratulations! You have now built a complete, production-ready admin dashboard sidebar using shadcn/ui and a community block from Shadcn Space.</p>
<p>Here is a recap of everything you covered:</p>
<ul>
<li><p>Setting up a Next.js project with shadcn/ui initialized and a pre-built sidebar block installed from Shadcn Space</p>
</li>
<li><p>Using <code>SidebarProvider</code> and <code>SidebarTrigger</code> to manage the sidebar open/closed state across a page layout through React context</p>
</li>
<li><p>Defining navigation data as a flat array of typed <code>NavItem</code> objects covering section labels, leaf items, and collapsible parent items</p>
</li>
<li><p>Rendering all three item types from a single <code>navData</code> source in the <code>NavMain</code> and <code>NavMainItem</code> components</p>
</li>
<li><p>Tracking <code>activeParent</code> and <code>activeChild</code> state in a single location and passing them as props so every item can read and update the shared selection state</p>
</li>
<li><p>Using <code>Collapsible</code> with a <code>useEffect</code> sync to keep parent items open when they are active, and animate the chevron icon on expand and collapse</p>
</li>
<li><p>Applying the <code>floating</code> variant, an arbitrary Tailwind slot selector, and <code>ScrollArea</code> with a calculated height to produce a polished, production-appropriate sidebar layout</p>
</li>
</ul>
<p>This pattern scales well beyond what you built here. You can extend <code>NavItem</code> with additional fields like badge counts, permission flags, or external link indicators. You can swap in real <code>href</code> values and connect <code>activeParent</code> and <code>activeChild</code> to your router so the selection always reflects the current URL. You can also add more sections to <code>navData</code> without touching any rendering logic.</p>
<p>For a quick checkout, we have used the Shadcn Space free Shadcn dashboard block in this <a href="https://shadcnspace.com/blocks/dashboard-ui/dashboard-shell"><strong>dashboard shell</strong></a>.</p>
<p>If you want to explore more pre-built admin UI blocks, components, and templates built on top of shadcn/ui, you can browse the full library at <a href="https://shadcnspace.com/"><strong>Shadcn Space</strong></a>.</p>
<h3 id="heading-resources"><strong>Resources</strong></h3>
<ul>
<li><p><a href="https://shadcnspace.com/blocks"><strong>Shadcn UI Blocks</strong></a></p>
</li>
<li><p><a href="https://shadcnspace.com/components"><strong>Shadcn UI Components</strong></a></p>
</li>
<li><p><a href="https://shadcnspace.com/docs/getting-started/blocks"><strong>Shadcn Space Getting Started Docs</strong></a></p>
</li>
<li><p><a href="https://www.figma.com/community/file/1597967874273587400/shadcn-space-figma-ui-kit"><strong>Figma UI Kit Design System</strong></a></p>
</li>
<li><p><a href="https://ui.shadcn.com/docs/components/sidebar"><strong>shadcn/ui Sidebar Docs</strong></a></p>
</li>
<li><p><a href="https://base-ui.com/"><strong>Base UI</strong></a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Responsive and Accessible UI Designs with React and Semantic HTML ]]>
                </title>
                <description>
                    <![CDATA[ Building modern React applications requires more than just functionality. It also demands responsive layouts and accessible user experiences. By combining semantic HTML, responsive design techniques,  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-responsive-accessible-ui-with-react-and-semantic-html/</link>
                <guid isPermaLink="false">69d539975da14bc70e76871d</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Responsive Web Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ semantichtml ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aria ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gopinath Karunanithi ]]>
                </dc:creator>
                <pubDate>Tue, 07 Apr 2026 17:06:31 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/d2651d02-040d-4c4f-bbfe-ef92097edab4.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building modern React applications requires more than just functionality. It also demands responsive layouts and accessible user experiences.</p>
<p>By combining semantic HTML, responsive design techniques, and accessibility best practices (like ARIA roles and keyboard navigation), developers can create interfaces that work across devices and for all users, including those with disabilities.</p>
<p>This article shows how to design scalable, inclusive React UIs using real-world patterns and code examples.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-overview">Overview</a></p>
</li>
<li><p><a href="#heading-why-accessibility-and-responsiveness-matter">Why Accessibility and Responsiveness Matter</a></p>
</li>
<li><p><a href="#heading-core-principles-of-accessible-and-responsive-design">Core Principles of Accessible and Responsive Design</a></p>
</li>
<li><p><a href="#heading-using-semantic-html-in-react">Using Semantic HTML in React</a></p>
</li>
<li><p><a href="#heading-structuring-a-page-with-semantic-elements">Structuring a Page with Semantic Elements</a></p>
</li>
<li><p><a href="#heading-building-responsive-layouts">Building Responsive Layouts</a></p>
</li>
<li><p><a href="#heading-accessibility-with-aria">Accessibility with ARIA</a></p>
</li>
<li><p><a href="#heading-keyboard-navigation">Keyboard Navigation</a></p>
</li>
<li><p><a href="#heading-focus-management">Focus Management</a></p>
</li>
<li><p><a href="#heading-forms-and-accessibility">Forms and Accessibility</a></p>
</li>
<li><p><a href="#heading-responsive-typography-and-images">Responsive Typography and Images</a></p>
</li>
<li><p><a href="#heading-building-a-fully-accessible-responsive-component-end-to-end-example">Building a Fully Accessible Responsive Component (End-to-End Example)</a></p>
</li>
<li><p><a href="#heading-testing-accessibility">Testing Accessibility</a></p>
</li>
<li><p><a href="#heading-best-practices">Best Practices</a></p>
</li>
<li><p><a href="#heading-when-not-to-overuse-accessibility-features">When NOT to Overuse Accessibility Features</a></p>
</li>
<li><p><a href="#heading-future-enhancements">Future Enhancements</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before following along, you should be familiar with:</p>
<ul>
<li><p>React fundamentals (components, hooks, JSX)</p>
</li>
<li><p>Basic HTML and CSS</p>
</li>
<li><p>JavaScript ES6 features</p>
</li>
<li><p>Basic understanding of accessibility concepts (helpful but not required)</p>
</li>
</ul>
<h2 id="heading-overview">Overview</h2>
<p>Modern web applications must serve a diverse audience across a wide range of devices, screen sizes, and accessibility needs. Users today expect seamless experiences whether they are browsing on a desktop, tablet, or mobile device – and they also expect interfaces that are usable regardless of physical or cognitive limitations.</p>
<p>Two essential principles help achieve this:</p>
<ul>
<li><p>Responsive design, which ensures layouts adapt to different screen sizes</p>
</li>
<li><p>Accessibility, which ensures applications are usable by people with disabilities</p>
</li>
</ul>
<p>In React applications, these principles are often implemented incorrectly or treated as afterthoughts. Developers may rely heavily on div-based layouts, ignore semantic HTML, or overlook accessibility features such as keyboard navigation and screen reader support.</p>
<p>This article will show you how to build responsive and accessible UI designs in React using semantic HTML. You'll learn how to:</p>
<ul>
<li><p>Structure components using semantic HTML elements</p>
</li>
<li><p>Build responsive layouts using modern CSS techniques</p>
</li>
<li><p>Improve accessibility with ARIA attributes and proper roles</p>
</li>
<li><p>Ensure keyboard navigation and screen reader compatibility</p>
</li>
<li><p>Apply best practices for scalable and inclusive UI design</p>
</li>
</ul>
<p>By the end of this guide, you'll be able to create React interfaces that are not only visually responsive but also accessible to all users.</p>
<h2 id="heading-why-accessibility-and-responsiveness-matter">Why Accessibility and Responsiveness Matter</h2>
<p>Responsive and accessible design isn't just about compliance. It directly impacts usability, performance, and reach.</p>
<p><strong>Accessibility benefits:</strong></p>
<ul>
<li><p>Supports users with visual, motor, or cognitive impairments</p>
</li>
<li><p>Improves SEO and content discoverability</p>
</li>
<li><p>Enhances usability for all users</p>
</li>
</ul>
<p><strong>Responsiveness benefits:</strong></p>
<ul>
<li><p>Ensures consistent UX across devices</p>
</li>
<li><p>Reduces bounce rates on mobile</p>
</li>
<li><p>Improves performance and scalability</p>
</li>
</ul>
<p>Ignoring these principles can result in broken layouts on smaller screens, poor screen reader compatibility, and limited reach and usability.</p>
<h2 id="heading-core-principles-of-accessible-and-responsive-design">Core Principles of Accessible and Responsive Design</h2>
<p>Before diving into the code, it’s important to understand the foundational principles.</p>
<h3 id="heading-1-semantic-html-first">1. Semantic HTML First</h3>
<p>Semantic HTML refers to using HTML elements that clearly describe their meaning and role in the interface, rather than relying on generic containers like <code>&lt;div&gt; or &lt;span&gt;.</code>These elements provide built-in accessibility, improve SEO, and make code more readable.</p>
<p>For example:</p>
<p><strong>Non-semantic:</strong></p>
<pre><code class="language-html">&lt;div onClick={handleClick}&gt;Submit&lt;/div&gt;
</code></pre>
<p><strong>Semantic:</strong></p>
<pre><code class="language-html">&lt;button type="button" onClick={handleClick}&gt;Submit&lt;/button&gt;
</code></pre>
<p>Another example:</p>
<p><strong>Non-semantic:</strong></p>
<pre><code class="language-html">&lt;div className="header"&gt;My App&lt;/div&gt;
</code></pre>
<p><strong>Semantic:</strong></p>
<pre><code class="language-html">&lt;header&gt;My App&lt;/header&gt;
</code></pre>
<p>Using semantic elements such as <code>&lt;header&gt;</code>, <code>&lt;nav&gt;</code>, <code>&lt;main&gt;</code>, <code>&lt;section&gt;</code>, <code>&lt;article&gt;</code>, and <code>&lt;button&gt;</code> helps browsers and assistive technologies (like screen readers) understand the structure and purpose of your UI without additional configuration.</p>
<p>Why this matters:</p>
<ul>
<li><p>Screen readers understand semantic elements automatically</p>
</li>
<li><p>It supports built-in accessibility (keyboard, focus, roles)</p>
</li>
<li><p>There's less need for ARIA attributes</p>
</li>
<li><p>It gives you better SEO and maintainability</p>
</li>
</ul>
<h3 id="heading-2-mobile-first-design">2. Mobile-First Design</h3>
<p>Mobile-first design means starting your UI design with the smallest screen sizes (typically mobile devices) and progressively enhancing the layout for larger screens such as tablets and desktops.</p>
<p>This approach makes sure that core content and functionality are prioritized, layouts remain simple and performant, and users on mobile devices get a fully usable experience.</p>
<p>In practice, mobile-first design involves:</p>
<ul>
<li><p>Using a single-column layout initially</p>
</li>
<li><p>Applying minimal styling and spacing</p>
</li>
<li><p>Avoiding complex UI patterns on small screens</p>
</li>
</ul>
<p>Then, you scale up using CSS media queries:</p>
<pre><code class="language-css">.container {
  display: flex;
  flex-direction: column;
}
@media (min-width: 768px) {
  .container {
    flex-direction: row;
  }
}
</code></pre>
<p>Here, the default layout is optimized for mobile, and enhancements are applied only when the screen size increases.</p>
<p><strong>Why this approach works:</strong></p>
<ul>
<li><p>Prioritizes essential content</p>
</li>
<li><p>Improves performance on mobile devices</p>
</li>
<li><p>Reduces layout bugs when scaling up</p>
</li>
<li><p>Aligns with how most users access web apps today</p>
</li>
</ul>
<h3 id="heading-3-progressive-enhancement">3. Progressive Enhancement</h3>
<p>Progressive enhancement is the practice of building a baseline user experience that works for all users (regardless of their device, browser capabilities, or network conditions) and then layering on advanced features for more capable environments.</p>
<p>This approach ensures that core functionality is always accessible, users on older devices or slow networks aren't blocked, and accessibility is preserved even when advanced features fail.</p>
<p>In practice, this means:</p>
<ul>
<li><p>Start with semantic HTML that delivers content and functionality</p>
</li>
<li><p>Add basic styling with CSS for layout and readability</p>
</li>
<li><p>Enhance interactivity using JavaScript (React) only where needed</p>
</li>
</ul>
<p>For example, a form should still be usable with plain HTML:</p>
<pre><code class="language-html">&lt;form&gt;
  &lt;label htmlFor="email"&gt;Email&lt;/label&gt;
  &lt;input id="email" type="email" /&gt;
  &lt;button type="submit"&gt;Submit&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<p>Then, React can enhance it with validation, dynamic feedback, or animations.</p>
<p>By prioritizing functionality first and enhancements later, you ensure your application remains usable in a wide range of real-world scenarios.</p>
<h3 id="heading-4-keyboard-accessibility">4. Keyboard Accessibility</h3>
<p>Keyboard accessibility ensures that users can navigate and interact with your application using only a keyboard. This is critical for users with motor disabilities and also improves usability for power users.</p>
<p>Key aspects of keyboard accessibility include:</p>
<ul>
<li><p>Ensuring all interactive elements (buttons, links, inputs) are focusable</p>
</li>
<li><p>Maintaining a logical tab order across the page</p>
</li>
<li><p>Providing visible focus indicators (for example, outline styles)</p>
</li>
<li><p>Supporting keyboard events such as Enter and Space</p>
</li>
</ul>
<p><strong>Bad Example (Not Accessible)</strong></p>
<pre><code class="language-html">&lt;div onClick={handleClick}&gt;Submit&lt;/div&gt;
</code></pre>
<p>This element:</p>
<ul>
<li><p>Cannot be focused with a keyboard</p>
</li>
<li><p>Does not respond to Enter/Space</p>
</li>
<li><p>Is invisible to screen readers</p>
</li>
</ul>
<p><strong>Good Example</strong></p>
<pre><code class="language-html">&lt;button type="button" onClick={handleClick}&gt;Submit&lt;/button&gt;
</code></pre>
<p>This automatically supports:</p>
<ul>
<li><p>Keyboard interaction</p>
</li>
<li><p>Focus management</p>
</li>
<li><p>Screen reader announcements</p>
</li>
</ul>
<p><strong>Custom Component Example (if needed)</strong></p>
<pre><code class="language-html">&lt;div
  role="button"
  tabIndex={0}
  onClick={handleClick}
  onKeyDown={(e) =&gt; {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      handleClick();
    }
  }}
&gt;
  Submit
&lt;/div&gt;
</code></pre>
<p>But only use this when native elements aren't sufficient.</p>
<p>These principles form the foundation of accessible and responsive design:</p>
<ul>
<li><p>Use semantic HTML to communicate intent</p>
</li>
<li><p>Design for mobile first, then scale up</p>
</li>
<li><p>Enhance progressively for better compatibility</p>
</li>
<li><p>Ensure full keyboard accessibility</p>
</li>
</ul>
<p>Applying these early prevents major usability and accessibility issues later in development.</p>
<h2 id="heading-using-semantic-html-in-react">Using Semantic HTML in React</h2>
<p>As we briefly discussed above, semantic HTML plays a critical role in both accessibility (a11y) and code readability. Semantic elements clearly describe their purpose to both developers and browsers, which allows assistive technologies like screen readers to interpret and navigate the UI correctly.</p>
<p>For example, when you use a <code>&lt;button&gt;</code> element, browsers automatically provide keyboard support, focus behavior, and accessibility roles. In contrast, non-semantic elements like <code>&lt;div&gt;</code>require additional attributes and manual handling to achieve the same functionality.</p>
<p>From a readability perspective, semantic HTML makes your code easier to understand and maintain. Developers can quickly identify the structure and intent of a component without relying on class names or external documentation.</p>
<p><strong>Bad Example (Non-semantic)</strong></p>
<pre><code class="language-html">&lt;div onClick={handleClick}&gt;Submit&lt;/div&gt;
</code></pre>
<p>Why this is problematic:</p>
<ul>
<li><p>The <code>&lt;div&gt;</code>element has no inherent meaning or role</p>
</li>
<li><p>It is not focusable by default, so keyboard users can't access it</p>
</li>
<li><p>It does not respond to keyboard events like Enter or Space unless explicitly coded</p>
</li>
<li><p>Screen readers do not recognize it as an interactive element</p>
</li>
</ul>
<p>To make this accessible, you would need to add:</p>
<p><code>role="button"</code></p>
<p><code>tabIndex="0"</code></p>
<p><code>Keyboard event handlers</code></p>
<p><strong>Good Example (Semantic)</strong></p>
<pre><code class="language-html">&lt;button type="button" onClick={handleClick}&gt;Submit&lt;/button&gt;
</code></pre>
<p>Why this is better:</p>
<ul>
<li><p>The <code>&lt;button&gt;</code> element is inherently interactive</p>
</li>
<li><p>It is automatically focusable and keyboard accessible</p>
</li>
<li><p>It supports Enter and Space key activation by default</p>
</li>
<li><p>Screen readers correctly announce it as a button</p>
</li>
</ul>
<p>This reduces complexity while improving accessibility and usability.</p>
<h3 id="heading-why-all-this-matters">Why all this matters:</h3>
<p>There are many reasons to use semantic HTML.</p>
<p>First, semantic elements like <code>&lt;button&gt;, &lt;a&gt;,</code> and <code>&lt;form&gt;</code> come with default accessibility behaviors such as focus management and keyboard interaction</p>
<p>It also reduces complexity: you don’t need to manually implement roles, keyboard handlers, or tab navigation</p>
<p>They provide better screen reader support as well. Assistive technologies can correctly interpret the purpose of elements and announce them appropriately</p>
<p>Semantic HTML also improves maintainability and helps other developers quickly understand the intent of your code without reverse-engineering behavior from event handlers</p>
<p>Finally, you'll generally have fewer bugs in your code. Relying on native browser behavior reduces the risk of missing critical accessibility features</p>
<p>Here's another example:</p>
<p><strong>Non-semantic:</strong></p>
<pre><code class="language-html">&lt;div className="nav"&gt;
  &lt;div onClick={goHome}&gt;Home&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p><strong>Semantic:</strong></p>
<pre><code class="language-html">&lt;nav&gt;
  &lt;a href="/"&gt;Home&lt;/a&gt;
&lt;/nav&gt;
</code></pre>
<p>Here, <code>&lt;nav&gt;</code> clearly defines a navigation region, and <code>&lt;a&gt;</code> provides built-in link behavior, including keyboard navigation and proper screen reader announcements.</p>
<h2 id="heading-structuring-a-page-with-semantic-elements">Structuring a Page with Semantic Elements</h2>
<p>When building a React application, structuring your layout with semantic HTML elements helps define clear regions of your interface. Instead of relying on generic containers like <code>&lt;div&gt;</code>, semantic elements communicate the purpose of each section to both developers and assistive technologies.</p>
<p>In the example below, we're creating a basic page layout using commonly used semantic elements such as <code>&lt;header&gt;</code>, <code>&lt;nav&gt;</code>, <code>&lt;main&gt;</code>, <code>&lt;section&gt;</code>, and <code>&lt;footer&gt;</code>. Each of these elements represents a specific part of the UI and contributes to better accessibility and maintainability.</p>
<pre><code class="language-javascript">function Layout() {
  return (
    &lt;&gt;
      {/* Skip link for keyboard and screen reader users */}
      &lt;a href="#main-content" className="skip-link"&gt;
        Skip to main content
      &lt;/a&gt;

      &lt;header&gt;
        &lt;h1&gt;My App&lt;/h1&gt;
      &lt;/header&gt;

      &lt;nav&gt;
        &lt;ul&gt;
          &lt;li&gt;&lt;a href="/"&gt;Home&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/nav&gt;

      &lt;main id="main-content"&gt;
        &lt;section&gt;
          &lt;h2&gt;Dashboard&lt;/h2&gt;
        &lt;/section&gt;
      &lt;/main&gt;

      &lt;footer&gt;
        &lt;p&gt;© 2026&lt;/p&gt;
      &lt;/footer&gt;
    &lt;/&gt;
  );
}
</code></pre>
<p>Each element in this layout has a specific role:</p>
<ul>
<li><p>The skip link allows screen reader users to skip to the main content</p>
</li>
<li><p><code>&lt;header&gt;</code>: Represents introductory content or branding</p>
</li>
<li><p><code>&lt;nav&gt;</code>: Contains navigation links</p>
</li>
<li><p><code>&lt;main&gt;</code>: Holds the primary content of the page</p>
</li>
<li><p><code>&lt;section&gt;</code>: Groups related content within the page</p>
</li>
<li><p><code>&lt;footer&gt;</code>: Contains closing or supplementary information</p>
</li>
</ul>
<p>Using these elements correctly ensures your UI is both logically structured and accessible by default.</p>
<h3 id="heading-why-this-structure-is-important">Why this structure is important:</h3>
<p>Properly structuring a page like this brings with it many benefits.</p>
<p>For example, it gives you Improved screen reader navigation. This is because semantic elements allow screen readers to identify different regions of the page (for example, navigation, main content, footer). Users can quickly jump between these sections instead of reading the page linearly</p>
<p>It also gives you better document structure. Elements like <code>&lt;main&gt;</code> and <code>&lt;section&gt;</code> define a logical hierarchy, making content easier to parse for both browsers and assistive technologies</p>
<p>Search engines also use semantic structure to better understand page content and prioritize important sections, resulting in better SEO.</p>
<p>It also makes your code more readable, so other devs can immediately understand the layout and purpose of each section without relying on class names or comments</p>
<p>And it provides built-in accessibility landmarks using elements like <code>&lt;nav&gt;</code> and <code>&lt;main&gt;</code>, allowing assistive technologies to provide shortcuts for users.</p>
<h2 id="heading-building-responsive-layouts">Building Responsive Layouts</h2>
<p>Responsive layouts ensure that your UI adapts smoothly across different screen sizes, from mobile devices to large desktop displays. Instead of building separate layouts for each device, modern CSS techniques like Flexbox, Grid, and media queries allow you to create flexible, fluid designs.</p>
<p>In this section, we’ll look at how layout behavior changes based on screen size, starting with a mobile-first approach and progressively enhancing the layout for larger screens.</p>
<p><strong>Using CSS Flexbox:</strong></p>
<pre><code class="language-css">.container {
  display: flex;
  flex-direction: column;
}

@media (min-width: 768px) {
  .container {
    flex-direction: row;
  }
}
</code></pre>
<p>On smaller screens (mobile), elements are stacked vertically using <code>flex-direction: column</code>, making content easier to read and scroll.</p>
<p>On larger screens (768px and above), the layout switches to a horizontal row, utilizing available screen space more efficiently.</p>
<p><strong>Why this helps:</strong></p>
<ul>
<li><p>Ensures content is readable on small devices without horizontal scrolling</p>
</li>
<li><p>Improves layout efficiency on larger screens</p>
</li>
<li><p>Supports a mobile-first design strategy by defining the default layout for smaller screens first and enhancing it progressively</p>
</li>
</ul>
<p><strong>Using CSS Grid:</strong></p>
<pre><code class="language-css">.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

@media (min-width: 768px) {
  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
</code></pre>
<p>On mobile devices, content is displayed in a single-column layout (<code>1fr</code>), ensuring each item takes full width.</p>
<p>On larger screens, the layout shifts to three equal columns using <code>repeat(3, 1fr)</code>, creating a grid structure.</p>
<p><strong>Why this helps:</strong></p>
<ul>
<li><p>Provides a clean and consistent way to manage complex layouts</p>
</li>
<li><p>Makes it easy to scale from simple to multi-column designs</p>
</li>
<li><p>Improves visual balance and spacing across different screen sizes</p>
</li>
</ul>
<p><strong>React Example:</strong></p>
<pre><code class="language-javascript">function CardGrid() {
  return (
    &lt;div className="grid"&gt;
      &lt;div className="card"&gt;Item 1&lt;/div&gt;
      &lt;div className="card"&gt;Item 2&lt;/div&gt;
      &lt;div className="card"&gt;Item 3&lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>The React component uses the .grid class to apply responsive Grid behavior. Each card automatically adjusts its position based on screen size.</p>
<p><strong>Why this is effective:</strong></p>
<ul>
<li><p>Separates structure (React JSX) from layout (CSS)</p>
</li>
<li><p>Allows you to reuse the same component across different screen sizes without modification</p>
</li>
<li><p>Ensures consistent responsiveness across your application with minimal code</p>
</li>
</ul>
<p>By combining Flexbox for one-dimensional layouts and Grid for two-dimensional layouts, you can build highly adaptable interfaces that respond efficiently to different devices and screen sizes.</p>
<h2 id="heading-accessibility-with-aria">Accessibility with ARIA</h2>
<p>ARIA (Accessible Rich Internet Applications) is a set of attributes that enhance the accessibility of web content, especially when building custom UI components that cannot be fully implemented using native HTML elements.</p>
<p>ARIA works by providing additional semantic information to assistive technologies such as screen readers. It does this through:</p>
<ul>
<li><p>Roles, which define what an element is (for example, button, dialog, menu)</p>
</li>
<li><p>States and properties, which describe the current condition or behavior of an element (for example, expanded, hidden, live updates)</p>
</li>
</ul>
<p>For example, when you create a custom dropdown using <code>&lt;div&gt;</code> elements, browsers don't inherently understand its purpose. By applying ARIA roles and attributes, you can communicate that this structure behaves like a menu and ensure it is interpreted correctly.</p>
<p>Just make sure you use ARIA carefully. Incorrect or unnecessary usage can reduce accessibility. Here's a key rule to follow: use native HTML first. Only use ARIA when necessary.</p>
<p>ARIA is especially useful for:</p>
<ul>
<li><p>Custom UI components (modals, tabs, dropdowns)</p>
</li>
<li><p>Dynamic content updates</p>
</li>
<li><p>Complex interactions not covered by standard HTML</p>
</li>
</ul>
<p>Something to note before we get into the examples here: real-world accessibility is complex. For production apps, you should typically prefer well-tested libraries like react-aria, Radix UI, or Headless UI. These examples are primarily for educational purposes and aren't production-ready.</p>
<p><strong>Example: Accessible Modal</strong></p>
<pre><code class="language-javascript">function Modal({ isOpen, onClose }) {
  const dialogRef = React.useRef();

  React.useEffect(() =&gt; {
    if (isOpen) {
      dialogRef.current?.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    &lt;div
      role="dialog"
      aria-modal="true"
      aria-labelledby="modal-title"
      tabIndex={-1}
      ref={dialogRef}
      onKeyDown={(e) =&gt; {
        if (e.key === 'Escape') onClose();
      }}
    &gt;
      &lt;h2 id="modal-title"&gt;Modal Title&lt;/h2&gt;
      &lt;button type="button" onClick={onClose}&gt;Close&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p><strong>How this works:</strong></p>
<ul>
<li><p><code>role="dialog"</code> identifies the element as a modal dialog</p>
</li>
<li><p><code>aria-modal="true"</code> indicates that background content is inactive</p>
</li>
<li><p><code>aria-labelledby</code> connects the dialog to its visible title for screen readers</p>
</li>
<li><p><code>tabIndex={-1}</code> allows the dialog container to receive focus programmatically</p>
</li>
<li><p>Focus is moved to the dialog when it opens</p>
</li>
<li><p>Pressing Escape closes the modal, which is a standard accessibility expectation</p>
</li>
</ul>
<p>This ensures that users can understand, navigate, and exit the modal using both keyboard and assistive technologies.</p>
<h3 id="heading-key-aria-attributes">Key ARIA Attributes</h3>
<h4 id="heading-1-role">1. role</h4>
<p>Defines the type of element and its purpose. For example, <code>role="dialog"</code> tells assistive technologies that the element behaves like a modal dialog.</p>
<h4 id="heading-2-aria-label">2. aria-label</h4>
<p>Provides an accessible name for an element when visible text is not sufficient. Screen readers use this label to describe the element to users.</p>
<h4 id="heading-3-aria-hidden">3. aria-hidden</h4>
<p>Indicates whether an element should be ignored by assistive technologies. For example, <code>aria-hidden="true"</code> hides decorative elements from screen readers.</p>
<h4 id="heading-4-aria-live">4. aria-live</h4>
<p>Used for dynamic content updates. It tells screen readers to announce changes automatically without requiring user interaction (for example, form validation messages or notifications).</p>
<p><strong>Example: Accessible Dropdown (Custom Component)</strong></p>
<pre><code class="language-javascript">function Dropdown({ isOpen, toggle }) {
  return (
    &lt;div&gt;
      &lt;button
        type="button"
        aria-expanded={isOpen}
        aria-controls="dropdown-menu"
        onClick={toggle}
      &gt;
        Menu
      &lt;/button&gt;

      {isOpen &amp;&amp; (
        &lt;ul id="dropdown-menu"&gt;
          &lt;li&gt;
            &lt;button type="button" onClick={() =&gt; console.log('Item 1')}&gt;
              Item 1
            &lt;/button&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;button type="button" onClick={() =&gt; console.log('Item 2')}&gt;
              Item 2
            &lt;/button&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      )}
    &lt;/div&gt;
  );
}
</code></pre>
<p><strong>How this works:</strong></p>
<ul>
<li><p><code>aria-expanded</code> indicates whether the dropdown is open or closed</p>
</li>
<li><p><code>aria-controls</code> links the button to the dropdown content via its id</p>
</li>
<li><p>The <code>&lt;button&gt;</code> element acts as the trigger and is fully keyboard accessible</p>
</li>
<li><p>The <code>&lt;ul&gt;</code> and <code>&lt;li&gt;</code> elements provide a natural list structure</p>
</li>
<li><p>Using <code>&lt;a&gt;</code> elements ensures proper navigation behavior and accessibility</p>
</li>
</ul>
<p>Why this approach is correct:</p>
<ul>
<li><p>It follows standard web patterns instead of application-style menus</p>
</li>
<li><p>It avoids misusing ARIA roles like role="menu", which require complex keyboard handling</p>
</li>
<li><p>Screen readers can correctly interpret the structure without additional roles</p>
</li>
<li><p>It keeps the implementation simple, accessible, and maintainable</p>
</li>
</ul>
<p>If you need advanced menu behavior (like arrow key navigation), then ARIA menu roles may be appropriate –&nbsp;but only when fully implemented according to the ARIA Authoring Practices.</p>
<p>Note: Most dropdowns in web applications are not true "menus" in the ARIA sense. Avoid using role="menu" unless you are implementing full keyboard navigation (arrow keys, focus management, and so on).</p>
<h2 id="heading-keyboard-navigation">Keyboard Navigation</h2>
<p>Keyboard navigation ensures that users can fully interact with your application using only a keyboard, without relying on a mouse. This is essential for users with motor disabilities, but it also benefits power users and developers who prefer keyboard-based workflows.</p>
<p>In a well-designed interface, users should be able to:</p>
<ul>
<li><p>Navigate through interactive elements using the Tab key</p>
</li>
<li><p>Activate buttons and links using Enter or Space</p>
</li>
<li><p>Clearly see which element is currently focused</p>
</li>
</ul>
<p>In the example below, we’ll look at common mistakes in keyboard handling and why relying on native HTML elements is usually the better approach.</p>
<p><strong>Example:</strong></p>
<p>Avoid adding custom keyboard handlers to native elements like <code>&lt;button&gt;</code>, as they already support keyboard interaction by default.</p>
<p>For example, this is all you need:</p>
<pre><code class="language-html">&lt;button type="button" onClick={handleClick}&gt;Submit&lt;/button&gt;
</code></pre>
<p>This automatically supports:</p>
<ul>
<li><p>Enter and Space key activation</p>
</li>
<li><p>Focus management</p>
</li>
<li><p>Screen reader announcements</p>
</li>
</ul>
<p>Adding manual keyboard event handlers here is unnecessary and can introduce bugs or inconsistent behavior.</p>
<p><strong>What this example shows:</strong></p>
<p>Avoid manually handling keyboard events for native interactive elements like <code>&lt;button&gt;</code>. These elements already provide built-in keyboard support and accessibility features.</p>
<p>For example:</p>
<pre><code class="language-html">&lt;button type="button" onClick={handleClick}&gt;Submit&lt;/button&gt;
</code></pre>
<p>Why this works:</p>
<ul>
<li><p>Supports both Enter and Space key activation by default</p>
</li>
<li><p>Is focusable and participates in natural tab order</p>
</li>
<li><p>Provides built-in accessibility roles and screen reader announcements</p>
</li>
<li><p>Reduces the need for additional logic or ARIA attributes</p>
</li>
</ul>
<p>Adding custom keyboard handlers (like onKeyDown) to native elements is unnecessary and can introduce bugs or inconsistent behavior. Always prefer native HTML elements for interactivity whenever possible.</p>
<h3 id="heading-avoiding-common-keyboard-traps">Avoiding Common Keyboard Traps</h3>
<p>One of the most common keyboard accessibility issues is “trapping users inside interactive components”, such as modals or custom dropdowns. This happens when focus is moved into a component but can't escape using Tab, Shift+Tab, or other keyboard controls. Users relying on keyboards may become stuck, unable to navigate to other parts of the page.</p>
<p>In the example below, you'll see a simple modal that tries to set focus, but doesn’t manage Tab behavior properly.</p>
<pre><code class="language-javascript">function Modal({ isOpen }) {
  const ref = React.useRef();

  React.useEffect(() =&gt; {
    if (isOpen) ref.current?.focus();
  }, [isOpen]);

  return (
    &lt;div role="dialog"&gt;
      &lt;button type="button" ref={ref}&gt;Close&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>What this code shows:</p>
<ul>
<li><p>When the modal opens, focus is moved to the Close button using <code>ref.current.focus()</code></p>
</li>
<li><p>The modal uses <code>role="dialog"</code> to communicate its purpose</p>
</li>
</ul>
<p>There are some issues with this code that you should be aware of. First, tabbing inside the modal may allow focus to move outside the modal if additional focusable elements exist.</p>
<p>Users may also become trapped if no mechanism returns focus to the triggering element when the modal closes.</p>
<p>There's also no handling of Shift+Tab or cycling focus is present.</p>
<p>This demonstrates a <strong>partial focus management</strong>, but it’s not fully accessible yet.</p>
<p>To improve focus management, you can trap focus within the modal by ensuring that Tab and Shift+Tab cycle only through elements inside the modal.</p>
<p>You can also return focus to the trigger: when the modal closes, return focus to the element that opened it.</p>
<p><strong>Example improvement (conceptual):</strong></p>
<pre><code class="language-javascript">function Modal({ isOpen, onClose, triggerRef }) {
  const modalRef = React.useRef();

  React.useEffect(() =&gt; {
    if (isOpen) {
      modalref.current?.focus();
      // Add focus trap logic here
    } else {
      triggerref.current?.focus();
    }
  }, [isOpen]);

  return (
    &lt;div role="dialog" ref={modalRef} tabIndex={-1}&gt;
      &lt;button type="button" onClick={onClose}&gt;Close&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Remember that this modal is not fully accessible without focus trapping. In production, use a library like <code>focus-trap-react</code>, <code>react-aria</code>, or Radix UI.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><p><code>tabIndex={-1}</code> allows the div to receive programmatic focus</p>
</li>
<li><p>Focus trap ensures users cannot tab out unintentionally</p>
</li>
<li><p>Returning focus preserves context, so users can continue where they left off</p>
</li>
</ul>
<p><strong>Best practices:</strong></p>
<ul>
<li><p>Always move focus into modals</p>
</li>
<li><p>Return focus to the trigger element when closed</p>
</li>
<li><p>Ensure Tab cycles correctly</p>
</li>
</ul>
<p>As a general rule, always prefer native HTML elements for interactivity. Only implement custom keyboard handling when building advanced components that cannot be achieved with standard elements.</p>
<h2 id="heading-focus-management">Focus Management</h2>
<p>Focus management is the practice of controlling where keyboard focus goes when users interact with components such as modals, forms, or interactive widgets. Proper focus management ensures that:</p>
<ul>
<li><p>Users relying on keyboards or assistive technologies can navigate seamlessly</p>
</li>
<li><p>Focus does not get lost or trapped in unexpected places</p>
</li>
<li><p>Users maintain context when content updates dynamically</p>
</li>
</ul>
<p>The example below shows a common approach that only partially handles focus:</p>
<p><strong>Bad Example:</strong></p>
<pre><code class="language-javascript">// Bad Example: Automatically focusing input without context
const ref = React.useRef();
React.useEffect(() =&gt; {
  ref.current?.focus();
}, []);
&lt;input ref={ref} placeholder="Name" /&gt;
</code></pre>
<p>In the above code, the input receives focus as soon as the component mounts, but there’s no handling for returning focus when the user navigates away.</p>
<p>If this input is inside a modal or dynamic content, users may get lost or trapped. There aren't any focus indicators or context for assistive technologies.</p>
<p>This is a minimal solution that can cause confusion in real applications.</p>
<p><strong>Improved Example:</strong></p>
<pre><code class="language-javascript">// Improved Example: Managing focus in a modal context
function Modal({ isOpen, onClose, triggerRef }) {  
const dialogRef = React.useRef();

  React.useEffect(() =&gt; {
    if (isOpen) {
      dialogRef.current?.focus();
    } else if (triggerRef?.current) {
      triggerref.current?.focus();
    }
  }, [isOpen]);

  React.useEffect(() =&gt; {
    function handleKeyDown(e) {
      if (e.key === 'Escape') {
        onClose();
      }
    }

    if (isOpen) {
      document.addEventListener('keydown', handleKeyDown);
    }

    return () =&gt; {
      document.removeEventListener('keydown', handleKeyDown);
    };
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return (
    &lt;div
      role="dialog"
      aria-modal="true"
      aria-labelledby="modal-title"
      tabIndex={-1}
      ref={dialogRef}
    &gt;
      &lt;h2 id="modal-title"&gt;Modal Title&lt;/h2&gt;
      &lt;button type="button" onClick={onClose}&gt;Close&lt;/button&gt;
      &lt;input type="text" placeholder="Name" /&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p><strong>Explanation:</strong></p>
<ul>
<li><p><code>tabIndex={-1}</code> enables the dialog container to receive focus</p>
</li>
<li><p>Focus is moved to the modal when it opens, ensuring keyboard users start in the correct context</p>
</li>
<li><p>Focus is returned to the trigger element when the modal closes, preserving user flow</p>
</li>
<li><p><code>aria-labelledby</code> provides an accessible name for the dialog</p>
</li>
<li><p>Escape key handling allows users to close the modal without a mouse</p>
</li>
</ul>
<p>Note: For full accessibility, you should also implement focus trapping so users cannot tab outside the modal while it is open.</p>
<p>Tip: In production applications, use libraries like react-aria, focus-trap-react, or Radix UI to handle focus trapping and accessibility edge cases reliably.</p>
<p>Also, keep in mind here that the document-level keydown listener is global, which affects the entire page and can conflict with other components.</p>
<pre><code class="language-javascript">document.addEventListener('keydown', handleKeyDown);
</code></pre>
<p>A safer alternative is to scope it to the modal:</p>
<pre><code class="language-javascript">&lt;div
  onKeyDown={(e) =&gt; {
    if (e.key === 'Escape') onClose();
  }}
&gt;
</code></pre>
<p>For simple cases, attach <code>onKeyDown</code> to the dialog instead of the document.</p>
<h4 id="heading-best-practice">Best Practice:</h4>
<p>For complex components, use libraries like <code>focus-trap-react</code> or <code>react-aria</code> to manage focus reliably, especially for modals, dropdowns, and popovers.</p>
<h2 id="heading-forms-and-accessibility">Forms and Accessibility</h2>
<p>Forms are critical points of interaction in web applications, and proper accessibility ensures that all users – including those using screen readers or other assistive technologies – can understand and interact with them effectively.</p>
<p>Proper labeling means that every input field, checkbox, radio button, or select element has an associated label that clearly describes its purpose. This allows screen readers to announce the input meaningfully and helps keyboard-only users understand what information is expected.</p>
<p>In addition to labeling, form accessibility includes:</p>
<ul>
<li><p>Providing clear error messages when input is invalid</p>
</li>
<li><p>Ensuring error messages are announced to assistive technologies</p>
</li>
<li><p>Maintaining logical focus order so users can navigate inputs easily</p>
</li>
</ul>
<p><strong>Bad Example:</strong></p>
<pre><code class="language-html">&lt;input type="text" placeholder="Name" /&gt;
</code></pre>
<p>Why this isn't good:</p>
<ul>
<li><p>This input relies only on a placeholder for context</p>
</li>
<li><p>Screen readers may not announce the purpose of the field clearly</p>
</li>
<li><p>Once a user starts typing, the placeholder disappears, leaving no guidance</p>
</li>
<li><p>Keyboard-only users may not have enough context to know what to enter</p>
</li>
</ul>
<p><strong>Good Example:</strong></p>
<pre><code class="language-html">&lt;label htmlFor="name"&gt;Name&lt;/label&gt;
&lt;input id="name" type="text" /&gt;
</code></pre>
<p>Why this is better:</p>
<ul>
<li><p>The <code>&lt;label&gt;</code> is explicitly associated with the input via <code>htmlFor / id</code></p>
</li>
<li><p>Screen readers announce "Name" before the input, providing clear context</p>
</li>
<li><p>Users navigating with Tab understand the field’s purpose</p>
</li>
<li><p>The label persists even when the user types, unlike a placeholder</p>
</li>
</ul>
<p><strong>Error Handling:</strong></p>
<pre><code class="language-html">&lt;label htmlFor="name"&gt;Name&lt;/label&gt;
&lt;input
  id="name"
  type="text"
  aria-describedby="name-error"
  aria-invalid="true"
/&gt;

&lt;p id="name-error" role="alert"&gt;
  Name is required
&lt;/p&gt;
</code></pre>
<p><strong>Explanation</strong></p>
<ul>
<li><p><code>aria-describedby</code> links the input to the error message using the element’s id</p>
</li>
<li><p>Screen readers announce the error message when the input is focused</p>
</li>
<li><p><code>aria-invalid="true"</code> indicates that the field currently contains an error</p>
</li>
<li><p><code>role="alert"</code> ensures the error message is announced immediately when it appears</p>
</li>
</ul>
<p>This creates a clear relationship between the input and its validation message, improving usability for screen reader users.</p>
<p>Tip: Only apply aria-invalid and error messages when validation fails. Avoid marking fields as invalid before user interaction.</p>
<h2 id="heading-responsive-typography-and-images">Responsive Typography and Images</h2>
<p>Responsive typography and images ensure that your content remains readable and visually appealing across a wide range of devices, from small smartphones to large desktop monitors.</p>
<p>This is important, because text should scale naturally so it remains legible on all screens, and images should adjust to container sizes to avoid layout issues or overflow. Both contribute to a better user experience and accessibility</p>
<p>In this section, we’ll cover practical ways to implement responsive typography and images in React and CSS.</p>
<pre><code class="language-css">h1 {
  font-size: clamp(1.5rem, 2vw, 3rem);
}
</code></pre>
<p>In this code:</p>
<ul>
<li><p>The <code>clamp()</code> function allows text to scale fluidly:</p>
</li>
<li><p>The first value (1.5rem) is the “minimum font size”</p>
</li>
<li><p>The second value (2vw) is the “preferred size based on viewport width”</p>
</li>
<li><p>The third value (3rem) is the “maximum font size”</p>
</li>
<li><p>This ensures headings are “readable on small screens” without becoming too large on desktops</p>
</li>
</ul>
<p>Alternative methods include using <code>media queries</code> to adjust font sizes at different breakpoints</p>
<p><strong>Responsive Images:</strong></p>
<pre><code class="language-html">&lt;img src="image.jpg" alt="Description" loading="lazy" /&gt;
</code></pre>
<p>In this code, responsive images adapt to different screen sizes and resolutions to prevent layout issues or slow loading times. Key techniques include:</p>
<h3 id="heading-1-fluid-images-using-css">1. Fluid images using CSS:</h3>
<pre><code class="language-css">img {
     max-width: 100%;
     height: auto;
   }
</code></pre>
<p>This makes sure that images never overflow their container and maintains aspect ratio automatically.</p>
<h3 id="heading-2-using-srcset-for-multiple-resolutions">2. Using <code>srcset</code> for multiple resolutions:</h3>
<pre><code class="language-html">&lt;img src="image-small.jpg"
     srcset="image-small.jpg 480w,
             image-medium.jpg 1024w,
             image-large.jpg 1920w"
     sizes="(max-width: 600px) 480px,
            (max-width: 1200px) 1024px,
            1920px"
     alt="Description"&gt;
</code></pre>
<p>This provides different image files depending on screen size or resolution and reduces loading times and improves performance on smaller devices.</p>
<h3 id="heading-3-always-include-descriptive-alt-text">3. Always include descriptive alt text</h3>
<p>This is critical for screen readers and accessibility. It also helps users understand the image if it cannot be loaded.</p>
<p>Tip: Combine responsive typography, images, and flexible layout containers (like CSS Grid or Flexbox) to create interfaces that scale gracefully across all devices and maintain accessibility.</p>
<h3 id="heading-4-ensure-sufficient-color-contrast">4. Ensure Sufficient Color Contrast</h3>
<p>Low contrast text can make content unreadable for many users.</p>
<pre><code class="language-css">.bad-text {
  color: #aaa;
}

.good-text {
  color: #222;
}
</code></pre>
<p>Use tools like WebAIM Contrast Checker and Chrome DevTools Accessibility panel to check your color contrasts. Also note that WCAG AA requires 4.5:1 contrast ratio for normal text.</p>
<h2 id="heading-building-a-fully-accessible-responsive-component-end-to-end-example">Building a Fully Accessible Responsive Component (End-to-End Example)</h2>
<p>To understand how responsiveness and accessibility work together in practice, let’s build a reusable accessible card component that adapts to screen size and supports keyboard and screen reader users.</p>
<h3 id="heading-step-1-component-structure-semantic-html">Step 1: Component Structure (Semantic HTML)</h3>
<pre><code class="language-javascript">function ProductCard({ title, description, onAction }) {
  return (
    &lt;article className="card"&gt;
      &lt;h3&gt;{title}&lt;/h3&gt;
      &lt;p&gt;{description}&lt;/p&gt;
      &lt;button type="button" onClick={onAction}&gt;
        View Details
      &lt;/button&gt;
    &lt;/article&gt;
  );
}
</code></pre>
<p><strong>Why This Works</strong></p>
<ul>
<li><p><code>&lt;article&gt;</code> provides semantic meaning for standalone content</p>
</li>
<li><p><code>&lt;h3&gt;</code> establishes a proper heading hierarchy</p>
</li>
<li><p><code>&lt;button&gt;</code> ensures built-in keyboard and accessibility support</p>
</li>
</ul>
<h3 id="heading-step-2-responsive-styling">Step 2: Responsive Styling</h3>
<pre><code class="language-css">.card {
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

@media (min-width: 768px) {
  .card {
    padding: 24px;
  }
}
</code></pre>
<p>This ensures comfortable spacing on mobile and improved readability on larger screens.</p>
<h3 id="heading-step-3-accessibility-enhancements">Step 3: Accessibility Enhancements</h3>
<pre><code class="language-html">&lt;button type="button" onClick={onAction}&gt;
  View Details
&lt;/button&gt;
</code></pre>
<p>The visible button text provides a clear and accessible label, so no additional ARIA attributes are needed.</p>
<h3 id="heading-step-4-keyboard-focus-styling">Step 4: Keyboard Focus Styling</h3>
<pre><code class="language-css">button:focus {
  outline: 2px solid blue;
  outline-offset: 2px;
}
</code></pre>
<p>Focus indicators are essential for keyboard users.</p>
<h3 id="heading-step-5-using-the-component">Step 5: Using the Component</h3>
<pre><code class="language-javascript">function App() {
  return (
    &lt;div className="grid"&gt;
      &lt;ProductCard
        title="Product 1"
        description="Accessible and responsive"
        onAction={() =&gt; alert('Clicked')}
      /&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p><strong>Key Takeaways</strong></p>
<p>This simple component demonstrates:</p>
<ul>
<li><p>Semantic HTML structure</p>
</li>
<li><p>Responsive design</p>
</li>
<li><p>Built-in accessibility via native elements</p>
</li>
<li><p>Minimal ARIA usage</p>
</li>
</ul>
<p>In real-world applications, this pattern scales into entire design systems.</p>
<h2 id="heading-testing-accessibility">Testing Accessibility</h2>
<p>Accessibility should be validated continuously, not just at the end of development. There are various automated tools you can use to help you with this process:</p>
<ul>
<li><p>Lighthouse (built into Chrome DevTools)</p>
</li>
<li><p>axe DevTools for detailed audits</p>
</li>
<li><p>ESLint plugins for accessibility rules</p>
</li>
</ul>
<h3 id="heading-manual-testing">Manual Testing</h3>
<p>But automated tools cannot catch everything. Manual testing is essential to make sure users can navigate using only the keyboard and use a screen reader (NVDA or VoiceOver. You should also test zoom levels (up to 200%) and check the color contrast manually.</p>
<p><strong>Example: ESLint Accessibility Plugin</strong></p>
<pre><code class="language-shell">npm install eslint-plugin-jsx-a11y --save-dev
</code></pre>
<p>This helps catch accessibility issues during development.</p>
<h2 id="heading-best-practices">Best Practices</h2>
<ul>
<li><p>Use semantic HTML first</p>
</li>
<li><p>Avoid unnecessary ARIA</p>
</li>
<li><p>Test keyboard navigation</p>
</li>
<li><p>Design mobile-first</p>
</li>
<li><p>Ensure color contrast</p>
</li>
<li><p>Use consistent spacing</p>
</li>
</ul>
<h2 id="heading-when-not-to-overuse-accessibility-features">When NOT to Overuse Accessibility Features</h2>
<ul>
<li><p>Avoid adding ARIA when native HTML works</p>
</li>
<li><p>Do not override browser defaults unnecessarily</p>
</li>
<li><p>Avoid complex custom components without accessibility support</p>
</li>
</ul>
<h2 id="heading-future-enhancements">Future Enhancements</h2>
<ul>
<li><p>Design systems with accessibility built-in</p>
</li>
<li><p>Automated accessibility testing in CI/CD</p>
</li>
<li><p>Advanced focus management libraries</p>
</li>
<li><p>Accessibility-first component libraries</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building responsive and accessible React applications is not a one-time effort—it is a continuous design and engineering practice. Instead of treating accessibility as a checklist, developers should integrate it into the core of their component design process.</p>
<p>If you are starting out, focus on using semantic HTML and mobile-first layouts. These two practices alone solve a large percentage of accessibility and responsiveness issues. As your application grows, introduce ARIA enhancements, keyboard navigation, and automated accessibility testing.</p>
<p>The key is to build interfaces that work for everyone by default. When responsiveness and accessibility are treated as first-class concerns, your React applications become more usable, scalable, and future-proof.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Animated Shadcn Tab Component with Shadcn/ui ]]>
                </title>
                <description>
                    <![CDATA[ Tab components are everywhere: dashboards, settings panels, product pages. But most implementations are static, lifeless, and forgettable. What if your tabs felt alive, with smooth spring animations,  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-animated-shadcn-tab-component-with-shadcn-ui/</link>
                <guid isPermaLink="false">69ca85f69fffa747403074fe</guid>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ shadcn ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vaibhav Gupta ]]>
                </dc:creator>
                <pubDate>Mon, 30 Mar 2026 14:17:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/961a288f-30b9-4085-a1fc-7da13ffce38f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Tab components are everywhere: dashboards, settings panels, product pages. But most implementations are static, lifeless, and forgettable. What if your tabs felt alive, with smooth spring animations, a stacked card effect on hover, and a polished active indicator that glides between buttons?</p>
<p>A basic tab switcher can show and hide content. A better one gives users a clear active state, smooth transitions, and a little bit of motion that makes the interface feel alive. That's the idea behind this component: a reusable animated tab system built in the Shadcn style, with React, Tailwind CSS, and Motion.</p>
<p>In this tutorial, you’ll build exactly that: a fully animated tab component built by Shadcn/ui, Framer Motion, and a ready-to-use registry component from Shadcn Space.</p>
<p>By the end, you’ll have a reusable <code>&lt;Tabs/&gt;</code> component with:</p>
<ul>
<li><p>A spring-animated active pill indicator</p>
</li>
<li><p>A stacked card effect that fans out on hover</p>
</li>
<li><p>A smooth entrance animation when the active tab changes</p>
</li>
<li><p>Fully theme-aware styling using Shadcn/ui CSS variables</p>
</li>
</ul>
<p><strong>Video walkthrough</strong>: If you prefer to follow along visually, watch the full tutorial on YouTube:</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/n6dvjVxy02U" 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-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-what-youll-build">What You’ll Build</a></p>
</li>
<li><p><a href="#heading-install-the-component-via-shadcn-space-cli">Install the Component via Shadcn Space CLI</a></p>
</li>
<li><p><a href="#heading-understand-the-component-structure">Understand the Component Structure</a></p>
</li>
<li><p><a href="#heading-step-1-define-the-tab-data-types">Step 1 - Define the Tab Data Types</a></p>
</li>
<li><p><a href="#heading-step-2-build-the-tab-data-array">Step 2 - Build the Tab Data Array</a></p>
</li>
<li><p><a href="#heading-step-3-build-the-tabs-component-tab-bar-state">Step 3 - Build the Tabs Component (Tab Bar + State)</a></p>
</li>
<li><p><a href="#heading-step-4-build-the-fadeinstack-component">Step 4 - Build the FadeInStack Component</a></p>
</li>
<li><p><a href="#heading-step-5-compose-the-page-component">Step 5 - Compose the Page Component</a></p>
</li>
<li><p><a href="#heading-step-6-customize-the-component">Step 6 - Customize the Component</a></p>
</li>
<li><p><a href="#heading-live-preview">Live Preview</a></p>
</li>
<li><p><a href="#heading-key-concepts-recap">Key Concepts Recap</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you begin, make sure you have a working knowledge of:</p>
<ul>
<li><p>React and TypeScript basics</p>
</li>
<li><p>Tailwind CSS utility classes</p>
</li>
<li><p>The basics of Shadcn/ui (component installation and theming)</p>
</li>
</ul>
<p>You’ll also need a Next.js or Vite project with the following already set up:</p>
<ul>
<li><p>Shadcn/ui installed and initialized</p>
</li>
<li><p>Framer Motion (also referred to as motion/react) installed</p>
</li>
</ul>
<h2 id="heading-what-youll-build">What You’ll Build</h2>
<p>Here’s an overview of the component architecture you’ll create in this tutorial:</p>
<pre><code class="language-typescript">AnimatedTabMotion (page/demo entry point)
└── Tabs (tab bar + content orchestrator)
├── Tab buttons (with spring-animated active pill)
└── FadeInStack (stacked, animated content panels)
</code></pre>
<p>The key behaviors are:</p>
<ol>
<li><p><strong>Spring pill animation</strong> – A spring pill animation is a UI effect in which the active tab indicator, a rounded, pill-shaped highlight, physically moves from one button to another using a spring physics curve rather than a standard CSS transition. Instead of teleporting or fading, the pill slides between tabs with a subtle bounce at the end, mimicking the momentum of a real physical object.</p>
</li>
<li><p><strong>Stacked card effect</strong> – inactive tab panels are rendered behind the active one, scaled down and slightly faded, giving a layered depth illusion.</p>
</li>
<li><p><strong>Fan-out on hover</strong> – when the user hovers over the content area, the stacked cards spread out vertically.</p>
</li>
<li><p><strong>Bounce entrance</strong> – the top (active) card animates downward and back into place when a new tab is selected.</p>
</li>
</ol>
<h2 id="heading-install-the-component-via-shadcn-space-cli">Install the Component via Shadcn Space CLI</h2>
<p>Shadcn Space is a registry of production-ready Shadcn/ui-compatible components. Instead of scaffolding this component from scratch, you can pull it directly into your project using the Shadcn CLI.</p>
<p>Check out their <a href="https://shadcnspace.com/docs/getting-started/how-to-use-shadcn-cli">Getting Started guide</a> to learn how to use the Shadcn CLI with third-party registries.</p>
<p>Run <strong>one</strong> of the following commands, depending on your package manager:</p>
<p><strong>pnpm</strong></p>
<pre><code class="language-typescript">pnpm dlx shadcn@latest add @shadcn-space/tabs-01
</code></pre>
<p><strong>npm</strong></p>
<pre><code class="language-typescript">npx shadcn@latest add @shadcn-space/tabs-01
</code></pre>
<p><strong>Yarn</strong></p>
<pre><code class="language-typescript">yarn dlx shadcn@latest add @shadcn-space/tabs-01
</code></pre>
<p><strong>Bun</strong></p>
<pre><code class="language-typescript">bunx --bun shadcn@latest add @shadcn-space/tabs-01
</code></pre>
<p>This scaffolds the component file into your project, pre-wired to your existing Shadcn/ui theme tokens. You can then customize or extend it as needed, which is exactly what you’ll learn in this tutorial.</p>
<h2 id="heading-understand-the-component-structure">Understand the Component Structure</h2>
<p>Before writing any code, let’s review the full component and break it into logical pieces. Here is the complete implementation:</p>
<pre><code class="language-typescript">"use client";

import { useState } from "react";
import { motion } from "motion/react";
import { cn } from "@/lib/utils";

type Tab = {
  title: string;
  value: string;
  content?: React.ReactNode;
};

type TabsProps = {
  tabs: Tab[];
    containerClassName?: string;
  activeTabClassName?: string;
  tabClassName?: string;
  contentClassName?: string;
};

const tabs = [
  {
    title: "Product",
    value: "product",
    content: (
      &lt;div className="w-full overflow-hidden relative rounded-2xl p-10 text-xl md:text-4xl font-bold text-foreground bg-muted h-[300px] border border-border"&gt;
        &lt;p&gt;Product Tab&lt;/p&gt;
      &lt;/div&gt;
    ),
  },
  {title: "Services",
    value: "services",
    content: (
      &lt;div className="w-full overflow-hidden relative rounded-2xl p-10 text-xl md:text-4xl font-bold text-foreground bg-muted h-[300px] border border-border"&gt;
        &lt;p&gt;Services tab&lt;/p&gt;
      &lt;/div&gt;
    ),
  },
  {
    title: "Playground",
    value: "playground",
    content: (
      &lt;div className="w-full overflow-hidden relative rounded-2xl p-10 text-xl md:text-4xl font-bold text-foreground bg-muted h-[300px] border border-border"&gt;
        &lt;p&gt;Playground tab&lt;/p&gt;
      &lt;/div&gt;
    ),
  },
 {
    title: "Content",
    value: "content",
    content: (
      &lt;div className="w-full overflow-hidden relative rounded-2xl p-10 text-xl md:text-4xl font-bold text-foreground bg-muted h-[300px] border border-border"&gt;
        &lt;p&gt;Content tab&lt;/p&gt;
      &lt;/div&gt;
    ),
  },
  {
    title: "Random",
    value: "random",
    content: (
      &lt;div className="w-full overflow-hidden relative rounded-2xl p-10 text-xl md:text-4xl font-bold text-foreground bg-muted h-[300px] border border-border"&gt;
        &lt;p&gt;Random tab&lt;/p&gt;
      &lt;/div&gt;
    ),
  },
];

const Tabs = ({
  tabs,
  containerClassName,
  activeTabClassName,
  tabClassName,
  contentClassName,
}: TabsProps) =&gt; {
  const [activeIdx, setActiveIdx] = useState(0);
  const [hovering, setHovering] = useState(false);

  const handleSelect = (idx: number) =&gt; {
    setActiveIdx(idx);
  };
const reorderedTabs = [
    tabs[activeIdx],
    ...tabs.filter((_, i) =&gt; i !== activeIdx),
  ];

  return (
    &lt;&gt;
      &lt;div
        className={cn(
          "flex flex-row items-center justify-start [perspective:1000px] relative overflow-auto sm:overflow-visible no-visible-scrollbar max-w-full w-full",
          containerClassName,
        )}
      &gt;
        {tabs.map((tab, idx) =&gt; {
          const isActive = idx === activeIdx;
          return (
            &lt;button
            key={tab.value}
              onClick={() =&gt; handleSelect(idx)}
              onMouseEnter={() =&gt; setHovering(true)}
              onMouseLeave={() =&gt; setHovering(false)}
              className={cn("relative px-4 py-2 rounded-full", tabClassName)}
              style={{ transformStyle: "preserve-3d" }}
            &gt;
              {isActive &amp;&amp; (
                &lt;motion.div
                  layoutId="clickedbutton"
                  transition={{ type: "spring", bounce: 0.3, duration: 0.6 }}
                  className={cn(
                    "absolute inset-0 bg-primary rounded-full",
                    activeTabClassName,
                  )}
                /&gt;
              )}
&lt;span
                className={cn(
                  "relative block text-sm",
                  isActive ? "text-background": "text-foreground",
                )}
              &gt;
                {tab.title}
              &lt;/span&gt;
            &lt;/button&gt;
          );
        })}
      &lt;/div&gt;
      &lt;FadeInStack
        tabs={reorderedTabs}
        hovering={hovering}
        className={cn("mt-10", contentClassName)}
      /&gt;
    &lt;/&gt;
  );
};

type FadeInStackProps = {
  className?: string;
  tabs: Tab[];
  hovering?: boolean;
};

const FadeInStack = ({ className, tabs, hovering }: FadeInStackProps) =&gt; {
  return (
    &lt;div className="relative w-full h-[300px]"&gt;
      {tabs.map((tab, idx) =&gt; (
        &lt;motion.div
          key={tab.value}
          layoutId={tab.value}
          style={{
            scale: 1 - idx * 0.1,
            top: hovering ? idx * -15 : 0,
            zIndex: -idx,
            opacity: idx &lt; 3 ? 1 - idx * 0.1 : 0,
          }}
          animate={{
            y: idx === 0 ? [0, 40, 0] : 0,
          }}
          className={cn("w-full h-full absolute top-0 left-0", className)}
        &gt;
          {tab.content}
        &lt;/motion.div&gt;
      ))}
    &lt;/div&gt;
  );
};

export default function AnimatedTabMotion() {
  return (
    &lt;&gt;
      &lt;div className="[perspective:1000px] relative flex flex-col max-w-5xl mx-auto w-full items-start justify-start mb-13"&gt;
        &lt;Tabs tabs={tabs} /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );

}
</code></pre>
<p>Now, let’s break this down piece by piece.</p>
<h2 id="heading-step-1-define-the-tab-data-types">Step 1: Define the Tab Data Types</h2>
<pre><code class="language-typescript">type Tab = {
title: string;
value: string;
content?: React.ReactNode;
};
type TabsProps = {
tabs: Tab[];
containerClassName?: string;
activeTabClassName?: string;
tabClassName?: string;
contentClassName?: string;
};
</code></pre>
<p>The <code>Tab</code> type defines the shape of each tab item:</p>
<ul>
<li><p><code>title</code> – the label rendered in the tab button.</p>
</li>
<li><p><code>value</code> – a unique key used to identify each tab (and as the Framer Motion <code>layoutId</code>).</p>
</li>
<li><p><code>content</code> – an optional <code>React.ReactNode</code>, meaning you can pass any JSX as the panel body.</p>
</li>
</ul>
<p>The <code>TabsProps</code> type makes the <code>Tabs</code> component highly composable. Every visual layer has an override <code>className</code>, so you can restyle the active pill, individual tab buttons, and the content area independently without touching the core logic.</p>
<h2 id="heading-step-2-build-the-tab-data-array">Step 2: Build the Tab Data Array</h2>
<pre><code class="language-typescript">const tabs = [
{
title: “Product”,
value: “product”,
content: (

Product Tab

), }, // ... more tabs ];
</code></pre>
<p>Each tab’s <code>content</code> is a JSX element styled with Shadcn/ui semantic tokens like <code>bg-muted</code>, <code>text-foreground</code> and <code>border-border</code>. This is intentional: these tokens automatically adapt to your light/dark theme without any extra configuration.</p>
<p>You can replace these placeholder <code>&lt;div&gt;</code> panels with any real content: charts, forms, tables, media, whatever your use case demands.</p>
<h2 id="heading-step-3-build-the-tabs-component-tab-bar-state">Step 3: Build the Tabs Component (Tab Bar + State)</h2>
<pre><code class="language-typescript">const [activeIdx, setActiveIdx] = useState(0);
const [hovering, setHovering] = useState(false);
</code></pre>
<p>Two pieces of state drive the entire component:</p>
<ul>
<li><p><code>activeIdx</code> tracks which tab is currently selected (by array index).</p>
</li>
<li><p><code>hovering</code> tracks whether the user’s cursor is over any tab button, which is passed to <code>FadeInStack</code> to trigger the fan-out effect.</p>
</li>
</ul>
<h3 id="heading-reorder-tabs-for-the-stack-effect">Reorder Tabs for the Stack Effect</h3>
<pre><code class="language-typescript">const reorderedTabs = [
tabs[activeIdx],
…tabs.filter((_, i) =&gt; i !== activeIdx),
];
</code></pre>
<p>This is one of the most clever aspects of the architecture. Instead of showing only the active tab’s content, you <strong>always render all tab panels</strong> – but you put the active one first in the array. This is what enables the stacked-cards visual:</p>
<ul>
<li><p>Index 0 = the active panel, rendered on top with full scale and opacity.</p>
</li>
<li><p>Index 1, 2 = the next panels, stacked behind with reduced scale and opacity.</p>
</li>
<li><p>Index 3+ = hidden (opacity 0).</p>
</li>
</ul>
<h3 id="heading-render-the-tab-buttons-with-a-spring-pill">Render the Tab Buttons with a Spring Pill</h3>
<pre><code class="language-typescript">{tabs.map((tab, idx) =&gt; {
const isActive = idx === activeIdx;
return (
   &lt;button
    key={tab.value}
    onClick={() =&gt; handleSelect(idx)}
    onMouseEnter={() =&gt; setHovering(true)}
    onMouseLeave={() =&gt; setHovering(false)}
    className={cn(“relative px-4 py-2 rounded-full”, tabClassName)}
    style={{ transformStyle: “preserve-3d” }}
    &gt;
    {isActive &amp;&amp; (
       &lt;motion.div
        layoutId=“clickedbutton”
        transition={{ type: “spring”, bounce: 0.3, duration: 0.6 }}
        className={cn(
        “absolute inset-0 bg-primary rounded-full”,
        activeTabClassName,
     )}
   /&gt;
)}
&lt;span
    className={cn(
        “relative block text-sm”,
        isActive ? “text-background” : “text-foreground”,
    )}
    &gt;
      {tab.title}
     &lt;/span&gt;
  &lt;/button&gt;
);
})}
</code></pre>
<p>The magic here is <code>layoutId=“clickedbutton”</code> on the <code>motion.div</code>. When only one element with a given <code>layoutId</code> is mounted at a time, Framer Motion tracks its position in the DOM. When it unmounts from one button and mounts onto another, Framer Motion <code>automatically animates the transition</code> is between the two DOM positions. This creates the sliding pill effect with zero manual calculation.</p>
<p>The transition config uses a spring with <code>bounce: 0.3</code> a <code>duration: 0.6</code>, giving it a natural, slightly elastic feel rather than a mechanical linear slide.</p>
<p>The <code>transformStyle: “preserve-3d”</code> on the button enables 3D CSS transforms, which pair with the <code>[perspective:1000px]</code> on the container for a subtle depth effect.</p>
<h2 id="heading-step-4-build-the-fadeinstack-component">Step 4: Build the FadeInStack Component</h2>
<pre><code class="language-typescript">const FadeInStack = ({ className, tabs, hovering }: FadeInStackProps) =&gt; {
  return (
    &lt;div className="relative w-full h-[300px]"&gt;
      {tabs.map((tab, idx) =&gt; (
        &lt;motion.div
          key={tab.value}
          layoutId={tab.value}
          style={{
            scale: 1 - idx * 0.1,
            top: hovering ? idx * -15 : 0,
            zIndex: -idx,
            opacity: idx &lt; 3 ? 1 - idx * 0.1 : 0,
          }}
          animate={{
            y: idx === 0 ? [0, 40, 0] : 0,
          }}
          className={cn("w-full h-full absolute top-0 left-0", className)}
        &gt;
          {tab.content}
        &lt;/motion.div&gt;
      ))}
    &lt;/div&gt;
  );
};
</code></pre>
<p>Let’s unpack the visual logic for each <code>motion.div</code>:</p>
<h3 id="heading-scale-1-idx-01"><code>scale: 1 - idx * 0.1</code></h3>
<p>Each card behind the active one is scaled down by 10% per layer. So:</p>
<ul>
<li><p>Active card (idx 0): <code>scale: 1.0</code></p>
</li>
<li><p>Second card (idx 1): <code>scale: 0.9</code></p>
</li>
<li><p>Third card (idx 2): <code>scale: 0.8</code></p>
</li>
</ul>
<p>This creates clear depth separation between the stacked layers.</p>
<h3 id="heading-top-hovering-idx-15-0"><code>top: hovering ? idx * -15 : 0</code></h3>
<p>When <code>hovering</code> is <code>true</code>, each card shifts upward by <code>idx * 15px</code><em>. The active card doesn’t move</em> <code>(idx 15 = 0)</code>, but the cards behind it fan out at -15px, -30px, and so on. This gives a satisfying “deck spreading” effect on hover.</p>
<h3 id="heading-zindex-idx"><code>zIndex: -idx</code></h3>
<p>Negative z-index stacks cards in order: the active card sits on top (z-index 0), while subsequent cards descend further behind.</p>
<h3 id="heading-opacity-idx-lt-3-1-idx-01-0"><code>opacity: idx &lt; 3 ? 1 - idx * 0.1 : 0</code></h3>
<p>Cards at index 3 and beyond are hidden entirely. The first three cards fade progressively: 1.0, 0.9, 0.8.</p>
<h3 id="heading-animate-y-idx-0-0-40-0-0"><code>animate={{ y: idx === 0 ? [0, 40, 0] : 0 }}</code></h3>
<p>Only the active card (idx 0) gets this keyframe animation. When a tab is selected, and the <code>reorderedTabs</code> array is rebuilt, the new active card enters via a downward dip (<code>y: 40</code>) and bounces back to its rest position. This is a quick, tactile confirmation that the tab has changed.</p>
<h3 id="heading-layoutidtabvalue"><code>layoutId={tab.value}</code></h3>
<p>Each card also has a <code>layoutId</code> matching one <code>value</code>. When <code>reorderedTabs</code> is recomputed, and array positions shift, Framer Motion can track each card’s identity and animate it smoothly between positions, preventing jarring jumps.</p>
<h2 id="heading-step-5-compose-the-page-component">Step 5: Compose the Page Component</h2>
<pre><code class="language-typescript">export default function AnimatedTabMotion() {
  return (
    &lt;div className="[perspective:1000px] relative flex flex-col max-w-5xl mx-auto w-full items-start justify-start mb-13"&gt;
      &lt;Tabs tabs={tabs} /&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>The outer wrapper applies <code>[perspective:1000px]</code> – a Tailwind arbitrary property that sets the CSS <code>perspective</code> value. This is what gives the 3D depth to the <code>transformStyle: “preserve-3d”</code> on the tab buttons.</p>
<p>The <code>max-w-5xl</code> and <code>mx-auto</code> center the component on wide screens while <code>items-start</code> left-aligns the tab bar, which matches most real-world UI patterns.</p>
<h2 id="heading-step-6-customize-the-component">Step 6: Customize the Component</h2>
<p>Because <code>Tabs</code> accepts class-name overrides for every visual layer, so you can fully restyle the component to match your design system. Here’s an example with a darker active pill and a tighter layout:</p>
<pre><code class="language-typescript">&lt;Tabs
  tabs={tabs}
  containerClassName="gap-1"
  tabClassName="text-xs px-3 py-1.5"
  activeTabClassName="bg-zinc-900 dark:bg-white"
  contentClassName="mt-6"
/&gt;
</code></pre>
<p>You can also replace the placeholder content panels with real content. Here’s an example using a card with a real description:</p>
<pre><code class="language-typescript">const tabs = [
  {
    title: "Overview",
    value: "overview",
    content: (
      &lt;div className="w-full rounded-2xl p-8 bg-muted border border-border h-[300px] flex flex-col gap-4"&gt;
        &lt;h2 className="text-2xl font-bold text-foreground"&gt;Product Overview&lt;/h2&gt;
        &lt;p className="text-muted-foreground text-sm leading-relaxed"&gt;
          Our platform helps teams ship faster with a fully integrated design-to-code workflow.
        &lt;/p&gt;
      &lt;/div&gt;
    ),
  },
  // ...
];
</code></pre>
<h2 id="heading-live-preview">Live Preview</h2>
<img src="https://cdn.hashnode.com/uploads/covers/68b53a3d851476bd2ce87f12/af4a2ba6-dd70-4e77-8c38-7d390060db0d.gif" alt="af4a2ba6-dd70-4e77-8c38-7d390060db0d" style="display:block;margin:0 auto" width="1016" height="608" loading="lazy">

<h2 id="heading-key-concepts-recap">Key Concepts Recap</h2>
<p>Here’s a summary of the core Framer Motion techniques used in this component:</p>
<table style="min-width:415px"><colgroup><col style="min-width:25px"><col style="width:390px"></colgroup><tbody><tr><td><p><strong>Technique</strong></p></td><td><p><strong>What it does</strong></p></td></tr><tr><td><p><code>layoutId</code> on <code>motion.div</code></p></td><td><p>Animates a shared element between DOM positions (the sliding pill)</p></td></tr><tr><td><p><code>layoutId</code> on <code>motion.div</code> per tab</p></td><td><p>Tracks card identity during re-ordering, so Framer Motion animates position changes</p></td></tr><tr><td><p><code>animate={{ y: [0, 40, 0] }}</code></p></td><td><p>Keyframe animation for the bounce entrance on tab change</p></td></tr><tr><td><p><code>style={{ scale, top, zIndex, opacity }}</code></p></td><td><p>Inline reactive styles that create the stacked-card depth effect</p></td></tr><tr><td><p><code>transition={{ type: "spring" }}</code></p></td><td><p>Applies a physics-based spring curve instead of a CSS easing function</p></td></tr></tbody></table>

<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you built a fully animated, theme-aware tab component using Shadcn/ui and Framer Motion. You learned how to:</p>
<ul>
<li><p>Use <code>layoutId</code> to create a spring-animated sliding pill indicator</p>
</li>
<li><p>Render all tab panels simultaneously and reorder them to create a stacked card effect</p>
</li>
<li><p>Drive hover and depth effects with inline reactive <code>style</code> props</p>
</li>
<li><p>Apply Framer Motion keyframe animations for a tactile bounce entrance</p>
</li>
<li><p>Keep the component fully customizable via class name overrides</p>
</li>
</ul>
<p>This pattern, combining Shadcn/ui’s semantic design tokens with Framer Motion’s layout animations, scales well beyond tabs. You can apply the same <code>layoutId</code> and stack reorder technique to carousels, image galleries, notification toasts, and more.</p>
<p>You can explore the full component and more animated UI blocks at Shadcn Space, where the CLI command makes it trivial to drop production-quality components directly into your project.</p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><p><a href="https://shadcnspace.com/components/tabs">Shadcn Space Tabs Component</a></p>
</li>
<li><p><a href="https://shadcnspace.com/docs/getting-started/how-to-use-shadcn-cli">Shadcn Space Getting Started Guide</a></p>
</li>
<li><p><a href="https://motion.dev/">Framer Motion Documentation</a></p>
</li>
<li><p><a href="https://ui.shadcn.com/">Shadcn/ui Documentation</a></p>
</li>
<li><p><a href="https://youtu.be/n6dvjVxy02U?si=pDpi2vC8oBjZlVsF">Video Tutorial on YouTube</a></p>
</li>
</ul>
<p>I wrote this article with the help of Mihir Koshti (Sr. Full Stack Developer) – <a href="https://www.linkedin.com/in/mihir-koshti/">Connect on LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Make a Dropdown Menu with shadcn/ui ]]>
                </title>
                <description>
                    <![CDATA[ Dropdown menus are little pop-up menus that help you show more options without cluttering your screen. They’re super helpful in websites and apps. In this guide, you’ll learn how to build a dropdown menu using shadcn/ui. It’s a tool that works well w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/shadcn-ui-dropdown-menu/</link>
                <guid isPermaLink="false">687964f6562b6ce291fda4e2</guid>
                
                    <category>
                        <![CDATA[ shadcn ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tailwind CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ajay Kalal ]]>
                </dc:creator>
                <pubDate>Thu, 17 Jul 2025 21:02:46 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752786132476/fef60fd2-ad5e-4f9d-9dcf-de4b99adac99.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Dropdown menus are little pop-up menus that help you show more options without cluttering your screen. They’re super helpful in websites and apps.</p>
<p>In this guide, you’ll learn how to build a dropdown menu using shadcn/ui. It’s a tool that works well with Tailwind CSS and Radix UI to help you make nice-looking, easy-to-use menus.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-shadcnui">What is shadcn/ui?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-use-shadcnui-for-dropdowns">Why Use shadcn/ui for Dropdowns?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-build-a-dropdown-step-by-step">Let’s Build a Dropdown Step-by-Step</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-start-a-new-project">Step 1: Start a New Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-add-the-dropdown-menu-component">Step 2: Add the Dropdown Menu Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-import-what-you-need">Step 3: Import What You Need</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-build-a-simple-dropdown">Step 4: Build a Simple Dropdown</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-make-it-look-better">Step 5: Make It Look Better</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-make-it-work-on-all-screens">Step 6: Make It Work on All Screens</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-7-add-cool-icons">Step 7: Add Cool Icons</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-8-its-already-accessible">Step 8: It’s Already Accessible!</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-use-case-country-dropdown-with-flags">Real-World Use Case: Country Dropdown with Flags</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h3 id="heading-prerequisites">💡 Prerequisites</h3>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Basic knowledge of React and JavaScript</p>
</li>
<li><p>Node.js and a package manager like npm, pnpm, or yarn are installed</p>
</li>
<li><p>Familiarity with Tailwind CSS is a bonus, but not required</p>
</li>
</ul>
<p>We’ll walk through everything step by step, so don’t worry if you’re not an expert yet.</p>
<h2 id="heading-what-is-shadcnui">What is shadcn/ui?</h2>
<p><a target="_blank" href="https://ui.shadcn.com/docs/installation">shadcn/ui</a> is a group of tools (called components) that help you build parts of a website, like buttons, modals, and dropdowns. It’s built with Radix UI and styled using Tailwind CSS. It’s perfect if you’re using React or Next.js.</p>
<p>With shadcn/ui, you don’t get just styled components, you get full control over how everything works and looks. That makes it perfect for teams that want consistency in design without giving up flexibility.</p>
<h3 id="heading-why-use-shadcnui-for-dropdowns">Why Use shadcn/ui for Dropdowns?</h3>
<p>Dropdown menus are a great use case for shadcn/ui because:</p>
<ul>
<li><p>It’s easy to use with keyboard and screen readers</p>
</li>
<li><p>You can create custom looks using Tailwind CSS</p>
</li>
<li><p>You control how it works and looks</p>
</li>
<li><p>It works great in real websites and apps</p>
</li>
<li><p>It integrates well with modern React workflows</p>
</li>
</ul>
<h2 id="heading-lets-build-a-dropdown-step-by-step">Let’s Build a Dropdown Step-by-Step</h2>
<h3 id="heading-step-1-start-a-new-project-with-shadcnui">Step 1: Start a New Project with shadcn/ui</h3>
<p>You don’t need to set up React, Next.js, or Tailwind manually. Just run this command:</p>
<pre><code class="lang-bash">pnpm dlx shadcn@latest init
</code></pre>
<p>This will automatically create a new Next.js app with Tailwind CSS and shadcn/ui preconfigured.</p>
<p>Tip: You can also use <code>npx</code> instead of <code>pnpm dlx</code> if you prefer:</p>
<pre><code class="lang-bash">npx shadcn@latest init
</code></pre>
<h3 id="heading-step-2-add-the-dropdown-menu-component">Step 2: Add the Dropdown Menu Component</h3>
<p>After your project is ready, add the dropdown component using:</p>
<pre><code class="lang-bash">npx shadcn@latest add dropdown-menu
</code></pre>
<p>This will pull in all the necessary components to create a dropdown menu.</p>
<h3 id="heading-step-3-import-what-you-need">Step 3: Import What You Need</h3>
<p>In your React file, import the full dropdown module so you can access all its features:</p>
<pre><code class="lang-tsx">import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuGroup,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuPortal,
} from "@/components/ui/dropdown-menu"
</code></pre>
<h3 id="heading-step-4-build-a-simple-dropdown">Step 4: Build a Simple Dropdown</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752690572839/4cb2bd61-b843-4fe3-8530-4b341d38a633.jpeg" alt="Screenshot of basic dropdown we're building" class="image--center mx-auto" width="630" height="577" loading="lazy"></p>
<p>Here’s a basic dropdown example:</p>
<pre><code class="lang-tsx">export function ProfileMenu() {
  return (
    &lt;DropdownMenu&gt;
      &lt;DropdownMenuTrigger asChild&gt;
        &lt;button className="px-4 py-2 bg-primary text-white rounded"&gt;
          Open Menu
        &lt;/button&gt;
      &lt;/DropdownMenuTrigger&gt;
      &lt;DropdownMenuContent className="w-56"&gt;
        &lt;DropdownMenuLabel&gt;My Account&lt;/DropdownMenuLabel&gt;
        &lt;DropdownMenuSeparator /&gt;
        &lt;DropdownMenuItem&gt;Profile&lt;/DropdownMenuItem&gt;
        &lt;DropdownMenuItem&gt;Settings&lt;/DropdownMenuItem&gt;
        &lt;DropdownMenuItem&gt;Log out&lt;/DropdownMenuItem&gt;
      &lt;/DropdownMenuContent&gt;
    &lt;/DropdownMenu&gt;
  )
}
</code></pre>
<p>This is just the start. You can add groups, submenus, and keyboard shortcuts for power users.</p>
<h3 id="heading-step-5-make-it-look-better">Step 5: Make It Look Better</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752690441156/0c2b8e39-72ca-4823-8dd2-6af305f02275.jpeg" alt="Screenshot showing dropdown with styling applied" class="image--center mx-auto" width="671" height="601" loading="lazy"></p>
<p>Use Tailwind CSS to style your dropdown, and hover effects like this:</p>
<pre><code class="lang-tsx">&lt;DropdownMenu&gt;
        &lt;DropdownMenuTrigger asChild&gt;
          &lt;button className="px-3 py-1.5 bg-primary text-white text-sm font-medium rounded-md hover:bg-primary/90 transition-colors"&gt;
            Open Menu
          &lt;/button&gt;
        &lt;/DropdownMenuTrigger&gt;
        &lt;DropdownMenuContent className="w-52 border-gray-200 shadow-lg rounded-md space-y-0.5"&gt;
          &lt;DropdownMenuLabel className="text-xs text-gray-500"&gt;
            My Account
          &lt;/DropdownMenuLabel&gt;
          &lt;DropdownMenuSeparator className="border-t border-gray-100" /&gt;
          &lt;DropdownMenuItem className="px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-100 rounded-md cursor-pointer transition-colors"&gt;
            Profile
          &lt;/DropdownMenuItem&gt;
          &lt;DropdownMenuItem className="px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-100 rounded-md cursor-pointer transition-colors"&gt;
            Settings
          &lt;/DropdownMenuItem&gt;
          &lt;DropdownMenuItem className="px-3 py-1.5 text-sm text-red-600 hover:bg-red-50 rounded-md cur
</code></pre>
<h3 id="heading-step-6-make-it-work-on-all-screens">Step 6: Make It Work on All Screens</h3>
<p>Want your dropdown to be responsive? Use Tailwind’s responsive classes:</p>
<pre><code class="lang-tsx">&lt;DropdownMenuContent className="w-full md:w-64"&gt;
</code></pre>
<p>You can also dynamically position the dropdown using Radix's built-in portal support.</p>
<h3 id="heading-step-7-add-cool-icons">Step 7: Add Cool Icons</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752691587711/0a96c5ca-0fa2-4916-92d2-087f2071d40e.jpeg" alt="Screenshot of dropdown with icons added" class="image--center mx-auto" width="618" height="565" loading="lazy"></p>
<p>Install Lucide icons:</p>
<pre><code class="lang-bash">npm install lucide-react
</code></pre>
<p>Then use them in your menu:</p>
<pre><code class="lang-tsx">import { User, Settings, LogOut } from "lucide-react"

&lt;DropdownMenuItem&gt;
  &lt;User className="mr-2 h-4 w-4" /&gt; Profile
&lt;/DropdownMenuItem&gt;
</code></pre>
<p>Icons help users scan options quickly – a great touch for UX.</p>
<h3 id="heading-step-8-its-already-accessible">Step 8: It’s Already Accessible!</h3>
<p>shadcn/ui (thanks to Radix UI) makes your dropdown menu:</p>
<ul>
<li><p>Keyboard friendly</p>
</li>
<li><p>Screen-reader ready</p>
</li>
<li><p>Following best web practices</p>
</li>
</ul>
<p>You don’t need to configure accessibility – it just works :)</p>
<h2 id="heading-real-world-use-case-country-dropdown-with-flags">Real-World Use Case: Country Dropdown with Flags</h2>
<p>Looking for a more advanced dropdown? Here’s an amazing example that includes search, flag icons, and grouping:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752598285627/6cb8b27e-7cba-4d92-95c5-3bea44e0c01c.png" alt="Shadcn dropdown example" class="image--center mx-auto" width="879" height="483" loading="lazy"></p>
<p>👉 <a target="_blank" href="https://shadcn-country-dropdown.vercel.app/">shadcn-country-dropdown.vercel.app</a></p>
<p>It’s open-source and a great place to see what’s possible with shadcn/ui.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Using shadcn/ui to create a dropdown menu is fast, simple, and powerful. You get great styling, accessibility, and full control over how things look and work. Whether you’re just starting out or building for production, this is a solid tool to use.</p>
<p>Dropdowns are just the beginning. shadcn/ui offers a whole library of headless components for building modern UIs.</p>
<p>I hope you found this article helpful! If you're building a SaaS product or any web app that involves user interaction or conversion, consider enhancing user trust with real-time notifications like modal pop-ups, <a target="_blank" href="http://toastie.saasindie.com">sales pop up</a>, etc.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Powerful Motion Graphics Frameworks for Developers ]]>
                </title>
                <description>
                    <![CDATA[ Motion graphics are no longer just eye candy. They have become a key part of how users experience software, whether it’s a mobile app, a website, or even for making animated explainer videos.  When users tap a button, they expect it to respond smooth... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/powerful-motion-graphics-frameworks-for-developers/</link>
                <guid isPermaLink="false">685595022e5abc4d8aad787e</guid>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UX ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Fri, 20 Jun 2025 17:06:10 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750439122439/489ef402-89db-47b5-a392-4c0fdcd94d0a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Motion graphics are no longer just eye candy. They have become a key part of how users experience software, whether it’s a mobile app, a website, or even for making animated explainer videos. </p>
<p>When users tap a button, they expect it to respond smoothly. When data is loading, users expect some visual feedback. Even small touches, like a gentle bounce or a fading transition, can make an interface feel polished and professional.</p>
<p>For developers, motion graphics are now part of the job. Designers may create the initial assets, but it’s usually developers who bring them to life in the actual product. </p>
<p>That means knowing how to control animations with code, integrate them into app logic, and make sure they perform well across devices. </p>
<p>Fortunately, a number of powerful tools have emerged that make motion graphics more accessible to developers without requiring them to become expert animators.</p>
<p>Let’s dive into some of the best motion graphics tools that developers can use today, and why each one is worth learning.</p>
<h2 id="heading-lottie"><strong>Lottie</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750080168850/b8487ad6-5776-4c96-97a2-33cf30b752bb.png" alt="Lotte" class="image--center mx-auto" width="1400" height="612" loading="lazy"></p>
<p><a target="_blank" href="https://lottiefiles.com/">Lottie</a> has become one of the most widely used tools for integrating motion graphics into mobile apps and websites. </p>
<p>Originally developed by Airbnb, Lottie lets designers create animations in Adobe After Effects and export them as lightweight JSON files using the Bodymovin plugin. </p>
<p>As a developer, you don’t have to recreate complex animations manually or deal with heavy video files. Instead, you load the JSON file into your app using the Lottie library, and the animation plays natively.</p>
<p>One of Lottie’s biggest advantages is its cross-platform support. Whether you’re building for iOS, Android, the web, React Native, or Flutter, Lottie works the same way. </p>
<p>The animations are rendered as scalable vector graphics, so they stay sharp on any screen size or resolution. This makes them perfect for things like onboarding screens, button interactions, loading indicators, and even full-screen animated backgrounds.</p>
<p>Lottie also gives you a lot of control through code. You can play, pause, loop, or even dynamically change the speed of an animation. If you want an animation to start when a user scrolls to a certain point or clicks a button, you can easily hook into your app’s logic and control the animation’s state. </p>
<p>This flexibility makes Lottie a favorite for developers who want designer-level animations without the usual headaches of file size, performance, or cross-platform compatibility.</p>
<h2 id="heading-gsap"><strong>GSAP</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750080203585/ddcb08af-f25b-4b2c-8cbb-9d593baf2561.jpeg" alt="GSAP" class="image--center mx-auto" width="1280" height="640" loading="lazy"></p>
<p>While Lottie is great for pre-designed animations, sometimes you want full programmatic control over how elements move and interact. That’s where <a target="_blank" href="https://gsap.com/">GSAP</a>, short for GreenSock Animation Platform, really shines. </p>
<p>GSAP allows you to animate anything on the web: HTML elements, SVG graphics, Canvas drawings, and even WebGL content. It’s used by professional web developers and interactive designers around the world for its precision, performance, and flexibility.</p>
<p>With GSAP, you write animations directly in JavaScript. You’re not importing files created in a design tool. Instead, you describe the animations in code, which gives you complete control over timing, sequencing, and interaction. </p>
<p>You can chain multiple animations together, create synchronized timelines, and easily coordinate animations across multiple elements. The syntax is both simple and powerful, allowing you to start with basic effects and scale up to complex sequences as needed.</p>
<p>One of GSAP’s standout features is how well it handles performance. Animations stay smooth even when animating many elements at once, and the library takes care of browser quirks so you don’t have to worry about inconsistencies across platforms. </p>
<p>If you’ve ever struggled with CSS transitions or vanilla JavaScript animations, GSAP feels like a breath of fresh air. You get pixel-perfect control with readable and maintainable code, even as your animations grow more complex.</p>
<h2 id="heading-framer-motion"><strong>Framer Motion</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750080236390/a3a38be8-c11d-43ac-bcf8-0f89745f7607.png" alt="Framer Motion" class="image--center mx-auto" width="1600" height="691" loading="lazy"></p>
<p>If you’re building modern web apps with React, <a target="_blank" href="https://motion.dev/">Framer Motion</a> offers a different kind of power. </p>
<p>Unlike GSAP, which works everywhere, Framer Motion is built specifically for React’s component model. Instead of managing animations through external scripts or event listeners, you define them directly in your JSX code alongside the rest of your component logic.</p>
<p>This declarative approach means you simply describe what you want to happen, and Framer Motion takes care of the rest. You specify target values for things like position, opacity, or scale, and the library smoothly transitions from the current state to the new one whenever props change. </p>
<p>This makes it incredibly easy to animate things like page transitions, hover effects, collapsible panels, and other common UI interactions.</p>
<p>Framer Motion also supports more advanced features out of the box, such as gesture-based animations, layout transitions, and shared element transitions between different routes or components. </p>
<p>These kinds of features can be very challenging to implement manually, but Framer Motion makes them approachable even for developers who aren’t animation experts.</p>
<p>Another benefit is how naturally Framer Motion fits into the React ecosystem. Since you’re not writing separate animation code, your logic stays tightly integrated with your app state and component structure. This reduces bugs, simplifies maintenance, and helps keep your codebase clean and organised.</p>
<h2 id="heading-rive"><strong>Rive</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750080251167/6cbfc5ec-28cc-453d-9ea0-52633d4b5efe.jpeg" alt="Rive" class="image--center mx-auto" width="640" height="480" loading="lazy"></p>
<p><a target="_blank" href="https://rive.app/">Rive</a> represents a new way of thinking about motion graphics, one that blurs the line between design and code. </p>
<p>Unlike tools that focus only on timeline-based animations, Rive adds state machines and logic directly into the animation itself. This allows you to create interactive, real-time animations that respond to user input or application state changes.</p>
<p>In Rive’s editor, designers build both the visuals and the interaction logic. You can define how animations transition between different states based on triggers that your app can control. </p>
<p>As a developer, you don’t have to write complex animation logic yourself. Instead, you simply send events to the Rive runtime and let it handle the animation transitions.</p>
<p>For example, imagine a character that waves when a user taps the screen, then smiles if a task is completed. With Rive, the designer creates both the waving and smiling animations and wires up the logic that connects them. </p>
<p>You just tell the animation which state to enter based on your app’s data. The result feels dynamic and interactive, like a small game embedded into your UI.</p>
<p>Rive works across platforms, including web, mobile, and game engines, and the exported files are lightweight enough to use even in performance-sensitive environments. </p>
<p>It’s a tool that empowers both designers and developers to create richer experiences without a ton of back-and-forth or complicated handoffs.</p>
<h2 id="heading-threejs"><strong>Three.js</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750080261841/039f1365-e666-496c-a036-4e9f7c174329.png" alt="Three.js" class="image--center mx-auto" width="724" height="366" loading="lazy"></p>
<p>Sometimes, 2D animations aren’t enough. When you need true 3D motion graphics in the browser, <a target="_blank" href="https://threejs.org/">Three.js</a> is the go-to library for developers. </p>
<p>While not strictly a motion graphics tool in the traditional sense, Three.js allows you to create complex 3D scenes, animate objects, and build immersive experiences entirely with JavaScript.</p>
<p>Three.js abstracts much of the complexity of <a target="_blank" href="https://en.wikipedia.org/wiki/WebGL">WebGL</a>, making it more approachable for developers who may not have a deep background in computer graphics. You can load 3D models, apply materials and lighting, set up cameras, and create fully interactive environments that respond to user input.</p>
<p>Animation in Three.js can involve simple tasks like rotating a model or more complex sequences like animated camera moves or physics-based simulations. Because you have full access to the scene graph, you can control every detail of how your objects move and behave. </p>
<p>This opens up possibilities for product visualizations, interactive demos, educational tools, and even web-based games.</p>
<p>While Three.js has a steeper learning curve than the other tools mentioned here, the payoff is significant. You’re no longer limited to flat surfaces and basic transitions. With Three.js, you can build fully immersive experiences that were once only possible in native apps or games.</p>
<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>As motion graphics become more important in modern interfaces, developers have an expanding toolbox to help them deliver polished, interactive experiences. Each tool has its own strengths, depending on the project and platform.</p>
<p>As a developer, you don’t need to master all of these tools at once. Start with the one that fits your current project needs, and build your motion graphics skills from there. With practice, you’ll discover that motion isn’t just a visual extra — it’s part of how your software communicates, guides, and delights your users.</p>
<p>Hope you enjoyed this article. You can <a target="_blank" href="https://manishshivanandhan.com/">learn more about me</a> or <a target="_blank" href="https://www.linkedin.com/in/manishmshiva/">connect with me on Linkedidn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ From Concept to Code: How to Use AI Tools to Design and Build UI Components ]]>
                </title>
                <description>
                    <![CDATA[ How should a website look? What size should the buttons be? What layout should you use? Do your users need an OTP to reset their passwords? These are all questions that proper user interface and user experience (UI/UX) design answer. Design prototypi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-design-and-build-ui-components-with-ai/</link>
                <guid isPermaLink="false">670d75f8ed568d2eaf032897</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ekemini Samuel ]]>
                </dc:creator>
                <pubDate>Mon, 14 Oct 2024 19:50:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727885187463/f41f59b0-1ec0-4dbf-9d2a-437738584310.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>How should a website look? What size should the buttons be? What layout should you use? Do your users need an OTP to reset their passwords? These are all questions that proper user interface and user experience (UI/UX) design answer.</p>
<p>Design prototyping and testing are critical steps in optimizing website UX functionality. One <a target="_blank" href="https://www.forrester.com/report/The-Six-Steps-For-Justifying-Better-UX/RES117708">study</a> reported that improving <a target="_blank" href="https://www.hotjar.com/ux-design">UX design</a> led to a 400% increase in website conversions.</p>
<p>For such an important task, we need the best possible tools and resources we can get. And lately I’ve been enjoying using <a target="_blank" href="https://sourcegraph.com/cody">Sourcegraph’s Cody</a>. Cody is an AI tool that speeds up coding by helping you understand, write, and fix code. It accesses information from your entire codebase, and also references documentation pages, to provide context about functions and variables, help create new code, and improve your design system.</p>
<p>When combined with Tailwind CSS, which is a utility-first CSS framework, you can rapidly build UI components that are both functional and visually appealing.</p>
<p>In this tutorial, I’ll teach you how to build UIs faster with Cody and Tailwind CSS so you can leverage AI to streamline your workflow.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p>Basic understanding of JavaScript and front-end development.</p>
</li>
<li><p>Familiarity with Tailwind CSS.</p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/en">Node.js</a> installed on your system.</p>
</li>
<li><p>A code editor like Visual Studio Code (VS Code).</p>
</li>
<li><p>Cody. <a target="_blank" href="https://sourcegraph.com/cody">Sign up on Sourcegraph</a> to get access (it’s free).</p>
</li>
</ul>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-will-we-be-building">What Will We Be Building?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-environment">How to Set Up Your Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-ui-components-with-ai">How to Create UI Components with AI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-more-complicated-uis">More Complicated UIs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-and-manage-existing-codebases-with-cody">How to Improve and Manage Existing Codebases with Cody</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
</li>
</ul>
<h2 id="heading-what-will-we-be-building">What Will We Be Building?</h2>
<p>Let’s build yet another Todo App, but with a spin. Each to-do item will have a timer that can be started, paused, and reset. This might be useful to track how much time you spend working on specific tasks.</p>
<p>From the <a target="_blank" href="https://sourcegraph.com/docs/cody/capabilities/chat#selecting-context">Cody docs</a>, Cody's chat allows you to add files and symbols as context in your messages.</p>
<ul>
<li><p>Type <code>@</code> and then a filename to include a file as a context.</p>
</li>
<li><p>Type <code>@#</code> and then a symbol name to include the symbol's definition as context. Functions, methods, classes, types, and so on are all symbols.</p>
</li>
</ul>
<p>Even though Cody will be doing most of the heavy lifting, it’s great to have a plan of how we want the UI to look. Here are the wireframes I created with <a target="_blank" href="https://wireframe.cc/">wireframe.cc</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667247231/ada73031-20ac-4e07-8203-abe7d85a4d55.png" alt="Wireframe of the task component - overview state and detail state" class="image--center mx-auto" width="1044" height="652" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667377001/5ac6b7ce-e2a8-41d6-8378-e5ba36bb546d.png" alt="Wireframe of the header and footer" class="image--center mx-auto" width="1044" height="652" loading="lazy"></p>
<p>Alright! Let’s get into it.</p>
<h2 id="heading-how-to-set-up-your-environment">How to Set Up Your Environment</h2>
<p>This tutorial uses Visual Studio Code, but the development process is similar across other code editors. If you haven’t set up an editor yet, choose one that suits your preference and <a target="_blank" href="https://nodejs.org/en/learn/getting-started/how-to-install-nodejs">install Node.js</a>.</p>
<p>As of this article, Sourcegraph Cody is available on <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=sourcegraph.cody-ai">Visual Studio Code</a>, <a target="_blank" href="https://github.com/sourcegraph/sg.nvim#setup">Neovim</a>, <a target="_blank" href="https://sourcegraph.com/github.com/sourcegraph/cody@main/-/blob/cli/README.md">Cody CLI</a>, <a target="_blank" href="https://github.com/sourcegraph/emacs-cody">Emacs</a>, and <a target="_blank" href="https://plugins.jetbrains.com/plugin/9682-cody-ai-coding-assistant-with-autocomplete--chat">all JetBrains IDEs</a></p>
<h3 id="heading-how-to-add-cody-to-your-code-editor">How to add Cody to your code editor</h3>
<p>First, head over to the <a target="_blank" href="https://sourcegraph.com/cody/">Cody landing page</a>, click <strong>Get Cody for Free</strong>, and follow the prompts to sign up for a <a target="_blank" href="https://sourcegraph.com/">Sourcegraph</a> account using your preferred authentication method—GitHub, GitLab, or Google.</p>
<p>Pick the right option for your code editor. If you’re using Visual Studio Code, that would be, <strong>Install Cody in VS Code</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667454887/bccde86e-ac68-4425-a5d8-ffbc7febfd83.png" alt="Cody dashboard" class="image--center mx-auto" width="1538" height="813" loading="lazy"></p>
<p>Opening the extension in your code editor prompts a sign-in:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667554900/d956efbb-9712-4096-a968-69854d3b98c0.png" alt="Signing into Cody with your preferred method." class="image--center mx-auto" width="1082" height="924" loading="lazy"></p>
<p>After signing in, we’re ready to go.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667586141/428a2eac-f383-4d7f-a63e-5fdd5638f227.jpeg" alt="The Cody chat on VS Code" class="image--center mx-auto" width="1340" height="1324" loading="lazy"></p>
<h3 id="heading-how-to-set-up-the-project"><strong>How to set up the project</strong></h3>
<p>We’ll be working with a Vite + React + TailwindCSS project, but these ideas can easily be applied to any other framework (think Vue, Astro, Svelte, or regular Vanilla JS) or styling library (like Bootstrap, Bulma, Foundation CSS, or anything you’d prefer).</p>
<p>Run the following command to create a new React project, <strong>abc-planning-todo-app</strong>:</p>
<pre><code class="lang-bash">npm create vite@latest abc-planning-todo-app -- --template react
</code></pre>
<p>Then install Tailwind CSS:</p>
<pre><code class="lang-bash">npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre>
<p>Next, update <code>tailwind.config.js</code> with this code to configure Tailwind CSS for the project:</p>
<pre><code class="lang-javascript">
<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Delete everything in <code>./src/index.css</code> and add the following Tailwind CSS directives:</p>
<pre><code class="lang-javascript">
@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>
<h2 id="heading-how-to-create-ui-components-with-ai">How to Create UI Components with AI</h2>
<p>For consistency, let’s pick a color palette we’ll use for our to-do app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667638883/06819a4e-d7fd-4a24-b9b6-6ef8cda38b08.png" alt="Color palettes for the TODO UI" class="image--center mx-auto" width="1493" height="507" loading="lazy"></p>
<p>To use these colors in our Tailwind theme, we first need to give them descriptive names. Let's ask Cody for help!</p>
<p>For the rest of this article, all quotes represent a single prompt message used when chatting with Cody.</p>
<blockquote>
<p><em>What would be good names for the following hex colors?</em></p>
<ul>
<li><p><em>2B2D42</em></p>
</li>
<li><p><em>8D99AE</em></p>
</li>
<li><p><em>EDF2F4</em></p>
</li>
<li><p><em>EF233C</em></p>
</li>
<li><p><em>D90429</em></p>
</li>
</ul>
</blockquote>
<p>And then,</p>
<blockquote>
<p><em>Update</em> <strong><em>@tailwind.config.js</em></strong> <em>to include these 5 hex colors above:</em></p>
</blockquote>
<pre><code class="lang-javascript">
<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {
      <span class="hljs-attr">colors</span>: {
        <span class="hljs-string">'midnight-navy'</span>: <span class="hljs-string">'#2B2D42'</span>,
        <span class="hljs-string">'cloudy-sky'</span>: <span class="hljs-string">'#8D99AE'</span>,
        <span class="hljs-string">'ice-white'</span>: <span class="hljs-string">'#EDF2F4'</span>,
        <span class="hljs-string">'vibrant-red'</span>: <span class="hljs-string">'#EF233C'</span>,
        <span class="hljs-string">'ruby-red'</span>: <span class="hljs-string">'#D90429'</span>,
      },
    },
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<h3 id="heading-how-to-create-basic-ui-components"><strong>How to create basic UI components</strong></h3>
<p>First, we create the Header component. With templates for relatively uncomplicated components (for example text inputs, headers, buttons and dropdowns), it’s as simple as providing a name for the component, and the copy content it should contain.</p>
<blockquote>
<p><em>Create a simple React Header Component with only company name on the far left and company motto on the far right. Use the Tailwind theme</em> <strong><em>@tailwind.config.js</em></strong></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667685164/728a496e-c018-4f09-ac4a-1926f322b3f1.png" alt="Adding the prompt to Cody" class="image--center mx-auto" width="885" height="814" loading="lazy"></p>
<p>Cody will generate a Header component, which you can then copy and paste into <code>./src/components/Header.jsx</code>.</p>
<p>You can also save the code to a new file in your project directly from the Cody chat.</p>
<pre><code class="lang-javascript">
<span class="hljs-comment">// ./src/components/Header.jsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">const</span> Header = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-midnight-navy text-ice-white p-4 flex justify-between items-center"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold"</span>&gt;</span>
        Company
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-cloudy-sky italic"</span>&gt;</span>
        Innovate. Create. Inspire.
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;
</code></pre>
<p>Next, create the Footer component with this prompt:</p>
<blockquote>
<p><em>Create a simple React Footer Component with content Copyright C ABC Planning. Use the Tailwind theme</em> <strong><em>@tailwind.config.js</em></strong>*. Ensure footer is always at the bottom of the viewport*</p>
</blockquote>
<pre><code class="lang-javascript">
<span class="hljs-comment">// ./src/components/Footer.jsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">const</span> Footer = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-midnight-navy text-ice-white p-4 text-center fixed bottom-0 w-full"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-symbol">&amp;copy;</span> {new Date().getFullYear()} ABC Planning<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>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Footer;
</code></pre>
<p>Let’s take a look at what we have so far.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667795664/bb16ff44-1403-499f-9441-9dcc804ea06b.png" alt="The generated Header and Footer" class="image--center mx-auto" width="970" height="813" loading="lazy"></p>
<h2 id="heading-more-complicated-uis"><strong>More Complicated UIs</strong></h2>
<p>Next, we’ll need a container that will hold all our to-dos. Let’s ask Cody to create a to-do container component.</p>
<blockquote>
<p><em>Create a React TodoContainer Component that will serve as a container for Todo items. Use the Tailwind theme</em> <strong><em>@tailwind.config.js</em></strong>*. It should be mobile-responsive*.</p>
</blockquote>
<p>We tell Cody:</p>
<ul>
<li><p>What we want (a React component)</p>
</li>
<li><p>A brief description of how that component behaves/what it does (container for to-dos)</p>
</li>
<li><p>Any additional context – stylesheets, preferences, conventions, or anything else.</p>
</li>
</ul>
<pre><code class="lang-javascript">
<span class="hljs-comment">// ./src/components/TodoContainer.jsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">const</span> TodoContainer = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-ice-white min-h-screen p-4 sm:p-6 md:p-8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-4xl mx-auto"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-midnight-navy text-3xl font-bold mb-6"</span>&gt;</span>Todo List<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">className</span>=<span class="hljs-string">"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"</span>&gt;</span>
          {children}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TodoContainer;
</code></pre>
<p>A good <a target="_blank" href="https://sourcegraph.com/blog/chat-oriented-programming-in-action">Chat-oriented programming</a> (CHOP) strategy is to separate the creation of markup from the creation of content. This simplifies the prompting process.</p>
<p>Cody can <a target="_blank" href="https://sourcegraph.com/blog/chat-oriented-programming-in-action">g</a>enerate both very well, but trying to fine-tune a single prompt for both tasks might be challenging. Each prompt should have a clear, specific goal for best results.</p>
<p>Let's now focus on the <code>TodoItem</code> component and split it into sections:</p>
<ul>
<li><p>There's a <strong>to-do header</strong> which consists of a title with a border.</p>
</li>
<li><p>There's a <strong>View details</strong> button.</p>
</li>
<li><p>There's a timer showing how long ago the to-do was started.</p>
</li>
<li><p>There are control buttons (which reflect the state of the to-do)</p>
<blockquote>
<p><em>Create a React TodoItem Component.</em></p>
<p><em>It consists of:</em></p>
<p><em>1. Task title. A header text with a border around it.</em></p>
<p><em>2. A simplified link/button "View details" below the todo header box.</em></p>
<p><em>3. A digital timer that shows hours, minutes, and seconds in the format 00:00:00.</em></p>
<p><em>4. Todo actions - "Start", "Pause", "Reset". Use relevant colors for each. The timer should be able to be started, paused and reset when the respective button is pressed.</em></p>
<p><em>It should look like a card (tall not wide) on a desktop.</em></p>
<p><em>It will be used with</em> <strong><em>@TodoContainer.jsx</em></strong></p>
<p><em>Use</em> <strong><em>@tailwind.config.js</em></strong> <em>theme</em></p>
</blockquote>
<pre><code class="lang-javascript">  <span class="hljs-comment">// ./src/components/TodoItem.jsx</span>
  <span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">const</span> TodoItem = <span class="hljs-function">(<span class="hljs-params">{ title }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [time, setTime] = useState(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">const</span> [isRunning, setIsRunning] = useState(<span class="hljs-literal">false</span>);
    useEffect(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">let</span> interval;
      <span class="hljs-keyword">if</span> (isRunning) {
        interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
          setTime(<span class="hljs-function">(<span class="hljs-params">prevTime</span>) =&gt;</span> prevTime + <span class="hljs-number">1</span>);
        }, <span class="hljs-number">1000</span>);
      }
      <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">clearInterval</span>(interval);
    }, [isRunning]);
    <span class="hljs-keyword">const</span> formatTime = <span class="hljs-function">(<span class="hljs-params">seconds</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> hours = <span class="hljs-built_in">Math</span>.floor(seconds / <span class="hljs-number">3600</span>);
      <span class="hljs-keyword">const</span> minutes = <span class="hljs-built_in">Math</span>.floor((seconds % <span class="hljs-number">3600</span>) / <span class="hljs-number">60</span>);
      <span class="hljs-keyword">const</span> secs = seconds % <span class="hljs-number">60</span>;
      <span class="hljs-keyword">return</span> [hours, minutes, secs].map(<span class="hljs-function"><span class="hljs-params">v</span> =&gt;</span> v.toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">'0'</span>)).join(<span class="hljs-string">':'</span>);
    };
    <span class="hljs-keyword">const</span> handleStart = <span class="hljs-function">() =&gt;</span> setIsRunning(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> handlePause = <span class="hljs-function">() =&gt;</span> setIsRunning(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> handleReset = <span class="hljs-function">() =&gt;</span> {
      setIsRunning(<span class="hljs-literal">false</span>);
      setTime(<span class="hljs-number">0</span>);
    };
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-ice-white rounded-lg shadow-md p-4 flex flex-col space-y-4 h-full"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-bold text-midnight-navy border-2 border-midnight-navy rounded p-2 text-center"</span>&gt;</span>
          {title}
        <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-cloudy-sky hover:text-midnight-navy transition-colors"</span>&gt;</span>
          View details
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-mono text-midnight-navy text-center"</span>&gt;</span>
          {formatTime(time)}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between mt-auto"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleStart}</span> 
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-vibrant-red text-ice-white px-4 py-2 rounded hover:bg-ruby-red transition-colors"</span>
          &gt;</span>
            Start
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handlePause}</span> 
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-cloudy-sky text-midnight-navy px-4 py-2 rounded hover:bg-ice-white transition-colors"</span>
          &gt;</span>
            Pause
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleReset}</span> 
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-midnight-navy text-ice-white px-4 py-2 rounded hover:bg-cloudy-sky transition-colors"</span>
          &gt;</span>
            Reset
          <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>
    );
  };
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TodoItem;
</code></pre>
<p>  From our wireframes, each to-do will have a detail state when its description is visible and can be updated. We’ll tell Cody to update the existing <code>TodoItem.jsx</code> file:</p>
<blockquote>
<p><em>Update</em> <strong><em>@TodoItem.jsx</em></strong> <em>to have a second state, when the view detail button is pressed. In this state, the TodoItem is composed of the following elements only:</em></p>
<p><em>1. Task header with the task title, same as before</em></p>
<p><em>2. A textbox with a description of the task</em></p>
<p><em>3. A save button, that emits an updateTask event, and switches the component back to its default state.</em></p>
<p><em>Include animations between the transition to create a clean feel and experience</em></p>
</blockquote>
<pre><code class="lang-javascript">
  <span class="hljs-comment">// ./src/components/TodoItem.jsx</span>
  <span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">const</span> TodoItem = <span class="hljs-function">(<span class="hljs-params">{ title, description = <span class="hljs-string">''</span>, onUpdateTask }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [time, setTime] = useState(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">const</span> [isRunning, setIsRunning] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [isDetailView, setIsDetailView] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [taskDescription, setTaskDescription] = useState(description);
    useEffect(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">let</span> interval;
      <span class="hljs-keyword">if</span> (isRunning) {
        interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
          setTime(<span class="hljs-function">(<span class="hljs-params">prevTime</span>) =&gt;</span> prevTime + <span class="hljs-number">1</span>);
        }, <span class="hljs-number">1000</span>);
      }
      <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">clearInterval</span>(interval);
    }, [isRunning]);
    <span class="hljs-keyword">const</span> formatTime = <span class="hljs-function">(<span class="hljs-params">seconds</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> hours = <span class="hljs-built_in">Math</span>.floor(seconds / <span class="hljs-number">3600</span>);
      <span class="hljs-keyword">const</span> minutes = <span class="hljs-built_in">Math</span>.floor((seconds % <span class="hljs-number">3600</span>) / <span class="hljs-number">60</span>);
      <span class="hljs-keyword">const</span> secs = seconds % <span class="hljs-number">60</span>;
      <span class="hljs-keyword">return</span> [hours, minutes, secs].map(<span class="hljs-function"><span class="hljs-params">v</span> =&gt;</span> v.toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">'0'</span>)).join(<span class="hljs-string">':'</span>);
    };
    <span class="hljs-keyword">const</span> handleStart = <span class="hljs-function">() =&gt;</span> setIsRunning(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> handlePause = <span class="hljs-function">() =&gt;</span> setIsRunning(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> handleReset = <span class="hljs-function">() =&gt;</span> {
      setIsRunning(<span class="hljs-literal">false</span>);
      setTime(<span class="hljs-number">0</span>);
    };
    <span class="hljs-keyword">const</span> handleViewDetails = <span class="hljs-function">() =&gt;</span> setIsDetailView(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> handleSave = <span class="hljs-function">() =&gt;</span> {
      onUpdateTask({ title, <span class="hljs-attr">description</span>: taskDescription });
      setIsDetailView(<span class="hljs-literal">false</span>);
    };
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-ice-white rounded-lg shadow-md p-4 flex flex-col space-y-4 h-full transition-all duration-300 ease-in-out"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-bold text-midnight-navy border-2 border-midnight-navy rounded p-2 text-center"</span>&gt;</span>
          {title}
        <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        {isDetailView ? (
          <span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-grow p-2 border-2 border-cloudy-sky rounded resize-none transition-all duration-300 ease-in-out"</span>
              <span class="hljs-attr">value</span>=<span class="hljs-string">{taskDescription}</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTaskDescription(e.target.value)}
              placeholder="Enter task description..."
            /&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleSave}</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-vibrant-red text-ice-white px-4 py-2 rounded hover:bg-ruby-red transition-colors"</span>
            &gt;</span>
              Save
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/&gt;</span></span>
        ) : (
          <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleViewDetails}</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"text-cloudy-sky hover:text-midnight-navy transition-colors"</span>
            &gt;</span>
              View details
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-mono text-midnight-navy text-center"</span>&gt;</span>
              {formatTime(time)}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between mt-auto"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleStart}</span> 
                <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-vibrant-red text-ice-white px-4 py-2 rounded hover:bg-ruby-red transition-colors"</span>
              &gt;</span>
                Start
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handlePause}</span> 
                <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-cloudy-sky text-midnight-navy px-4 py-2 rounded hover:bg-ice-white transition-colors"</span>
              &gt;</span>
                Pause
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleReset}</span> 
                <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-midnight-navy text-ice-white px-4 py-2 rounded hover:bg-cloudy-sky transition-colors"</span>
              &gt;</span>
                Reset
              <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;/&gt;</span></span>
        )}
      &lt;/div&gt;
    );
  };
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TodoItem;
</code></pre>
<p>  Now let’s create a new root component that makes use of our components:</p>
<blockquote>
<p><em>Generate an App.jsx that makes use of</em> <strong><em>@Header.jsx</em></strong>*,* <strong><em>@TodoItem.jsx</em></strong>*,* <strong><em>@TodoContainer.jsx</em></strong>*, and* <strong><em>@Footer.jsx</em></strong> <em>appropriately. Use simple JavaScript variables as state data and implement whatever functions are relevant. Have a minimum of 5 to-do items.</em></p>
</blockquote>
<pre><code class="lang-javascript">
  <span class="hljs-comment">// ./src/App.jsx</span>
  <span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">import</span> Header <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Header'</span>;
  <span class="hljs-keyword">import</span> TodoContainer <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/TodoContainer'</span>;
  <span class="hljs-keyword">import</span> TodoItem <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/TodoItem'</span>;
  <span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Footer'</span>;
  <span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [todos, setTodos] = useState([
      { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Complete project proposal'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Draft and finalize the project proposal for the client meeting.'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Review code changes'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Go through the pull requests and provide feedback on the code changes.'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Prepare presentation'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Create slides for the upcoming team meeting presentation.'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">4</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Update documentation'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Review and update the project documentation with recent changes.'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Team meeting'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Attend the weekly team sync-up and discuss project progress.'</span> },
    ]);
    <span class="hljs-keyword">const</span> handleUpdateTask = <span class="hljs-function">(<span class="hljs-params">updatedTask</span>) =&gt;</span> {
      setTodos(todos.map(<span class="hljs-function"><span class="hljs-params">todo</span> =&gt;</span> 
        todo.id === updatedTask.id ? { ...todo, ...updatedTask } : todo
      ));
    };
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col min-h-screen"</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">className</span>=<span class="hljs-string">"flex-grow"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TodoContainer</span>&gt;</span>
            {todos.map(todo =&gt; (
              <span class="hljs-tag">&lt;<span class="hljs-name">TodoItem</span> 
                <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span>
                <span class="hljs-attr">title</span>=<span class="hljs-string">{todo.title}</span>
                <span class="hljs-attr">description</span>=<span class="hljs-string">{todo.description}</span>
                <span class="hljs-attr">onUpdateTask</span>=<span class="hljs-string">{(updatedTask)</span> =&gt;</span> handleUpdateTask({ id: todo.id, ...updatedTask })}
              /&gt;
            ))}
          <span class="hljs-tag">&lt;/<span class="hljs-name">TodoContainer</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">div</span>&gt;</span></span>
    );
  };
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>  Here’s what we now have:</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667940335/69911f3c-063b-40cc-80d6-27fd7b5aaa76.png" alt="Screenshot of the web app with the TodoList and TodoItem components" class="image--center mx-auto" width="989" height="813" loading="lazy"></p>
<h2 id="heading-how-to-improve-and-manage-existing-codebases-with-cody">How to Improve and Manage Existing Codebases with Cody</h2>
<p>To further boost your productivity, Cody offers <a target="_blank" href="https://sourcegraph.com/docs/cody/capabilities/commands#prompts">prompts</a> and <a target="_blank" href="https://sourcegraph.com/docs/cody/capabilities/commands#commands">commands</a>.</p>
<p>You can save frequently used prompts for future use and share them with other team members within your organization. Commands offer quick, ready-to-use shortcuts for common coding tasks like writing, describing, fixing, and identifying code issues.</p>
<h3 id="heading-lets-add-documentation-to-our-code"><strong>Let’s add documentation to our code!</strong></h3>
<p>Firstly, select the code you would like to generate documentation for, we’ll use <code>TodoItem.jsx</code> for this example. Run the <strong>Document Code</strong> command, and we get a JSDoc docstring for the Footer component class.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726668014952/8c65682a-d931-46df-bf49-df447df74443.png" alt="Adding documentation to the code" class="image--center mx-auto" width="1538" height="836" loading="lazy"></p>
<h3 id="heading-lets-make-our-components-more-accessible-and-inclusive"><strong>Let’s make our components more accessible and inclusive</strong></h3>
<p>We can save our favorite and frequently used chat prompts on the <a target="_blank" href="https://sourcegraph.com/prompts/new"><strong>Prompt Library</strong></a> via the Sourcegraph's Web UI.</p>
<p>Let’s create a new prompt to improve the accessibility of our web app and ensure it adheres to the <a target="_blank" href="https://www.w3.org/WAI/standards-guidelines/wcag/">WCAG standard</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726668303154/a83398cb-90b7-4d28-9c8b-a15b4751366c.png" alt="Creating a new prompt on the Prompt Library" class="image--center mx-auto" width="1538" height="813" loading="lazy"></p>
<p>We can now use this prompt back in VS Code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726668369322/5279315d-283f-4d24-9a8d-62316f7ec3b4.png" alt="Using our newly created prompt in VS Code" class="image--center mx-auto" width="764" height="820" loading="lazy"></p>
<p>Above you can see the results from our accessibility prompt.</p>
<h3 id="heading-how-to-use-ai-responsibly-for-code-generation">How to Use AI Responsibly for Code Generation</h3>
<p>AI tools can significantly speed up your workflow, but it's essential to remember that AI is still a work in progress. As powerful as these tools can be, they can also make mistakes or "hallucinate," producing code that seems correct but doesn't actually work in your specific context.</p>
<p>To use AI responsibly for coding, it’s crucial to approach it from a point of understanding what needs to be done. Before relying on AI, make sure you have a solid grasp of the task at hand. AI works best when used as a productivity booster rather than a replacement for your expertise.</p>
<p>Here are a few key things to keep in mind when working with AI-generated code:</p>
<ul>
<li><p><strong>Double-check the code:</strong> Always run and test the code generated by AI. Even if it looks correct at first glance, there could be subtle errors or inefficiencies. It's your responsibility to ensure the code is functional and meets your project’s requirements.</p>
</li>
<li><p><strong>Understand the output:</strong> Before using any AI-suggested code, take time to understand how it works. This will allow you to quickly identify any mistakes and integrate the code effectively with the rest of your project.</p>
</li>
</ul>
<p>When used thoughtfully and carefully, AI can make your development process more efficient and help you focus on higher-level tasks. However, it’s essential to balance its use with human oversight to ensure the quality and accuracy of the code you're building.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>Creating user-friendly UIs has traditionally been time-consuming and difficult to manage. But using Cody, we created an interactive and attractive user interface with minimal effort. Cody supported us throughout the entire development process.</p>
<p>Here are some potential enhancements you can make:</p>
<ul>
<li><p>We can’t create or delete tasks. Try to fix that.</p>
</li>
<li><p>Develop a component to display total tasks and accumulated hours.</p>
</li>
<li><p>Add test cases for each component. We can do this quickly using the <strong>Generate Unit Tests</strong> command.</p>
</li>
</ul>
<p>If you enjoyed learning about Cody, you can try more of its features and applications. <a target="_blank" href="https://sourcegraph.com/cody">Sign up for a free, forever account</a> and boost your productivity designing, creating, documenting, and managing applications.</p>
<h3 id="heading-further-reading">Further reading</h3>
<p>If you want to learn more, you can read this article about chat-oriented programming (CHOP) and how to use Cody for it: <a target="_blank" href="https://sourcegraph.com/blog/chat-oriented-programming-in-action">Chat-oriented programming (CHOP) in action</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Tooltips in Jetpack Compose ]]>
                </title>
                <description>
                    <![CDATA[ When I wrote my last article about Jetpack Compose, I stated there that Jetpack Compose is missing some (in my opinion) basic components, and one of them is the tooltip. At the time, there was no built-in composable to display tooltips and there were... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-tooltips-in-jetpack-compose/</link>
                <guid isPermaLink="false">66fd516514798d90f2228542</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Jetpack Compose ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tooltip ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tomer ]]>
                </dc:creator>
                <pubDate>Wed, 02 Oct 2024 13:57:57 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727813989960/b0a7ab29-d87c-4d87-9847-70b7e1c341b1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When I wrote my <a target="_blank" href="https://medium.com/better-programming/is-jetpack-compose-ready-for-you-eae6c93ad3f8">last article about Jetpack Compose</a>, I stated there that Jetpack Compose is missing some (in my opinion) basic components, and one of them is the tooltip.</p>
<p>At the time, there was no built-in composable to display tooltips and there were several alternative solutions circling online. The problem with those solutions was that once Jetpack Compose released newer versions, those solutions might break. So it wasn’t ideal and the community was left hoping that sometime in the future, support would be added for tooltips.</p>
<p>I’m glad to say that since <a target="_blank" href="https://developer.android.com/jetpack/androidx/releases/compose-material3#1.1.0">version 1.1.0 of Compose Material 3</a>, we now have built in tooltip support. 👏</p>
<p>While this in itself is great, more than a year has passed since that version was released. And with subsequent versions, the API related to tooltips changed drastically as well.</p>
<p>If you go over the changelog, you will see how the public and internal APIs have changed. So bear in mind, that when you read this article, things may have continued to change as everything related to Tooltips is still marked by the annotation <strong>ExperimentalMaterial3Api::class</strong>.</p>
<p>❗️ The version of material 3 used for this article is 1.2.1, which was released on March 6th, 2024</p>
<h2 id="heading-tooltip-types">Tooltip Types</h2>
<p>We now have support for two different types of tooltips:</p>
<ol>
<li><p>Plain tooltip</p>
</li>
<li><p>Rich media tooltip</p>
</li>
</ol>
<h3 id="heading-plain-tooltip">Plain Tooltip</h3>
<p>You can use the first kind to provide information about an icon button that wouldn’t be clear otherwise. For example, you can use a plain tooltip to indicate to a user what the icon button represents.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727602449314/94cf84bf-dec0-462c-a8a0-6f878e0d5db3.gif" alt="Basic tooltip example" class="image--center mx-auto" width="213" height="450" loading="lazy"></p>
<p>To add a tooltip to your application, you use the <strong>TooltipBox</strong> composable. This composable takes several arguments:</p>
<pre><code class="lang-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">TooltipBox</span><span class="hljs-params">(
    positionProvider: <span class="hljs-type">PopupPositionProvider</span>,
    tooltip: @<span class="hljs-type">Composable</span> <span class="hljs-type">TooltipScope</span>.() -&gt; <span class="hljs-type">Unit</span>,
    state: <span class="hljs-type">TooltipState</span>,
    modifier: <span class="hljs-type">Modifier</span> = Modifier,
    focusable: <span class="hljs-type">Boolean</span> = <span class="hljs-literal">true</span>,
    enableUserInput: <span class="hljs-type">Boolean</span> = <span class="hljs-literal">true</span>,
    content: @<span class="hljs-type">Composable</span> () -&gt; <span class="hljs-type">Unit</span>,
)</span></span>
</code></pre>
<p>Some of these should be familiar to you if you have used Composables before. I’ll highlight the ones that have a specific use case here:</p>
<ul>
<li><p>positionProvider - Of <strong>PopupPositionProvider</strong> type, and is used to calculate the position of the tooltip.</p>
</li>
<li><p>tooltip - This is where you can design the UI of how the tooltip will look like.</p>
</li>
<li><p>state - This holds the state that is associated with a specific Tooltip instance. It exposes methods like showing/dismissing the tooltip and when instantiating an instance of one, you can declare if the tooltip should be persistent or not (meaning if it should keep displaying on the screen until a user performs a click action outside the tooltip).</p>
</li>
<li><p>content - This is the UI that the tooltip will display above/below.</p>
</li>
</ul>
<p>Here is an example of instantiating a <strong>BasicTooltipBox</strong> with all the relevant arguments filled in:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)</span>
<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">BasicTooltip</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">val</span> tooltipPosition = TooltipDefaults.rememberPlainTooltipPositionProvider()
    <span class="hljs-keyword">val</span> tooltipState = rememberBasicTooltipState(isPersistent = <span class="hljs-literal">false</span>)

    BasicTooltipBox(positionProvider = tooltipPosition,
        tooltip =  { Text(<span class="hljs-string">"Hello World"</span>) } ,
        state = tooltipState) {
        IconButton(onClick = { }) {
            Icon(imageVector = Icons.Filled.Favorite, 
                 contentDescription = <span class="hljs-string">"Your icon's description"</span>)
        }
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727602558759/e00e0bed-6a95-489e-af5c-a7d9dcc33fe6.gif" alt="A basic tooltip" class="image--center mx-auto" width="213" height="450" loading="lazy"></p>
<p>Jetpack Compose has a built in class called TooltipDefaults. You can use this class to help you instantiate arguments that make up a TooltipBox. For instance, you could use <strong>TooltipDefaults.rememberPlainTooltipPositionProvider</strong> to correctly position the tooltip in relation to the anchor element.</p>
<h3 id="heading-rich-tooltip">Rich Tooltip</h3>
<p>A rich media tooltip takes more space than a plain tooltip and can be used to provide more context about the functionality of an icon button. When the tooltip is shown, you can add buttons and links to it to provide further explanation or definitions.</p>
<p>It is instantiated in a similar way as a plain tooltip, inside of a TooltipBox, but you use the RichTooltip composable.</p>
<pre><code class="lang-kotlin">TooltipBox(positionProvider = tooltipPosition,
        tooltip = {
                  RichTooltip(
                      title = { Text(<span class="hljs-string">"RichTooltip"</span>) },
                      caretSize = caretSize,
                      action = {
                          TextButton(onClick = {
                              scope.launch {
                                  tooltipState.dismiss()
                                  tooltipState.onDispose()
                              }
                          }) {
                              Text(<span class="hljs-string">"Dismiss"</span>)
                          }
                      }
                  ) {
                        Text(<span class="hljs-string">"This is where a description would go."</span>)
                  }
        },
        state = tooltipState) {
        IconButton(onClick = {
            <span class="hljs-comment">/* Icon button's click event */</span>
        }) {
            Icon(imageVector = tooltipIcon,
                contentDescription = <span class="hljs-string">"Your icon's description"</span>,
                tint = iconColor)
        }
    }
</code></pre>
<p>A few things to notice about a Rich tooltip:</p>
<ol>
<li><p>A Rich tooltip has support for a caret.</p>
</li>
<li><p>You can add an action (that is, a button) to the tooltip to give users an option to find out more information.</p>
</li>
<li><p>You can add logic to dismiss the tooltip.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727602624042/40160d88-4e8a-4487-835d-1b74a9dd7c72.png" alt="Rich tooltip without a caret" class="image--center mx-auto" width="375" height="792" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727602651265/f3e6f7fe-c4e1-4f98-972d-b20a273900b4.png" alt="Rich tooltip with a caret" class="image--center mx-auto" width="375" height="792" loading="lazy"></p>
<h3 id="heading-edge-cases">Edge Cases</h3>
<p>When you choose to mark your <strong>tooltip state as persistent</strong>, it means that once the user interacts with the UI that shows your tooltip, it will stay visible until the user presses anywhere else on the screen.</p>
<p>If you looked at the example of a Rich tooltip from above, you might have noticed that we have added a button to dismiss the tooltip once it’s clicked.</p>
<p>There is a problem that happens once a user presses that button. Since the dismiss action is performed on the tooltip, if a user wants to perform another long press on the UI item that invokes this tooltip, the tooltip won’t be shown again. This means that the state of the tooltip is persistent on it being dismissed. So, how do we go about and resolve this?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727602690256/a31b56bb-77c4-4444-bab6-7ffcca3f5207.gif" alt="Second long press does not trigger the tooltip" class="image--center mx-auto" width="213" height="450" loading="lazy"></p>
<p>In order to “reset” the state of the tooltip, we have to call the <strong>onDispose</strong> method that is exposed through the tooltip state. Once we do that, the tooltip state is reset and the tooltip will be shown again when the user performs a long press on the UI item.</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@OptIn(ExperimentalMaterial3Api::class)</span>
<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">RichTooltip</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">val</span> tooltipPosition = TooltipDefaults.rememberRichTooltipPositionProvider()
    <span class="hljs-keyword">val</span> tooltipState = rememberTooltipState(isPersistent = <span class="hljs-literal">true</span>)
    <span class="hljs-keyword">val</span> scope = rememberCoroutineScope()

    TooltipBox(positionProvider = tooltipPosition,
        tooltip = {
                  RichTooltip(
                      title = { Text(<span class="hljs-string">"RichTooltip"</span>) },
                      caretSize = TooltipDefaults.caretSize,
                      action = {
                          TextButton(onClick = {
                              scope.launch {
                                  tooltipState.dismiss()
                                  tooltipState.onDispose()  <span class="hljs-comment">/// &lt;---- HERE</span>
                              }
                          }) {
                              Text(<span class="hljs-string">"Dismiss"</span>)
                          }
                      }
                  ) {

                  }
        },
        state = tooltipState) {
        IconButton(onClick = {  }) {
            Icon(imageVector = Icons.Filled.Call, contentDescription = <span class="hljs-string">"Your icon's description"</span>)
        }
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727602730404/60f31668-ea66-4127-b6fc-41f3aca952ae.gif" alt="onDispose solves the issue" class="image--center mx-auto" width="213" height="450" loading="lazy"></p>
<p>Another scenario where the tooltip state does not reset is if instead of calling ourselves for the dismiss method per a user’s action, the user clicks outside of the tooltip, causing it to be dismissed. This calls the dismiss method behind the scenes and the tooltip state is set to dismissed. Long pressing on the UI element to see our tooltip again will result in nothing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727602758707/60387e08-72e6-45d4-bd47-ffb2708e0efe.gif" alt="The tooltip does not show again" class="image--center mx-auto" width="213" height="450" loading="lazy"></p>
<p>Our logic that calls the tooltip’s onDispose method does not get triggered, so how can we reset the tooltip’s state?</p>
<p>Currently, I haven’t been able to figure this out. It might be related to the tooltip’s <a target="_blank" href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/MutatorMutex">MutatorMutex</a>. Maybe with upcoming releases, there will be an API for this. I did notice that if other tooltips are present on the screen and they are pressed, this resets the previously clicked upon tooltip.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727602790121/25a81994-a508-4c71-8424-c45370a7999d.gif" alt="25a81994-a508-4c71-8424-c45370a7999d" class="image--center mx-auto" width="213" height="450" loading="lazy"></p>
<p>If you would like to see the code featured here, you can go to <a target="_blank" href="https://github.com/TomerPacific/MediumArticles/tree/master/TooltipExample">this GitHub repository</a></p>
<p>If you would like to see tooltips in an application, you can check it out <a target="_blank" href="https://play.google.com/store/apps/details?id=com.tomerpacific.laundry">here</a>.</p>
<h4 id="heading-references">References</h4>
<ul>
<li><p><a target="_blank" href="https://m3.material.io/components/tooltips/overview">Material3 Tooltip Overview</a></p>
</li>
<li><p><a target="_blank" href="https://developer.android.com/reference/kotlin/androidx/compose/material3/TooltipDefaults">Tooltip Defaults</a></p>
</li>
<li><p><a target="_blank" href="https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt">Tooltip Source Code</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Handle UI Events in Jetpack Compose ]]>
                </title>
                <description>
                    <![CDATA[ In this short and practical article, we will talk about how to handle UI events in Jetpack Compose. In the old system, we used OnClickListeners and other interfaces. In Compose, we can take full advantage of Kotlin’s Sealed Classes, Function Types an... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-handle-ui-events-in-jetpack-compose/</link>
                <guid isPermaLink="false">66d460c99f2bec37e2da066a</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Android Studio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Jetpack Compose ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kotlin ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ User Interface ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ryan Michael Kay ]]>
                </dc:creator>
                <pubDate>Tue, 16 Mar 2021 18:22:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/03/cat-4793068_1280-5.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this short and practical article, we will talk about how to handle UI events in Jetpack Compose.</p>
<p>In the old system, we used OnClickListeners and other interfaces. In Compose, we can take full advantage of Kotlin’s <strong>Sealed Classes</strong>, <strong>Function Types</strong> and <strong>Lambda Expressions</strong>.</p>
<p>If you do not know what a composable is, consider reading <a target="_blank" href="https://www.freecodecamp.org/news/jetpack-compose-beginner-tutorial-composables-recomposition/">this article which explains the fundamentals</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/LrNPw1LQHEw" 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>
<h2 id="heading-how-to-model-ui-events-with-a-sealed-class">How to Model UI Events with a Sealed Class</h2>
<p>First, we must learn what is meant by UI Events and how to model them with Sealed Classes.</p>
<p>I have described this same process for <a target="_blank" href="https://medium.com/swlh/simplify-your-ui-interactions-with-events-java-kotlin-any-language-5062c1b1e0e4">Java and Kotlin</a> (with the old view system) before, so I will keep this brief.</p>
<h3 id="heading-the-process">The Process</h3>
<p>For each screen or sub-screen of your UI, ask yourself this question: What are all the different ways which the user can interact with it?</p>
<p>Let's take an example from my first app built fully in compose, <a target="_blank" href="https://play.google.com/store/apps/details?id=com.bracketcove.graphsudoku">Graph Sudoku</a>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/graph_sudoku_small_screen.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Screenshot of a Sudoku Android App</em></p>
<p>The sealed class I use to represent the UI interactions of this screen looks like this:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">sealed</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActiveGameEvent</span> </span>{
    <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OnInput</span></span>(<span class="hljs-keyword">val</span> input: <span class="hljs-built_in">Int</span>) : ActiveGameEvent()
    <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OnTileFocused</span></span>(<span class="hljs-keyword">val</span> x: <span class="hljs-built_in">Int</span>, 
    <span class="hljs-keyword">val</span> y: <span class="hljs-built_in">Int</span>) : ActiveGameEvent()
    <span class="hljs-keyword">object</span> OnNewGameClicked : ActiveGameEvent()
    <span class="hljs-keyword">object</span> OnStart : ActiveGameEvent()
    <span class="hljs-keyword">object</span> OnStop : ActiveGameEvent()
}
</code></pre>
<p>To explain briefly:</p>
<ul>
<li><p>OnInput represents a user touching an input button (like 0, 1, 2, 3, 4)</p>
</li>
<li><p>OnTileFocused represents a user selecting a tile (like the amber highlighted one)</p>
</li>
<li><p>OnNewGameClicked is self-explanatory</p>
</li>
<li><p>OnStart and OnStop are lifecycle events which my composables do not care about, but they are used in the Activity which acts as a Container for the composables</p>
</li>
</ul>
<p>Once you have your sealed class set up, you can now handle a wide variety of events using a single event handler function. Sometimes it might make more sense to have multiple event handler functions, so keep in mind that <strong>this approach must be adapted to your project's specific requirements</strong>.</p>
<h2 id="heading-how-to-connect-your-software-architecture">How to Connect Your Software Architecture</h2>
<p>What you have handling these events is totally up to you. Some people think that MVVM is the golden standard of software architectures, but it seems like more and more people are realizing that <strong>there is no single architecture which works best for every situation</strong>.</p>
<p>For Android with Compose, my current approach is to use a very 3rd party minimalist approach which typically has these things in each feature (screen):</p>
<ul>
<li><p>A (Presentation) Logic class <strong>as an event handler</strong></p>
</li>
<li><p>A ViewModel to store the data necessary to render the View (as the name implies)</p>
</li>
<li><p>An Activity which acts as a Container (not a god object)</p>
</li>
<li><p>Composables to form the View</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/model_view_whatever-3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Model-View-Whatever</em></p>
<p>I do not care what you use as long as you are applying <a target="_blank" href="https://youtu.be/B_C41SF0KbI">separation of concerns</a>. This is how I arrived at this architecture, by simply asking what should and should not be put together in the same class.</p>
<p>Whether you want your ViewModel, a Fragment, or an Activity to be your event handler, all of them can be set up the same way: <strong>Function Types!</strong></p>
<p>Within your class of choice, set up an event handler function which accepts your sealed class as its argument:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActiveGameLogic</span></span>(
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> container: ActiveGameContainer?,
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> viewModel: ActiveGameViewModel,
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> gameRepo: IGameRepository,
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> statsRepo: IStatisticsRepository,
    dispatcher: DispatcherProvider
) : BaseLogic&lt;ActiveGameEvent&gt;(dispatcher),
    CoroutineScope {
    <span class="hljs-comment">//...</span>
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onEvent</span><span class="hljs-params">(event: <span class="hljs-type">ActiveGameEvent</span>)</span></span> {
        <span class="hljs-keyword">when</span> (event) {
            <span class="hljs-keyword">is</span> ActiveGameEvent.OnInput -&gt; onInput(
                event.input,
                viewModel.timerState
            )
            ActiveGameEvent.OnNewGameClicked -&gt; onNewGameClicked()
            ActiveGameEvent.OnStart -&gt; onStart()
            ActiveGameEvent.OnStop -&gt; onStop()
            <span class="hljs-keyword">is</span> ActiveGameEvent.OnTileFocused -&gt; onTileFocused(event.x, event.y)
        }
    }
    <span class="hljs-comment">//...</span>
}
</code></pre>
<p>This approach is very organized and makes it easy to test every Unit in this 3rd party library free class through a single entry point.</p>
<p>However, we are not done yet. Naturally, we need a way to get a reference to this event handler function, <code>onEvent</code>, to our Composables. We can do this using a <strong>function reference</strong>:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActiveGameActivity</span> : <span class="hljs-type">AppCompatActivity</span></span>(), ActiveGameContainer {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">lateinit</span> <span class="hljs-keyword">var</span> logic: ActiveGameLogic

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState)

        <span class="hljs-keyword">val</span> viewModel = ActiveGameViewModel()

        setContent {
            ActiveGameScreen(
                onEventHandler = logic::onEvent,
                viewModel
            )
        }

        logic = buildActiveGameLogic(<span class="hljs-keyword">this</span>, viewModel, applicationContext)
    }

      <span class="hljs-comment">//...</span>
}
</code></pre>
<p>I am sure some of you are wondering why I am using an Activity. You can ask me during a <a target="_blank" href="https://youtu.be/-xV8k-4UW50">livestream Q&amp;A sometime for a detailed answer</a>.</p>
<p>In short, Fragments appear to be a bit pointless with Compose with my approach to architecture (I do not use Jetpack Navigation), and there is nothing wrong with using Activities as a feature specific container. <strong>Just avoid writing god activities, basically.</strong></p>
<p>To be specific, the way you make a reference to a function in Kotlin, is by providing the <strong>class/interface name</strong> (or <strong>skip that if it is a Top-Level function</strong>), followed by <strong>two colons</strong>, and the <strong>name of the function without any arguments or brackets</strong>:</p>
<pre><code class="lang-pgsql">onEventHandler = logic::onEvent
</code></pre>
<h2 id="heading-how-to-replace-onclicklistener-with-jetpack-compose-onclick-modifier">How to Replace onClickListener With Jetpack Compose onClick Modifier</h2>
<p>With that stuff ready, we can look at how this works within the composable. Naturally, your root composable will need the event handler function as a parameter:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">ActiveGameScreen</span><span class="hljs-params">(
    onEventHandler: (<span class="hljs-type">ActiveGameEvent</span>) -&gt; <span class="hljs-type">Unit</span>,
    viewModel: <span class="hljs-type">ActiveGameViewModel</span>
)</span></span> {
<span class="hljs-comment">//...</span>
}
</code></pre>
<p>It can be a bit tricky to get function type syntax correctly, but understand that this <strong>really is a reference to a function,</strong> which is not so different from a reference to a class.</p>
<p>Just as you should not build god objects, you should not build giant composables:</p>
<ol>
<li><p>Break your UI down into the <strong>smallest reasonable parts</strong></p>
</li>
<li><p>Wrap them in a composable function</p>
</li>
<li><p>For each composable which has a UI interaction associated with it, <strong>it must be given a reference to your event handler function</strong></p>
</li>
</ol>
<p>Here is a composable which represents the input buttons of the Sudoku app, which is given the event handler by reference:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">SudokuInputButton</span><span class="hljs-params">(
    onEventHandler: (<span class="hljs-type">ActiveGameEvent</span>) -&gt; <span class="hljs-type">Unit</span>,
    number: <span class="hljs-type">Int</span>
)</span></span> {
    Button(
        onClick = { onEventHandler.invoke(ActiveGameEvent.OnInput(number)) },
        modifier = Modifier
            .requiredSize(<span class="hljs-number">56</span>.dp)
            .padding(<span class="hljs-number">2</span>.dp)
    ) {
        Text(
            text = number.toString(),
            style = inputButton.copy(color = MaterialTheme.colors.onPrimary),
            modifier = Modifier.fillMaxSize()
        )
    }
}
</code></pre>
<p>To actually pass the event to the logic class, we must use the <code>invoke</code> function, which will accept arguments as per the function type definition (which accepts an <code>ActiveGameEvent</code> in this case).</p>
<p>At this point, you are ready to handle UI interaction events in Kotlin (compose or not) by taking full advantage of this beautiful and modern programming language.</p>
<p>If you liked this article, share it on social media and consider checking out the resources below to support an independent programmer and content creator.</p>
<h3 id="heading-social">Social</h3>
<p>You can find me on <a target="_blank" href="https://www.instagram.com/rkay301/">Instagram here</a> and on <a target="_blank" href="https://twitter.com/wiseAss301">Twitter here</a>.</p>
<h3 id="heading-here-are-some-of-my-tutorials-amp-courses">Here are some of my tutorials &amp; courses</h3>
<p><a target="_blank" href="https://www.youtube.com/channel/UCSwuCetC3YlO1Y7bqVW5GHg">https://youtube.com/wiseass</a> <a target="_blank" href="https://www.freecodecamp.org/news/author/ryan-michael-kay/">https://www.freecodecamp.org/news/author/ryan-michael-kay/</a> <a target="_blank" href="https://skl.sh/35IdKsj">https://skl.sh/35IdKsj</a> (introduction to Android with Android Studio)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A Jetpack Compose Tutorial for Beginners – How To Understand Composables & Recomposition ]]>
                </title>
                <description>
                    <![CDATA[ This tutorial will teach you a few fundamental concepts and terms related to the Jetpack Compose UI Library on Android. While this is a beginner's guide to Compose, it will not be a beginner's guide to Android – so you should have built at least an a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/jetpack-compose-beginner-tutorial-composables-recomposition/</link>
                <guid isPermaLink="false">66d460cdbd438296f45cd3b0</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ryan Michael Kay ]]>
                </dc:creator>
                <pubDate>Mon, 08 Mar 2021 21:19:42 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/6042c692a7946308b7682cbb.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This tutorial will teach you a few fundamental concepts and terms related to the Jetpack Compose UI Library on Android.</p>
<p>While this is a beginner's guide to Compose, it will not be a beginner's guide to Android – so you should have built at least an application or two (though not in Compose, necessarily).</p>
<p>Before we begin, I was initially planning to write a follow up article directed towards more senior developers until I came across Leland Richardson’s <a target="_blank" href="https://medium.com/androiddevelopers/understanding-jetpack-compose-part-1-of-2-ca316fe39050">two part article series</a>. Leland is not only a Software Engineer working on the Jetpack Compose team, but I see that he is a great writer as well.</p>
<p>While I feel my article will stand on its own as an introduction to the basics of Jetpack Compose, I <strong>strongly suggest</strong> you read his articles once you have gained some practical experience with Compose (or right away if you prefer to learn that way).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/ijwBr4oeX0I" 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>
<h3 id="heading-key-termsconcepts-explained-in-this-article">Key Terms/Concepts Explained in this article:</h3>
<ul>
<li><p>A brief review of the old View System and Hierarchy</p>
</li>
<li><p>Composables and how they stand in relation to Views</p>
</li>
<li><p>Recomposition and how to avoid doing it very poorly!</p>
</li>
</ul>
<h1 id="heading-what-is-a-composable">What Is A Composable?</h1>
<p>In this section, we will discuss the most fundamental part of the Jetpack Compose library. If you are a seasoned Android developer, you may wish to skip to the sub-section titled “Are Composables Views?”</p>
<p>If you are not already familiar with the View system, you should read the next section as it is necessary to motivate and understand what a Composable is.</p>
<h2 id="heading-view-hierarchy">View Hierarchy</h2>
<p>In the context of the Android SDK (the libraries we use to make user interfaces on this platform), a View is what we use to give structure and style to our applications.</p>
<p>It is the most fundamental kind of building block or element of a given user interface (UI), and each of these building blocks will contain the following kinds of information (among other things):</p>
<ul>
<li><p>X and Y start and end positions which tell the computer where to draw the view on the device screen</p>
</li>
<li><p>Color and alpha (transparency) values</p>
</li>
<li><p>Font information, text, symbols, and images</p>
</li>
<li><p>Behaviour based on events such as user interaction (clicks) or changes in the application’s data (more on that later)</p>
</li>
</ul>
<p>It is important to understand that <strong>a View can be something like a button</strong> (commonly referred to as a “widget”), <strong>but it can also be a container of the whole screen, part of the screen, or for other child Views</strong>.</p>
<p>Such <strong>containers</strong> are commonly referred to as Layouts or Viewgroups depending on the context. And, while sharing most of the same kinds of information as a widget, they also contain information about how to arrange and display other Views which are <strong>nested</strong> within them.</p>
<p>With that in mind, we get to the important part of this review of the View system: The <strong>View Hierarchy</strong>. For Web Developers, the View Hierarchy is essentially Android’s version of the Document Object Model (DOM).</p>
<p>For Android Developers, you can think of the View Hierarchy as a virtual representation of all the Views which you defined either in XML files or programmatically in Java or Kotlin.</p>
<p>To illustrate this, let's look at such an XML file (there’s no need to study it closely, just note the names). Then, using a debugger/stepper tool, we will look at what it looks like in the memory space of the Fragment which inflates this file:</p>
<p><strong>fragment_hour_view.xml:</strong></p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version=”1.0" encoding=”utf-8"?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">androidx.constraintlayout.widget.ConstraintLayout</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">”http://schemas.android.com/apk/res/android</span>"
<span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">”match_parent”</span>
<span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">”match_parent”</span>
<span class="hljs-attr">android:id</span>=<span class="hljs-string">”@+id/root_hour_view_fragment”</span>
<span class="hljs-attr">xmlns:app</span>=<span class="hljs-string">”http://schemas.android.com/apk/res-auto</span>"
&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">androidx.compose.ui.platform.ComposeView</span>
<span class="hljs-attr">android:id</span>=<span class="hljs-string">”@+id/tlb_hour_view”</span>
//<span class="hljs-attr">...</span>
 /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">com.wiseassblog.samsaradayplanner.ui.managehourview.HourToggleView</span>
<span class="hljs-attr">android:id</span>=<span class="hljs-string">”@+id/vqht_one”</span>
//<span class="hljs-attr">...</span>
/&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">com.wiseassblog.samsaradayplanner.ui.managehourview.HourToggleView</span>
<span class="hljs-attr">android:id</span>=<span class="hljs-string">”@+id/vqht_two”</span>
//<span class="hljs-attr">...</span>
/&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">com.wiseassblog.samsaradayplanner.ui.managehourview.HourToggleView</span>
<span class="hljs-attr">android:id</span>=<span class="hljs-string">”@+id/vqht_three”</span>
//<span class="hljs-attr">...</span>
/&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">com.wiseassblog.samsaradayplanner.ui.managehourview.HourToggleView</span>
<span class="hljs-attr">android:id</span>=<span class="hljs-string">”@+id/vqht_four”</span>
//<span class="hljs-attr">...</span>
/&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">androidx.constraintlayout.widget.ConstraintLayout</span>&gt;</span>
</code></pre>
<p><strong>Memory Space of (Fragment)HourView.kt:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-22.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Image of a View Hierarchy</em></p>
<p>The debugger and stepper tools are some of my favourite ways to learn about what is going on under the hood of the code that I use from various libraries. Give it a try some time!</p>
<p>The purpose of showing you this XML file and what it turns into in a <strong>process</strong> (a process is <strong>simply a program that is running</strong> on a device), is to demonstrate how nested Views in an XML file translate into a nested View Hierarchy at runtime.</p>
<p>Hopefully, with a simple but concrete model of how the old system works, we can compare it with the new one.</p>
<h2 id="heading-are-composables-views">Are Composables Views?</h2>
<p>This was one of the first questions I asked when I started working with Compose, and the answer I have arrived at is both <strong>yes</strong> and <strong>no</strong>.</p>
<p><strong>Yes</strong>, in the sense that a Composable fulfills the <strong>same conceptual role as a View</strong> in the old system. A Composable can be a widget like a button, or a container such as a ConstraintLayout (it is worth noting that there is a Composable implementation of ConstraintLayout available).</p>
<p><strong>No</strong>, in the sense that the UI is no longer represented virtually in a View Hierarchy (apart from situations involving interoperability). With that being said, compose does not use magic to virtually represent and keep track of the UI. This means that it must have its own thing which is conceptually similar to a View Hierarchy.</p>
<p>Let us take a very brief look at this thing. Here, we have an Activity which uses the <code>setContent {…}</code> function to bind a Composable to itself:</p>
<p><strong>ActiveGameActivity.kt:</strong></p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActiveGameActivity</span> : <span class="hljs-type">AppCompatActivity</span></span>(), ActiveGameContainer {
<span class="hljs-keyword">private</span> <span class="hljs-keyword">lateinit</span> <span class="hljs-keyword">var</span> logic: ActiveGameLogic
<span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
    <span class="hljs-keyword">super</span>.onCreate(savedInstanceState)
    <span class="hljs-keyword">val</span> viewModel = ActiveGameViewModel()
    setContent {
        ActiveGameScreen(
            onEventHandler = {
                logic.onEvent(it)
            },
            viewModel
        )
    }
    logic = buildActiveGameLogic(<span class="hljs-keyword">this</span>, viewModel, applicationContext)
}
<span class="hljs-comment">//…</span>
}
</code></pre>
<p><strong>ActiveGameScreen.kt:</strong></p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">ActiveGameScreen</span><span class="hljs-params">(
    onEventHandler: ((<span class="hljs-type">ActiveGameEvent</span>) -&gt; <span class="hljs-type">Unit</span>),
    viewModel: <span class="hljs-type">ActiveGameViewModel</span>
)</span></span> {
    <span class="hljs-comment">//...</span>

    GraphSudokuTheme {
        Column(
            Modifier
                .background(MaterialTheme.colors.primary)
                .fillMaxHeight()
        ) {
            ActiveGameToolbar(
                clickHandler = {
                    onEventHandler.invoke(
                        ActiveGameEvent.OnNewGameClicked
                    )
                }
            )

            Box {
              <span class="hljs-comment">//content</span>
            }
        }
    }
}
</code></pre>
<p>In Compose, the View Hierarchy is replaced with something that we can locate if we dig really deeply into the <strong>mWindow</strong> field of this Activity. Within that field is the conceptual replacement of the View Hierarchy: <strong>The</strong> <code>**Composer**</code> <strong>and its</strong> <code>**slotTable**</code><strong>.</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-26.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>At this point, if you want a detailed overview of the <code>Composer</code> and its <code>slotTable</code>, I must again suggest that you read Leland’s article (he goes into detail in <a target="_blank" href="https://medium.com/androiddevelopers/under-the-hood-of-jetpack-compose-part-2-of-2-37b2c20c6cdd">part 2</a>). There is more to the Compose Hierarchy than the Composer and its slotTable, but that should be sufficient to get us started.</p>
<p>In general terms, Jetpack Compose uses what we might call its Compose Hierarchy (which is made of, and managed by things like the Composer and its slotTable).</p>
<p>Again, this is the same conceptual idea as the View hierarchy, a bunch of objects in memory space which collectively represent the UI, but it is implemented very differently.</p>
<p>There's an important difference, though, which is tricky to understand technically, but easy to understand in principle. This is the way in which Compose handles updates to the Compose Hierarchy: <strong>Recomposition</strong>.</p>
<h1 id="heading-recomposition-how-to-update-compose-ui">Recomposition: How To Update Compose UI</h1>
<p>For my ESL friends, the word Compose comes from the latin <em>componere</em>, which roughly means “to put together.” Someone who writes music is often called a “Composer,” which can be thought of as the one who puts together the notes coming from one or more instruments into a composition (song).</p>
<p>Putting together implies that there are individual pieces. It's important to understand that almost any good software developer makes at least some effort to break their code down into the <strong>smallest reasonable parts</strong>.</p>
<p>I mention <strong>reasonable</strong>, because I think principles like DRY (Don’t Repeat Yourself) should be followed only to the extent that they solve more problems than they create.</p>
<p>There are many benefits to applying this concept, which is often called modularity, (or as I prefer, Separation of Concerns, or SOC). I am aware that some of you reading this might think I am just copying what Leland said in his article, but I have been talking about SOC as the Golden Principle Of Software Architecture for <a target="_blank" href="https://rkay301.medium.com/programming-fundamentals-part-5-separation-of-concerns-software-architecture-f04a900a7c50">many years already</a>.</p>
<p>Where this plays into Compose, is the same principle which we see in the popular Javascript library <strong>React</strong>. When done properly, Compose will only “recompose” (redraw, re-render, update, whatever) the Composables (parts/elements of the UI) which need to be recomposed.</p>
<p>This is ENORMOUSLY important when it comes to the performance of an application. This is because redrawing the UI, whether in the old View system or in Compose, is costly for system resources.</p>
<p>In case you were not aware, the entire purpose of the old RecyclerView (which was the first thing I ever made a tutorial on back in 2016!) was to employ the ViewHolder pattern to a list of data. This avoided the need to constantly inflate (make) new Views for each list item.</p>
<p>My goal in this article was to focus mostly on the theory, as I will be writing plenty of practical content over the next few months. However, I will finish the article off with a story from my direct experience, which will help you to further understand how recomposition works, and <strong>how to avoid doing it very poorly!</strong></p>
<h1 id="heading-the-stopwatch-example">The Stopwatch Example</h1>
<p>For my first full Compose application, I decided to build Sudoku. There are a number of reasons why, including the fact that I wanted a project which did not have an insanely complicated UI. I also wanted the chance to deep dive into Graph DS and Algos, which are quite suitable for Sudoku puzzles.</p>
<p>One thing I wanted was a Stopwatch which would keep track of how long it took the user to complete a puzzle:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-27.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Graph Sudoku puzzle</em></p>
<p>As is often the case in my profession, I expected this timer to be much easier to add than it really was. I messed around with Android’s Chronometer class as well as the Java Timer class, and both of them presented different but still application-breaking problems.</p>
<p>Eventually I took a step back and realized that I was writing in Kotlin. So I set up a Coroutine based timer in my presentation logic class (it ended up making the most sense to put it there), which would update my viewmodel each second:</p>
<pre><code class="lang-kotlin">Class ActiveGameLogic(…):…{
<span class="hljs-comment">//…</span>
<span class="hljs-keyword">inline</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">startCoroutineTimer</span><span class="hljs-params">(
    delayMillis: <span class="hljs-type">Long</span> = <span class="hljs-number">0</span>,
    repeatMillis: <span class="hljs-type">Long</span> = <span class="hljs-number">1000</span>,
    <span class="hljs-keyword">crossinline</span> action: () -&gt; <span class="hljs-type">Unit</span>
)</span></span> = launch {
    delay(delayMillis)
    <span class="hljs-keyword">if</span> (repeatMillis &gt; <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
            action()
            delay(repeatMillis)
        }
    } <span class="hljs-keyword">else</span> {
        action()
    }
}
<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onStart</span><span class="hljs-params">()</span></span> =
launch {
    gameRepo.getCurrentGame(
    { puzzle, isComplete -&gt;
        viewModel.initializeBoardState(
            puzzle,
            isComplete
    )
        <span class="hljs-keyword">if</span> (!isComplete) timerTracker = startCoroutineTimer {
            viewModel.updateTimerState()
        }
    },{
        container?.onNewGameClick()
    })
}
<span class="hljs-comment">//…</span>
}
</code></pre>
<p>The ViewModel (not from AAC – I write my own VMs. But Compose already has good interoperability with AAC VMs from what I can see.) exposed references to callback functions, which is what I would use to update my Composables:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActiveGameViewModel</span> </span>{
    <span class="hljs-comment">//…</span>
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> subTimerState: ((<span class="hljs-built_in">Long</span>) -&gt; <span class="hljs-built_in">Unit</span>)? = <span class="hljs-literal">null</span>
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> timerState: <span class="hljs-built_in">Long</span> = <span class="hljs-number">0L</span>
    <span class="hljs-comment">//…</span>
    <span class="hljs-keyword">internal</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">updateTimerState</span><span class="hljs-params">()</span></span>{
        timerState++
        subTimerState?.invoke(timerState)
    }
<span class="hljs-comment">//…</span>
}
</code></pre>
<p><strong>Now comes the important part!</strong> We can trigger recomposition of the Compose Hierarchy by using certain features of compose, such as the <code>remember</code> function:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">var</span> timerState <span class="hljs-keyword">by</span> remember {
    mutableStateOf(“”)
}
</code></pre>
<p>If you must know, these features store the state of whatever you are remembering in the <code>slotTable</code>. In short, the word state here means the current “state” of the data, which starts out being just an empty String.</p>
<p><strong>Here is where I screwed things up</strong>. I had pulled my simple timer composable into its own function (applied SOC), and I was passing in <code>timerState</code> as a parameter to that composable.</p>
<p>However, the above snippets were sitting in the parent composable of the timer, which was a container for the most complicated portion of the UI (a 9x9 Sudoku requires a large number of widgets):</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">GameContent</span><span class="hljs-params">(
    onEventHandler: (<span class="hljs-type">ActiveGameEvent</span>) -&gt; <span class="hljs-type">Unit</span>,
    viewModel: <span class="hljs-type">ActiveGameViewModel</span>
)</span></span> {
    Surface(
        Modifier
            .wrapContentHeight()
            .fillMaxWidth()
    ) {
        BoxWithConstraints(Modifier.background(MaterialTheme.colors.primary)) {
            <span class="hljs-comment">//…</span>
            ConstraintLayout {
                <span class="hljs-keyword">val</span> (board, timer, diff, inputs) = createRefs()
                <span class="hljs-keyword">var</span> isComplete <span class="hljs-keyword">by</span> remember {
                    mutableStateOf(<span class="hljs-literal">false</span>)
                }
                <span class="hljs-keyword">var</span> timerState <span class="hljs-keyword">by</span> remember {
                    mutableStateOf(<span class="hljs-string">""</span>)
                }
                viewModel.subTimerState = {
                    timerState = it.toTime()
                }
                viewModel.subIsCompleteState = { isComplete = it }
            <span class="hljs-comment">//…Sudoku board</span>
            <span class="hljs-comment">//Timer</span>
                Box(Modifier
                    .wrapContentSize()
                    .constrainAs(timer) {
                        top.linkTo(board.bottom)
                        start.linkTo(parent.start)
                    }
                    .padding(start = <span class="hljs-number">16</span>.dp))
                {
                    TimerText(timerState)
                }
            <span class="hljs-comment">//…difficulty display</span>
            <span class="hljs-comment">//…Input buttons</span>
            }
        }
    }
}
<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">TimerText</span><span class="hljs-params">(timerState: <span class="hljs-type">String</span>)</span></span> {
    Text(
        text = timerState,
        style = activeGameSubtitle.copy(color = MaterialTheme.colors.secondary)
    )
}
</code></pre>
<p>This was causing some considerable lag and unresponsiveness. By making heavy usage of the debugger, I was able to find out why. Because my <code>timerState</code> variable was created and updated inside the parent Composable, it was triggering a recomposition of that entire portion of the UI. <strong>Every. Single. Tick.</strong></p>
<p>After moving the appropriate code into the <code>TimerText</code> composable, things worked very smoothly:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">TimerText</span><span class="hljs-params">(viewModel: <span class="hljs-type">ActiveGameViewModel</span>)</span></span> {
    <span class="hljs-keyword">var</span> timerState <span class="hljs-keyword">by</span> remember {
        mutableStateOf(<span class="hljs-string">""</span>)
    }

    viewModel.subTimerState = {
        timerState = it.toTime()
    }

    Text(
        text = timerState,
        style = activeGameSubtitle.copy(color = MaterialTheme.colors.secondary)
    )
}
</code></pre>
<p>Hopefully I have given you a working understanding of recomposition and one of the biggest ways to do it incorrectly.</p>
<p>Avoiding unnecessary recompositions is incredibly important for performance. And so far it seems that applying SOC rigorously, even to the point of keeping remember state in separate composables, should become standard practice.</p>
<h1 id="heading-resources-amp-support">Resources &amp; Support</h1>
<p>If you liked this article, please share it on social media and check out my other articles on <a target="_blank" href="https://www.freecodecamp.org/news/author/ryan-michael-kay/">freeCodeCamp here</a>. I also have a <a target="_blank" href="https://youtube.com/wiseass">YouTube channel</a> with hundreds of tutorials, and am an active writer on various platforms.</p>
<h3 id="heading-connect-with-me-on-social-media">Connect with me on social media</h3>
<p>You can find me on <a target="_blank" href="https://www.instagram.com/rkay301/">Instagram here</a> and on <a target="_blank" href="https://twitter.com/wiseAss301">Twitter here</a>.</p>
<p>Also, I want to point out the single resource I used to get started with Jetpack Compose: <a target="_blank" href="https://github.com/android/compose-samples">Working code samples from good developers</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Keep a Navbar at the Top of My Viewport? ]]>
                </title>
                <description>
                    <![CDATA[ If you're working on the Product Landing Page project and are having trouble with some of the user stories, you're not alone. User story #13 gives a lot of people trouble. It reads: The navbar should always be at the top of the viewport. Non-fixed,... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-keep-a-navbar-at-the-top-of-my-viewport/</link>
                <guid isPermaLink="false">66c352fbc2631756f9f063dd</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ toothbrush ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 02 Jun 2020 02:12:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c9aab740569d1a4ca2701.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're working on the <a target="_blank" href="https://www.freecodecamp.org/learn/responsive-web-design/responsive-web-design-projects/build-a-product-landing-page">Product Landing Page</a> project and are having trouble with some of the user stories, you're not alone.</p>
<p>User story #13 gives a lot of people trouble. It reads:</p>
<blockquote>
<p>The navbar should always be at the top of the viewport.</p>
</blockquote>
<h3 id="heading-non-fixed-normal-navbar">Non-fixed, normal navbar</h3>
<p>Imagine you have the following HTML:</p>
<pre><code class="lang-html">...
<span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"header"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://static1.squarespace.com/static/54d3e88ce4b0be204d0da36a/t/566f5b70bfe873371e44c7b0/1525197822184/"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"logo"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"header-img"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"nav-bar"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span> 
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#about-us"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span>&gt;</span>About Us<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#videos"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span>&gt;</span>Demo<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#photos"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span>&gt;</span>Photo Gallery<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact-us"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
...
</code></pre>
<p>But when you scroll down the page, the navbar eventually leaves the view:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/Peek-2020-05-25-17-46.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-create-a-fixed-navbar">How to create a fixed navbar</h3>
<p>To create a fixed navbar, or a navbar that's always at the top of the viewport even as you scroll down the page, there are a few things you need to do.</p>
<p>First, target the header and fix it to the page with the following rule:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">header</span> {
  <span class="hljs-attribute">position</span>: fixed;
}
</code></pre>
<p>You'll notice that the navbar contracts to its default width, so set its width to the full width of the page:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">header</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
}
</code></pre>
<p>Depending on the <code>display</code> properties of the other elements, you may need to manually set the <code>top</code> and <code>left</code> positions of the navbar:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">header</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
}
</code></pre>
<p>Then all you need to do is apply some extra styling to get things looking good:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">header</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#cc0000</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Exo 2'</span>, sans-serif;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1em</span>;
}
</code></pre>
<h3 id="heading-fixed-navbar-the-result">Fixed navbar — the result</h3>
<p>After that, your navbar should still be visible even as you scroll down the page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/Peek-2020-05-25-17-45.gif" alt="Image" width="600" height="400" loading="lazy"></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to improve your UI/UX design skills as a developer ]]>
                </title>
                <description>
                    <![CDATA[ By Stephen McLean If you had asked me four years ago, when I became a CS graduate, what kind of career path I saw myself following, I probably would have told you back-end Java development or something similar. I was never creative, in the paint meet... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-improve-your-ui-ux-design-skills-as-a-developer-1fd96a49d807/</link>
                <guid isPermaLink="false">66d4614e230dff0166905881</guid>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UX ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 24 Apr 2019 22:18:19 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*pVsQ9Re0clHQGDWg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Stephen McLean</p>
<p>If you had asked me four years ago, when I became a CS graduate, what kind of career path I saw myself following, I probably would have told you back-end Java development or something similar. I was never creative, in the paint meets canvas kind of way, so I didn’t really consider a career path involving front-end development and design.</p>
<p>As we all know, things change. Over the course of my career, I have become more and more interested in UI development and design. I have been lucky enough in my job to have gained exposure to front-end development and to have been allowed to invest time in improving the skills necessary to be a front-end developer.</p>
<p>Over the past 12 months, I have been striving to improve my understanding of UI/UX design, both because of personal interest and to allow me to be more effective when working with designers.</p>
<p>In this post, I would like to share the lessons I have learned, the resources I have gathered, and the mistakes I have made in the hopes that other developers will be able to learn from my journey.</p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ul>
<li><a class="post-section-overview" href="#fb0b">It’s not just talent</a></li>
<li><a class="post-section-overview" href="#bb43">Look, think, steal</a></li>
<li><a class="post-section-overview" href="#759a">Learn the theory</a></li>
<li><a class="post-section-overview" href="#1373">Build something</a></li>
<li><a class="post-section-overview" href="#7f61">Don’t give up</a></li>
<li><a class="post-section-overview" href="#4562">Resources</a></li>
<li><a class="post-section-overview" href="#d161">Conclusion</a></li>
</ul>
<h3 id="heading-its-not-just-talent">It’s not just talent</h3>
<p>This one seems obvious to me now, but there was a time when I thought that designers were wizards that were born with the ability to create amazing looking apps, and websites.</p>
<p>It turns out that they’re not. They have just worked hard to perfect their craft. In the same way that learning to code comes easier to some people, there are people who will have a natural talent for design, but talent doesn’t mean much without hard work.</p>
<p>Design is something that can be learned. Don’t make the mistake I did and write yourself off because you’re not creative in the traditional sense. Solving programming problems takes creativity; think about solving design challenges in the same way.</p>
<h3 id="heading-look-think-steal">Look, think, steal</h3>
<p>In the same way that reading good code will help you become a better developer, learning from what others have created will help you become a better designer.</p>
<p>The next time you visit a website or use an app, <em>look</em> at it and really <em>think</em> about why it looks and behaves the way it does. Why are the elements are placed where they are? Why is that button a particular color? Why do you have to perform actions in a certain order?</p>
<p>Take Medium’s applause button for example. Why didn’t they just use a “like” button like every other social media website? In my opinion, it was a clever design decision not to. Instead, they are able to reinforce the idea of community that drives the site by carefully designing an interaction. Giving an article or response fifty claps takes time for a user to do, but it helps them to really connect with the content they are viewing and the person who created it.</p>
<p>Sites like <a target="_blank" href="https://www.awwwards.com/">Awwwards</a> and <a target="_blank" href="https://dribbble.com/">Dribbble</a> have proven invaluable to me in the last year. Many of the designs I have created have taken <em>liberal</em> inspiration from the creations of other great designers.</p>
<h3 id="heading-learn-the-theory">Learn the theory</h3>
<p>When I first started putting together designs of my own, I couldn’t figure out why they looked and behaved so poorly. The flows were awkward, the colors mismatched, and the layouts were inconsistent.</p>
<p>I thought I could just jump straight in and use my development knowledge along with my basic knowledge of how sites <em>should</em> work, as a user, and it would work out fine.</p>
<p>I was very, very wrong. I ended up creating designs from a developers point of view, instead of using design theory as a base.</p>
<p>Developers know how a site <em>works</em>, but designers know how a site <em>behaves and feels.</em> There is a big gap that you can only fill by gaining a foundation in design knowledge.</p>
<p>To gain a basic understanding of design theory, I mostly used Medium and Udemy. You can find links to the <a class="post-section-overview" href="#4562">resources I used</a> in the section below.</p>
<h3 id="heading-build-something">Build something</h3>
<p>Any developer or CS professor will tell you that the best way to become a better programmer is to write code. The same principle applies to design.</p>
<p>It doesn’t matter what you design, as long as you learn from it. Treat it as a real project: Think about the users. Create wireframes, mockups, and prototypes. Iterate on the original design to improve it. You can use sites like <a target="_blank" href="https://www.reddit.com/r/design_critiques/">Reddit</a> to get feedback on what you create.</p>
<p>You can even pair it with improving your development skills. Most of my recent side projects have been motivated by a joint desire to improve both my web development and design skills. After you design your project, you can develop it and see both sides of the coin.</p>
<p>Here are some ideas to get you started:</p>
<ul>
<li>Design an <em>interaction.</em> Use Medium’s applause button for inspiration.</li>
<li>Design an app for your favorite charity. Think about the different kinds of users: potential, infrequent, and regular patrons.</li>
<li>Design your CV. Think about the colors you use and what the mean. Think about how your CV will be <em>used.</em> Will it just be viewed on a screen, or will it be printed out? How does that influence the design?</li>
<li>Redesign an existing website. It could be your local transport provider or a global brand. Compare your design with the original and think about the differences from a user’s perspective.</li>
<li>Design something physical, such as a new method of transport or maybe an alternative to something simple like a fork or chair. I have found thinking about the <a target="_blank" href="https://www.amazon.com/Design-Everyday-Things-Donald-Norman/dp/0465067107/ref=tmm_pap_swatch_0?_encoding=UTF8&amp;qid=&amp;sr=">design of everyday things</a> to be a really useful way of getting in the mindset to design something intuitive.</li>
</ul>
<h3 id="heading-dont-give-up">Don’t give up</h3>
<p>When I look back on what I created a year ago, it’s hard not to wince. But everyone starts somewhere. Seeing the progression in what you create will be worth it. As long as you are learning from what you’re doing then your time is being well spent.</p>
<p>It might help to keep track of what you have learned at a regular interval. Every month I look back at some of the things I have worked on and try to take as much as I can from the experience. Even if it’s only to recognize some small piece of information you picked up from reading an article, it’s still progress.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/4WVxeDLz1qVdJ2-i4avjX67sSzR9H8eTelas" alt="Image" width="800" height="473" loading="lazy">
<em>Some designs I have created from oldest (left) to newest (right)</em></p>
<h3 id="heading-resources">Resources</h3>
<p>Below is a small collection of my favorite tools, articles, and courses. You can find <em>many</em> more that I haven’t listed <a target="_blank" href="https://github.com/gztchan/awesome-design">here</a>, and <a target="_blank" href="https://github.com/LisaDziuba/Awesome-Design-Tools">here</a>.</p>
<h4 id="heading-tools">Tools</h4>
<p><a target="_blank" href="https://www.figma.com/">Figma</a> — Design and prototyping with collaboration.</p>
<p><a target="_blank" href="https://www.framer.com/">FramerX</a> — Similar to Figma, but with added React, and discoverable components.</p>
<p><a target="_blank" href="https://coolors.co/">Coolors</a> — Discover and generate color palletes.</p>
<p><a target="_blank" href="https://webaim.org/resources/contrastchecker/">WebAIM Contrast Checker</a> — Make sure your colors are accessible.</p>
<p><a target="_blank" href="https://archetypeapp.com/#">Archetype</a> — Typography system editor and generator.</p>
<h4 id="heading-articles">Articles</h4>
<p><a target="_blank" href="https://medium.com/refactoring-ui/7-practical-tips-for-cheating-at-design-40c736799886">7 Practical Tips for Cheating at Design</a></p>
<p><a target="_blank" href="https://medium.com/sketch-app-sources/design-cheatsheet-274384775da9">10 cheat codes for designing User Interfaces</a></p>
<p><a target="_blank" href="https://uxplanet.org/10-small-design-mistakes-we-still-make-1cd5f60bc708">10 Small Design Mistakes We Still Make</a></p>
<p><a target="_blank" href="https://uxdesign.cc/design-better-forms-96fadca0f49c">Design Better Forms</a></p>
<p><a target="_blank" href="https://uxplanet.org/a-complete-list-of-ux-deliverables-d62ccf1de434">A Complete List Of UX Deliverables</a></p>
<p><a target="_blank" href="https://uxplanet.org/the-psychology-principles-every-ui-ux-designer-needs-to-know-24116fd65778">The Psychology Principles Every UI/UX Designer Needs to Know</a></p>
<p><a target="_blank" href="https://blog.prototypr.io/how-to-use-colors-in-ui-design-16406ec06753">How to use colors in UI Design</a></p>
<p><a target="_blank" href="https://uxplanet.org/16-quotes-you-need-to-read-as-ux-designer-e47f982e312c">16 Quotes You Need to Read as UX Designer</a></p>
<p><a target="_blank" href="https://blog.prototypr.io/product-design-principles-in-a-single-card-2f6023419a87">Product Design Principles in a Single Card</a></p>
<h4 id="heading-courses">Courses</h4>
<p><a target="_blank" href="https://www.udemy.com/course/user-experience-design-fundamentals/">User Experience Design Fundamentals</a> — Udemy</p>
<p><a target="_blank" href="https://www.udemy.com/course/the-complete-app-design-course-ux-and-ui-design/">The Complete App Design Course — UX, UI and Design Thinking</a> — Udemy</p>
<p><a target="_blank" href="https://www.uxtraining.com/">UXTraining.com</a></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>I hope you have been able to take something from my journey. If you have design tips or resources that have helped you learn, I would love to hear them in the responses.</p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to create a Buttons UI Kit ]]>
                </title>
                <description>
                    <![CDATA[ By Florin Pop The theme for week #6 of the Weekly Coding Challenge is: Buttons “A button? ?” you might ask… Yes! A button! ? “But why?” Because a button is one of the building blocks of any website/web application. Whether you are on Facebook or Twit... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-buttons-ui-kit-fdd354ee0815/</link>
                <guid isPermaLink="false">66d45ef9bc9760a197a103c5</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 19 Apr 2019 17:27:57 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*eUqmaII3aMOcV9hw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Florin Pop</p>
<p>The <strong>theme</strong> for week #6 of the <a target="_blank" href="https://www.florin-pop.com/blog/2019/03/weekly-coding-challenge/">Weekly Coding Challenge</a> is:</p>
<h3 id="heading-buttons">Buttons</h3>
<p>“A button? ?” you might ask… Yes! A button! ?</p>
<p>“But why?”</p>
<p>Because a button is one of the building blocks of any website/web application. Whether you are on Facebook or Twitter or Google, etc, you’ll always find a button that allows you to interact with the application in some way. So this week we’re going to build buttons — lots of buttons!</p>
<p>If you want to participate in the Challenge, feel free to build any kind of buttons: <strong>3D buttons</strong>, buttons with <strong>ripple effect</strong>, <strong>animating</strong> buttons, etc — the sky is the limit ?. B<strong>e creative!</strong> You know how much I value creativity! ?</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*2aQjTjjd0_RMJpFR.gif" alt="Image" width="480" height="270" loading="lazy"></p>
<p>In this article we are going to build multiple buttons and put all of them in a <a target="_blank" href="https://codepen.io/FlorinPop17/full/MRbOMJ">Buttons UI Kit</a>:</p>
<p>Before we move to the implementation part, let’s see the different states in which a button can be:</p>
<ol>
<li><strong>Default</strong> state</li>
<li><strong>Hover</strong> state — when the mouse is over the button</li>
<li><strong>Active</strong> state — when the button is pressed</li>
<li><strong>Focus</strong> state — when the button is highlighted. Allowed on elements that accept keyboard events. This is used to give users that only use the keyboard some guidance as they traverse the application.</li>
<li><strong>Disabled</strong> state</li>
</ol>
<p>We need to style the buttons to cover all these states.</p>
<p>Also, we’ll have three different button types: <code>primary</code>, <code>secondary</code> and <code>tertiary</code> and two extra sizes: <code>large</code> and <code>small</code>.</p>
<h3 id="heading-the-html">The HTML</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Primary<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>&gt;</span>Default<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">class</span>=<span class="hljs-string">"btn btn-primary btn-hover"</span>&gt;</span>Hover<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">class</span>=<span class="hljs-string">"btn btn-primary"</span> <span class="hljs-attr">disabled</span>&gt;</span>Disabled<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">class</span>=<span class="hljs-string">"btn btn-primary btn-large"</span>&gt;</span>Large<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">class</span>=<span class="hljs-string">"btn btn-primary btn-small"</span>&gt;</span>Small<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Secondary<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-secondary"</span>&gt;</span>Default<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">class</span>=<span class="hljs-string">"btn btn-secondary btn-hover"</span>&gt;</span>Hover<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">class</span>=<span class="hljs-string">"btn btn-secondary"</span> <span class="hljs-attr">disabled</span>&gt;</span>Disabled<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">class</span>=<span class="hljs-string">"btn btn-secondary btn-large"</span>&gt;</span>Large<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">class</span>=<span class="hljs-string">"btn btn-secondary btn-small"</span>&gt;</span>Small<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Tertiary<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-tertiary"</span>&gt;</span>Default<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">class</span>=<span class="hljs-string">"btn btn-tertiary btn-hover"</span>&gt;</span>Hover<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">class</span>=<span class="hljs-string">"btn btn-tertiary"</span> <span class="hljs-attr">disabled</span>&gt;</span>Disabled<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">class</span>=<span class="hljs-string">"btn btn-tertiary btn-large"</span>&gt;</span>Large<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">class</span>=<span class="hljs-string">"btn btn-tertiary btn-small"</span>&gt;</span>Small<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>We are using classes to differentiate between the different types of buttons.</p>
<h3 id="heading-the-css">The CSS</h3>
<p><code>.btn</code> is the main class used by all of our buttons:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn</span> {
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">2px</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#ffffff</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Open Sans'</span>, sans-serif;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span> <span class="hljs-number">24px</span>;
}
</code></pre>
<p>☝️ Some general styling to make it look better than the default version. ?</p>
<p>Next, we have the different states:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn-hover</span>,
<span class="hljs-selector-class">.btn</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0.9</span>;
}

<span class="hljs-selector-class">.btn</span><span class="hljs-selector-pseudo">:disabled</span> {
    <span class="hljs-attribute">background-color</span>: transparent;
    <span class="hljs-attribute">cursor</span>: not-allowed;
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0.7</span>;
}

<span class="hljs-selector-class">.btn</span><span class="hljs-selector-pseudo">:active</span> {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;
}

<span class="hljs-selector-class">.btn</span><span class="hljs-selector-pseudo">:focus</span> {
    <span class="hljs-attribute">outline</span>: <span class="hljs-number">0</span>;
}
</code></pre>
<p>In order to have some difference between the states, we can play with the <code>opacity</code> property.</p>
<p>Initially the button has <code>opacity: 1</code> and we reduce that <code>opacity</code> slightly when we hover over the button and then a little more when the button is <code>disabled</code>. When we click on the button though, we'll set the opacity back to 1 as it gives a nice effect.</p>
<p>For the <code>focus</code> state, we remove the default <code>outline</code> property and we'll add a <code>box-shadow</code> property instead, but we'll do this separately for each button type because the color is changing depending on the class (see below).</p>
<p>The individual colors are set to the <code>.btn-primary</code>, <code>.btn-secondary</code> and <code>.btn-tertiary</code> classes:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn-primary</span> {
    <span class="hljs-attribute">border-color</span>: <span class="hljs-built_in">var</span>(--primary-color);
    <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--primary-color);
}

<span class="hljs-selector-class">.btn-primary</span><span class="hljs-selector-pseudo">:disabled</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--primary-color);
}

<span class="hljs-selector-class">.btn-primary</span><span class="hljs-selector-pseudo">:focus</span> {
    <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">var</span>(--primary-color);
}

<span class="hljs-selector-class">.btn-secondary</span> {
    <span class="hljs-attribute">border-color</span>: <span class="hljs-built_in">var</span>(--secondary-color);
    <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--secondary-color);
}

<span class="hljs-selector-class">.btn-secondary</span><span class="hljs-selector-pseudo">:disabled</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary-color);
}

<span class="hljs-selector-class">.btn-secondary</span><span class="hljs-selector-pseudo">:focus</span> {
    <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">var</span>(--secondary-color);
}

<span class="hljs-selector-class">.btn-tertiary</span> {
    <span class="hljs-attribute">border-color</span>: <span class="hljs-built_in">var</span>(--tertiary-color);
    <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--tertiary-color);
}

<span class="hljs-selector-class">.btn-tertiary</span><span class="hljs-selector-pseudo">:disabled</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--tertiary-color);
}

<span class="hljs-selector-class">.btn-tertiary</span><span class="hljs-selector-pseudo">:focus</span> {
    <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">var</span>(--tertiary-color);
}
</code></pre>
<p>As you can see we use the <a target="_blank" href="https://www.w3schools.com/css/css3_variables.asp">CSS variables</a> method to set the same color on different properties. But for this to work, we need to declare the color variables on <code>:root</code> as follows:</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attribute">--primary-color</span>: <span class="hljs-number">#3457dc</span>;
    <span class="hljs-attribute">--secondary-color</span>: <span class="hljs-number">#ea4d67</span>;
    <span class="hljs-attribute">--tertiary-color</span>: <span class="hljs-number">#ea674d</span>;
}
</code></pre>
<p>Note that it’s good practice to add the <code>:root</code> declaration in the top of the css file.</p>
<p>Lastly, let’s add the two extra sizes; <code>.btn-large</code> and <code>.btn-small</code> classes:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn-large</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">12px</span> <span class="hljs-number">36px</span>;
}

<span class="hljs-selector-class">.btn-small</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">12px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">4px</span> <span class="hljs-number">12px</span>;
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>See how easy it is to create a <em>Buttons UI Kit</em>? ?</p>
<p>As a <strong>bonus features</strong> you can add a <code>ripple effect</code> to the buttons via <strong>JavaScript</strong>. I did this in a previous article - you can check it out by clicking <a target="_blank" href="https://www.florin-pop.com/blog/2017/09/button-ripple-effect">here</a>!</p>
<p>I hope you liked this challenge and I can’t wait to see what you’re going to create!</p>
<p>Happy Coding! ?</p>
<p><em>Originally published at <a target="_blank" href="https://www.florin-pop.com/blog/2019/04/buttons-ui-kit/">www.florin-pop.com</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Fundamental design principles for non-designers ]]>
                </title>
                <description>
                    <![CDATA[ By Anna 4erepawko Mészáros This one is for all the content creators out there who can’t afford to pay a professional designer, as well as for non-designers on a team where designers are always too busy to help. If you can follow these simple steps, I... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/fundamental-design-principles-for-non-designers-ad34c30caa7/</link>
                <guid isPermaLink="false">66c34b1ca124e2df05195f48</guid>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ graphic design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UX ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 05 Mar 2019 17:24:36 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*FOsR41GXae-KPlvQM1vWpg.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Anna 4erepawko Mészáros</p>
<p><em>This one is for all the content creators out there who can’t afford to pay a professional designer, as well as for non-designers on a team where designers are always too busy to help. If you can follow these simple steps, I guarantee your designs will look better.</em></p>
<p>This list was born out of years of observing my non-designer friends, family and colleagues struggle with designing things they need in their everyday life. Personal newsletters, CVs and portfolios, pictures for posts on Facebook or Instagram, video thumbnails for YouTube and so on.</p>
<p>All of these people came to me for help and advice on how to make things look better. I’m a firm believer in the whole “teach a person to fish” idea, so rather than designing things FOR them, I tried to give them valuable advice that could help with similar issues in the future.</p>
<p>As time went on, I realised <strong>I’ve been giving people all the same tips over and over again</strong>. I have worded them differently each time, but all of my advice I can distill down to <strong>these four principles</strong>.</p>
<p>I am aware that there are thousands of lists just like this one out there, but I find these lists to be too excessive. You can’t really consistently follow a list of 25 “simple” (nothing containing 25 parts can be truly “simple”) steps as a non-designer. <strong>And you don’t have to.</strong></p>
<p>Will this help you create shiny beautiful designs? No. Will this help you create great, clear and comprehensible designs that everyone can easily understand and interact with? Absolutely. So, without further ado, I present to you:</p>
<h3 id="heading-4-fundamental-design-principles-anyone-can-follow-to-achieve-great-results">4 Fundamental Design Principles Anyone Can Follow To Achieve Great Results</h3>
<h3 id="heading-contrast">Contrast</h3>
<p>Make sure there is enough contrast between all elements.</p>
<p>Why? Because things that are slightly different, but not different enough, create a kind of <a target="_blank" href="https://en.wikipedia.org/wiki/Uncanny_valley">Uncanny Valley</a>. They feel eerie and repulsive to a human eye, and/or are difficult to comprehend. Now we don’t want that in our designs, do we?</p>
<p>Elements of your design should be either <strong>exactly the same</strong>, or <strong>significantly different</strong>.</p>
<p>Issues with contrast mostly manifest themselves in 4 different ways:</p>
<ol>
<li><strong>Colour:</strong> use dark on light colours and vice versa.</li>
</ol>
<p><strong><em>Example:</em></strong> <em>Never use combinations like grey on a slightly darker grey or light pink on light blue — they are extremely difficult to read/interact with.</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*1c9lIQ_y0Cz82vs4uF05ZA.png" alt="Image" width="800" height="380" loading="lazy"></p>
<ol start="2">
<li><strong>Size:</strong> only put elements next to each other that are either <strong>exactly the same size</strong>, or a <strong>considerably different size.</strong></li>
</ol>
<p><strong><em>Example:</em></strong> <em>Don’t put 32pt and 36pt text lines next to each other. 18pt and 36pt, on the other hand, will work great together.</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Vy_CjoiJ4s3_U3jNW9JZlA.png" alt="Image" width="800" height="368" loading="lazy"></p>
<ol start="3">
<li><strong>Weight:</strong> same as with Size, only put elements next to each other that are either <strong>exactly the same weight</strong>, or a <strong>considerably different weight.</strong></li>
</ol>
<p><strong><em>Example:</em></strong> <em>Don’t put “Bold” and “Black” weights of the same typeface next to each other — because they look too similar. “Light” and “Bold” weights, on the other hand, will work great together.</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*WibV_UOHFJxjD1EUbs2sxQ.png" alt="Image" width="800" height="320" loading="lazy"></p>
<ol start="4">
<li><strong>Style:</strong> don’t put one italic type next to another italic type, or one serif typeface next to another serif typeface. Combine things that are different.</li>
</ol>
<p><strong><em>Example:</em></strong> <em>Don’t put “Times New Roman” and “Georgia” next to each other — they look too similar. Combine fonts that have considerably different styles.</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*kYKhe2rzZkyB8Oi84a6euQ.png" alt="Image" width="800" height="320" loading="lazy"></p>
<h3 id="heading-consistency">Consistency</h3>
<p>Make sure similar elements appear in similar ways.</p>
<p>Why? First of all, by keeping things consistent (and, therefore, simple), you allow people to focus their attention on <strong>important</strong> aspects of your design rather than being distracted by things changing all the time.</p>
<p>Second, consistency increases trust and <strong>makes things look actually designed</strong>, rather than just quickly thrown together.</p>
<p>Things to stick to once you have selected them:</p>
<ul>
<li>Typeface/font</li>
<li>Colour palette/shade of colour</li>
<li>Grid</li>
<li>Alignment</li>
<li>Style of decorative elements</li>
</ul>
<p>And so on...</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*W843TWXVVLson0ME-6crWQ.png" alt="Image" width="800" height="513" loading="lazy">
_Placeholder text courtesy of [Batman Ipsum](http://batman-ipsum.com/" rel="noopener" target="<em>blank" title=")</em></p>
<p>When you are working on many different pieces that appear next to each other (for example, video thumbnails for your YouTube channel, or covers for your Medium articles), choose one overall style for all of them and stick to it as well.</p>
<h3 id="heading-occams-razor">Occam’s Razor</h3>
<h4 id="heading-aka-reduce-visual-noise">aka Reduce Visual Noise</h4>
<p>The fewer the number of elements you use in your design, the better.</p>
<p>Why? It’s hard for the human brain to process information and make decisions with an overload of input. Use as few decorative elements (typefaces, colours, shadows, frames, strokes, icons, patterns and so on) as possible.</p>
<p>Apply a design version of <a target="_blank" href="https://en.wikipedia.org/wiki/Occam%27s_razor">Occam’s Razor</a> to everything:</p>
<p><em>If something can be achieved with just 2 elements, don’t use 3. If something can be achieved with 10 elements, don’t use 20. You get it.</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Bjh-mMQ5VQ99TvHPyCCmNQ.png" alt="Image" width="800" height="513" loading="lazy">
_Placeholder text courtesy of [Batman Ipsum](http://batman-ipsum.com/" rel="noopener" target="<em>blank" title=")</em></p>
<p>If you don’t like old English scholastic philosophers and prefer things you can see on Netflix, sure: be the <a target="_blank" href="https://konmari.com/">Marie Kondo</a> of your own designs. Whatever doesn’t spark joy (or isn’t a useful part of a design), has to go.</p>
<h3 id="heading-space">Space</h3>
<p>The way things are positioned sends a meta-level message about their meaning.</p>
<p>Why is this important? Because minding how you position things and how much space you add around them helps reduce the complexity of a design, and, therefore, makes it both more pleasing to look at AND easier to interact with.</p>
<p>To use space in your design to convey meaning mind these 3 things:</p>
<p><strong>1. Proximity = Relatedness</strong></p>
<p><em>Things that are <strong>closer</strong> to each other than to the other elements, are perceived as being <strong>related more</strong> to each other than to the other elements.</em></p>
<p>This one is the most important because I feel like it’s being ignored too often (even by some people claiming to be professional designers, not to name any names).</p>
<p>It can be applied in many different ways, for example:</p>
<ul>
<li>There should be more space between the lines than between each word in a line — and, similarly, more space between different paragraphs than between the lines inside a paragraph.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*HQyTLidmveDLqN7su2evmg.png" alt="Image" width="800" height="320" loading="lazy"></p>
<ul>
<li>Elements of a design should have less space between each other than between those elements and the edges of the composition.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Wb5mwX_UEIn0Wwsp7ObCNQ.png" alt="Image" width="800" height="320" loading="lazy"></p>
<ul>
<li>Labels and supporting information should be positioned near the elements that they describe/relate to.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*zNbzNX9H9GBkfNz2EO5qww.png" alt="Image" width="800" height="304" loading="lazy"></p>
<p><strong>2.</strong> <strong>Negative Space</strong></p>
<p>In combination with Occam’s Razor, give your designs as much negative space as you possibly can, to declutter them and make their meaning more obvious.</p>
<p>Putting too many elements into a limited amount of space is like trying to listen to three different songs at once. It’s hard to understand what is being said.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*mNNmcsVmOooLRgtF3CJw1g.png" alt="Image" width="800" height="450" loading="lazy">
<em>Look at Apple’s website. One can very clearly understand what is being said (or, rather, sold).</em></p>
<p><strong>3.</strong> <strong>Importance and order</strong></p>
<p>This one is extremely common sense but I feel like I still have to mention it here.</p>
<p>Things that matter most, you put first, and/or make them take up the most amount of space. Use sequences of things to convey order. Et cetera. You can definitely figure out the rest.</p>
<h3 id="heading-the-end">The End</h3>
<p><strong>Congrats!</strong> If you have followed all these steps while making whatever it is you are making, it most likely looks very good by the industry standards.</p>
<p>For everything else, there is always a designer.</p>
<hr>
<p><em>My name is Mészáros Anna, I’m a graphic/UI designer turned UX/UI designer with an academic background in philosophy and interest in all things design ethics. I’d love to connect with more like-minded individuals, so feel free to drop me a message and follow me on either <a target="_blank" href="https://medium.com/@4erepawko">Medium</a> or <a target="_blank" href="https://twitter.com/4erepawko">Twitter</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to get the best performance out of your Angular apps ]]>
                </title>
                <description>
                    <![CDATA[ By Mark Grichanik Angular is a great framework and can be used for developing large scale applications, but can be tricky to fine tune and achieve good load time and run-time performance. In this post, I’ll detail some best practices I have learned a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-get-the-best-performance-out-of-your-angular-apps-d5132a6c3335/</link>
                <guid isPermaLink="false">66c3526abc39b1419091be4d</guid>
                
                    <category>
                        <![CDATA[ Angular ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 04 Mar 2019 21:43:35 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*ryh4WCX9tu1IdgFd" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mark Grichanik</p>
<p>Angular is a great framework and can be used for developing large scale applications, but can be tricky to fine tune and achieve good load time and run-time performance. In this post, I’ll detail some best practices I have learned along the way, so you will not make the same mistakes I made.</p>
<h3 id="heading-change-detection">Change detection</h3>
<p>Change detection is Angular’s mechanism to see if there are any values that have been changed and require the view to be updated. By default, Angular runs change detection with nearly every user interaction. In order to know if the view should be rendered again, Angular accesses the new updated value, compares it with the old one and makes the decision. As your application grows, it will include a lot of expressions, and having a change detection cycle on each one of them will cause a performance problem.</p>
<p>We can optimize things if we create a ‘dumb’ component with a certain attribute to handle the change detection cycles. This component relies only on non specific input data and in that way, we can tell Angular only to run change detection when an input changes or when we manually trigger it. When a reference type is immutable, every time the object is being updated, the reference on the stack memory will have to change. Now we can have a simple reference check on the object between the memory address and the stack. If the memory address has changed, then we check all the values. This will skip change detection in that component.</p>
<p>We need to keep in mind that primitive types such as numbers, booleans, strings, etc are passed by value. Objects, arrays and functions are also passed by value, but the value is a copy of a reference.</p>
<blockquote>
<p>You can look for more details on that <a target="_blank" href="https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0">here</a>.</p>
</blockquote>
<p>And now we will see two examples of how this is implemented.</p>
<h4 id="heading-example-changedetectionstrategydefault">Example: <em>ChangeDetectionStrategy.Default</em></h4>
<p><strong>You don’t have to specify changeDetection type, it will be ‘ChangeDetectionStrategy.Default’ by default.</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*CmTLsaZ6lBFG2nLr23UEnw.gif" alt="Image" width="418" height="80" loading="lazy">
<em>Default change detection</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*M2e8BlgCZ2xEY51A" alt="Image" width="800" height="533" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@timmossholder?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="_blank" title=""&gt;Tim Mossholder on &lt;a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="<em>blank" title=")</em></p>
<h4 id="heading-changedetectionstrategyonpush">ChangeDetectionStrategy.OnPush</h4>
<p>In order to use the OnPush change detection, we need to modify the child component from the first example.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*2H5bcmDVupujYig-KKUNTQ.gif" alt="Image" width="418" height="80" loading="lazy">
<em>OnPush change detection</em></p>
<h3 id="heading-minimize-dom-manipulations">Minimize DOM manipulations</h3>
<p>If you have some list of data that was retrieved from some server and you need to show it, you are probably using the Angular directive, <a target="_blank" href="https://angular.io/api/common/NgForOf"><strong><em>ngFor</em></strong></a><strong><em>.</em></strong> Angular will create a new template for you for each item in that list.</p>
<p>If at some point some of the data has been changed, Angular can’t really know that and will replace the whole list, instead of just the items that were changed. In order to improve that, Angular provide us with the <code>trackBy</code> function.<code>trackBy</code> takes a function which has two arguments: <code>index</code> and <code>[item](https://angular.io/api/core/IterableChangeRecord#item)</code>. If <code>trackBy</code> is given, Angular tracks changes by the return value of the function.</p>
<p>Syntax:</p>
<p>Most common use is just to return the index itself or item.id as a unique identifier for the item: <code>trackByFn(index, item){ return item.id; }</code>.</p>
<p>With that, Angular can track which items have been added or removed according to the unique identifier and create or destroy only the things that were changed.</p>
<h3 id="heading-avoid-using-methods-in-your-template">Avoid using methods in your template</h3>
<p>While it is very convenient to use methods in Angular templates, Angular will recalculate them on each change detection. For larger lists it will affect rendering time and the application may even get stuck due to huge memory consumption. In the following example, Angular will run <code>getNumberOfCars</code> on each change detection cycle (ie upon adding a new row).</p>
<p>How can we handle this situation? We can pre-compute the results and then just access the data we have computed. In our example, we can add a new attribute to the person object, vehiclesNumber, which holds in advance the amount of vehicles each person has. The other way to do this is by implementing the method getNumberOfCars as a pure pipe.</p>
<p>According to <a target="_blank" href="https://angular.io/guide/pipes">Angular pipe documentation</a>:</p>
<blockquote>
<p>Angular executes a <em>pure pipe</em> only when it detects a <em>pure change</em> to the input value. A pure change is either a change to a primitive input value (<code>String</code>, <code>Number</code>, <code>Boolean</code>, <code>Symbol</code>) or a changed object reference (<code>Date</code>, <code>Array</code>, <code>Function</code>, <code>Object</code>).</p>
<p>Angular ignores changes within (composite) objects.</p>
<p>This may seem restrictive but it’s also fast. An object reference check is fast — much faster than a deep check for differences — so Angular can quickly determine if it can skip both the pipe execution and a view update.</p>
</blockquote>
<p>The pipe will still be executed on each change detection cycle. However, if a pipe is executed more than once with the same parameters, the results of the first execution are returned. Meaning, Angular will cache the results for better performance.</p>
<p>Let’s see an example.</p>
<p>Without a pipe:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*E10fN3t0GC2PvG6cvhkhRw.gif" alt="Image" width="502" height="348" loading="lazy"></p>
<p>While with a pipe we will get:</p>
<p>We can see it will recalculate only on the new data, instead of the whole list.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*1u5T_iGl9h5uM-TBF5uzYg.gif" alt="Image" width="502" height="348" loading="lazy"></p>
<h3 id="heading-use-prod-flag-in-production">Use Prod flag in production</h3>
<p>It will disable Angular’s development mode, which turns off assertions and other checks within the framework. This will also increase your performance. You can find more details <a target="_blank" href="https://angular.io/api/core/enableProdMode">here</a>.</p>
<h3 id="heading-dont-use-consolelog-in-production-code">Don’t use console.log in production code</h3>
<p>console.log prints can really slow down your application, as it takes some time to compute what you want to print. Also, for long information it will also consume some more time for the printing process.</p>
<h3 id="heading-dont-forget-to-unsubscribe-from-your-observables">Don’t forget to unsubscribe from your observables</h3>
<p>Your subscription holds a reference to your component instance. If you will not unsubscribe from it, the instance will not be cleared by the garbage collector which will cause a memory leak. You can unsubscribe easily by using <code>ngOnDestory(){this.subscription.unsubscribe();}</code>. You can read more about it <a target="_blank" href="https://angular.io/guide/observables">here</a>.</p>
<h3 id="heading-final-words">Final Words</h3>
<p>If you run into any issues, feel free to drop me a line at : <a target="_blank" href="mailto:markgrichanik@gmail.com"><em>markgrichanik[at]gmail[dot]com</em></a>.</p>
<p>I would also love to hear any feedback/tips you have while working on large scale applications with Angular.</p>
<blockquote>
<p>If you liked this article, ? away so that others can read it as well</p>
</blockquote>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
