<?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" version="2.0"><channel><title><![CDATA[Harshna Haswani]]></title><description><![CDATA[Harshna Haswani]]></description><link>https://blog.harshnahaswani.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1769441029094/6807eed4-da35-4e5c-a6ae-af8227e59006.png</url><title>Harshna Haswani</title><link>https://blog.harshnahaswani.com</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 16 Apr 2026 23:08:01 GMT</lastBuildDate><atom:link href="https://blog.harshnahaswani.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Portfolio Chatbot Architecture]]></title><description><![CDATA[I recently added a chatbot to my portfolio to help visitors navigate my work and get in touch. Visitors can ask about my experience or send a message without leaving the page.
In this post I cover rou]]></description><link>https://blog.harshnahaswani.com/portfolio-chatbot-architecture-design-decisions-and-the-whys</link><guid isPermaLink="true">https://blog.harshnahaswani.com/portfolio-chatbot-architecture-design-decisions-and-the-whys</guid><category><![CDATA[architecture]]></category><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[langgraph]]></category><category><![CDATA[langchain]]></category><category><![CDATA[chatbot]]></category><category><![CDATA[Design]]></category><category><![CDATA[RAG ]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Wed, 18 Feb 2026 08:55:29 GMT</pubDate><content:encoded><![CDATA[<p>I recently added a chatbot to my portfolio to help visitors navigate my work and get in touch. Visitors can ask about my experience or send a message without leaving the page.</p>
<p>In this post I cover routing, a two-tier model strategy, form-only contact, build-time context, rate limiting, persona boundaries, and state and security.</p>
<p>The system is built as a directed graph. Every message is classified by intent, then routed to either a Q&amp;A path or a programmatic contact flow.</p>
<iframe></iframe>

<h2>1. Deterministic Routing: Why a Router?</h2>
<p>I wanted two distinct behaviors. The bot should answer questions based on my experience or offer a clear path to contact me.</p>
<p>Mixing these into one giant prompt was possible, but messy and prone to hallucinated instructions. By using a router, the system stays simple: one step classifies the intent, and the next step runs a specialized task. This separation makes it easier to add rate limits, fallback logic, and a "chat unavailable" path without tangling the core logic.</p>
<h2>2. The Multi-Tier Model Strategy</h2>
<p>To optimize for both speed and cost, I use a two-tier model setup:</p>
<p><strong>The classifier (fast and cheap):</strong> A model optimized for low-latency, single-token-style output handles the initial routing. It only needs to output one word: question or contact.</p>
<p><strong>The responder (strong and capable):</strong> A model used for open-ended generation over a large context is only invoked for the answer node, where it has to combine my resume, projects, and blog posts into a coherent reply.</p>
<p><strong>The fallback:</strong> If the fast model is down, the system falls back to the stronger model for classification but applies a stricter rate limit to protect my quota. If both fail, the UI directs the user to the standalone contact page.</p>
<blockquote>
<p>To find the current node, click on “View logic”.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771399881983/8a7012b3-a9b6-4e1d-983a-d27f6381c56a.png" alt="" style="display:block;margin:0 auto" /></blockquote>
<h2>3. Form-Only Contact</h2>
<p>At first I tried a conversational lead-gen flow, asking for a name, then an email, then a message. It was brittle. Short names or test messages often triggered a "topic switch" misclassification.</p>
<p>So, I switched back to a standard UI. When a user expresses contact intent, the chat input is disabled and a structured form is shown directly in the chat thread.</p>
<p>This takes away the back-and-forth of message and also maintains a UI that is familiar to the user during data collection.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771392908829/17545e3d-475b-4f7e-9bf5-fb71b6a1c1a0.png" alt="" style="display:block;margin:0 auto" />

<p>Additionally, I added an "Ask something else" button dismisses the form and clears contact state. The server also appends a short prompt so that the user is informed of the switch.</p>
<p>This is helpful if the user wants to clarify something before messaging, changes their mind about contact. It also helps on the off-chance that the AI hallucinates. The user can bring the focus back.</p>
<iframe></iframe>

<h2>4. RAG-Lite: Build-Time Context and Caching</h2>
<p>I did not need a vector database for a portfolio since I don't have huge content. My knowledge model also provides an ample context window.</p>
<p>Instead I use a build-time blog cache:</p>
<ul>
<li><p>A script fetches my latest blog posts from my external CMS during the build.</p>
</li>
<li><p>The result is stored as a local JSON file.</p>
</li>
<li><p>The answer node reads this file at request time and injects it into the system prompt.</p>
</li>
</ul>
<p>If the blog fetch fails at build time, the script keeps the previous cache or writes an empty list so the build still succeeds. That way there is no runtime dependency on my CMS, and context (links and titles) stays accurate and fast. The answer prompt is also structured into sections (work history, projects, skills, blog, hire/contact) so the model can focus on the relevant part and cite sources by name and link, and visitors can click through to a project or post.</p>
<p>Using a RAG context, also removes unnecessary latency.</p>
<h2>5. Resilience: Layered Rate Limiting</h2>
<p>To protect my free-tier quotas and limit spam, I use three layers of rate limiting:</p>
<ul>
<li><p><strong>Standard chat limit:</strong> A cap on general Q&amp;A requests.</p>
</li>
<li><p><strong>Fallback limit:</strong> A tighter cap when the stronger model is used for classification (e.g. when the fast model is down).</p>
</li>
<li><p><strong>Contact limit:</strong> A strict, per-identifier limit on form submissions to reduce email spam.</p>
</li>
</ul>
<h2>6. Persona Boundaries and Adversarial Questions</h2>
<p>A portfolio bot needs a clear persona and explicit boundaries.</p>
<p><strong>Out-of-bounds:</strong> The prompt defines the bot as representing me, a full stack developer. It only answers about my experience, projects, skills, blog, and hiring or contact. For anything else it politely declines and points to the contact form.</p>
<p>That keeps the bot on topic and avoids answering things I don’t want it to (e.g. medical or legal). Since scope lives in one place, it is easy to tighten or relax later.</p>
<p><strong>Negative framing:</strong> For questions like "Why shouldn't I hire her?" the model is instructed not to repeat the negative framing. Instead it pivots to fit: it asks about tech stack or project needs or suggests a call to discuss.</p>
<iframe></iframe>

<h2>7. State Management and Security</h2>
<p><strong>Persistence:</strong> Conversations are keyed by a unique identifier. State (message history, contact flags) lives in a server-side key-value store with a 24-hour TTL. When the user reopens the chat, the client fetches the thread by that identifier so history loads without storing it all in the browser. The server stays stateless and the client stays light.</p>
<p><strong>Safety:</strong> Since the model outputs markdown, the client converts it to HTML and passes it through a strict sanitization layer. The sanitization layer only allows safe tags like <code>&lt;a&gt;</code>, <code>&lt;code&gt;</code>, and <code>&lt;strong&gt;</code>. Citations and links from the model become clickable without trusting raw HTML. This helps prevent XSS attacks.</p>
<blockquote>
<p>Thanks for reading! Let me know what you liked. Is there anything that I could do differently? Have you implemented something similar?</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Jan Recap: 3-Part A11y Series, Agentic AI, and portfolio v1]]></title><description><![CDATA[Welcome to the first edition of my newsletter! January was a mix of shipping content and starting new learning paths. I wrapped up a writing project on Accessibility, spent time understanding the landscape of Agentic AI, and continued polishing my te...]]></description><link>https://blog.harshnahaswani.com/jan-recap-3-part-a11y-series-agentic-ai-and-portfolio-v1</link><guid isPermaLink="true">https://blog.harshnahaswani.com/jan-recap-3-part-a11y-series-agentic-ai-and-portfolio-v1</guid><category><![CDATA[newsletter]]></category><category><![CDATA[Monthly review]]></category><category><![CDATA[2026]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Fri, 30 Jan 2026 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>Welcome to the first edition of my newsletter! January was a mix of shipping content and starting new learning paths. I wrapped up a writing project on Accessibility, spent time understanding the landscape of Agentic AI, and continued polishing my technical notes from last year.</p>
<p>Here is the detailed breakdown of my January.</p>
<hr />
<p><strong>✅ Published:</strong></p>
<p><strong>The Web Accessibility Series</strong> I spent a good chunk of January writing. I’m happy to share that one of my major works—a <a target="_blank" href="https://harshna-haswani.hashnode.dev/series/a11y-unlocking-the-16-percent"><strong>3-part series on Web Accessibility</strong></a> —is now live. What started as a simple refresher on browser dev tools became a deeper dig into the engineering side of inclusion.</p>
<ul>
<li><p><strong>Part 1:</strong> <a target="_blank" href="https://blog.harshnahaswani.com/the-invisible-loss-why-your-site-is-losing-16-of-its-traffic-at-the-door">The Invisible Loss: Why Your Site is Losing 16% of Its Traffic at the Door</a></p>
</li>
<li><p><strong>Part 2:</strong> <a target="_blank" href="https://blog.harshnahaswani.com/the-science-of-sight-colors-fonts-and-the-ibm-standard">The Science of Sight: Colors, Fonts, and the IBM Standard</a></p>
</li>
<li><p><strong>Part 3:</strong> <a target="_blank" href="https://harshna-haswani.hashnode.dev/the-developers-audit-for-a11y">The Developer’s Audit for A11Y</a></p>
</li>
</ul>
<hr />
<p><strong>🤖 Learning Log: Agentic AI (Overview)</strong></p>
<p>I’ve started a new learning track this month focused on <a target="_blank" href="https://www.ibm.com/think/topics/ai-agents#7281535"><strong>Agentic AI</strong> via IBM Learn</a>.</p>
<ul>
<li><p><strong>Current Status:</strong> January was all about the <em>overview</em>—understanding the high-level concepts of how agents differ from standard LLMs.</p>
</li>
<li><p><strong>The Plan:</strong> I’m still reading through the materials. Over February, I plan to finish the introduction part.</p>
</li>
</ul>
<hr />
<p><strong>🧹 Housekeeping: Polishing the Archive</strong> Back in October 2025, I launched v0.5 of <a target="_blank" href="http://www.harshnahaswani.com"><strong>harshnahaswani.com</strong></a>. This month, I focused on cleaning up the content. I had some raw technical notes. I converted 9 of them into structured blog posts (excluding the 3 part A11Y series).</p>
<hr />
<p><strong>👀 Looking Forward to February</strong> I have some specific releases and posts lined up for next month:</p>
<ol>
<li><p><strong>ASCII Graphs Update:</strong> I’m working on the next release for <a target="_blank" href="https://ascii-graphs.vercel.app/">ascii-graphs.vercel.app</a>. Expect some new features soon.</p>
</li>
<li><p><strong>Portfolio Update:</strong> Some accessibility fixes and newsletter signup integration</p>
</li>
<li><p><strong>Design Deep Dive:</strong> I’m writing a "meta" blog post about the <strong>style selection</strong> for my portfolio—breaking down why I chose this specific aesthetic and the design decisions behind it.</p>
</li>
<li><p><strong>More Learning:</strong> Continuing the Agentic AI deep dive.</p>
</li>
<li><p><strong>Housekeeping:</strong> At least 2 more blogs from my technical notes.</p>
</li>
</ol>
<p>I love feedback. I would really appreciate it if you drop a comment below or send me a mail!</p>
<p>Thanks for reading!</p>
<p>– Harshna</p>
]]></content:encoded></item><item><title><![CDATA[The Developer’s Audit for A11Y]]></title><description><![CDATA[In Part 2, we looked at the science of sight and accessible palettes; today, we look at the execution. An accessible design is only as good as the code supporting it. If the underlying structure is brittle, the door remains locked for 16% of your use...]]></description><link>https://blog.harshnahaswani.com/the-developers-audit-for-a11y</link><guid isPermaLink="true">https://blog.harshnahaswani.com/the-developers-audit-for-a11y</guid><category><![CDATA[Web Development]]></category><category><![CDATA[Web Accessibility]]></category><category><![CDATA[a11y]]></category><category><![CDATA[UX]]></category><category><![CDATA[Firefox]]></category><category><![CDATA[devtools]]></category><category><![CDATA[safari]]></category><category><![CDATA[debugging]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[HTML5]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Tue, 27 Jan 2026 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>In <a target="_blank" href="https://harshna-haswani.hashnode.dev/the-science-of-sight-colors-fonts-and-the-ibm-standard">Part 2</a>, we looked at the science of sight and accessible palettes; today, we look at the <strong>execution</strong>. An accessible design is only as good as the code supporting it. If the underlying structure is brittle, the door remains locked for 16% of your users.</p>
<p>Here is how to audit your site for the traffic waiting to get in.</p>
<h2 id="heading-1-the-no-mouse-challenge-keyboard-navigation">1. The "No-Mouse" Challenge: Keyboard Navigation</h2>
<p>For users with mobility issues, tremors, or chronic pain, the mouse is a barrier. They rely on the <code>Tab</code> key or assistive switches.</p>
<ul>
<li><p><strong>Visible Focus States:</strong> Never use <code>outline: none;</code> in your CSS without providing a high-contrast replacement. If a user tabs to a button, they need a "Focus Ring" to see where they are.</p>
</li>
<li><p><strong>Tab Traps:</strong> A common failure point in development is the modal or mobile menu. Ensure users don't get "stuck" inside a pop-up with no way to exit via keyboard.</p>
</li>
<li><p><strong>Logical Flow:</strong> Your <code>tabindex</code> should follow the visual path of the page. If the cursor jumps from the header to the footer and back to the sidebar, the site is functionally broken for keyboard users.</p>
</li>
</ul>
<h2 id="heading-2-the-code-behind-the-vision">2. The Code Behind the Vision</h2>
<p>Effective accessibility requires moving beyond the visual layer into the document structure.</p>
<ul>
<li><p><strong>Aria-Labels for "Silent" Icons:</strong> If you have a button that is simply a "Magnifying Glass" icon, a screen reader will likely announce it as "Button, unlabelled." By adding <code>aria-label="Search"</code>, you give that element a clear purpose. If the icon has no purpose, hide it with <code>aria-hidden=”true”</code>.</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769710676791/1b0fba66-5932-48c6-ad01-37a424ceaf11.png" alt="Image shows aesthetic icon hidden with aria-hidden attribute for assistive technology" class="image--center mx-auto" /></li>
</ul>
</li>
<li><p><strong>Scalable Units (The</strong> <code>rem</code> Fix): While we discussed why text needs to scale in Part 2, the developer’s job is the math. Use <code>rem</code> for font sizes so that when a user changes their browser's root font size, your UI responds fluidly. Reference the <a target="_blank" href="https://www.mozillafoundation.org/en/docs/design/websites/accessibility-guidelines/">Mozilla Accessibility Guidelines</a> for best practices on building these responsive structures.</p>
</li>
<li><p><strong>Semantic HTML:</strong> Use <code>&lt;button&gt;</code> for actions and <code>&lt;a&gt;</code> for navigation. Using a <code>&lt;div&gt;</code> with an <code>onClick</code> event might look the same, but it is invisible to assistive technologies unless you manually add the necessary ARIA roles.</p>
</li>
</ul>
<h2 id="heading-3-your-professional-audit-toolkit">3. Your Professional Audit Toolkit</h2>
<p>Stop guessing and start validating. Leverage these industry-standard tools to automate your workflow and catch <a target="_blank" href="https://www.w3.org/TR/WCAG21/#dfn-essential">essential accessibility</a> errors instantly:</p>
<ul>
<li><p><a target="_blank" href="https://chromewebstore.google.com/detail/wave-evaluation-tool/jbbplnpkjmmeebjpijfedlgcdilocofh?authuser=2"><strong>WAVE Evaluation Tool</strong></a><strong>:</strong> The gold standard for browser-based audits. It provides a visual overlay of your live site, flagging missing alt-text, contrast failures, and structural issues.</p>
</li>
<li><p><a target="_blank" href="https://www.browserstack.com/guide/wcag-chrome-extension"><strong>BrowserStack WCAG Guide</strong></a><strong>:</strong> An excellent resource for running guided manual tests to ensure your site meets specific compliance levels.</p>
</li>
</ul>
<p>Check issue with WAVE</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769710032085/c5305b78-05ba-43df-820d-9bba6aa42be9.png" alt="WAVE evaluation tool points out that adjacent links go to the same url on harshnahaswani.com. It also shows that the site has no other issues and has 10 out of 10 AIM score" class="image--center mx-auto" /></p>
<p>Tabbed navigation inspection with WAVE</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769709829447/1f627af2-fdc3-4da4-8e98-61697946e8d3.png" alt="WAVE evaluation tool shows the keyboard navigation path for harshnahaswani.com" class="image--center mx-auto" /></p>
<h2 id="heading-4-the-pro-powerhouse-firefox-accessibility-inspector">4. The Pro Powerhouse: Firefox Accessibility Inspector</h2>
<p>Firefox has arguably the most powerful built-in tool for accessibility testing. By right-clicking any element and selecting <strong>"Inspect Accessibility Properties,"</strong> you can verify your work in real-time.</p>
<ul>
<li><p><strong>The Tree View:</strong> See exactly what a screen reader "sees" before you ever publish your code.</p>
</li>
<li><p><strong>Color Blindness Simulation:</strong> Filter your live site through various lenses to ensure the colors hold up in production environments.</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769709116425/2334a92b-ff61-4dfe-9d8b-200f750069a3.png" alt="Image shows firefox's accessibility tools with simulation options for Protanopia (red-blind),  Deuteranopia (green-blind), Tritanopia(blue-blind),  Achromatopsia (no color), and  no contrast." class="image--center mx-auto" /></li>
</ul>
</li>
<li><p><strong>Contrast Warnings:</strong> Firefox will automatically flag elements that fail the <strong>AA (4.5:1)</strong> or <strong>AAA (7:1)</strong> thresholds, highlighting the specific line of CSS that needs adjustment.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769709379472/91e60443-e8de-496a-b0e7-15ae08e49733.png" alt="Image shows Firefox browser's accessibility tools flagging warning for low contrast, and missing tab navigation for various text nodes." class="image--center mx-auto" /></p>
</li>
</ul>
<h2 id="heading-conclusion-opening-the-door">Conclusion: Opening the Door</h2>
<p>Accessibility isn't a "one-and-done" checklist; it is a fundamental shift in how we build for the web. By mastering these tools—from the WAVE extension to the Firefox Inspector—you aren't just "fixing bugs." You are ensuring that the 16% of users who have been standing at the door can finally walk in.</p>
<p>The web was built to be universal. As developers, it is our job to make sure it stays that way.</p>
<p><em>This article was copyedited with Gemini.</em></p>
]]></content:encoded></item><item><title><![CDATA[The Science of Sight: Colors, Fonts, and the IBM Standard]]></title><description><![CDATA[In our first article, we discussed the "Invisible 16%"—the billion-plus users who are often locked out of the web due to poor design. Today, we go inside the design studio to look at the most common barrier to entry: Visual Accessibility.
Designing f...]]></description><link>https://blog.harshnahaswani.com/the-science-of-sight-colors-fonts-and-the-ibm-standard</link><guid isPermaLink="true">https://blog.harshnahaswani.com/the-science-of-sight-colors-fonts-and-the-ibm-standard</guid><category><![CDATA[Web Accessibility]]></category><category><![CDATA[inclusive design]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[#WCAG]]></category><category><![CDATA[IBM]]></category><category><![CDATA[color blindness]]></category><category><![CDATA[typography]]></category><category><![CDATA[a11y]]></category><category><![CDATA[UI]]></category><category><![CDATA[UX]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Tue, 20 Jan 2026 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p><a target="_blank" href="https://harshna-haswani.hashnode.dev/the-invisible-loss-why-your-site-is-losing-16-of-its-traffic-at-the-door">In our first article</a>, we discussed the "Invisible 16%"—the billion-plus users who are often locked out of the web due to poor design. Today, we go inside the design studio to look at the most common barrier to entry: <strong>Visual Accessibility.</strong></p>
<p>Designing for the eyes isn’t just about aesthetics; it’s about math, clarity, and understanding the medical reality of how different people perceive light and color.</p>
<h2 id="heading-1-the-math-of-contrast-aa-vs-aaa">1. The Math of Contrast: AA vs. AAA</h2>
<p>Most designers know that light gray text on a white background is a "trend," but for millions of users, that trend makes a website invisible. To solve this, the Web Content Accessibility Guidelines (WCAG) use <strong>contrast ratios</strong>.</p>
<ul>
<li><p><strong>Level AA (The Standard):</strong> Requires a contrast ratio of at least <strong>4.5:1</strong> for normal text. This is the baseline to ensure users with <a target="_blank" href="https://www.vision-and-eye-health.com/contrast-sensitivity/">moderate low vision</a> can read your content.</p>
</li>
<li><p><strong>Level AAA (The Gold Standard):</strong> Requires a ratio of <strong>7:1</strong>. This makes your site incredibly crisp for everyone and is essential for higher-level inclusivity.</p>
</li>
</ul>
<h2 id="heading-2-the-ibm-standard-palette-engineering">2. The IBM Standard: Palette Engineering</h2>
<p>You don't have to guess if your colors work. Tech giants like <strong>IBM</strong> have turned accessibility into a science through their <strong>Carbon Design System</strong>. They utilize a "Color Grade" system (ranging from 10 to 100) where the numerical "step" between two colors mathematically guarantees their accessibility.</p>
<ul>
<li><p><strong>For Dark Backgrounds:</strong> Any color graded <strong>60 or higher</strong> is safe to use with white text.</p>
</li>
<li><p><strong>For Light Backgrounds:</strong> Any color graded <strong>50 or lower</strong> is safe to use with black text.</p>
</li>
</ul>
<p>By adopting a palette that accounts for this <a target="_blank" href="https://www.w3.org/TR/WCAG21/#non-text-contrast">non-text contrast</a>, you ensure that "actionable" elements like buttons, icons, and input borders are just as visible and functional as your primary headlines.</p>
<h2 id="heading-3-beyond-color-why-a-single-highlight-fails">3. Beyond Color: Why a "Single Highlight" Fails</h2>
<p>Color blindness affects roughly 1 in 12 men and 1 in 200 women. If your website uses <em>only</em> color to convey meaning then, many users won't see the difference. Example, a red border for an error or a green highlight for a "success" state</p>
<p>According to <a target="_blank" href="https://www.w3.org/TR/WCAG21/#use-of-color">official WCAG Use of Color standards</a>, color should never be the only messenger.</p>
<p><strong>The Rule:</strong> Always pair color with a secondary cue, such as an icon, a text label, or an underline.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769714065175/32428311-c6cd-432d-b9ca-e0056d485047.png" alt class="image--center mx-auto" /></p>
<p>In the above image, the blog section (right) has a indication of links with the right arrow while the project section(left) becomes ambiguous for a person who can’t distinguish color (in simpler terms, no color).</p>
<p>I also want to point out that this is from <a target="_blank" href="http://www.harshnahaswani.com">my own site</a>, that had an update a few days ago. As people, it is very easy for us to miss things.</p>
<p>A few minutes of our time to check for accessibility can easily help us recognize and fix them.</p>
<h3 id="heading-the-designers-testing-toolkit">🛠️ The Designer’s Testing Toolkit</h3>
<p>Before you finalize a design, test your assumptions with these simulators:</p>
<ul>
<li><p><a target="_blank" href="https://davidmathlogic.com/colorblind/"><strong>David Math Logic Palette Builder</strong></a><strong>:</strong> Create a palette that is mathematically safe for color-blind users from the start.</p>
</li>
<li><p><a target="_blank" href="https://www.color-blindness.com/coblis-color-blindness-simulator/"><strong>Coblis Simulator:</strong></a> Upload your mockups to see exactly how they appear to someone with different types of color vision deficiency.</p>
</li>
<li><p><a target="_blank" href="https://jfly.uni-koeln.de/color/"><strong>Jfly Color Palette</strong></a><strong>:</strong> A master reference for a universal color set that is unambiguous to all.</p>
</li>
</ul>
<h2 id="heading-4-typography-more-than-just-style">4. Typography: More Than Just Style</h2>
<p>A font can be "beautiful" but functionally broken. For users with dyslexia or low vision, certain typefaces are a maze.</p>
<h3 id="heading-avoid-decorative-and-cursive-fonts">Avoid Decorative and Cursive Fonts</h3>
<p>While cursive looks "premium," it is a nightmare for accessibility for two reasons:</p>
<ol>
<li><p><strong>Character Ambiguity:</strong> Cursive fonts often have "ligatures" (where letters join together). Example, for users with Dyslexia, it becomes impossible to distinguish where an 'm' ends and an 'n' begins in the word Alumni.</p>
</li>
<li><p><strong>OCR Failures:</strong> Screen readers often struggle to decode stylized characters, sometimes misreading them as symbols or skipping them entirely.</p>
</li>
</ol>
<p>See the Distropiax font for example on the word "Alumni"":</p>
<ul>
<li><p>“l“ “u“ pair and “n“ “i“ pair is connected at the bottom seeming like a single letter</p>
</li>
<li><p>“m“ and “n“ are very similar</p>
</li>
<li><p>”a” seems more like a symbol/logo than a letter</p>
</li>
</ul>
<p>These things also increase cognitive load.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769711495875/3a734d54-3e98-46b8-b49d-7dd9bae5b9d4.png" alt="Image shows issues with ligatures in Distropiax font" class="image--center mx-auto" /></p>
<p><a target="_blank" href="https://dyslexiahelp.umich.edu/wp-content/uploads/2014/02/good_fonts_for_dyslexia_study.pdf">Research by University of Michigan, USA</a> found that font types have an impact on readability of people with dyslexia. Good fonts for people with dyslexia are <strong>Helvetica, Courier, Arial, Verdana, and CMU</strong>.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Font styles that <strong>increased readability</strong></td><td>sans serif, monospaced, and roman</td></tr>
</thead>
<tbody>
<tr>
<td>Font styles that <strong>decreased readability</strong></td><td>italic</td></tr>
</tbody>
</table>
</div><h3 id="heading-the-no-images-of-text-rule">The "No Images of Text" Rule</h3>
<p>Never "bake" your text into a <code>.jpg</code> or <code>.png</code> file. It creates a technical dead end:</p>
<ul>
<li><p><strong>Zero Scalability:</strong> When a user zooms in to 200%, a font rendered in CSS remains crisp. An image of text becomes a blurry, pixelated mess.</p>
</li>
<li><p><strong>The Alt-Text Trap:</strong> Alt-Text isn't a substitute for real text. Users cannot select, copy, or highlight text inside an image. If they need to copy a coupon code from an image, they are stuck. Reference: <a target="_blank" href="https://www.w3.org/TR/WCAG21/#dfn-images-of-text">W3C definition on images of text</a>.</p>
</li>
</ul>
<p>See the below images, at <strong>125% zoom</strong>, it reaches maximum width of the screen and the text can no longer increase in size. The letters in the image have lost their sharpness while the plain takes still remains sharp.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769713266720/75cf2d7a-3816-45a4-8463-bde12747cfd4.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-use-scalable-units-rem-over-px">Use Scalable Units (<code>rem</code> over <code>px</code>)</h3>
<p>Stop using pixels (<code>px</code>) for font sizes. Using <code>rem</code> allows your text to scale based on the user's browser settings. If a user needs 24pt font to see, your site should "listen" to that preference rather than locking them into your design choice.</p>
<p>In the image below, all components have smoothly scaled even at <strong>400% zoom</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769713541005/c4c4072a-2741-46d0-aa60-72310a96632e.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-fonts-to-avoid">Fonts to avoid</h3>
<ul>
<li><p><strong>Low Contrast/Thin Strokes:</strong> Makes letters difficult to distinguish:</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769692562115/99a754b5-fd25-4af2-ac82-19f1d6ca6f46.png" alt="Image shows difficulty in reading due to thin, tightly packed typeface of Desolation regular font" class="image--center mx-auto" /></li>
</ul>
</li>
<li><p><strong>Crowded Lettering:</strong> Letters are too close together, causing them to blend. Example: Serif fonts like Times New Roman</p>
</li>
<li><p><strong>Mirroring/Similarity:</strong> Letters that are too similar increase confusion.  Example:</p>
<ul>
<li><p>'b', 'd',</p>
</li>
<li><p>'p', 'q',</p>
</li>
<li><p>'l' (lowercase L), 'I' (Capital I), '1' (number one)</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-5-the-browser-stress-test">5. The Browser "Stress Test"</h2>
<p>You can perform a DIY audit using the built-in tools in <strong>Firefox</strong> or <strong>Safari</strong>. Open your site and try to:</p>
<ul>
<li><p>Increase the font size to 200%.</p>
</li>
<li><p>Force a change to a basic system font.</p>
</li>
<li><p>Check if your <a target="_blank" href="https://www.w3.org/TR/WCAG21/#dfn-essential">essential information</a> remains readable.</p>
</li>
</ul>
<p>If your layout overlaps or disappears, your design isn't truly accessible yet.</p>
<hr />
<p><em>Coming Next:</em> <strong><em>Part 3—The Developer’s Audit.</em></strong> <em>We’ll get technical with the WAVE extension, BrowserStack, and the Firefox Accessibility Inspector.</em></p>
<p><em>This article was copyedited with Gemini.</em></p>
]]></content:encoded></item><item><title><![CDATA[The Invisible Loss: Why Your Site is Losing 16% of Its Traffic at the Door]]></title><description><![CDATA[Imagine you own a physical retail store. Every morning, you stand at the entrance and watch as 100 people try to walk in. For 16 of them, the door is locked. They pull the handle, realize they can't get in, and walk straight to your competitor across...]]></description><link>https://blog.harshnahaswani.com/the-invisible-loss-why-your-site-is-losing-16-of-its-traffic-at-the-door</link><guid isPermaLink="true">https://blog.harshnahaswani.com/the-invisible-loss-why-your-site-is-losing-16-of-its-traffic-at-the-door</guid><category><![CDATA[web acce]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Web Design]]></category><category><![CDATA[a11y]]></category><category><![CDATA[UX]]></category><category><![CDATA[inclusive design]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[#WCAG]]></category><category><![CDATA[digital strategy]]></category><category><![CDATA[devtools]]></category><category><![CDATA[debugging]]></category><category><![CDATA[HTML5]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Tue, 13 Jan 2026 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>Imagine you own a physical retail store. Every morning, you stand at the entrance and watch as 100 people try to walk in. For 16 of them, the door is locked. They pull the handle, realize they can't get in, and walk straight to your competitor across the street.</p>
<p>In the physical world, you’d call a locksmith immediately. But in the digital world, thousands of businesses leave that door locked every day without realizing it.</p>
<h2 id="heading-the-16-metric-its-not-a-niche-its-the-market">The 16% Metric: It’s Not a Niche; It’s the Market</h2>
<p>According to the World Health Organization (WHO), an estimated <strong>1.3 billion people</strong> (1 in 6 people worldwide) experience a significant disability. That is <strong>16% of the global population</strong>.</p>
<p>When we talk about "web accessibility," we aren't just talking about a minor technical adjustment. We are talking about serving:</p>
<ul>
<li><p><strong>Visual Impairments:</strong> From total blindness to age-related low vision.</p>
</li>
<li><p><strong>Motor Disabilities:</strong> Users who cannot use a mouse and rely entirely on keyboards or switches.</p>
</li>
<li><p><strong>Cognitive Differences:</strong> Users with dyslexia, ADHD, or processing disorders.</p>
</li>
</ul>
<p>If your website isn't accessible, you are effectively turning away 16% of your potential revenue. In the UK alone, this "Purple Pound" (the spending power of disabled households) is worth an estimated <strong>£274 billion</strong>. Can your business afford to leave that on the table?</p>
<h2 id="heading-the-curb-cut-effect-better-for-one-better-for-all">The "Curb Cut" Effect: Better for One, Better for All</h2>
<p>Accessibility is often misunderstood as something we do only for a specific group. In reality, accessible design makes the web better for <em>everyone</em>. This is known as the <strong>Curb Cut Effect</strong>.</p>
<p>Curb cuts (the slopes in sidewalks) were originally designed for wheelchair users. Today, they are used by parents with strollers, travelers with suitcases, and delivery workers with dollies. On your website, this looks like:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>For Permanent Disability</strong></td><td><strong>For Everyone Else (Situational)</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>High Contrast</strong></td><td>Essential for users with <strong>low vision</strong> or color blindness.</td><td>Helps you read on a phone in <strong>direct sunlight</strong>.</td></tr>
<tr>
<td><strong>Captions</strong></td><td>Essential for the <strong>deaf or hard-of-hearing</strong> community.</td><td>Allows watching videos in a <strong>loud train</strong> or a quiet office.</td></tr>
<tr>
<td><strong>Keyboard Navigation</strong></td><td>A necessity for users with <strong>motor impairments</strong>.</td><td>A "power user" favorite for <strong>speed and efficiency</strong>.</td></tr>
<tr>
<td><strong>Large Touch Targets</strong></td><td>Crucial for those with <strong>tremors</strong> or limited fine motor control.</td><td>Helpful for anyone using a mobile device <strong>one-handed</strong> while walking.</td></tr>
<tr>
<td><strong>Clear, Simple Prose</strong></td><td>Essential for <strong>cognitive disabilities</strong> or dyslexia.</td><td>Benefits <strong>non-native speakers</strong> or anyone in a rush.</td></tr>
</tbody>
</table>
</div><h2 id="heading-understanding-the-ladder-aa-vs-aaa">Understanding the Ladder: AA vs. AAA</h2>
<p>To keep the web consistent, we use the <strong>WCAG (Web Content Accessibility Guidelines)</strong>. Think of these as the building codes for the internet. They are broken into three levels of compliance:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Level</strong></td><td><strong>Standing</strong></td><td><strong>The Reality</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>A</strong></td><td><strong>The Bare Minimum</strong></td><td>The site is technically "usable," but remains difficult for many.</td></tr>
<tr>
<td><strong>AA</strong></td><td><strong>The Global Standard</strong></td><td>The legal requirement for most businesses. It removes the most common barriers.</td></tr>
<tr>
<td><strong>AAA</strong></td><td><strong>The Gold Standard</strong></td><td>The highest level of inclusivity, making the site effortless for the widest possible audience.</td></tr>
</tbody>
</table>
</div><h2 id="heading-the-silent-door-is-real">The "Silent Door" is Real</h2>
<p>In this three-part series, we are going to look at the tools and techniques required to unlock your digital door. We’ll explore how companies like IBM use math to pick accessible colors and how you can use simple browser tools to see your site through your users' eyes.</p>
<p>For today, your only task is to acknowledge the data. Your "missing 16%" is waiting to get in. Are you ready to unlock the door?</p>
<hr />
<p><em>Coming Next:</em> <strong><em>Part 2—The Science of Sight.</em></strong> <em>We dive into color contrast math, the IBM Carbon palette, and why your font choice might be pushing readers away.</em></p>
<p><em>This article was copyedited with Gemini.</em></p>
]]></content:encoded></item><item><title><![CDATA[How Wikipedia Searches 65 Million Articles in Milliseconds]]></title><description><![CDATA[Wikipedia hosts over 65 million articles.
When you type "Virat Kohli" into the search bar, the system has to scan that massive dataset, handle typos, rank results by relevance, and deliver the answer in milliseconds.
If you tried this with a standard...]]></description><link>https://blog.harshnahaswani.com/how-wikipedia-searches-65-million-articles-in-milliseconds</link><guid isPermaLink="true">https://blog.harshnahaswani.com/how-wikipedia-searches-65-million-articles-in-milliseconds</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[Wikipedia]]></category><category><![CDATA[Search Engines]]></category><category><![CDATA[System Design]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[design thinking]]></category><category><![CDATA[software development]]></category><category><![CDATA[search]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Mon, 15 Dec 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn-images-1.medium.com/max/1440/1*Dt8FnaIi0mHefv6EcvHB0Q.jpeg" alt /></p>
<p>Wikipedia hosts over 65 million articles.</p>
<p>When you type "Virat Kohli" into the search bar, the system has to scan that massive dataset, handle typos, rank results by relevance, and deliver the answer in milliseconds.</p>
<p>If you tried this with a standard SQL database query (LIKE %Virat%), it would trigger a <strong>Full Table Scan</strong>—reading every single row. At Wikipedia’s scale, that would take seconds or even <strong>timeout</strong>.</p>
<p>Hence, they use Elasticsearch.</p>
<p>Here is the engineering logic behind that speed, broken down into 4 steps.</p>
<h3 id="heading-1-the-data-documents-not-tables">1. The Data: Documents, Not Tables</h3>
<p>Relational databases split data across multiple tables to reduce redundancy. Elasticsearch optimizes strictly for <strong>retrieval speed.</strong></p>
<p>It stores data as <strong>denormalized JSON</strong> Documents. Here is what the document for "Virat Kohli" looks like in our index:</p>
<pre><code class="lang-json"> {
  <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Virat Kohli"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Indian cricketer (born 1988)"</span>,
  <span class="hljs-attr">"content_urls"</span>: {
    <span class="hljs-attr">"desktop"</span>: { <span class="hljs-attr">"page"</span>: <span class="hljs-string">"https://en.wikipedia.org/wiki/Virat_Kohli"</span> }
  },
  <span class="hljs-attr">"extract"</span>: <span class="hljs-string">"Virat Kohli is an Indian international cricketer..."</span>
}
</code></pre>
<p>By keeping the data together in one object, we avoid expensive JOIN operations during the search.</p>
<h3 id="heading-2-analysis-breaking-down-the-input">2. Analysis: Breaking Down the Input</h3>
<p>When we save this document, Elasticsearch runs it through an Analysis Pipeline. The goal is to <em>convert raw text into searchable tokens</em>.</p>
<p>For example, if the input is "Virat Parvam", the engine performs two key steps:</p>
<ol>
<li><p>Lowercase: "Virat Parvam" to "virat parvam"</p>
</li>
<li><p>Tokenize: Split by whitespace: ["virat", "parvam"]</p>
</li>
</ol>
<p>The engine indexes these specific tokens, not the raw sentence. This is why a search for "virat" (lowercase) will still match "Virat" (uppercase).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769430780550/feccd9e9-a11c-48ca-a342-f172e7ebbebc.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-3-the-index-the-back-of-the-book">3. The Index: The "Back of the Book"</h3>
<p>This is the secret sauce. Instead of scanning every document (like SQL), Elasticsearch uses an <strong>Inverted Index</strong>.</p>
<p>Think of it like the index at the back of a textbook. It lists every unique word and the exact Document IDs where that word appears.</p>
<p>Here is a simplified view of the index for our dataset:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Token</strong></td><td><strong>Document IDs</strong></td></tr>
</thead>
<tbody>
<tr>
<td>kohli</td><td>[doc289, doc578]</td></tr>
<tr>
<td>viral</td><td>[doc457]</td></tr>
<tr>
<td>virat</td><td>[doc1859, doc8834]</td></tr>
</tbody>
</table>
</div><p>When you search for "Virat":</p>
<p>The engine jumps straight to "V" in the index. It finds "Virat".<br />It instantly retrieves the list: <code>[doc1859, doc8834]</code>.</p>
<p>This changes the time complexity from O(N) (linear scan) to O(1) (<strong>instant lookup</strong>).</p>
<h3 id="heading-4-ranking-its-not-just-found-its-best">4. Ranking: It’s Not Just "Found", It’s "Best"</h3>
<p>We found multiple documents containing "Virat". But which one is first?</p>
<ul>
<li><p>The article titled "Virat Kohli" and a list of centuries by Virat Kohli</p>
</li>
<li><p>An author who wrote a book about him</p>
</li>
<li><p>Cartoon roughly based on Virat Kohli</p>
</li>
<li><p>Similar name</p>
<p>  <img src="https://cdn-images-1.medium.com/max/1440/1*oG00H-knsgIPjsuy6PjYOg.png" alt /></p>
</li>
</ul>
<p>Elasticsearch assigns a Relevance Score to every result (using the <strong>BM25 algorithm</strong>). It calculates this based on three main factors:</p>
<ol>
<li><p>Term Frequency (TF): How often does the word appear in the document? If a document mentions "Virat" 10 times, it is likely more relevant than a document that mentions him once.</p>
</li>
<li><p>Inverse Document Frequency (IDF): How rare is the word? Common words like "the" have a low score. Rare words like "Virat" have a high score.</p>
</li>
<li><p>Custom boosting logic: E.g. A match in the Title field is often weighted as more important than a match in the Body field.</p>
</li>
</ol>
<p>Consider that the following criteria is used to assign points</p>
<ul>
<li><p>Virat: 0.25 (term match in title)</p>
</li>
<li><p>Kohli: 0.5 (Rare term match in title)</p>
</li>
<li><p>Virat in description: 0.20</p>
</li>
<li><p>Kohli in description: 0.45</p>
</li>
<li><p>Exact query match: 0.2</p>
</li>
</ul>
<p>Here is how our results might be scored:</p>
<ol>
<li><p>Virat Kohli (Exact Match + High Field Boost): Score 0.95</p>
</li>
<li><p>List of Centuries... (High Term Frequency): Score 0.75</p>
</li>
<li><p>Virat in description (Low Term Frequency): Score 0.20</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769431881522/240bd0d3-4a2b-47fe-948a-5dd923a26638.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-summary">Summary</h3>
<p>The shift from SQL to Elasticsearch isn’t just about changing databases; it’s about changing your mindset. We move from asking "How do I save space?" to "How do I find this instantly?" This retrieval-first approach relies on four key architectural pillars:</p>
<ol>
<li><p>​ Ingestion: Storing data as JSON documents.</p>
</li>
<li><p>​ Analysis: Tokenizing text into searchable terms.</p>
</li>
<li><p>​ Indexing: Using an Inverted Index for O(1) lookups.</p>
</li>
<li><p>​ Ranking: Scoring results by relevance.</p>
</li>
</ol>
<p>This is a perfect example of how choosing the right data structure(Inverted Index) solves scalability problems that raw computing power cannot.</p>
<hr />
<blockquote>
<p>I use Elasticsearch in production daily. I wrote this article to solidify the fundamentals and visualize the mechanics. If you’re working on similar search scaling challenges, feel free to reach out!</p>
</blockquote>
<p><em>Note: This article was drafted with the assistance of Google Gemini to refine the technical explanations and structure.</em></p>
]]></content:encoded></item><item><title><![CDATA[Architecting a Food/Grocery Ordering system📝]]></title><description><![CDATA[1. The Maze of Architecture (Behind the Tap) 🌐
Ever wondered how a simple tap on your favourite delivery app translates to hot food at your door? The instantaneous experience hides a sophisticated organisational structure designed for massive scale,...]]></description><link>https://blog.harshnahaswani.com/architecting-a-foodgrocery-ordering-system</link><guid isPermaLink="true">https://blog.harshnahaswani.com/architecting-a-foodgrocery-ordering-system</guid><category><![CDATA[System Architecture]]></category><category><![CDATA[System Design]]></category><category><![CDATA[layered architecture]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[event-driven-architecture]]></category><category><![CDATA[model view controller]]></category><category><![CDATA[mvc]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Tue, 18 Nov 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-1-the-maze-of-architecture-behind-the-tap"><strong>1. The Maze of Architecture (Behind the Tap) 🌐</strong></h2>
<p>Ever wondered how a simple tap on your favourite delivery app translates to hot food at your door? The instantaneous experience hides a sophisticated organisational structure designed for massive <strong>scale</strong>, <strong>speed</strong>, and <strong>reliability</strong>.</p>
<p>Architecture isn’t about guessing a specific company’s design; it’s about mastering universal <strong>patterns</strong> that solve problems like managing complex codebases and scaling teams. We’ll walk through the essential patterns — from organising a single file to deploying hundreds of independent services — using the development of a major ordering app as our guide.</p>
<h2 id="heading-2-foundation-1-the-layered-architecture-organising-one-service"><strong>2. Foundation 1: The Layered Architecture (Organising One Service)🏗️</strong></h2>
<p>While building any application, the first challenge is organising your code. <strong>Layered Architecture</strong> solves this by enforcing a strict <strong>separation of concerns</strong> into horizontal layers.</p>
<p><strong>Analogy:</strong> Think of a <strong>Restaurant Kitchen</strong>. Each station has a specific job and only communicates with the station directly above or below it.</p>
<p>In our ordering app, the layers would look like this:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Layer</strong></td><td><strong>Responsibility</strong></td><td><strong>Example (The Order Request)</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Presentation</td><td>Handles the UI and user input.</td><td>The mobile app screen, or a Next.js component rendering the cart.</td></tr>
<tr>
<td>Application</td><td>Orchestrates the flow; manages transactions.</td><td>The code that coordinates calls between the Business and Persistence layers.</td></tr>
<tr>
<td>Business/Domain</td><td>Contains the core business rules.</td><td>calculateDeliveryFee(), applyPromocode(), checking coupon eligibility rules.</td></tr>
<tr>
<td>Persistence</td><td>Translates data requests to the database.</td><td>The code using Prisma or Drizzle to run the INSERT or SELECT statement.</td></tr>
</tbody>
</table>
</div><p><img src="https://miro.medium.com/v2/resize:fit:1260/1*V0Z--SzCULVd6LzdASiuQQ.png" alt /></p>
<p><strong>Key Takeaway:</strong> This structure ensures the core business rules are isolated from database code and the UI, making the application <strong>testable</strong> and <strong>maintainable</strong>.</p>
<h2 id="heading-3-foundation-2-model-view-controller-mvc-structuring-a-feature"><strong>3. Foundation 2: Model-View-Controller (MVC) (Structuring a Feature) 🧩</strong></h2>
<p><strong>Model-View-Controller (MVC)</strong> is the design pattern that structures a single feature(Checkout screen) within the Presentation and Application layers.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>MVC Component</strong></td><td><strong>Layered Placement</strong></td><td><strong>Role in the "Checkout" Feature</strong></td></tr>
</thead>
<tbody>
<tr>
<td>View (V)</td><td>Presentation Layer</td><td>Design and state management: The displayed cart total, delivery address input fields.</td></tr>
<tr>
<td>Controller (C)</td><td>Application Layer</td><td>Input Handler / Coordinator: The API route or function that receives the "Place Order" request.</td></tr>
<tr>
<td>Model (M)</td><td>Business/Persistence</td><td>Core Logic &amp; Data: The code that defines the rules and manages data. Is cart eligible for coupon</td></tr>
</tbody>
</table>
</div><p><img src="https://miro.medium.com/v2/resize:fit:1260/1*t6IcQsmY9nSF8D0S4cO8Nw.png" alt /></p>
<h3 id="heading-the-crucial-separation-controller-vs-model"><strong>The Crucial Separation: Controller vs. Model</strong></h3>
<p>This split defines architectural quality, particularly in <strong>validation</strong>:</p>
<ul>
<li><p><strong>Controller Responsibility:</strong> <strong>Input Validation</strong> (The “Syntax Check”). The Controller performs checks on the request <strong>format</strong> using tools like <strong>Zod</strong>. If a field is missing or the input is malformed, it rejects the request immediately.</p>
</li>
<li><p><strong>Model (M) Responsibility:</strong> <strong>Domain Validation</strong> (The “Business Rules Check”). The Model handles checks requiring business context:</p>
<ul>
<li><p><strong>Coupon Eligibility:</strong> <em>E.g., Is the cart value over ₹500 to be eligible for N coupons?</em></p>
</li>
<li><p><strong>Payment Rules:</strong> <em>E.g., Can a specific type of credit card be used against a particular coupon code?</em></p>
</li>
</ul>
</li>
</ul>
<p><strong>In practice, the Model is typically split into two modules (classes or services):</strong></p>
<ol>
<li><p><strong>Business Service:</strong> Holds rules and domain validation (<code>calculateTax</code>, <code>applyPromocode</code>).</p>
</li>
<li><p><strong>Data Repository (Persistence Layer):</strong> Handles all database interactions (<code>saveToDB</code>, <code>loadUser</code>).</p>
</li>
</ol>
<h2 id="heading-4-level-up-microservices-the-distributed-ecosystem"><strong>4. Level Up: Microservices (The Distributed Ecosystem) 🚀</strong></h2>
<p>To handle the demands of millions of users, the ordering app cannot rely on a single-layered application (<strong>Monolith</strong>). It must transition to <strong>Microservices Architecture</strong>.</p>
<p><strong>Analogy:</strong> We move from one large kitchen to a <strong>Food Court</strong> where specialised “stalls” (Microservices) operate independently.</p>
<p>A microservice is a <strong>small, autonomous application</strong> focused on a single business domain, or <strong>Bounded Context</strong>.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1260/1*TlUuJwkeZS4V_43Lay3hVA.png" alt /></p>
<h3 id="heading-microservices-breakdown-by-domain"><strong>Microservices Breakdown by Domain:</strong></h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Microservice Domain</strong></td><td><strong>Responsibility</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Catalogue Service</td><td>Manages all menu items, pricing, restaurant details(Owns the Catalogue Database)</td></tr>
<tr>
<td>Order Service</td><td>Manages placing, tracking, and cancellation of orders (Owns the Order Database).</td></tr>
<tr>
<td>Search Service</td><td>Provides lightning-fast item lookups (feeding an Elasticsearch index).</td></tr>
</tbody>
</table>
</div><h3 id="heading-core-principle-event-driven-architecture-eda"><strong>Core Principle: Event-Driven Architecture (EDA)</strong></h3>
<p>Services cannot share databases. They communicate asynchronously using <strong>Events</strong>.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1260/1*7eE8rJy_VHHuVln6aj-W4Q.png" alt /></p>
<ol>
<li><p><strong>Event Publisher:</strong> The <strong>Catalogue Service</strong> (using a <code>afterUpdate</code> hook, similar to those in Keystone or Payload CMS) publishes a <code>MENU_UPDATED</code> <strong>Event</strong> to a central <strong>Message Broker</strong> (e.g., Kafka).</p>
</li>
<li><p><strong>Event Subscriber:</strong> The <strong>Search Service</strong> subscribes to this event, fetches the updated data via the Catalogue’s public API, and updates its <strong>Elasticsearch</strong> index.</p>
</li>
</ol>
<p>This loose communication is essential for large-scale resilience.</p>
<h2 id="heading-5-the-two-cops-analogy-gateway-vs-controller"><strong>5. The Two “Cops” Analogy: Gateway vs. Controller🚨</strong></h2>
<p>Understanding the roles of the two distinct coordinators is key to the microservices flow:</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1260/1*xk5QmuAStDhdWXzJD6qINw.png" alt /></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Role</strong></td><td><strong>Analogy</strong></td><td><strong>Component</strong></td><td><strong>Logic Focus</strong></td></tr>
</thead>
<tbody>
<tr>
<td>External Coordinator</td><td>Traffic Cop</td><td>API Gateway</td><td>Checks if you are allowed to enter the system (Authentication), and directs traffic (Routing). No business logic.</td></tr>
<tr>
<td>Internal Coordinator</td><td>Elevator Guide</td><td>Controller/Application Layer</td><td>Sits inside the specific service. Checks if your ticket (data) is valid (Input Validation) and manages the internal business flow.</td></tr>
</tbody>
</table>
</div><h2 id="heading-6-putting-it-all-together-a-complete-order-flow"><strong>6. Putting It All Together: A Complete Order Flow</strong></h2>
<p>Trace the <strong>“Place Order”</strong> request through the entire system:</p>
<ol>
<li><p>User (View) clicks “Place Order.”</p>
</li>
<li><p>Request hits <strong>API Gateway (Traffic Cop)</strong>: Authenticates, routes to <strong>Order Service</strong>.</p>
</li>
<li><p><strong>Order Service’s Controller (Elevator Guide)</strong> receives request, validates input format (Zod/Yup).</p>
</li>
<li><p>Controller calls <strong>Order Service’s Business Logic (Model)</strong>: Runs rules like coupon eligibility, calculates final price.</p>
</li>
<li><p>Business Logic calls <strong>Persistence Repository (Model)</strong>: Uses <strong>Prisma/Drizzle</strong> to <code>saveOrder()</code> to its private DB.</p>
</li>
<li><p>Order Service publishes <code>ORDER_PLACED</code> event.</p>
</li>
<li><p><strong>Logistics Service</strong> and <strong>Notification Service</strong> subscribe and react accordingly.</p>
</li>
</ol>
<h2 id="heading-7-conclusion-building-the-next-generation"><strong>7. Conclusion: Building the Next Generation</strong></h2>
<p>You’ve moved from understanding how to organise a single function to mastering the architecture that governs the world’s largest digital systems. Architecture is not a destination; it’s a framework for making informed decisions about <strong>isolation</strong>, <strong>scaling</strong>, and <strong>maintainability</strong>. Mastering the strict boundaries between <strong>Layers</strong>, the clarity of <strong>MVC</strong> roles, and the independence of <strong>Microservices</strong> sets you up to build the next generation of scalable applications.</p>
<blockquote>
<p><em>AI used for content editing and image generation</em></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Retrospection on the Game Theory]]></title><description><![CDATA[Image from “The Evolution of Trust”
Playing the interactive game “The Evolution of Trust” was a deeply thought-provoking experience, prompting me to examine my own habitual responses in personal and professional interactions.
🤔 The Mirror of Interac...]]></description><link>https://blog.harshnahaswani.com/retrospection-on-the-game-theory</link><guid isPermaLink="true">https://blog.harshnahaswani.com/retrospection-on-the-game-theory</guid><category><![CDATA[trust]]></category><category><![CDATA[human behaviour]]></category><category><![CDATA[psychology]]></category><category><![CDATA[communication]]></category><category><![CDATA[Forgiveness]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Mon, 17 Nov 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn-images-1.medium.com/max/1440/1*6M7ckZsgwWOgLDzKQPj1tQ.png" alt /></p>
<p><em>Image from “The Evolution of Trust”</em></p>
<p>Playing the interactive game “<a target="_blank" href="https://ncase.me/trust/">The Evolution of Trust</a>” was a deeply thought-provoking experience, prompting me to examine my own habitual responses in personal and professional interactions.</p>
<h4 id="heading-the-mirror-of-interaction">🤔 The Mirror of Interaction</h4>
<p>We often focus on the other player, but what about the “hats” we wear? Our behaviour changes dramatically depending on the situation:</p>
<ul>
<li><p><strong>The Constant Kindred:</strong> When someone is consistently kind, do I even notice? Do I acknowledge, appreciate, or try to reciprocate that kindness?</p>
</li>
<li><p><strong>The Perpetual Antagonist:</strong> When someone acts against me, do I try to communicate and understand their motivation, or do I silently stew in resentment?</p>
</li>
<li><p><strong>The Unpleasant Surprise:</strong> When facing a difficult situation, do I take a moment to process and respond thoughtfully, or do I simply react?</p>
</li>
</ul>
<p>I realised that we are not fixed “players,” but rather <em>we adopt roles</em> — sometimes the Copycat, other times the Grudger, and occasionally the Detective. Cultivating a <em>steadfastness in character</em> — putting our minds to action — is key.</p>
<h4 id="heading-game-theory-and-the-real-world">📈 Game Theory and the Real World</h4>
<p>In an ideal game theory scenario, where players interact repeatedly, strategies like Copycat (<em>Tit-for-Tat</em>) tend to thrive and “win” over time. They begin by cooperating and then simply mirror the opponent’s last move.</p>
<p>However, life is not an ideal scenario.</p>
<p><strong>Limited Chances:</strong> We often don’t get multiple chances to evaluate and trust a person. One critical error can lead to a huge loss, and this is what immediately breeds distrust.</p>
<p><strong>The Miscommunication Barrier:</strong> Communication today is riddled with potential errors: a toneless email, a laggy video call, or a vague text message. This makes interactions inherently imperfect.</p>
<p>The game confirms that when <strong>miscommunication is introduced</strong>, the cooperative <strong>Copycat</strong> strategy quickly evolves into a <strong>cycle of mutual cheating</strong>.</p>
<h4 id="heading-the-tipping-point-of-miscommunication">📉 The Tipping Point of Miscommunication</h4>
<p>The results from “The Evolution of Trust” regarding miscommunication are particularly illuminating:</p>
<ul>
<li><p><strong>0% Error:</strong> The fair Copycat wins.</p>
</li>
<li><p><strong>1% to 9% Error:</strong> The slightly more forgiving Copykitten wins (it cooperates even after one slight misstep).</p>
</li>
<li><p><strong>10% to 49% Error:</strong> The selfish, unforgiving Always Cheat strategy wins.</p>
</li>
<li><p><strong>50% Error:</strong> Nobody wins; chaos reigns.</p>
</li>
</ul>
<blockquote>
<p>This is why “miscommunication” is such an interesting barrier to trust: a little bit of it leads to forgiveness, but too much and it leads to widespread distrust! I think our modern media technology, as much as it’s helped us increase communication… has increased our miscommunication much more.</p>
<p>~ Game Theory: The Evolution of Trust</p>
</blockquote>
<h4 id="heading-what-do-we-learn">💡 What Do We Learn?</h4>
<p>The game theory suggests a clear path forward for building stronger relationships:</p>
<ol>
<li><p>Seek Multiple Interactions: Trust is developed through repetition and consistency. We need enough interactions for a reliable pattern of behaviour to emerge.</p>
</li>
<li><p>Embrace Forgiveness: Don’t react to a single miscommunication or misstep. Give the other person the benefit of the doubt, adopting a Copykitten-like strategy.</p>
</li>
<li><p>Find Compromise: Look for win-win outcomes — strategies that seek to maximise both parties’ gains in the long run.</p>
</li>
</ol>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*w2rFA8iNxEMN_9t1EmXsbw.png" alt /></p>
]]></content:encoded></item><item><title><![CDATA[How to Filter 1 Billion Requests Without Crashing Your Database]]></title><description><![CDATA[https://tenor.com/view/scam-alert-scam-phishing-hack-fraud-gif-62215083004720289
 
GIF credits: Tenor
Imagine you are building a URL spam filter for a service like Gmail. You have a list of 100 million malicious URLs, and you need to check every sing...]]></description><link>https://blog.harshnahaswani.com/how-to-filter-1-billion-requests-without-crashing-your-database</link><guid isPermaLink="true">https://blog.harshnahaswani.com/how-to-filter-1-billion-requests-without-crashing-your-database</guid><category><![CDATA[bloom filter]]></category><category><![CDATA[System Design]]></category><category><![CDATA[scalability]]></category><category><![CDATA[Database Optimization,]]></category><category><![CDATA[high latency]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Tue, 14 Oct 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/scam-alert-scam-phishing-hack-fraud-gif-62215083004720289">https://tenor.com/view/scam-alert-scam-phishing-hack-fraud-gif-62215083004720289</a></div>
<p> </p>
<p><em>GIF credits: Tenor</em></p>
<p>Imagine you are building a URL spam filter for a service like Gmail. You have a list of <strong>100 million malicious URLs</strong>, and you need to check every single link clicked by your <strong>1 billion active users</strong> against this list.</p>
<p>The most obvious simple way would be to store the bad URLs in a SQL database and query it every time a user clicks a link.</p>
<p>If you try this in production, your infrastructure will crash in minutes.</p>
<p>In this post, I want to explore why the "obvious" solution fails at scale and how a probabilistic data structure called a <strong>Bloom Filter</strong> solves the problem by trading a tiny bit of accuracy for massive speed.</p>
<h2 id="heading-the-challenge-the-scale-wall">The Challenge: The "Scale" Wall</h2>
<p>When you are dealing with web-scale traffic, you hit three distinct bottlenecks:</p>
<ol>
<li><p><strong>Storage:</strong> A blacklist of 100 million URLs takes up about <strong>10 GB</strong> of space. That’s too big to keep in the fast L1/L2 cache of a standard server, forcing you to use slower storage.</p>
</li>
<li><p><strong>Throughput:</strong> With 1 billion daily active users, you might face peaks of <strong>30,000 queries per second (QPS)</strong>. A standard database instance simply cannot handle that many read operations without an expensive cluster.</p>
</li>
<li><p><strong>Latency:</strong> Users expect instant page loads. A round-trip to a database (network + disk I/O) takes about <strong>2 milliseconds</strong>. That sounds fast, but in the world of high-frequency requests, it’s an eternity.</p>
</li>
</ol>
<p>But here is the real kicker: <strong>99% of your traffic is safe</strong>.</p>
<p>If you use a traditional database, you are wasting expensive computing resources to check 990 million "safe" links every day. You are effectively burning money to answer "No" over and over again.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769841222255/a9ea75d9-318a-4461-98f9-ba8fe4963e84.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-solution-the-bloom-filter">The Solution: The Bloom Filter</h2>
<p>A Bloom Filter is a space-efficient probabilistic data structure that tells you whether an element <em>might</em> be in a set or is <em>definitely not</em> in the set.</p>
<p>Think of it as a highly efficient gatekeeper. Instead of storing the full URL string (which is heavy), we project it onto a compact array of bits (0s and 1s) using hash functions.</p>
<h3 id="heading-how-it-changes-the-architecture">How It Changes the Architecture</h3>
<p>We place the Bloom Filter in the memory (RAM) of the application server itself. Before we ever bother the database, we ask the filter: <em>"Have you seen this URL?"</em></p>
<ol>
<li><p><strong>If the Filter says "No":</strong> The URL is <strong>100% safe</strong>. We let the user proceed immediately. No database call. Zero network latency.</p>
</li>
<li><p><strong>If the Filter says "Maybe":</strong> It <em>might</em> be a malicious URL, or it might be a "False Positive." Only then do we check the database to confirm.</p>
</li>
</ol>
<h2 id="heading-the-impact-by-the-numbers">The Impact: By The Numbers</h2>
<p>The difference between the "Traditional Approach" and the "Bloom Filter Approach" is not just incremental; it's exponential.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Metric</strong></td><td><strong>Traditional DB Lookup</strong></td><td><strong>With Bloom Filter</strong></td><td><strong>Improvement</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Storage (RAM)</strong></td><td>~10 GB</td><td><strong>~125 MB</strong></td><td>99% Smaller</td></tr>
<tr>
<td><strong>Database Load</strong></td><td>30,000 QPS</td><td><strong>~300 QPS</strong></td><td>98% Less Load</td></tr>
<tr>
<td><strong>Latency</strong></td><td>2 milliseconds</td><td><strong>Nanoseconds</strong></td><td>20,000x Faster</td></tr>
</tbody>
</table>
</div><p>By accepting a standard <strong>1% False Positive rate</strong>, we instantly eliminate <strong>99%</strong> of the unnecessary traffic that would otherwise hit our database. We shrink a 10 GB problem into a 125 MB solution that fits on the RAM of a cheap laptop.</p>
<h3 id="heading-industry-adoption">Industry Adoption</h3>
<ul>
<li><p>Google (Safe Browsing)</p>
</li>
<li><p>Medium (Feed Deduplication)</p>
</li>
<li><p>Cassandra (Database Speed)</p>
</li>
<li><p>Akamai (CDN Caching)</p>
</li>
<li><p>Bitcoin (Wallet Syncing)</p>
</li>
</ul>
<h2 id="heading-conclusion-the-power-of-good-enough">Conclusion: The Power of "Good Enough"</h2>
<p>In system design, we are often taught that accuracy is paramount. But Bloom Filters teach us that sometimes, knowing what is <strong>definitely NOT</strong> true is just as valuable as knowing what <strong>IS</strong> true.</p>
<p>If you can tolerate saying "Maybe" occasionally, but can <strong>never</strong> afford to be slow, use a Bloom Filter.</p>
<p>For a deeper dive into how bloom filters work with an interactive demo, check out <a target="_blank" href="https://samwho.dev/bloom-filters/">https://samwho.dev/bloom-filters</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Stop Hacking Your Checkboxes: A Modern Approach]]></title><description><![CDATA[Learn to ditch the fragile, label*-based hacks. This guide details a modern, robust method using appearance: none, clip-path, and Grid to create custom checkboxes that are clean, scalable, and semantically* correct

Unpacking the “Fragile Hack”: Why ...]]></description><link>https://blog.harshnahaswani.com/stop-hacking-your-checkboxes-a-modern-approach</link><guid isPermaLink="true">https://blog.harshnahaswani.com/stop-hacking-your-checkboxes-a-modern-approach</guid><category><![CDATA[CSS]]></category><category><![CDATA[Semantic Web]]></category><category><![CDATA[semantichtml]]></category><category><![CDATA[checkbox]]></category><category><![CDATA[checklist]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Mon, 13 Oct 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>Learn to <em>ditch the fragile,</em> <strong><em>label*</em></strong>-based hacks<em>. This guide details a modern, robust method using <code>appearance: none</code>, <code>clip-path</code>, and Grid to create custom checkboxes that are clean, scalable, and </em>semantically* correct</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769428105468/39042b19-c7dd-4833-a327-bf1d8e0ec5eb.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-unpacking-the-fragile-hack-why-the-old-way-fails">Unpacking the “Fragile Hack”: Why the Old Way Fails</h3>
<p>The standard method involves hiding the real <code>&lt;input&gt;</code> and using the <code>&lt;label&gt;</code>'s pseudo-elements to create the checkbox graphic. While it looks right, it's a house of cards built on three core failures:</p>
<h4 id="heading-1-brittle-layouts-and-styling">1. Brittle Layouts and Styling</h4>
<p>The hack forces the <code>&lt;label&gt;</code> to manage both its text and the checkbox graphic, leading to constant layout battles.</p>
<ul>
<li><p><strong>Manual Positioning:</strong> You’re forced to use <code>padding-left</code> on the label to make space, then <code>position: absolute</code> to place the fake checkbox. This is brittle.</p>
</li>
<li><p><strong>Breaks with Dynamic Content:</strong> If the label text changes font size or wraps to a new line, the alignment of the checkbox graphic breaks. Fixing this requires even more complex CSS.</p>
</li>
</ul>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*FYeRhYLlwfozaTMI6Cg9uA.png" alt="Checkbox with mis-aligned tick mark" /></p>
<hr />
<ul>
<li><strong>Style Conflicts:</strong> A simple change to the label’s <code>line-height</code> or <code>display</code> property for other reasons can completely misalign your checkbox.</li>
</ul>
<h4 id="heading-2-compromised-html-semantics">2. Compromised HTML Semantics</h4>
<p>This hack violates the fundamental principle of separation of concerns.</p>
<ul>
<li><strong>An Element Doing Two Jobs:</strong> A <code>&lt;label&gt;</code>'s job is to describe an input. This hack forces it into a presentational role, making it both a label <em>and</em> a graphical button. This makes the code less intuitive and harder to debug.</li>
</ul>
<h4 id="heading-3-critical-accessibility-pitfalls">3. Critical Accessibility Pitfalls</h4>
<p>This is the most serious failure.</p>
<ul>
<li><p><strong>Forgotten Focus States:</strong> Styling focus states becomes a complex, multi-step process (<code>input:focus + label::before</code>). This is frequently forgotten, resulting in components that provide <strong>no visual feedback for keyboard users</strong>—a major accessibility failure.</p>
</li>
<li><p><strong>Failure in High Contrast Mode:</strong> A checkmark created with <code>background-color</code> on a pseudo-element often disappears completely in Windows High Contrast Mode, making it impossible for users with vision impairments to see the checked state.</p>
</li>
<li><p>By using the fragile hack, you lose <strong>maintainability</strong>, <strong>simplicity</strong>, and <strong>robustness</strong>. Your component becomes a chore to update and is guaranteed to fail in common edge cases.</p>
</li>
</ul>
<hr />
<h3 id="heading-crafting-a-simpler-decoupled-version">Crafting a simpler, decoupled version</h3>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*uGsvQ0nNeTMTOgUOqzOadQ.png" alt /></p>
<h4 id="heading-step-1-nuke-the-default-style"><strong>Step 1: Nuke the default style 💥</strong></h4>
<p>First, we remove the browser’s default styles with <code>appearance: none</code>. This gives us a blank canvas. We then style the <code>&lt;input&gt;</code> itself to be our box, using <code>em</code> units, so it scales with your text.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span> {
<span class="hljs-comment">/*  reset browser styles  */</span>
  <span class="hljs-attribute">appearance</span>: none;
  <span class="hljs-attribute">-webkit-appearance</span>: none;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;

<span class="hljs-comment">/*  scale size according to font  */</span>
  <span class="hljs-attribute">width</span>: <span class="hljs-number">1.2em</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">1.2em</span>;
  <span class="hljs-attribute">font-size</span>: inherit;
}
</code></pre>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*wiz_HBFAuM8HFeHgXaKqKw.png" alt="Reset the browser styles" /></p>
<hr />
<h4 id="heading-step-2-create-the-base-for-the-container-and-tick"><strong>Step 2: Create the base for the container and tick</strong></h4>
<pre><code class="lang-css"><span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span> {
  <span class="hljs-comment">/* exisitng styles... */</span>

  <span class="hljs-comment">/* define the checkbox container */</span>
  <span class="hljs-attribute">border</span>: <span class="hljs-number">0.15em</span> solid <span class="hljs-number">#5a5a5a</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.3em</span>;
}

<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span><span class="hljs-selector-pseudo">::before</span> {
  <span class="hljs-comment">/* Setting the base for checkmark. */</span>
  <span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">80%</span>;
  <span class="hljs-attribute">margin</span>: auto;
}
</code></pre>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*fTJ3UfpHmTWDxcc4hTvPHA.png" alt /></p>
<p>Defining checkbox container and tickmark base</p>
<p>Two things to notice here:</p>
<ul>
<li><p>The label and the checkbox baselines are not aligned properly.</p>
</li>
<li><p>We can’t visualise the tick mark base.</p>
</li>
</ul>
<p>Let’s add a <code>background-color: limegreen;</code> on the pseudo selector <code>:before</code> when <code>:checked</code>. We still can’t see the tick base. That is because it needs to be positioned on the UI. Traditionally, it was done using <code>position: absolute;</code>. In modern CSS, it can be done using <code>grid</code>.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span> {
  <span class="hljs-comment">/* exisitng styles... */</span>

  <span class="hljs-comment">/* center position the tick */</span>
  <span class="hljs-attribute">display</span>: inline-grid;
  <span class="hljs-attribute">place-items</span>: center;
}

<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span><span class="hljs-selector-pseudo">::before</span> {
  <span class="hljs-comment">/* exisitng styles... */</span>
}

<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span><span class="hljs-selector-pseudo">:checked</span><span class="hljs-selector-pseudo">::before</span> {
  <span class="hljs-attribute">background-color</span>: limegreen;
}
</code></pre>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*gPTccck8Txe7Ahbfsb-jXQ.png" alt /></p>
<hr />
<h4 id="heading-step-3-drawing-the-tick"><strong>Step 3: Drawing the tick</strong></h4>
<p>We use CSS property clip-path and polygon() function to draw are tick on the screen.</p>
<pre><code class="lang-css"><span class="hljs-comment">/* other existing styles */</span>

<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span><span class="hljs-selector-pseudo">:checked</span><span class="hljs-selector-pseudo">::before</span> {
  <span class="hljs-attribute">background</span>: limegreen;

  <span class="hljs-comment">/*  drawing the tick mark as x,y points */</span>
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">polygon</span>(
    <span class="hljs-number">0%</span> <span class="hljs-number">65%</span>,
    <span class="hljs-number">41%</span> <span class="hljs-number">77%</span>,
    <span class="hljs-number">100%</span> <span class="hljs-number">0%</span>,
    <span class="hljs-number">46%</span> <span class="hljs-number">100%</span>
  );
}
</code></pre>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*sIGybe4N4bT28xhdHLbTtQ.png" alt /></p>
<p>Use polygon() and clip-path to define the tick mark.</p>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*EpscKvcc-UeTo4sFw-CaZg.png" alt /></p>
<p>For further enhancements, you can add <code>border-color</code>, <code>background-color</code>, and <strong>transform</strong> styles to the input when checked.</p>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*YViamPilmhm0p1kNygjtHQ.png" alt /></p>
<hr />
<h4 id="heading-step-4-accessibility-and-other-concerns"><strong>Step 4: Accessibility and other concerns</strong></h4>
<p>There is an issue with this. In <strong>Windows high-contrast mode</strong>, all the backgrounds are stripped. Only black and white remains. When this happens, our checked state will disappear.</p>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*dirwtsV1CsJZB2jgJ5M_Vw.png" alt /></p>
<p>Tick mark disappears in Windows high contrast mode (WHCM)</p>
<p>We can fix this using a neat CSS trick. When Windows forces black and white colours, we add a CSS media query to ensure the background picks the available system colours.</p>
<p>We have 2 options :</p>
<ul>
<li><p><strong>Highlight</strong>: This keyword maps to the system’s “selection” or “accent” colour (often blue by default).</p>
</li>
<li><p><strong>CanvasText</strong>: This keyword maps to the default text color in the user’s OS theme. In a <strong>light high-contrast theme</strong>, it’s typically black. In a <strong>dark theme</strong>, it’s white.</p>
</li>
</ul>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*mjZLc0_AuIO38timTikp9A.png" alt /></p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> (<span class="hljs-attribute">forced-colors:</span> active) {
  <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span><span class="hljs-selector-pseudo">:checked</span><span class="hljs-selector-pseudo">::before</span> {
    <span class="hljs-attribute">background-color</span>: Highlight;
  }
}
</code></pre>
<p>Similarly, handle accessibility for keyboard by styling <code>:focus-visible</code> for the input. And, the <code>:disabled</code> and <code>:read-only</code> states with <code>cursor: not-allowed</code> to give a clear idea to the user. We can also use the <code>aria-checked</code> HTML attribute for screen readers.</p>
<h4 id="heading-step-5-automating-using-css-variables-optional">Step 5: Automating using CSS variables (optional)</h4>
<p>Furthermore, we can use CSS variables to style the checklist based on the section background colours. This helps the checklist seamlessly blend with the flowing content.</p>
<p>For automating this, I use 80% of the current text colour as the background colour</p>
<pre><code class="lang-css"><span class="hljs-comment">/* keep existing value as fallback for older browsers*/</span>
<span class="hljs-selector-tag">background</span>: <span class="hljs-selector-tag">limegreen</span>;  
<span class="hljs-selector-tag">background-color</span>: <span class="hljs-selector-tag">var</span>(<span class="hljs-selector-tag">--checkbox-bg</span>, <span class="hljs-selector-tag">rgb</span>(<span class="hljs-selector-tag">from</span> <span class="hljs-selector-tag">currentColor</span> <span class="hljs-selector-tag">r</span> <span class="hljs-selector-tag">g</span> <span class="hljs-selector-tag">b</span> / 80%));
</code></pre>
<p><img src="https://cdn-images-1.medium.com/max/2160/1*YQFd7wQBGwnec8_i5cBfyA.png" alt /></p>
<p><img src="https://cdn-images-1.medium.com/max/2160/1*Tt-XisiVG9DBL8Ibpa7RTw.png" alt /></p>
<p><strong>Note</strong> that we haven’t touched the <code>label</code> element. We can now independently style it as a common component for all input types without worrying about disturbing the checkbox styles. This also helps maintain the semantic HTML structure.</p>
<hr />
<h3 id="heading-key-takeaways">Key Takeaways</h3>
<ul>
<li><p>✅ <strong>Use</strong> <code>appearance: none</code> on <code>&lt;input&gt;</code> to reset browser styles.</p>
</li>
<li><p>❌ <strong>Don’t hide the input</strong> and style the <code>&lt;label&gt;</code>.</p>
</li>
<li><p>✅ <strong>Use</strong> <code>clip-path</code> and <code>polygon</code>for a clean checkmark shape.</p>
</li>
<li><p>✅ <strong>Use</strong> <code>display: inline-grid</code> for centring.</p>
</li>
<li><p>✅ <strong>Build for Scalability:</strong> Use <code>em</code> units for sizing so your checkbox scales with the surrounding text, and use CSS variables for easy theming.</p>
</li>
<li><p>✅ <strong>Prioritise Accessibility:</strong> Always include a clear state for keyboard users. Use the <code>@media (forced-colors: active)</code> query to provide an alternative style for Windows High Contrast Mode.</p>
</li>
<li><p>✅ <strong>Avoid accidental shrinking:</strong> I prefer adding <code>flex-shrink:0;</code> as a fail-safe in my input to avoid accidental shrinking in a <em>possible</em> flex parent. You might miss these if it only happens on smaller devices.</p>
</li>
</ul>
<p><img src="https://cdn-images-1.medium.com/max/1440/1*prt9q1hE2qL9rey4DteC6g.png" alt /></p>
<p><em>Image shows accidental shrinking in flex-parent, especially in mobile devices.</em></p>
<hr />
<blockquote>
<p>Over to You</p>
<p>I wrote this guide with an intention to provide a solid foundation for modern checkbox styling, giving you techniques you can carry forward into more advanced components.</p>
<p>But this is just my take, and I’m always eager to learn from the community. I’d love to hear your thoughts — did I miss anything? Have you run into a specific edge case where this might fail, or do you have a different technique you prefer? Accessibility concerns?</p>
<p>All suggestions and critiques are welcome in the comments below. Let’s learn from each other!</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Playing with SVGs]]></title><description><![CDATA[SVG is one of my favourite formats for images in apps. It’s clean, scalable, and customisable
Need the same icon to work on both light and dark backgrounds? We usually change the color in iconsets. But what about site logos or custom icons downloaded...]]></description><link>https://blog.harshnahaswani.com/playing-with-svgs</link><guid isPermaLink="true">https://blog.harshnahaswani.com/playing-with-svgs</guid><category><![CDATA[SVG]]></category><category><![CDATA[Custom Software Development]]></category><category><![CDATA[CSS3]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[React]]></category><category><![CDATA[DX]]></category><category><![CDATA[ux design]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Tue, 30 Sep 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>SVG is one of my favourite formats for images in apps. It’s clean, scalable, and customisable</p>
<p>Need the same icon to work on both light and dark backgrounds? We usually change the color in iconsets. But what about site logos or custom icons downloaded from Figma? Do we make the user download multiple files just to handle a complex gradient change?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769405024659/b9e15828-43e6-43a4-a881-098b001d2242.png" alt="Picture showcasing individual SVGs used for each color" class="image--center mx-auto" /></p>
<p>Instead, we should identify the hard-coded values in the SVG. If you read the SVG code, you’ll notice that the colour often comes from a block of linearGradient code. Specifically, the <code>stop-color</code> attribute. This might vary for other SVGs, so look out for fill and stroke parameters. They may contain direct colours or point to another element using the <code>url</code> function. If a <code>url</code> is used, locate the <code>&lt;defs&gt;</code> element with the corresponding ID (Image shows path pointing to <code>&lt;defs&gt;</code> with ID “g1“</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769405137968/98a201af-16ff-49ab-9962-d4adccf1fb51.png" alt="Lookout for fill and stroke parameters. They may contain direct colors or point to another element using the url function. If url is given, ID find the &lt;defs&gt; element with the required ID" class="image--center mx-auto" /></p>
<h2 id="heading-strategy-1-the-custom-component-css-variables">Strategy 1: The Custom Component (CSS Variables)</h2>
<p>How do we do this with a single SVG file in React?</p>
<ol>
<li><p>First, save the SVG as a component rather than importing it as an image.</p>
</li>
<li><p>You need to replace the static ID (like "g1") with a unique, dynamic ID. This is crucial because we are referencing a DOM element in the fill by its ID. It will always take the value of the first occurrence in the DOM, so if different instances need different colours, they require a unique ID. Also, <strong>don’t miss the hash (#) symbol</strong> when writing the url access string.</p>
</li>
<li><p>Finally, assign CSS variables to the <code>stopColor</code> attribute. I prefer adding a default fallback value to the variables for safety, like <code>var(--color, "#455673")</code>.</p>
</li>
</ol>
<p>You can now use a single SVG component for multiple variations.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769405607541/d02a1e9c-f617-4222-ae65-66cb4fd02f4c.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769405641140/d87c0f5f-4451-4fa5-a4da-9a7484026169.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769405866279/860bc4f2-2e9b-4af8-a69d-b347eef7e7f4.png" alt class="image--center mx-auto" /></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/dkzp45?view=editor+%2B+preview&amp;module=%2Fsrc%2FApp.js">https://codesandbox.io/embed/dkzp45?view=editor+%2B+preview&amp;module=%2Fsrc%2FApp.js</a></div>
<p> </p>
<hr />
<h2 id="heading-strategy-2-the-external-defs-trick-for-icon-sets">Strategy 2: The "External Defs" Trick (For Icon Sets)</h2>
<p>What about icon sets like <a target="_blank" href="https://hugeicons.com/">Hugeicons</a>? Often, gradient features are locked behind "Pro" tiers, or the library simply doesn't support them.</p>
<p>We can bypass this by defining a gradient in a <strong>separate, invisible SVG</strong> and referencing it in the icon's stroke.</p>
<h3 id="heading-step-1-create-a-gradient-definition-component">Step 1: Create a Gradient Definition Component</h3>
<p>This component renders an invisible SVG (<code>height=0, width=0</code>) that serves purely as a container for your gradient definition.</p>
<pre><code class="lang-xml">const SVGDefinition = ({ id, className }) =&gt; {
  return (
    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> 
      <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inline h-0 w-0"</span> 
      <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span> // <span class="hljs-attr">Good</span> <span class="hljs-attr">for</span> <span class="hljs-attr">accessibility</span> <span class="hljs-attr">since</span> <span class="hljs-attr">this</span> <span class="hljs-attr">is</span> <span class="hljs-attr">invisible</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">defs</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">linearGradient</span>
          <span class="hljs-attr">id</span>=<span class="hljs-string">{id}</span>
          <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"100%"</span> // <span class="hljs-attr">gradient</span>
          <span class="hljs-attr">gradientUnits</span>=<span class="hljs-string">"userSpaceOnUse"</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span> // <span class="hljs-attr">If</span> <span class="hljs-attr">using</span> <span class="hljs-attr">Tailwind</span> <span class="hljs-attr">classes</span> <span class="hljs-attr">for</span> <span class="hljs-attr">colors</span>
        &gt;</span>
          {/* Use stopColor (camelCase) */}
          <span class="hljs-tag">&lt;<span class="hljs-name">stop</span> <span class="hljs-attr">offset</span>=<span class="hljs-string">"0%"</span> <span class="hljs-attr">stopColor</span>=<span class="hljs-string">"var(--gradient-from)"</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">stop</span> <span class="hljs-attr">offset</span>=<span class="hljs-string">"100%"</span> <span class="hljs-attr">stopColor</span>=<span class="hljs-string">"var(--gradient-to)"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">linearGradient</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">defs</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
  );
};
</code></pre>
<h3 id="heading-step-2-apply-it-to-the-icon">Step 2: Apply it to the Icon</h3>
<p>Place the definition alongside your icon, then override the icon's <code>stroke</code> style to point to your new gradient ID.</p>
<pre><code class="lang-xml">const Component = () =&gt; {
  const gradientId = 'unique_gradient_id'; // Descriptive variable name

  return (
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">SVGDefinition</span> <span class="hljs-attr">id</span>=<span class="hljs-string">{gradientId}</span> /&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">IconFromIconset</span> 
         <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
           // <span class="hljs-attr">React</span> <span class="hljs-attr">inline</span> <span class="hljs-attr">styles</span> <span class="hljs-attr">require</span> <span class="hljs-attr">quotes</span> <span class="hljs-attr">and</span> <span class="hljs-attr">comma</span> <span class="hljs-attr">separation</span>
           <span class="hljs-attr">stroke:</span> `<span class="hljs-attr">url</span>(#${<span class="hljs-attr">gradientId</span>}) !<span class="hljs-attr">important</span>`, 
         }}
       /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  );
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769406395214/bebd9f8b-6eff-4957-a70a-15cb2e532831.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-why-this-works">Why this works</h3>
<p>The <code>url(#id)</code> syntax in SVG doesn't care where the ID live. It just looks for a matching ID anywhere in the DOM. By injecting our own <code>&lt;defs&gt;</code> nearby, we can "paint" the third-party icon with our custom gradient.</p>
<h2 id="heading-notes">Notes:</h2>
<ol>
<li><p>The defined gradient needs to be position absolute with <code>width:0</code> and <code>height:0</code>. This ensures it doesn’t take up any physical space on your page.</p>
</li>
<li><p>If this gradient is a primary brand colour used everywhere, you can simply add this definition to the top of your body tag to reduce overhead. You wouldn't need a wrapper in that case, just use the icon with the adjusted stroke value.</p>
</li>
</ol>
<blockquote>
<p>I have tested this approach on size, stroke widths, fill, and filter attributes. Let me know if you find something intersting or have any feedback :D</p>
</blockquote>
<h2 id="heading-references">References:</h2>
<ol>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/defs#/">https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/defs#/</a></p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/ru/docs/Web/SVG/Reference/Element/linearGradient#/">https://developer.mozilla.org/ru/docs/Web/SVG/Reference/Element/linearGradient#/</a></p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/stop-color#/">https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/stop-color#/</a></p>
</li>
</ol>
<p><em>Previously published on Medium</em></p>
]]></content:encoded></item><item><title><![CDATA[Web Storage APIs]]></title><description><![CDATA[Modern web applications often need to save data directly in the browser of the user, whether it is preserving a shopping cart, keeping a user logged in, or remembering dark mode preferences. While Cookies were once the only option, the Web Storage AP...]]></description><link>https://blog.harshnahaswani.com/web-storage-apis</link><guid isPermaLink="true">https://blog.harshnahaswani.com/web-storage-apis</guid><category><![CDATA[web storage api]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[localstorage]]></category><category><![CDATA[sessionStorage]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Thu, 07 Apr 2022 06:30:00 GMT</pubDate><content:encoded><![CDATA[<p>Modern web applications often need to save data directly in the browser of the user, whether it is preserving a shopping cart, keeping a user logged in, or remembering dark mode preferences. While Cookies were once the only option, the <strong>Web Storage API</strong> provides a more intuitive way to handle client side data.</p>
<p>By the end of this blog, you will have a clear understanding of Web Storage APIs, how to implement them, and most importantly, when <strong>not</strong> to use them.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>To get the most out of this guide, you should have a basic understanding of:</p>
<ul>
<li><p><strong>JavaScript</strong> (Objects, Strings, and basic syntax)</p>
</li>
<li><p><strong>APIs</strong> (How interfaces interact with the browser)</p>
</li>
<li><p><strong>Client side rendering</strong> (How the browser paints the UI)</p>
</li>
</ul>
<hr />
<h2 id="heading-what-is-the-web-storage-api">What is the Web Storage API?</h2>
<p>The Web Storage API provides mechanisms for browsers to store key value pairs. Unlike cookies, this data is never sent to the server with every HTTP request, reducing network traffic.</p>
<p>There are two main mechanisms within this API:</p>
<ol>
<li><p><strong>LocalStorage</strong></p>
</li>
<li><p><strong>SessionStorage</strong></p>
</li>
</ol>
<p>Both share the same storage limit (typically around <strong>5MB</strong> per origin) and use the same methods to set and retrieve data.</p>
<h3 id="heading-1-localstorage">1. LocalStorage</h3>
<p><code>localStorage</code> persists data even after the browser window is closed. It has no expiration date; the data remains until the user manually clears the cache or your web app clears it programmatically.</p>
<p><strong>Common Use Cases</strong></p>
<ul>
<li><p>Storing User Theme preferences (Dark or Light mode).</p>
</li>
<li><p>Persisting authentication tokens (caution advised).</p>
</li>
<li><p>Saving "draft" data for long forms.</p>
</li>
</ul>
<p><strong>Syntax</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Save data (Key, Value)</span>
<span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'theme'</span>, <span class="hljs-string">'dark'</span>);

<span class="hljs-comment">// Retrieve data</span>
<span class="hljs-keyword">const</span> currentTheme = <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'theme'</span>);

<span class="hljs-comment">// Remove specific data</span>
<span class="hljs-built_in">localStorage</span>.removeItem(<span class="hljs-string">'theme'</span>);

<span class="hljs-comment">// Clear all data</span>
<span class="hljs-built_in">localStorage</span>.clear();
</code></pre>
<h3 id="heading-2-sessionstorage">2. SessionStorage</h3>
<p><code>sessionStorage</code> works almost exactly like LocalStorage, but with one key difference: <strong>Persistence</strong>. Data stored in <code>sessionStorage</code> is cleared as soon as the tab or window is closed.</p>
<p><strong>Common Use Cases</strong></p>
<ul>
<li><p>One time login redirects.</p>
</li>
<li><p>Sensitive banking session data (where you want automatic cleanup).</p>
</li>
<li><p>Filtering or sorting states for a specific tab.</p>
</li>
</ul>
<p><strong>Syntax</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Save data</span>
sessionStorage.setItem(<span class="hljs-string">'sessionID'</span>, <span class="hljs-string">'12345'</span>);

<span class="hljs-comment">// Retrieve data</span>
<span class="hljs-keyword">const</span> session = sessionStorage.getItem(<span class="hljs-string">'sessionID'</span>);
</code></pre>
<hr />
<h2 id="heading-cons-of-web-storage-api">Cons of Web Storage API</h2>
<p>While <code>localStorage</code> and <code>sessionStorage</code> are incredibly easy to use, they come with a significant architectural flaw: <strong>They are Synchronous.</strong></p>
<p>JavaScript runs on a <strong>single main thread</strong>. This thread is responsible for everything: executing your JavaScript, parsing HTML, and rendering the UI (painting pixels to the screen).</p>
<p>Because Web Storage APIs are synchronous, when your code reads from or writes to storage, the browser <strong>halts the entire main thread</strong> until that operation is complete.</p>
<h3 id="heading-the-performance-impact">The Performance Impact</h3>
<p>If you are storing a small string, the delay is negligible (microseconds). However, if you are storing large datasets (approaching the 5MB limit) or complex JSON objects that need to be serialized or deserialized, the impact becomes noticeable.</p>
<ol>
<li><p><strong>UI Freezing:</strong> While the browser is writing to <code>localStorage</code>, it cannot respond to clicks, scrolling, or animations. The app will feel "janky" or unresponsive.</p>
</li>
<li><p><strong>I/O Blocking:</strong> Reading data from the disk (where storage lives) is much slower than reading from memory (RAM).</p>
</li>
</ol>
<blockquote>
<p><strong>Warning:</strong> Never use Web Storage for high frequency data (like tracking mouse movements) or large datasets (like a list of 10,000 products).</p>
</blockquote>
<hr />
<h2 id="heading-summary-advantages-and-disadvantages">Summary: Advantages and Disadvantages</h2>
<p><strong>Ease of Use</strong></p>
<ul>
<li><p><strong>Pros:</strong> Extremely simple API (<code>setItem</code> and <code>getItem</code>). No setup required.</p>
</li>
<li><p><strong>Cons:</strong> Limited to Strings only (must <code>JSON.stringify</code> objects).</p>
</li>
</ul>
<p><strong>Persistence</strong></p>
<ul>
<li><p><strong>Pros:</strong> Great for saving state across sessions (LocalStorage).</p>
</li>
<li><p><strong>Cons:</strong> Data can accumulate and waste space if not managed.</p>
</li>
</ul>
<p><strong>Performance</strong></p>
<ul>
<li><p><strong>Pros:</strong> Fast for very small amounts of data.</p>
</li>
<li><p><strong>Cons:</strong> <strong>Synchronous and Blocking.</strong> Can freeze the main thread and degrade UI performance.</p>
</li>
</ul>
<p><strong>Security</strong></p>
<ul>
<li><strong>Cons:</strong> <strong>Vulnerable to XSS.</strong> Any JavaScript running on your page can read this data. Never store sensitive secrets here.</li>
</ul>
<hr />
<h2 id="heading-better-alternatives-and-best-practices">Better Alternatives and Best Practices</h2>
<p>If you need to store complex data or large files without slowing down your app, consider <strong>IndexedDB</strong>. It is an asynchronous, transactional database system built into the browser. It does not block the main thread and can store significantly more data than Web Storage.</p>
<p>I have listed down a few articles below that dive deeper into these alternatives and best practices. Do check them out to deepen your understanding.</p>
<h3 id="heading-references-and-further-reading">References and Further Reading</h3>
<ul>
<li><p><a target="_blank" href="https://web.dev/storage-for-the-web/">Storage for the Web (web.dev)</a></p>
</li>
<li><p><a target="_blank" href="https://betterprogramming.pub/the-different-types-of-browser-storage-82b918cb3cf8">The Different Types of Browser Storage</a></p>
</li>
<li><p><a target="_blank" href="https://www.sitepoint.com/client-side-storage-options-comparison/">Client Side Storage Options Comparison</a></p>
</li>
<li><p><a target="_blank" href="https://javascript.info/localstorage">JavaScript.info: LocalStorage</a></p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">MDN Web Docs: Web Storage API</a></p>
</li>
<li><p><a target="_blank" href="https://www.geeksforgeeks.org/localstorage-and-sessionstorage-web-storage-apis/">GeeksForGeeks: LocalStorage and SessionStorage</a></p>
</li>
<li><p><a target="_blank" href="https://codesource.io/complete-guide-to-local-storage-in-javascript/">Complete Guide to Local Storage</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Stacks and Queues]]></title><description><![CDATA[Why Data Structures Matter
Knowledge of storing, retrieving, and structuring data is vital for software developers. It allows them to create cost-effective applications that are not only easy to use but also fast and efficient.
However, knowing the d...]]></description><link>https://blog.harshnahaswani.com/stacks-and-queues</link><guid isPermaLink="true">https://blog.harshnahaswani.com/stacks-and-queues</guid><category><![CDATA[data structures]]></category><category><![CDATA[Stacks]]></category><category><![CDATA[Queues]]></category><category><![CDATA[Stacks Queues]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Thu, 01 Oct 2020 06:30:00 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-why-data-structures-matter">Why Data Structures Matter</h3>
<p>Knowledge of storing, retrieving, and structuring data is vital for software developers. It allows them to create cost-effective applications that are not only easy to use but also fast and efficient.</p>
<p>However, knowing the definitions is just a small part of the puzzle. The key factor is <strong>practical usage</strong>. Don't just gather information; try to understand <em>why</em> structures are designed the way they are. Deeply understanding the logic behind these structures helps you retain the knowledge for longer periods and apply it effectively to real-world problems.</p>
<hr />
<h3 id="heading-1-the-stack">1. The Stack</h3>
<p>The best real-world example of a stack is the <strong>"Tower of Hanoi"</strong> puzzle. In this structure, a ring can be removed only if the ones above it are removed first. Similarly, new rings can only be added to the very top of the existing ones. No ring can be added to the middle or removed at random.</p>
<h4 id="heading-technical-breakdown-lifo"><strong>Technical Breakdown: LIFO</strong></h4>
<p>Stacks function exactly like the rings. In technical terms, this is called <strong>LIFO</strong> (<strong>L</strong>ast-<strong>I</strong>n-<strong>F</strong>irst-<strong>O</strong>ut). The element placed last is the first one to be removed.</p>
<ul>
<li><p><strong>Push:</strong> The action of inserting or adding an element to the stack.</p>
</li>
<li><p><strong>Pop:</strong> The action of removing an element from the stack.</p>
</li>
<li><p><strong>Entry/Exit:</strong> The same point (the top) is used for both entry and exit.</p>
</li>
</ul>
<p><strong>Common Applications:</strong> The most common application of a stack is during <strong>recursion</strong> and managing <strong>function calls</strong> in programming languages.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769445568607/4eba5867-4686-4858-8e1c-6fbbbca35b9a.png" alt class="image--center mx-auto" /></p>
<hr />
<h3 id="heading-2-the-queue">2. The Queue</h3>
<p>Unlike stacks, a queue has two control points: one for entry and one for exit. Take a <strong>one-way road</strong> as an example. You can only move in one direction. If you are stuck in traffic, you cannot cut in front; you have to wait for the person before you to move.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769445652544/0d0bc097-8654-40c2-8591-0e927cb6a3b0.jpeg" alt class="image--center mx-auto" /></p>
<h4 id="heading-technical-breakdown-fifo"><strong>Technical Breakdown: FIFO</strong></h4>
<p>Technically, this is called <strong>FIFO</strong> (<strong>F</strong>irst-<strong>I</strong>n-<strong>F</strong>irst-<strong>O</strong>ut) or "First Come, First Served." If you book your movie ticket first, you get the seats first.</p>
<ul>
<li><p><strong>Enqueue:</strong> The action of inserting an element into the queue (joining the line).</p>
</li>
<li><p><strong>Dequeue:</strong> The action of removing an element from the queue (leaving the line).</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769445592946/df796400-c97d-4bd2-b73f-1d8993a9d538.jpeg" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h4 id="heading-types-of-queues"><strong>Types of Queues</strong></h4>
<ul>
<li><p><strong>Linear Queue:</strong> This is the standard road traffic. You enter at the back and leave at the front.</p>
</li>
<li><p><strong>Circular Queue:</strong> Imagine the road connects back to the beginning. If the end of the queue is reached, but there is empty space at the start (because cars have left), new elements can wrap around to the beginning.</p>
</li>
<li><p><strong>Priority Queue:</strong> Imagine you are in a hurry to reach a destination. Because reaching that place is a priority, you go faster. Example: an ambulance bypassing traffic. In data structures, if an item is tagged with "high priority," it is processed before others, regardless of when it arrived.</p>
</li>
</ul>
<p><strong>Common Applications:</strong> Queues are used when tasks do not need to be done immediately but must be organized. Examples include <strong>mailing services</strong>, <strong>printer buffers</strong>, and <strong>CPU scheduling</strong>.</p>
]]></content:encoded></item><item><title><![CDATA[Choosing the Right Color Palette]]></title><description><![CDATA[Choosing the right colors is critical. More than just aesthetics, your visual choices set the mood and tone of the entire experience. A well-chosen palette can make the simplest web app look polished and professional, playing a key role in creating u...]]></description><link>https://blog.harshnahaswani.com/choosing-the-right-color-palette</link><guid isPermaLink="true">https://blog.harshnahaswani.com/choosing-the-right-color-palette</guid><category><![CDATA[colortheory]]></category><category><![CDATA[color]]></category><category><![CDATA[psychology]]></category><category><![CDATA[UI]]></category><category><![CDATA[UX]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[color psychology]]></category><category><![CDATA[conversion]]></category><dc:creator><![CDATA[Harshna Haswani]]></dc:creator><pubDate>Wed, 30 Sep 2020 06:30:00 GMT</pubDate><content:encoded><![CDATA[<p>Choosing the right colors is critical. More than just aesthetics, your visual choices set the mood and tone of the entire experience. A well-chosen palette can make the simplest web app look polished and professional, playing a key role in creating user-friendly websites.</p>
<h3 id="heading-the-science-of-color-theory">The Science of Color Theory</h3>
<p>While intuition plays a role, color theory provides the roadmap. At its core is the <strong>Color Wheel</strong>, a visual organization of color hues around a circle. Understanding the relationships between these hues is the first step to intentional design.</p>
<ul>
<li><p><strong>Primary Colors</strong>: Red, Blue, and Yellow. These are the roots of every other color and cannot be created by mixing.</p>
</li>
<li><p><strong>Secondary Colors</strong>: Green, Orange, and Purple. These are formed by mixing two primary colors.</p>
</li>
<li><p><strong>Tertiary Colors</strong>: The result of mixing a primary and a secondary color, giving us complex hues like blue-green or red-orange.</p>
</li>
</ul>
<p>Beyond the wheel, you have <strong>Temperature</strong>. Warm colors (reds, oranges, yellows) tend to evoke energy and action, while cool colors (blues, greens, purples) generally provide a sense of calm and professionalism.</p>
<h3 id="heading-decoding-color-psychology">Decoding Color Psychology</h3>
<p>Colors do more than look good; they trigger specific psychological responses in your users. This is crucial for branding and conversion.</p>
<ul>
<li><p><strong>Blue</strong>: Trust, security, and stability. This is why it is the dominant color in fintech and social media apps.</p>
</li>
<li><p><strong>Red</strong>: Urgency, excitement, and passion. It is perfect for "Buy Now" buttons or error states but can be overwhelming if overused.</p>
</li>
<li><p><strong>Green</strong>: Growth, health, and success. It is the universal sign for "correct" or "completed" actions.</p>
</li>
<li><p><strong>Black &amp; White</strong>: Sophistication and clarity. Luxury brands often use monochrome palettes to let the product speak for itself. Example: Apple Airpods site</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769439966892/203c1423-75a3-4ab5-b444-a5f192fb8b07.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-mastering-schemes-monotone-and-duotone">Mastering Schemes: Monotone and Duotone</h3>
<p>When in doubt, simplifying your palette can lead to the most striking results.</p>
<p><strong>Monotone (Monochromatic)</strong> This scheme uses a single base hue but varies the saturation and lightness to create different shades and tints. It is arguably the safest way to design because the colors are guaranteed to match. It creates a clean, cohesive look that is very easy on the eyes.</p>
<p><strong>Duotone</strong> As the name implies, this relies on two contrasting colors. It was popularized by Spotify and is excellent for making a bold statement.</p>
<h3 id="heading-the-golden-rule-of-balance-60-30-10">The Golden Rule of Balance: 60-30-10</h3>
<p>Once you have your colors, how do you apply them without making the UI look chaotic? The interior design rule of <strong>60-30-10</strong> works perfectly for web interfaces:</p>
<ol>
<li><p><strong>60% Primary Color</strong>: This is your neutral background (whites, soft grays, or dark mode bases). It sets the stage.</p>
</li>
<li><p><strong>30% Secondary Color</strong>: This is your brand color. Use it for cards, headers, or distinct sections to create visual interest.</p>
</li>
<li><p><strong>10% Accent Color</strong>: This is your "Call to Action" color. Use it strictly for buttons, links, and critical notifications to draw the eye immediately.</p>
</li>
</ol>
<h3 id="heading-handling-bright-colors">Handling Bright Colors</h3>
<p>While they are fun, bright colors should be used sparsely. Staring at highly saturated colors for long periods causes eye strain and fatigue. However, they are powerful tools when used correctly.</p>
<p>Because our eyes are naturally drawn to sharp, bright colors, they make excellent highlighters. Yellow, Orange, Red, and Neon are best used as accents. Their muted versions can be used for backgrounds to soften the page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769438697986/709d6c25-e0f3-49a8-b018-2a8586b7e922.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-designing-for-comfort-low-blue-light">Designing for Comfort: Low Blue Light</h3>
<p>Ever wondered why "Dark Mode" feels so much more comfortable? Colors with high levels of blue light often cause eye strain. This is why a stark white background can be tiring to look at.</p>
<p>Instead, try using tinted or neutral shades for your backgrounds. The key is to minimize green and blue tones in your base colors to create a softer, more readable experience.</p>
<h3 id="heading-tools-to-help-you-choose">Tools to Help You Choose</h3>
<p>You do not need to guess. There are powerful tools that can generate accessible and harmonious palettes for you:</p>
<ul>
<li><p><a target="_blank" href="https://color.adobe.com/create/color-wheel"><strong>Adobe Color</strong></a>: The industry standard. It allows you to browse trends, extract themes from images, and check contrast ratios to ensure your text is readable.</p>
</li>
<li><p><a target="_blank" href="https://www.canva.com/colors/color-palette-generator/"><strong>Canva</strong></a>: Great for beginners. Their color palette generator can instantly pull the dominant colors from a photo you plan to use on your site.</p>
</li>
<li><p><a target="_blank" href="https://coolors.co/"><strong>Coolors</strong></a>: A super fast generator that lets you lock in colors you like and hit the spacebar to cycle through matching options until you find the perfect set.</p>
</li>
</ul>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>The key is simply to find the right level of contrast between the text and the background. Just remember to choose pleasing colors that serve a purpose.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769439677334/4d2bc0ce-1e47-499d-afcb-0e8ef0c669be.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-references">References:</h3>
<ol>
<li><p>Color Theory:</p>
<ol>
<li><p><a target="_blank" href="https://www.canva.com/colors/color-wheel/">https://www.canva.com/colors/color-wheel/</a> (blog)</p>
</li>
<li><p><a target="_blank" href="https://www.canva.com/design-school/resources/understanding-color-theory#/">https://www.canva.com/design-school/resources/understanding-color-theory#/</a> (Video)</p>
</li>
</ol>
</li>
<li><p>Color Meanings:</p>
<ol>
<li><p><a target="_blank" href="https://www.canva.com/colors/color-meanings/">https://www.canva.com/colors/color-meanings/</a></p>
</li>
<li><p><a target="_blank" href="https://www.adobe.com/creativecloud/design/discover/color-meaning.html#/">https://www.adobe.com/creativecloud/design/discover/color-meaning.html#/</a></p>
</li>
</ol>
</li>
<li><p><a target="_blank" href="https://www.videomaker.com/how-to/editing/color-correction/adobe-color/#/">https://www.videomaker.com/how-to/editing/color-correction/adobe-color/#/</a></p>
</li>
<li><p><a target="_blank" href="https://www.digitalsilk.com/digital-trends/neon-color-palettes">https://www.digitalsilk.com/digital-trends/neon-color-palettes</a></p>
</li>
<li><p><a target="_blank" href="https://lifestyle.sustainability-directory.com/learn/why-do-highly-saturated-colors-cause-visual-fatigue/#/">https://lifestyle.sustainability-directory.com/learn/why-do-highly-saturated-colors-cause-visual-fatigue/#/</a></p>
</li>
</ol>
]]></content:encoded></item></channel></rss>