<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: chittaranjan nivargi</title>
    <description>The latest articles on DEV Community by chittaranjan nivargi (@cnivargi).</description>
    <link>https://dev.to/cnivargi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3598078%2F481b301e-fcf9-4325-81ef-ca574cf0d422.jpg</url>
      <title>DEV Community: chittaranjan nivargi</title>
      <link>https://dev.to/cnivargi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cnivargi"/>
    <language>en</language>
    <item>
      <title>How I Cut My Site Load Time From 10s to 2s With 3 Simple Script Changes</title>
      <dc:creator>chittaranjan nivargi</dc:creator>
      <pubDate>Sun, 21 Dec 2025 19:24:31 +0000</pubDate>
      <link>https://dev.to/cnivargi/how-i-cut-my-site-load-time-from-10s-to-2s-with-3-simple-script-changes-hlb</link>
      <guid>https://dev.to/cnivargi/how-i-cut-my-site-load-time-from-10s-to-2s-with-3-simple-script-changes-hlb</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: My Financial Tools Site Was Painfully Slow
&lt;/h2&gt;

&lt;p&gt;I run &lt;a href="https://www.toolsforindia.com/" rel="noopener noreferrer"&gt;ToolsForIndia.com&lt;/a&gt; - a collection of financial calculators (SIP, PPF, EMI, etc.) for Indian users. Simple static HTML pages. No frameworks. No fancy stuff.&lt;/p&gt;

&lt;p&gt;But PageSpeed Insights was giving me a 35/100 score.&lt;/p&gt;

&lt;p&gt;Initial load time: 10.2 seconds on 3G.&lt;/p&gt;

&lt;p&gt;For users in India (where 3G is still common), this was unusable.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Was Causing the Slowdown?
&lt;/h2&gt;

&lt;p&gt;I ran Chrome DevTools Performance tab and found the culprits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tailwind CDN (blocking): 4.8 seconds&lt;/li&gt;
&lt;li&gt;Google Analytics (blocking): 2.1 seconds&lt;/li&gt;
&lt;li&gt;Email capture script (blocking): 1.9 seconds&lt;/li&gt;
&lt;li&gt;AdSense (already async): 0.8 seconds&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total blocking time: ~9 seconds before any content rendered.&lt;br&gt;
The irony? All three scripts were non-critical for initial page render.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Fix: Defer Everything That's Not Critical
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Fix #1: Defer Tailwind CSS (Biggest Impact)
&lt;/h3&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="https://cdn.tailwindcss.com"&amp;gt;&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script defer src="https://cdn.tailwindcss.com"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What &lt;code&gt;defer&lt;/code&gt; does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Script downloads in parallel with HTML parsing&lt;/li&gt;
&lt;li&gt;Executes after HTML parsing completes&lt;/li&gt;
&lt;li&gt;Maintains execution order (important for dependencies)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Impact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load time: 10.2s → 5.4s (saved 4.8 seconds)&lt;/li&gt;
&lt;li&gt;First Contentful Paint improved by 60%&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Fix #2: Defer Google Analytics
&lt;/h3&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXX"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
    gtag('config', 'G-XXXXXX');
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
window.addEventListener('load', function() {
    var script = document.createElement('script');
    script.async = true;
    script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXX';
    document.head.appendChild(script);

    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
    gtag('config', 'G-XXXXXX');
});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why this approach:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Analytics loads after the page fully loads&lt;/li&gt;
&lt;li&gt;User sees content immediately&lt;/li&gt;
&lt;li&gt;No impact on tracking (slight delay is acceptable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Impact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load time: 5.4s → 3.3s (saved 2.1 seconds)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Fix #3: Defer Email Capture Script
&lt;/h3&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html&amp;lt;script async src="https://eomail5.com/form/FORM_ID.js" 
        data-form="FORM_ID"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html&amp;lt;script&amp;gt;
window.addEventListener('load', function() {
    var script = document.createElement('script');
    script.async = true;
    script.src = 'https://eomail5.com/form/FORM_ID.js';
    script.setAttribute('data-form', 'FORM_ID');
    document.head.appendChild(script);
});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Impact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load time: 3.3s → 2.1s (saved 1.2 seconds)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbv6ozxpseklp16bae27u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbv6ozxpseklp16bae27u.png" alt="Final Results" width="757" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;async&lt;/code&gt; vs &lt;code&gt;defer&lt;/code&gt; vs Manual Loading
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- async: Downloads in parallel, executes ASAP (may block) --&amp;gt;
&amp;lt;script async src="script.js"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;!-- defer: Downloads in parallel, executes after HTML parse --&amp;gt;
&amp;lt;script defer src="script.js"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;!-- Manual: Full control, loads after page ready --&amp;gt;
&amp;lt;script&amp;gt;
window.addEventListener('load', function() {
    // Load script here
});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When to use what:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Critical CSS/JS: Inline or normal blocking&lt;/li&gt;
&lt;li&gt;UI frameworks (Tailwind, Bootstrap): defer&lt;/li&gt;
&lt;li&gt;Analytics, ads: Manual load after page ready&lt;/li&gt;
&lt;li&gt;Third-party widgets: Manual load (lowest priority)&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;The "Template Header" Approach&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since I have 20+ HTML pages, I don't want to update each one individually.&lt;br&gt;
Solution: Create a reusable header template with all fixes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- header-template.html --&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;

    &amp;lt;!-- Deferred Tailwind --&amp;gt;
    &amp;lt;script defer src="https://cdn.tailwindcss.com"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;!-- Deferred Analytics --&amp;gt;
    &amp;lt;script&amp;gt;
    window.addEventListener('load', function() {
        if (localStorage.getItem('analytics-consent') === 'granted') {
            var script = document.createElement('script');
            script.async = true;
            script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXX';
            document.head.appendChild(script);

            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', 'G-XXXXXX');
        }
    });
    &amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy-paste this into all pages. One-time fix, massive impact.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Test on Real Devices&lt;br&gt;
PageSpeed Insights is great, but test on actual 3G/4G connections.&lt;br&gt;
I used Chrome DevTools:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open DevTools → Network tab&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Throttling dropdown → "Slow 3G"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hard refresh (Cmd+Shift+R)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This showed me the real user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Learned NOT to Do
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Don't defer critical CSS
&lt;/h4&gt;

&lt;p&gt;I tried deferring ALL CSS initially. Result: FOUC (Flash of Unstyled Content) that looked terrible.&lt;br&gt;
Rule: If CSS is needed for above-the-fold content, keep it blocking or inline it.&lt;/p&gt;
&lt;h4&gt;
  
  
  Don't break dependencies
&lt;/h4&gt;

&lt;p&gt;Tailwind needs to load before custom Tailwind classes work. Using &lt;code&gt;async&lt;/code&gt; instead of &lt;code&gt;defer&lt;/code&gt; caused race conditions.&lt;br&gt;
Rule: Use &lt;code&gt;defer&lt;/code&gt; for scripts with dependencies; they execute in order.&lt;/p&gt;
&lt;h4&gt;
  
  
  Don't over-optimize
&lt;/h4&gt;

&lt;p&gt;I tried code-splitting, tree-shaking Tailwind, etc. Not worth it for a simple site.&lt;br&gt;
Rule: Fix the low-hanging fruit first. 80/20 principle.&lt;/p&gt;
&lt;h3&gt;
  
  
  The "Good Enough" Philosophy
&lt;/h3&gt;

&lt;p&gt;Before: Chasing 100/100 PageSpeed score with complex build tools.&lt;br&gt;
After: 92/100 with 3 script changes. Good enough.&lt;br&gt;
My site is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static HTML (no SSR, no frameworks)&lt;/li&gt;
&lt;li&gt;Uses CDN scripts (no build process)&lt;/li&gt;
&lt;li&gt;Easy to maintain (no webpack config hell)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now it's fast.&lt;/p&gt;


&lt;h3&gt;
  
  
  Your Turn: Quick Audit Checklist
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Run PageSpeed Insights on your site&lt;/li&gt;
&lt;li&gt;Check "Reduce JavaScript execution time" section&lt;/li&gt;
&lt;li&gt;Identify blocking scripts:

&lt;ul&gt;
&lt;li&gt;Analytics? → Defer after page load&lt;/li&gt;
&lt;li&gt;UI frameworks (Tailwind, Bootstrap)? → Add &lt;code&gt;defer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Third-party widgets? → Load last&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Test on slow connection (Chrome DevTools throttling)&lt;/li&gt;
&lt;li&gt;Re-run PageSpeed → Enjoy your 50+ point improvement&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  Code Repository
&lt;/h3&gt;

&lt;p&gt;I've open-sourced the before/after versions:&lt;br&gt;
Before (slow):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;script src="https://cdn.tailwindcss.com"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script async src="https://www.googletagmanager.com/gtag/js?id=G-XXX"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script async src="https://eomail5.com/form/XXX.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;!-- Content --&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After (fast):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;!-- Defer Tailwind --&amp;gt;
    &amp;lt;script defer src="https://cdn.tailwindcss.com"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;!-- Defer Analytics --&amp;gt;
    &amp;lt;script&amp;gt;
    window.addEventListener('load', function() {
        var ga = document.createElement('script');
        ga.async = true;
        ga.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXX';
        document.head.appendChild(ga);

        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'G-XXX');
    });
    &amp;lt;/script&amp;gt;

    &amp;lt;!-- Defer Email Script --&amp;gt;
    &amp;lt;script&amp;gt;
    window.addEventListener('load', function() {
        var email = document.createElement('script');
        email.async = true;
        email.src = 'https://eomail5.com/form/XXX.js';
        email.setAttribute('data-form', 'XXX');
        document.head.appendChild(email);
    });
    &amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;!-- Content --&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You don't need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Webpack&lt;/li&gt;
&lt;li&gt;Build tools&lt;/li&gt;
&lt;li&gt;Framework migrations&lt;/li&gt;
&lt;li&gt;Complex optimizations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You just need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand defer vs async&lt;/li&gt;
&lt;li&gt;Defer non-critical scripts&lt;/li&gt;
&lt;li&gt;Test on real connections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: 79% faster load time with 3 changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;What's your biggest site speed bottleneck? Share in the comments!&lt;/p&gt;




&lt;p&gt;Building financial tools for 1 billion Indians at &lt;a href="https://www.toolsforindia.com/" rel="noopener noreferrer"&gt;ToolsForIndia.com&lt;/a&gt;. Follow for more performance tips and dev lessons.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Meet the Creator: Chittaranjan Gopalrao Nivargi&lt;br&gt;
Founder &amp;amp; Creator&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>performance</category>
      <category>webperf</category>
    </item>
    <item>
      <title>Why We Ditched React and Built Financial Calculators in Vanilla JavaScript (And How It Made Everything Better)</title>
      <dc:creator>chittaranjan nivargi</dc:creator>
      <pubDate>Sat, 15 Nov 2025 12:37:04 +0000</pubDate>
      <link>https://dev.to/cnivargi/why-we-ditched-react-and-built-financial-calculators-in-vanilla-javascript-and-how-it-made-2nl</link>
      <guid>https://dev.to/cnivargi/why-we-ditched-react-and-built-financial-calculators-in-vanilla-javascript-and-how-it-made-2nl</guid>
      <description>&lt;h3&gt;
  
  
  The Framework Trap
&lt;/h3&gt;

&lt;p&gt;Every developer has been there. You start a new project and immediately reach for your favorite framework:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app my-calculator
# Installing 1,453 packages...
# 3 minutes later...
# node_modules folder: 289 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three minutes and 289 MB later, you have a "Hello World" that takes 2 seconds to load on 3G.&lt;br&gt;
For a calculator.&lt;br&gt;
When we started building ToolsForIndia.com, we almost fell into the same trap. But then we asked a simple question:&lt;br&gt;
"Do we actually need React for this?"&lt;br&gt;
Spoiler: We didn't. And going vanilla JavaScript made everything faster, simpler, and more maintainable.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Requirements: What We Actually Needed
&lt;/h3&gt;

&lt;p&gt;Before choosing our tech stack, we listed our actual requirements:&lt;br&gt;
Must Have:&lt;/p&gt;

&lt;p&gt;✅ Fast load times (&amp;lt; 1 second on 3G)&lt;br&gt;
✅ Client-side calculations (privacy)&lt;br&gt;
✅ Export to PDF/CSV&lt;br&gt;
✅ URL parameter sharing&lt;br&gt;
✅ Mobile responsive&lt;br&gt;
✅ Works offline (after first load)&lt;/p&gt;

&lt;p&gt;Nice to Have:&lt;/p&gt;

&lt;p&gt;✅ Charts and visualizations&lt;br&gt;
✅ Dark mode (future)&lt;br&gt;
✅ Multiple language support (future)&lt;/p&gt;

&lt;p&gt;Don't Need:&lt;/p&gt;

&lt;p&gt;❌ Real-time collaboration&lt;br&gt;
❌ Complex state management&lt;br&gt;
❌ Server-side rendering&lt;br&gt;
❌ Database integration (yet)&lt;br&gt;
❌ User authentication (yet)&lt;/p&gt;

&lt;p&gt;Verdict: None of our "must haves" require a framework. We were about to add 289 MB of dependencies for features we didn't need.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Stack: Intentionally Simple
&lt;/h3&gt;

&lt;p&gt;Here's our entire tech stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;!-- Tailwind CSS via CDN --&amp;gt;
    &amp;lt;script src="https://cdn.tailwindcss.com"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;!-- Chart.js for visualizations --&amp;gt;
    &amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;!-- jsPDF for exports --&amp;gt;
    &amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;!-- Pure HTML --&amp;gt;
    &amp;lt;div id="calculator"&amp;gt;
        &amp;lt;!-- No JSX, no virtual DOM, just HTML --&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;script&amp;gt;
        // Pure JavaScript
        // No webpack, no babel, no build step
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total bundle size: ~200 KB&lt;br&gt;
Load time on 3G: &amp;lt; 1 second&lt;br&gt;
Build time: 0 seconds (no build step!)&lt;/p&gt;
&lt;h3&gt;
  
  
  Architecture Decisions: Why Each Choice
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. Vanilla JavaScript Over React
&lt;/h4&gt;

&lt;p&gt;React Approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// React component
import React, { useState, useEffect } from 'react';

function Calculator() {
    const [amount, setAmount] = useState(0);
    const [result, setResult] = useState(0);

    useEffect(() =&amp;gt; {
        calculateResult();
    }, [amount]);

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;input onChange={(e) =&amp;gt; setAmount(e.target.value)} /&amp;gt;
            &amp;lt;p&amp;gt;Result: {result}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Advantages:
&lt;/h4&gt;

&lt;p&gt;✅ No framework overhead&lt;br&gt;
✅ No virtual DOM reconciliation&lt;br&gt;
✅ Direct DOM updates (faster for simple UIs)&lt;br&gt;
✅ No JSX compilation needed&lt;br&gt;
✅ Anyone can read the code (no framework-specific patterns)&lt;/p&gt;
&lt;h4&gt;
  
  
  When React Would Win:
&lt;/h4&gt;

&lt;p&gt;✅ Complex state management&lt;br&gt;
✅ Many component re-renders&lt;br&gt;
✅ Deeply nested component trees&lt;br&gt;
✅ Team already knows React&lt;/p&gt;

&lt;p&gt;For calculators? Vanilla wins.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Tailwind CSS via CDN (Not Compiled)
&lt;/h4&gt;

&lt;p&gt;Controversial choice. Every Tailwind expert will tell you to use PostCSS and PurgeCSS to remove unused classes.&lt;br&gt;
We use the full CDN version.&lt;br&gt;
Why?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- Our approach: One CDN link --&amp;gt;
&amp;lt;script src="https://cdn.tailwindcss.com"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- 50 KB gzipped, cached across sites --&amp;gt;

&amp;lt;!-- "Proper" approach: --&amp;gt;
&amp;lt;!-- Install Node.js, npm, PostCSS, PurgeCSS --&amp;gt;
&amp;lt;!-- Configure build pipeline --&amp;gt;
&amp;lt;!-- Run build on every change --&amp;gt;
&amp;lt;!-- Get 8 KB CSS file --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trade-off: We ship 42 KB extra CSS that's not used.&lt;br&gt;
Gain: Zero build complexity, instant development, CDN caching.&lt;br&gt;
For tools with &amp;lt; 10,000 daily users, this is the right trade-off.&lt;br&gt;
When Compiled Tailwind Wins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High traffic (millions of users)&lt;/li&gt;
&lt;li&gt;Every KB matters&lt;/li&gt;
&lt;li&gt;Have dedicated frontend team&lt;/li&gt;
&lt;li&gt;Can maintain build pipeline&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  3. Client-Side Calculations Only
&lt;/h4&gt;

&lt;p&gt;Every calculation happens in JavaScript. Zero server calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SIP Future Value calculation (runs in browser)
function calculateSIP(monthlyInvestment, returnRate, years) {
    const months = years * 12;
    const monthlyRate = returnRate / 12 / 100;

    const futureValue = monthlyInvestment * 
        (Math.pow(1 + monthlyRate, months) - 1) / 
        monthlyRate * 
        (1 + monthlyRate);

    return Math.round(futureValue);
}

// No API call, no server, no latency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Advantages:
&lt;/h4&gt;

&lt;p&gt;✅ Instant results (no network latency)&lt;br&gt;
✅ Privacy (data never leaves browser)&lt;br&gt;
✅ Works offline (after first page load)&lt;br&gt;
✅ Scales infinitely (no server costs)&lt;br&gt;
✅ No backend maintenance&lt;/p&gt;
&lt;h4&gt;
  
  
  When Server-Side Wins:
&lt;/h4&gt;

&lt;p&gt;✅ Need to persist data&lt;br&gt;
✅ Complex calculations (&amp;gt; 5 seconds)&lt;br&gt;
✅ Multi-user features&lt;br&gt;
✅ Need to update logic without redeploying&lt;/p&gt;

&lt;p&gt;For calculators? Client-side wins every time.&lt;/p&gt;
&lt;h4&gt;
  
  
  4. localStorage for State (Not Redux)
&lt;/h4&gt;

&lt;p&gt;We save user scenarios to localStorage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Save calculation
function saveScenario(name, data) {
    const scenarios = JSON.parse(localStorage.getItem('scenarios') || '[]');
    scenarios.push({ name, data, timestamp: Date.now() });
    localStorage.setItem('scenarios', JSON.stringify(scenarios));
}

// Load scenarios
function loadScenarios() {
    return JSON.parse(localStorage.getItem('scenarios') || '[]');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No Redux. No Context API. No state management library.&lt;/p&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;p&gt;✅ Persists across sessions&lt;br&gt;
✅ No server storage needed&lt;br&gt;
✅ Simple API (2 methods: getItem, setItem)&lt;br&gt;
✅ 5-10 MB storage per domain&lt;/p&gt;

&lt;p&gt;Limitations:&lt;/p&gt;

&lt;p&gt;❌ Can't sync across devices&lt;br&gt;
❌ User can clear it&lt;br&gt;
❌ Not suitable for sensitive data&lt;/p&gt;

&lt;p&gt;For saving calculator scenarios? Perfect fit.&lt;/p&gt;
&lt;h4&gt;
  
  
  5. URL Parameters for Sharing
&lt;/h4&gt;

&lt;p&gt;Want to share a calculation? We encode it in the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Generate shareable URL
function generateShareURL() {
    const params = new URLSearchParams({
        monthly: document.getElementById('monthly').value,
        years: document.getElementById('years').value,
        rate: document.getElementById('rate').value
    });

    return `${window.location.origin}${window.location.pathname}?${params}`;
}

// On page load, read params
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('monthly')) {
    document.getElementById('monthly').value = urlParams.get('monthly');
    calculateResult(); // Auto-calculate
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No database. No shortened URLs. Just URL parameters.&lt;br&gt;
Advantages:&lt;/p&gt;

&lt;p&gt;✅ Works immediately (no backend)&lt;br&gt;
✅ Shareable via any medium&lt;br&gt;
✅ Bookmarkable&lt;br&gt;
✅ SEO-friendly (Google indexes different params)&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;toolsforindia.com/tools/sip-calculator.html?
monthly=10000&amp;amp;years=15&amp;amp;rate=12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyone clicking that link sees your exact calculation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance: The Numbers
&lt;/h3&gt;

&lt;p&gt;We ran Lighthouse audits on our calculators:&lt;/p&gt;

&lt;h4&gt;
  
  
  SIP Calculator:
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Performance: 98/100&lt;br&gt;
Accessibility: 95/100&lt;br&gt;
Best Practices: 100/100&lt;br&gt;
SEO: 100/100&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Load Times (tested on 3G):
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;First Contentful Paint: 0.8s&lt;br&gt;
Time to Interactive: 1.2s&lt;br&gt;
Total Bundle Size: 187 KB&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Compare to typical React app:
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;FCP: 2-3s&lt;br&gt;
TTI: 3-5s&lt;br&gt;
Bundle: 500KB - 2MB&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Development Experience: Surprisingly Good
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No Build Step = Instant Feedback&lt;/li&gt;
&lt;li&gt;Change code → Refresh browser → See result&lt;/li&gt;
&lt;li&gt;No waiting for webpack. No hot module replacement bugs. No build errors.&lt;/li&gt;
&lt;li&gt;Easy Debugging
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Set breakpoint in browser DevTools
// See actual code, not transpiled/minified version
// Step through line by line
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With frameworks, you're debugging transpiled code or source maps. With vanilla JS, you're debugging what actually runs.&lt;br&gt;
Easy Onboarding&lt;br&gt;
New developer joins? They need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;That's it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No React concepts. No webpack config. No babel presets.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-offs: What We Gave Up
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Component Reusability
&lt;/h4&gt;

&lt;p&gt;React components are easy to reuse. With vanilla JS, we copy-paste more.&lt;br&gt;
Our solution: Keep components small and independent.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. State Management at Scale
&lt;/h4&gt;

&lt;p&gt;If we had 50+ interactive elements with complex dependencies, Redux would help.&lt;br&gt;
Current reality: Each calculator has ~10 inputs. Vanilla JS handles this fine.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Developer Ecosystem
&lt;/h4&gt;

&lt;p&gt;React has thousands of ready-made components (date pickers, charts, etc.).&lt;br&gt;
Our solution: Use vanilla JS libraries (Chart.js, jsPDF) that don't require frameworks.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Testing
&lt;/h4&gt;

&lt;p&gt;React has great testing tools (Jest, React Testing Library).&lt;br&gt;
Our solution: Manual testing for now. Will add vanilla JS tests when needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  When You Should Use a Framework
&lt;/h3&gt;

&lt;p&gt;Don't take this as "frameworks are bad." Use them when:&lt;br&gt;
✅ Building complex SPAs with many views&lt;br&gt;
✅ Team already knows the framework&lt;br&gt;
✅ Need component reusability at scale&lt;br&gt;
✅ Complex state management (&amp;gt;50 pieces of state)&lt;br&gt;
✅ Real-time features&lt;br&gt;
✅ Need server-side rendering&lt;br&gt;
For calculators, landing pages, blogs, small tools? Vanilla JS is often the better choice.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Results: User Feedback
&lt;/h4&gt;

&lt;p&gt;After 30 days:&lt;br&gt;
Speed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Finally, a calculator that loads instantly!"&lt;/li&gt;
&lt;li&gt;"Works great even on slow internet"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Privacy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Love that it doesn't need signup"&lt;/li&gt;
&lt;li&gt;"Calculation happens in browser = my data stays private"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simplicity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Clean design, no clutter"&lt;/li&gt;
&lt;li&gt;"Just does what it says"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Question Framework Defaults
Just because everyone uses React doesn't mean you need it. Evaluate based on requirements, not trends.&lt;/li&gt;
&lt;li&gt;Optimize for Real Users
Our users are in India, often on 3G. Fast load times &amp;gt; fancy animations.&lt;/li&gt;
&lt;li&gt;Ship First, Optimize Later
We shipped with CDN Tailwind. If we hit 100K users, we'll compile it. Don't optimize prematurely.&lt;/li&gt;
&lt;li&gt;Constraints Breed Creativity
No frameworks forced us to write cleaner, more intentional code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Code: Open for Inspection
&lt;/h3&gt;

&lt;p&gt;Visit any calculator page, right-click, "View Page Source."&lt;br&gt;
You'll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Readable HTML&lt;/li&gt;
&lt;li&gt;Understandable JavaScript&lt;/li&gt;
&lt;li&gt;No minification (yet)&lt;/li&gt;
&lt;li&gt;No obfuscation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is intentional. We want developers to learn from our code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try It Yourself
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;SIP Calculator: &lt;a href="https://dev.tourl"&gt;toolsforindia.com/tools/sip-calculator.html&lt;/a&gt;&lt;br&gt;
PPF Calculator: &lt;a href="https://dev.tourl"&gt;toolsforindia.com/tools/ppf-calculator.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open DevTools. Check the Network tab. See the load times yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion: Right Tool for the Job
&lt;/h3&gt;

&lt;p&gt;React is amazing for what it does. But it's not always the right tool.&lt;br&gt;
For ToolsForIndia.com, vanilla JavaScript gave us:&lt;/p&gt;

&lt;p&gt;⚡ 5x faster load times&lt;br&gt;
🔒 Better privacy (client-side only)&lt;br&gt;
🧹 Simpler codebase&lt;br&gt;
💰 Lower complexity cost&lt;/p&gt;

&lt;p&gt;Would we use React for a complex dashboard with 100+ components? Absolutely.&lt;br&gt;
For calculators? Vanilla JS wins.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Building ToolsForIndia.com with intentionally simple tech. Previously worked with React/Vue/Angular. Now shipping faster with vanilla JS. Believes in using the simplest tool that solves the problem.&lt;br&gt;
🔗 &lt;a href="https://dev.tourl"&gt;toolsforindia.com&lt;/a&gt;&lt;br&gt;
🐦 @toolsforindia&lt;br&gt;
📧 &lt;a href="https://dev.tourl"&gt;contact@toolsforindia.com&lt;/a&gt;&lt;br&gt;
P.S. View source on our calculators. All code is unminified and readable. Learn, copy, improve.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>Building a Password Checker with Vanilla JS (No Frameworks!)</title>
      <dc:creator>chittaranjan nivargi</dc:creator>
      <pubDate>Thu, 06 Nov 2025 06:14:03 +0000</pubDate>
      <link>https://dev.to/cnivargi/building-a-password-checker-with-vanilla-js-no-frameworks-1ofo</link>
      <guid>https://dev.to/cnivargi/building-a-password-checker-with-vanilla-js-no-frameworks-1ofo</guid>
      <description>&lt;h2&gt;
  
  
  I got locked out of the GST portal for the third time because my password "didn't meet requirements."
&lt;/h2&gt;

&lt;p&gt;No explanation. Just "Invalid password."&lt;br&gt;
After wasting 30 minutes trying different passwords, I decided to build a tool that would tell me exactly what was wrong with my password for different government portals.&lt;br&gt;
Result: PasswordChecker.in - Built in 2 weekends with vanilla JavaScript. No React. No Vue. No frameworks.&lt;br&gt;
Here's how I did it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why No Frameworks?
&lt;/h2&gt;

&lt;p&gt;Speed. I wanted the tool to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast to load (&amp;lt; 50KB total)&lt;/li&gt;
&lt;li&gt;Fast to build (2 weekends)&lt;/li&gt;
&lt;li&gt;Fast to run (real-time password checking)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adding React would add 130KB+ before I wrote a single line of code. For a single-page tool, that's overkill.&lt;/p&gt;

&lt;p&gt;The Tech Stack&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Frontend: Vanilla JavaScript + Tailwind CSS&lt;/li&gt;
&lt;li&gt;Styling: Tailwind CDN (for rapid prototyping)&lt;/li&gt;
&lt;li&gt;API: Have I Been Pwned (k-Anonymity model)&lt;/li&gt;
&lt;li&gt;Hosting: Vercel&lt;/li&gt;
&lt;li&gt;Source Control: GitHub&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Total bundle size: ~48KB (including Tailwind)&lt;br&gt;
Feature #1: Real-Time Password Strength Checker&lt;br&gt;
The Requirements&lt;br&gt;
Different Indian government portals have different password requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const portalRequirements = {
  uidai: {
    minLength: 8,
    requiresUppercase: true,
    requiresLowercase: true,
    requiresNumber: true,
    requiresSpecial: true,
  },
  gstn: {
    minLength: 10,
    requiresUppercase: true,
    requiresLowercase: true,
    requiresNumber: true,
    requiresSpecial: true,
    maxLength: 15,
  },
  incomeTax: {
    minLength: 12,
    requiresUppercase: true,
    requiresLowercase: true,
    requiresNumber: true,
    requiresSpecial: true,
    maxLength: 14,
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Core Logic
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function checkPasswordStrength(password) {
  const checks = {
    length: password.length &amp;gt;= 8,
    uppercase: /[A-Z]/.test(password),
    lowercase: /[a-z]/.test(password),
    number: /[0-9]/.test(password),
    special: /[!@#$%^&amp;amp;*()_+\-=\[\]{};':"\\|,.&amp;lt;&amp;gt;\/?]/.test(password),
  };

  // Calculate score (0-100)
  let score = 0;
  if (checks.length) score += 20;
  if (checks.uppercase) score += 20;
  if (checks.lowercase) score += 20;
  if (checks.number) score += 20;
  if (checks.special) score += 20;

  // Additional points for length
  if (password.length &amp;gt;= 12) score += 10;
  if (password.length &amp;gt;= 16) score += 10;

  return {
    score: Math.min(score, 100),
    checks,
    strength: getStrengthLabel(score),
  };
}

function getStrengthLabel(score) {
  if (score &amp;gt;= 80) return 'Very Strong';
  if (score &amp;gt;= 60) return 'Strong';
  if (score &amp;gt;= 40) return 'Moderate';
  if (score &amp;gt;= 20) return 'Weak';
  return 'Very Weak';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-Time Updates
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const passwordInput = document.getElementById('password');
const strengthMeter = document.getElementById('strength-meter');

passwordInput.addEventListener('input', (e) =&amp;gt; {
  const password = e.target.value;
  const result = checkPasswordStrength(password);

  // Update UI
  updateStrengthMeter(result);
  updateChecklist(result.checks);
  checkPortalCompliance(password);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Feature #2: Password Generator
&lt;/h2&gt;

&lt;p&gt;Users needed passwords that were:&lt;/p&gt;

&lt;p&gt;Strong (meets requirements)&lt;br&gt;
Memorable (not random gibberish)&lt;br&gt;
Portal-specific (works for UIDAI, GST, etc.)&lt;/p&gt;

&lt;p&gt;The Memorable Password Algorithm&lt;br&gt;
Instead of generating &lt;code&gt;xK9!mP2@&lt;/code&gt;, generate &lt;code&gt;Quick7Tiger$42&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function generateReadablePassword(requirements) {
  const adjectives = ['Quick', 'Brave', 'Smart', 'Cool', 'Swift'];
  const nouns = ['Tiger', 'Eagle', 'Wolf', 'Lion', 'Hawk'];
  const specials = ['@', '#', '$', '%', '&amp;amp;', '*'];

  const adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
  const number1 = Math.floor(Math.random() * 9) + 1;
  const noun = nouns[Math.floor(Math.random() * nouns.length)];
  const special = specials[Math.floor(Math.random() * specials.length)];
  const number2 = Math.floor(Math.random() * 90) + 10;

  let password = `${adjective}${number1}${noun}${special}${number2}`;

  // Adjust for portal requirements
  if (requirements.minLength &amp;gt; password.length) {
    password += Math.random().toString(36).substring(2, requirements.minLength - password.length + 2);
  }

  return password;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: Passwords like &lt;code&gt;Smart5Wolf@73&lt;/code&gt; - easy to remember, meets all requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature #3: Data Breach Checker
&lt;/h2&gt;

&lt;p&gt;This was the most complex feature. I needed to check if a password appeared in data breaches without sending the password to any server.&lt;br&gt;
The k-Anonymity Model&lt;br&gt;
Have I Been Pwned (HIBP) uses k-Anonymity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hash the password (SHA-1)&lt;/li&gt;
&lt;li&gt;Send only the first 5 characters of the hash&lt;/li&gt;
&lt;li&gt;Server returns all breached passwords matching those 5 chars&lt;/li&gt;
&lt;li&gt;Client checks if full hash is in the list
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function checkPasswordBreach(password) {
  // Hash password
  const hash = await sha1(password);
  const prefix = hash.substring(0, 5);
  const suffix = hash.substring(5).toUpperCase();

  // Query HIBP API (only send first 5 chars)
  const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`);
  const data = await response.text();

  // Check if full hash is in response
  const hashes = data.split('\n');
  for (let line of hashes) {
    const [hashSuffix, count] = line.split(':');
    if (hashSuffix === suffix) {
      return {
        found: true,
        count: parseInt(count),
      };
    }
  }

  return { found: false };
}

// SHA-1 implementation
async function sha1(str) {
  const buffer = new TextEncoder().encode(str);
  const hash = await crypto.subtle.digest('SHA-1', buffer);
  const hashArray = Array.from(new Uint8Array(hash));
  return hashArray.map(b =&amp;gt; b.toString(16).padStart(2, '0')).join('');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Privacy: Your password never leaves your browser. Only the first 5 characters of its hash are sent.&lt;/p&gt;
&lt;h2&gt;
  
  
  Feature #4: Multi-Portal Compatibility Test
&lt;/h2&gt;

&lt;p&gt;Test one password against 10 portals simultaneously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testPortalCompatibility(password) {
  const portals = [
    { name: 'UIDAI', requirements: portalRequirements.uidai },
    { name: 'GSTN', requirements: portalRequirements.gstn },
    { name: 'Income Tax', requirements: portalRequirements.incomeTax },
    // ... more portals
  ];

  return portals.map(portal =&amp;gt; ({
    name: portal.name,
    compatible: checkRequirements(password, portal.requirements),
    issues: getIssues(password, portal.requirements),
  }));
}

function checkRequirements(password, requirements) {
  if (password.length &amp;lt; requirements.minLength) return false;
  if (requirements.maxLength &amp;amp;&amp;amp; password.length &amp;gt; requirements.maxLength) return false;
  if (requirements.requiresUppercase &amp;amp;&amp;amp; !/[A-Z]/.test(password)) return false;
  if (requirements.requiresLowercase &amp;amp;&amp;amp; !/[a-z]/.test(password)) return false;
  if (requirements.requiresNumber &amp;amp;&amp;amp; !/[0-9]/.test(password)) return false;
  if (requirements.requiresSpecial &amp;amp;&amp;amp; !/[!@#$%^&amp;amp;*]/.test(password)) return false;

  return true;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Local Development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# No build step!
python -m http.server 8000
# Visit localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git push origin main
# Vercel auto-deploys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No webpack. No babel. No build process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Optimizations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Debounced Input
Don't check password on every keystroke:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() =&amp;gt; func.apply(this, args), wait);
  };
}

const debouncedCheck = debounce((password) =&amp;gt; {
  checkPasswordStrength(password);
}, 300);

passwordInput.addEventListener('input', (e) =&amp;gt; {
  debouncedCheck(e.target.value);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Lazy Load Breach Check
Only check breaches when user clicks "Check for Breaches":
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Don't auto-check (expensive API call)
breachCheckButton.addEventListener('click', async () =&amp;gt; {
  showLoader();
  const result = await checkPasswordBreach(password);
  hideLoader();
  displayResult(result);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Cache API Responses
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const breachCache = new Map();

async function checkPasswordBreach(password) {
  const hash = await sha1(password);
  const prefix = hash.substring(0, 5);

  // Check cache first
  if (breachCache.has(prefix)) {
    return checkHash(breachCache.get(prefix), hash);
  }

  // Fetch and cache
  const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`);
  const data = await response.text();
  breachCache.set(prefix, data);

  return checkHash(data, hash);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Vanilla JS is Fast Enough
&lt;/h3&gt;

&lt;p&gt;For most tools, you don't need a framework. Vanilla JS is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster to load&lt;/li&gt;
&lt;li&gt;Easier to debug&lt;/li&gt;
&lt;li&gt;Fewer dependencies&lt;/li&gt;
&lt;li&gt;More portable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Real-Time Feedback is Critical
&lt;/h3&gt;

&lt;p&gt;Users type fast. Your UI must keep up. Debouncing is your friend.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Privacy Matters
&lt;/h3&gt;

&lt;p&gt;For a password tool, &lt;strong&gt;client-side only&lt;/strong&gt; was non-negotiable. No backend. No database. No tracking.&lt;/p&gt;

&lt;p&gt;Users can inspect the source and verify nothing is sent to any server (except the HIBP API with anonymized data).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Government Standards are Inconsistent
&lt;/h3&gt;

&lt;p&gt;Every portal has different requirements. No standardization. This makes users' lives harder—and creates opportunities for tools like mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Planned Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser extension (auto-fill government portals)&lt;/li&gt;
&lt;li&gt;Bulk password checking (CSV upload)&lt;/li&gt;
&lt;li&gt;API for developers&lt;/li&gt;
&lt;li&gt;Dark mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tech Debt:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add proper testing (currently manually tested)&lt;/li&gt;
&lt;li&gt;Consider Web Components for reusability&lt;/li&gt;
&lt;li&gt;Add service worker for offline support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://www.passwordchecker.in" rel="noopener noreferrer"&gt;passwordchecker.in&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; (Will open-source if there's interest—comment below!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You don't need React to build useful tools. Sometimes vanilla JavaScript is the right choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep it simple&lt;/li&gt;
&lt;li&gt;Client-side for privacy&lt;/li&gt;
&lt;li&gt;Real-time feedback&lt;/li&gt;
&lt;li&gt;Focus on UX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built in 2 weekends. 500+ users in the first week. Zero frameworks.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Questions? Ask in the comments!&lt;/strong&gt;&lt;/p&gt;




</description>
      <category>security</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
