<?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>Forem: Sufal Thakre</title>
    <description>The latest articles on Forem by Sufal Thakre (@sufalthakre).</description>
    <link>https://forem.com/sufalthakre</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%2F3525000%2F6a0090ef-2a2d-451a-af15-5a5bc68e2fca.jpg</url>
      <title>Forem: Sufal Thakre</title>
      <link>https://forem.com/sufalthakre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sufalthakre"/>
    <language>en</language>
    <item>
      <title>🛠️Building a Simple File Organizer CLI Using Node.js</title>
      <dc:creator>Sufal Thakre</dc:creator>
      <pubDate>Thu, 29 Jan 2026 05:44:51 +0000</pubDate>
      <link>https://forem.com/sufalthakre/building-a-simple-file-organizer-cli-using-nodejs-40ko</link>
      <guid>https://forem.com/sufalthakre/building-a-simple-file-organizer-cli-using-nodejs-40ko</guid>
      <description>&lt;p&gt;When working daily on my system, my &lt;strong&gt;Downloads folder keeps getting messy&lt;/strong&gt; — images, PDFs, videos, code files, all mixed together. Manually organizing them again and again felt unnecessary.&lt;/p&gt;

&lt;p&gt;So, as part of an assignment (and also to solve my own problem), I decided to build a &lt;strong&gt;simple command-line utility using Node.js&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ❓ The Problem
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Files from browsers, tools, and apps pile up quickly&lt;/li&gt;
&lt;li&gt;Finding the right file becomes time-consuming&lt;/li&gt;
&lt;li&gt;Manually creating folders and moving files is repetitive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted a &lt;strong&gt;small, reusable tool&lt;/strong&gt; that could do this automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 The Solution
&lt;/h2&gt;

&lt;p&gt;I built a &lt;strong&gt;Node.js CLI tool&lt;/strong&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes a folder path as input&lt;/li&gt;
&lt;li&gt;Reads all files in that folder&lt;/li&gt;
&lt;li&gt;Categorizes them based on file extensions&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creates folders like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Documents&lt;/li&gt;
&lt;li&gt;Videos&lt;/li&gt;
&lt;li&gt;Audio&lt;/li&gt;
&lt;li&gt;Code&lt;/li&gt;
&lt;li&gt;Archives&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Moves files into the correct folders safely&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;It uses &lt;strong&gt;only Node.js standard libraries&lt;/strong&gt; — no external dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node.js&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;JavaScript (ES Modules)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fs&lt;/code&gt; module for file operations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt; module for path handling&lt;/li&gt;
&lt;li&gt;Command-line arguments via &lt;code&gt;process.argv&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ▶️ How to Run the Tool
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"/path/to/your/folder"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"~/Downloads"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool converts the path to an absolute path, validates it, and then starts organizing files.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Design Decisions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Used synchronous file operations for clarity and simplicity&lt;/li&gt;
&lt;li&gt;Converted relative paths to absolute paths to avoid errors&lt;/li&gt;
&lt;li&gt;Stored only necessary data in memory&lt;/li&gt;
&lt;li&gt;Focused on clean and readable logic&lt;/li&gt;
&lt;li&gt;Built as a CLI tool to keep it lightweight and fast&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎥 Demo Video
&lt;/h2&gt;

&lt;p&gt;I also recorded a short demo video explaining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The problem&lt;/li&gt;
&lt;li&gt;How the tool works&lt;/li&gt;
&lt;li&gt;My design choices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📹 Video: &lt;/p&gt;

&lt;h2&gt;
  
  
  

  &lt;iframe src="https://www.youtube.com/embed/kARk33K5XcU"&gt;
  &lt;/iframe&gt;



&lt;/h2&gt;

&lt;h2&gt;
  
  
  📦 Source Code
&lt;/h2&gt;

&lt;p&gt;GitHub Repository:&lt;br&gt;
👉 &lt;a href="https://github.com/Sufalthakre18/file-organizer-cli" rel="noopener noreferrer"&gt;https://github.com/Sufalthakre18/file-organizer-cli&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Working with the filesystem in Node.js&lt;/li&gt;
&lt;li&gt;Handling paths correctly across systems&lt;/li&gt;
&lt;li&gt;Writing CLI-based tools&lt;/li&gt;
&lt;li&gt;Thinking about real-world usability instead of just features&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🙌 Feedback Welcome
&lt;/h2&gt;

&lt;p&gt;This is a simple project, but it solved a real problem for me.&lt;br&gt;
I’d love feedback, suggestions, or ideas to improve it further.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>cli</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Building GigFlow: A Real-Time Freelance Marketplace with Secure Hiring Logic</title>
      <dc:creator>Sufal Thakre</dc:creator>
      <pubDate>Fri, 16 Jan 2026 06:35:56 +0000</pubDate>
      <link>https://forem.com/sufalthakre/building-gigflow-a-real-time-freelance-marketplace-with-secure-hiring-logic-1be7</link>
      <guid>https://forem.com/sufalthakre/building-gigflow-a-real-time-freelance-marketplace-with-secure-hiring-logic-1be7</guid>
      <description>&lt;p&gt;Modern marketplaces aren’t just about CRUD APIs — they’re about &lt;strong&gt;correctness, trust, and real-time feedback&lt;/strong&gt;. In this post, I’ll walk through how I built &lt;strong&gt;GigFlow&lt;/strong&gt;, a full-stack freelance marketplace where clients can post jobs, freelancers can bid, and hiring happens &lt;strong&gt;atomically and in real time&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Video:
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/vCuG7OR1dqE"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;This project focuses heavily on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure authentication&lt;/li&gt;
&lt;li&gt;Correct hiring logic&lt;/li&gt;
&lt;li&gt;Preventing race conditions&lt;/li&gt;
&lt;li&gt;Real-time updates using Socket.io&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 What is GigFlow?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GigFlow&lt;/strong&gt; is a mini freelance marketplace where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any authenticated user can post a gig (client role)&lt;/li&gt;
&lt;li&gt;Any user can bid on gigs (freelancer role)&lt;/li&gt;
&lt;li&gt;A client can hire &lt;strong&gt;exactly one&lt;/strong&gt; freelancer per gig&lt;/li&gt;
&lt;li&gt;All other bids are automatically rejected&lt;/li&gt;
&lt;li&gt;The hired freelancer gets a &lt;strong&gt;real-time notification&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core challenge wasn’t the UI — it was ensuring &lt;strong&gt;data integrity and correctness under concurrency&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧱 Tech Stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;React (Vite)&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;Context API for state management&lt;/li&gt;
&lt;li&gt;Socket.io Client&lt;/li&gt;
&lt;li&gt;Fetch API with credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js + Express&lt;/li&gt;
&lt;li&gt;MongoDB + Mongoose&lt;/li&gt;
&lt;li&gt;JWT Authentication (HttpOnly cookies)&lt;/li&gt;
&lt;li&gt;Socket.io&lt;/li&gt;
&lt;li&gt;MongoDB Transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: Vercel&lt;/li&gt;
&lt;li&gt;Backend: Render&lt;/li&gt;
&lt;li&gt;Database: MongoDB Atlas&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔐 Authentication (JWT + HttpOnly Cookies)
&lt;/h2&gt;

&lt;p&gt;Authentication is handled using &lt;strong&gt;JWT stored in HttpOnly cookies&lt;/strong&gt;, which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents access from JavaScript (XSS-safe)&lt;/li&gt;
&lt;li&gt;Works cleanly with &lt;code&gt;credentials: "include"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verifies the JWT&lt;/li&gt;
&lt;li&gt;Attaches the authenticated user to &lt;code&gt;req.user&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This enables role-less design — users can act as both client and freelancer without separate accounts.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Core Data Models
&lt;/h2&gt;

&lt;h3&gt;
  
  
  User
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gig
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assigned&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;assignedTo&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bid
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;gigId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;freelancerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hired&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rejected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧠 The Hiring Problem (The Hard Part)
&lt;/h2&gt;

&lt;p&gt;The most important rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Only one freelancer can be hired for a gig — ever.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Edge case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two clients (or two tabs) click &lt;strong&gt;Hire&lt;/strong&gt; at the same time&lt;/li&gt;
&lt;li&gt;Without protection, two bids could be marked as hired&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where many systems fail.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 Solving Race Conditions with MongoDB Transactions
&lt;/h2&gt;

&lt;p&gt;The hire logic is wrapped inside a &lt;strong&gt;MongoDB session + transaction&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  High-level flow:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Start a session&lt;/li&gt;
&lt;li&gt;Check if the gig is still &lt;code&gt;open&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update gig → &lt;code&gt;assigned&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mark selected bid → &lt;code&gt;hired&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mark all other bids → &lt;code&gt;rejected&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Commit transaction&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If &lt;strong&gt;any step fails&lt;/strong&gt;, everything rolls back.&lt;/p&gt;

&lt;p&gt;This guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exactly one hired freelancer&lt;/li&gt;
&lt;li&gt;No partial or inconsistent state&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ Real-Time Updates with Socket.io
&lt;/h2&gt;

&lt;p&gt;Once a freelancer is hired:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The backend emits a &lt;code&gt;hired&lt;/code&gt; event to that freelancer’s socket room&lt;/li&gt;
&lt;li&gt;The freelancer’s dashboard updates instantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No polling. No refresh. Instant feedback.&lt;/p&gt;




&lt;h2&gt;
  
  
  🖥️ Frontend Flow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Client
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create gig&lt;/li&gt;
&lt;li&gt;View bids&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Hire&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Gig becomes assigned&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Freelancer
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Submit bids&lt;/li&gt;
&lt;li&gt;Track bid status&lt;/li&gt;
&lt;li&gt;Receive real-time hire notification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;State is kept minimal and predictable using &lt;strong&gt;Context API&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌍 Deployment Lessons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backend (Render)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Environment variables must be explicitly set&lt;/li&gt;
&lt;li&gt;CORS must allow the deployed frontend domain&lt;/li&gt;
&lt;li&gt;Socket.io needs the same CORS config as Express&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Frontend (Vercel)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;API base URL must point to deployed backend&lt;/li&gt;
&lt;li&gt;Fetch requests must include credentials&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 What This Project Demonstrates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Correct API design&lt;/li&gt;
&lt;li&gt;Secure authentication&lt;/li&gt;
&lt;li&gt;Transactional integrity&lt;/li&gt;
&lt;li&gt;Real-time communication&lt;/li&gt;
&lt;li&gt;Production deployment awareness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a “todo app” — it’s a system designed to behave correctly &lt;strong&gt;under pressure&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live App: &lt;a href="https://account-manager-vite.vercel.app/" rel="noopener noreferrer"&gt;https://account-manager-vite.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Repo: &lt;a href="https://github.com/Sufalthakre18/gigflow-fullstack-marketplace" rel="noopener noreferrer"&gt;https://github.com/Sufalthakre18/gigflow-fullstack-marketplace&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Home page : 
&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%2Fkguatntz537ztjxqo0ue.png" alt=" "&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The hardest part of full-stack development isn’t writing code — it’s &lt;strong&gt;making sure the system behaves correctly when multiple things happen at once&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;GigFlow was built with that principle in mind.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build a Modern Weather Forecast App with JavaScript &amp; Tailwind</title>
      <dc:creator>Sufal Thakre</dc:creator>
      <pubDate>Thu, 27 Nov 2025 00:05:51 +0000</pubDate>
      <link>https://forem.com/sufalthakre/build-a-modern-weather-forecast-app-with-javascript-tailwind-38p1</link>
      <guid>https://forem.com/sufalthakre/build-a-modern-weather-forecast-app-with-javascript-tailwind-38p1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A small, real-world project to practice APIs, responsive UI, and frontend polish.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo Video
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/eMVTr1F8nR0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  I built a modern, responsive Weather Forecast App using Vanilla JavaScript and Tailwind CSS, powered by the Open-Meteo API. It supports city search, geolocation, a 5-day forecast, animated UI, a °C/°F toggle (today only), and recent searches saved to localStorage. This article shows why and how I built it, the core code, setup steps, and lessons learned.
&lt;/h2&gt;




&lt;h2&gt;
  
  
  Why build this
&lt;/h2&gt;

&lt;p&gt;Small projects let you practice real problems: async API calls, data normalization, UI updates, responsive layout, and error handling. I wanted a project that looks polished and is practical — useful for a portfolio and for learning frontend fundamentals.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it does (features)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Search weather by city name (geocoding)&lt;/li&gt;
&lt;li&gt;Use browser geolocation to get local weather&lt;/li&gt;
&lt;li&gt;Show current temperature, humidity, wind speed and weather description&lt;/li&gt;
&lt;li&gt;5-day forecast with icons, min/max temps, wind &amp;amp; humidity&lt;/li&gt;
&lt;li&gt;°C / °F toggle (applies to today's temperature)&lt;/li&gt;
&lt;li&gt;Recent searches saved to &lt;code&gt;localStorage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Animated backgrounds (sunny, rainy, cloudy, stormy, snowy)&lt;/li&gt;
&lt;li&gt;Small SVG area charts for wind &amp;amp; humidity&lt;/li&gt;
&lt;li&gt;Graceful error handling with a custom popup (no &lt;code&gt;alert()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript (ES6+)&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;Open-Meteo API (no API key required)&lt;/li&gt;
&lt;li&gt;Static HTML/CSS/JS (no build tools required)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How it works (data flow)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;User searches city → geocoding API returns latitude &amp;amp; longitude.&lt;/li&gt;
&lt;li&gt;Use lat/lon to fetch forecast and current weather from Open-Meteo.&lt;/li&gt;
&lt;li&gt;Normalize fields, update DOM with current and daily data.&lt;/li&gt;
&lt;li&gt;Save city to recent searches (localStorage).&lt;/li&gt;
&lt;li&gt;Handle errors, show popup, and keep UI responsive.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key code snippets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Robust fetch with timeout &amp;amp; validation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchWeatherData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locationName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;temperature_2m,relative_humidity_2m,wind_speed_10m,weathercode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;temperature_2m_max,temperature_2m_min,weathercode,wind_speed_10m_max,relative_humidity_2m_mean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;temperature_unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;celsius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;forecast_days&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;WEATHER_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Weather API error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Current weather section updation in UI&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;currentTemp&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;currentTemperatur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentTemp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;span class="text-4xl md:text-6xl"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_units&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTemp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;locationDisplay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;locationName&lt;/span&gt;
    &lt;span class="nx"&gt;humidityLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relative_humidity_2m&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;
    &lt;span class="nx"&gt;windSpeed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wind_speed_10m&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;km/h`&lt;/span&gt;
    &lt;span class="nx"&gt;weatherCondition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weatherMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weathercode&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="nx"&gt;weatherDescription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weatherMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weathercode&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No data available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="nx"&gt;weatherExplanation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weatherMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weathercode&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="nx"&gt;rawTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


    &lt;span class="c1"&gt;// 5 days forecast updation&lt;/span&gt;
    &lt;span class="nx"&gt;forecastCard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m_min&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minTempUnit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily_units&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m_min&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m_max&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxTempUnit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily_units&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m_max&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wind_speed_10m_max&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;humidity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relative_humidity_2m_mean&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleDateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;weekday&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// weather code for cloude icons &lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weathericon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCloudIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weathercode&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;h3 class="text-xs md:text-sm text-white/60 mb-1"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dayName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/h3&amp;gt;
          &amp;lt;h2 class="text-xs mb-2 md:mb-3 text-white/50"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/h2&amp;gt;
          &amp;lt;i class="fas fa-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weathericon&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; text-3xl md:text-4xl lg:text-5xl my-2 md:my-3" style="text-shadow: 0 0 20px rgba(255,255,255,0.5)"&amp;gt;&amp;lt;/i&amp;gt;
          &amp;lt;div class="flex justify-around text-sm mt-2"&amp;gt;
              &amp;lt;span&amp;gt;&amp;lt;i class="fas fa-thermometer-half mr-1"&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;minTemp&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;minTempUnit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;maxTemp&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;maxTempUnit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;
              &amp;lt;span&amp;gt;&amp;lt;i class="fas fa-wind mr-1"&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;wind&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;km/h&amp;lt;/span&amp;gt;
              &amp;lt;span&amp;gt;&amp;lt;i class="fas fa-droplet mr-1"&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%&amp;lt;/span&amp;gt;
          &amp;lt;/div&amp;gt;`&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nf"&gt;changeWeatherBackground&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weathercode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;updateGraphs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wind_speed_10m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relative_humidity_2m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;locationBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;i class="fas fa-location-arrow mr-2"&amp;gt;&amp;lt;/i&amp;gt;Use My Location&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Extreme temp alerts&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Extreme Heat Alert: Temperature above 40°C! Stay hydrated and avoid direct sun.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature_2m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Extreme Cold Alert: Temperature below 5°C! Dress warmly and limit outdoor exposure.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Weather fetch failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to load weather. Check internet or try again later.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Temperature toggle (today only)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// toggle button for c/f&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rawTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;°C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;toggleUnit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;°C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;°F&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;°C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;°C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawTemp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawTemp&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;currentTemperatur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;span class="text-4xl md:text-6xl"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Challenges &amp;amp; lessons
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;API fields sometimes vary — defensive data normalization was needed.&lt;/li&gt;
&lt;li&gt;Animations caused repaint issues on low-end devices — solved with layers and reduced blur.&lt;/li&gt;
&lt;li&gt;Geolocation permission errors must be handled gracefully for a good UX.&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%2Fa5v3pl4814nxev5e347e.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%2Fa5v3pl4814nxev5e347e.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Source code: &lt;a href="https://github.com/Sufalthakre18/weather-forecast-application" rel="noopener noreferrer"&gt;Github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demo: &lt;a href="https://weather-forecast-application-lyart.vercel.app/" rel="noopener noreferrer"&gt;url&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Built a Full-Stack Student Registration System with Vanilla JS + Tailwind in Just 7 Days (and Made It Fully Responsive)</title>
      <dc:creator>Sufal Thakre</dc:creator>
      <pubDate>Wed, 19 Nov 2025 15:43:37 +0000</pubDate>
      <link>https://forem.com/sufalthakre/i-built-a-full-stack-student-registration-system-with-vanilla-js-tailwind-in-just-7-days-and-cea</link>
      <guid>https://forem.com/sufalthakre/i-built-a-full-stack-student-registration-system-with-vanilla-js-tailwind-in-just-7-days-and-cea</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;“A little progress each day adds up to big results.”&lt;/p&gt;

&lt;p&gt;Last week I finished Internshala’s &lt;strong&gt;Developing Interactive Websites with JavaScript&lt;/strong&gt; course and decided the final assignment wasn’t going to be just another “submit and forget” project.&lt;/p&gt;

&lt;p&gt;So I took the basic requirement and turned it into a &lt;strong&gt;complete CRUD web app&lt;/strong&gt; with validation, localStorage persistence, responsive design, and clean code.&lt;/p&gt;

&lt;p&gt;Live Demo → &lt;a href="https://student-registration-system-one-sandy.vercel.app/index.html" rel="noopener noreferrer"&gt;https://student-registration-system-one-sandy.vercel.app/index.html&lt;/a&gt;&lt;br&gt;&lt;br&gt;
GitHub Repo → &lt;a href="https://github.com/Sufalthakre18/Student-Registration-System" rel="noopener noreferrer"&gt;https://github.com/Sufalthakre18/Student-Registration-System&lt;/a&gt;&lt;br&gt;&lt;br&gt;
(Star it if you like it ⭐)&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Built – Student Registration System
&lt;/h3&gt;

&lt;p&gt;A modern, fully responsive student management system where you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register new students with full form validation&lt;/li&gt;
&lt;li&gt;View all students in a scrollable table&lt;/li&gt;
&lt;li&gt;Edit &amp;amp; Delete records with confirmation&lt;/li&gt;
&lt;li&gt;Everything persists using localStorage (no backend needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features I’m Proud Of
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Form validation (real-time)&lt;/td&gt;
&lt;td&gt;Custom regex + instant error messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No duplicate Student IDs&lt;/td&gt;
&lt;td&gt;Checked against localStorage array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inline editing&lt;/td&gt;
&lt;td&gt;Click → edit → save/cancel without page reload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto scrollbar after 5+ records&lt;/td&gt;
&lt;td&gt;Pure JS height detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100% responsive (mobile-first)&lt;/td&gt;
&lt;td&gt;Tailwind breakpoints: ≤640px → 641-1024px → ≥1025px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clean separation of concerns&lt;/td&gt;
&lt;td&gt;Separate files for utils, validation, UI logic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Tech Stack (All Vanilla – No Frameworks)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;HTML5 + Semantic tags&lt;/li&gt;
&lt;li&gt;Tailwind CSS (via CDN – production ready)&lt;/li&gt;
&lt;li&gt;Vanilla JavaScript (ES6+) – classes, modules, async-like flow&lt;/li&gt;
&lt;li&gt;localStorage for persistence&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Screenshot
&lt;/h3&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%2Fw9y3rnmba3c9ywrkpj1d.gif" 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%2Fw9y3rnmba3c9ywrkpj1d.gif" alt="Home Page " width="200" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Learned (The Real Wins)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deep DOM manipulation without jQuery or React&lt;/li&gt;
&lt;li&gt;Writing reusable validation functions&lt;/li&gt;
&lt;li&gt;Managing state manually with localStorage&lt;/li&gt;
&lt;li&gt;Mobile-first responsive design with Tailwind&lt;/li&gt;
&lt;li&gt;How to structure a bigger vanilla JS project so it doesn’t become spaghetti&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was my final project for Internshala’s course, but I went way beyond the requirements because I wanted something I could proudly show in interviews.&lt;/p&gt;

&lt;p&gt;Certificate for proof ↓ &lt;br&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%2Ffd2q2f41bzhlkj923zme.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%2Ffd2q2f41bzhlkj923zme.png" alt="Internshala Certificate" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re learning JavaScript in 2025, fork this repo and try adding dark mode or search/filter – PRs welcome!&lt;/p&gt;

&lt;p&gt;Let me know in the comments what feature you’d add next!&lt;/p&gt;

&lt;h1&gt;
  
  
  javascript #webdev #tailwindcss #100DaysOfCode #beginners #fullstack #vanillajs #internshala
&lt;/h1&gt;

&lt;p&gt;— Sufal Thakre&lt;br&gt;&lt;br&gt;
Building in public | Nov 2025&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>fullstack</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Building my Personal Portfolio with Tailwind CSS</title>
      <dc:creator>Sufal Thakre</dc:creator>
      <pubDate>Tue, 21 Oct 2025 03:47:51 +0000</pubDate>
      <link>https://forem.com/sufalthakre/building-my-personal-portfolio-with-tailwind-css-3gkl</link>
      <guid>https://forem.com/sufalthakre/building-my-personal-portfolio-with-tailwind-css-3gkl</guid>
      <description>&lt;p&gt;A single-page, responsive portfolio built during &lt;strong&gt;Internshala (Course 2)&lt;/strong&gt; using semantic HTML, Tailwind CSS, and vanilla JS. Features: day/night toggle, animated skill bars, interactive project cards, and a contact form. Live demo: &lt;a href="https://my-portfolio-pearl-xi-96.vercel.app/" rel="noopener noreferrer"&gt;https://my-portfolio-pearl-xi-96.vercel.app/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Live demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://my-portfolio-pearl-xi-96.vercel.app/" rel="noopener noreferrer"&gt;https://my-portfolio-pearl-xi-96.vercel.app/&lt;/a&gt;&lt;/p&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%2Ff3gadhvtu5xjzq0npqfi.gif" 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%2Ff3gadhvtu5xjzq0npqfi.gif" alt=" " width="600" height="333"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I built this
&lt;/h2&gt;

&lt;p&gt;I built and published my &lt;strong&gt;Personal Portfolio&lt;/strong&gt; as part of &lt;strong&gt;Internshala (Course 2)&lt;/strong&gt; to create a clean, responsive single-page site that demonstrates my frontend skills, attention to micro-interactions, and UI sensibility. The goal was to ship a polished product that recruiters and engineers can browse quickly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTML5&lt;/strong&gt; (semantic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt; (utility-first styling)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript&lt;/strong&gt; (vanilla for interactivity)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Font Awesome&lt;/strong&gt; + &lt;strong&gt;Google Fonts (Poppins)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I built — highlights
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌓 &lt;strong&gt;Day / Night theme toggle&lt;/strong&gt; with persistent preference (localStorage)&lt;/li&gt;
&lt;li&gt;📱 &lt;strong&gt;Responsive layouts&lt;/strong&gt; tuned for desktop, tablet (iPad Mini), and mobile (Samsung Galaxy S8+)&lt;/li&gt;
&lt;li&gt;✨ &lt;strong&gt;CSS animations &amp;amp; transitions&lt;/strong&gt; for micro-interactions (buttons, cards, headings)&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Interactive project cards&lt;/strong&gt; with hover transforms &amp;amp; live preview / GitHub links&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Animated skill bars&lt;/strong&gt; that animate into view on scroll&lt;/li&gt;
&lt;li&gt;📧 &lt;strong&gt;Contact form&lt;/strong&gt; with basic validation&lt;/li&gt;
&lt;li&gt;🏆 &lt;strong&gt;Certifications&lt;/strong&gt; section (including my Internshala certificate)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Responsive &amp;amp; accessibility notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I prioritized semantic HTML (&lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;) for screen readers and SEO.&lt;/li&gt;
&lt;li&gt;Navigation includes skip links and ARIA attributes where necessary.&lt;/li&gt;
&lt;li&gt;The design adapts with Tailwind responsive utilities — &lt;code&gt;md:flex&lt;/code&gt;, &lt;code&gt;lg:grid&lt;/code&gt;, etc. — and I manually tested key breakpoints.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Deployed on Vercel for fast static hosting and quick previews. Zero-config deployment makes iterative changes simple.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tailwind significantly speeds up layout tasks and keeps styling consistent.&lt;/li&gt;
&lt;li&gt;Micro-interactions (transitions, hover states) add polish but should be performant and accessible.&lt;/li&gt;
&lt;li&gt;Building a portfolio is as much about storytelling and UX as it is about code.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add project case studies (problem → approach → code → result)&lt;/li&gt;
&lt;li&gt;Add light analytics to understand which projects visitors view&lt;/li&gt;
&lt;li&gt;Consider a React-based rewrite for reusable components&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I built this portfolio during &lt;strong&gt;Internshala (Course 2)&lt;/strong&gt; to showcase my UI &amp;amp; frontend skills. I’m actively looking for &lt;strong&gt;Frontend / UI Developer&lt;/strong&gt; roles (remote or hybrid). If you’re hiring or know someone who is, I’d love to connect — DM me or check the resume and live demo on the site:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://my-portfolio-pearl-xi-96.vercel.app/" rel="noopener noreferrer"&gt;https://my-portfolio-pearl-xi-96.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;portfolio&lt;/code&gt;, &lt;code&gt;webdev&lt;/code&gt;, &lt;code&gt;frontend&lt;/code&gt;, &lt;code&gt;tailwindcss&lt;/code&gt;, &lt;code&gt;javascript&lt;/code&gt;, &lt;code&gt;internshala&lt;/code&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>fullstack</category>
      <category>javascript</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>🚀 Building a Portfolio Clone with HTML, CSS, JavaScript, and GSAP</title>
      <dc:creator>Sufal Thakre</dc:creator>
      <pubDate>Sat, 27 Sep 2025 02:49:44 +0000</pubDate>
      <link>https://forem.com/sufalthakre/building-a-portfolio-clone-with-html-css-javascript-and-gsap-5f84</link>
      <guid>https://forem.com/sufalthakre/building-a-portfolio-clone-with-html-css-javascript-and-gsap-5f84</guid>
      <description>&lt;p&gt;I’m currently learning Full-Stack Development, and as part of my journey I built a portfolio clone project to sharpen my HTML, CSS, and JavaScript skills.&lt;/p&gt;

&lt;p&gt;This project was a great way to understand the basics of structure, styling, and animation in modern web development.&lt;/p&gt;

&lt;p&gt;source code&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Sufalthakre18" rel="noopener noreferrer"&gt;
        Sufalthakre18
      &lt;/a&gt; / &lt;a href="https://github.com/Sufalthakre18/javascript_projects" rel="noopener noreferrer"&gt;
        javascript_projects
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Portfolio Website Frontend Clone&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🔗 Live Demo&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Check out the live version here:&lt;br&gt;
&lt;a href="https://sufalthakre18.github.io/javascript_projects/" rel="nofollow noopener noreferrer"&gt;Live Site&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;A sleek, modern portfolio website built with &lt;strong&gt;HTML&lt;/strong&gt;, &lt;strong&gt;CSS&lt;/strong&gt;, and &lt;strong&gt;JavaScript&lt;/strong&gt;. This project demonstrates front-end development skills through clean design, interactive elements, and smooth animations. It's a clone inspired by professional portfolios, featuring sections for hero/intro, skills/projects/certifications, about me, subscribe, and footer. Perfect for showcasing as a personal site or resume piece.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom Mouse Cursor&lt;/strong&gt;: A small white circle that follows the mouse with subtle scaling effects for a dynamic feel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page Load Animations&lt;/strong&gt;: Elements like the nav, headings, and footer fade in and slide into place on load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Hover Effects&lt;/strong&gt;: Hover over skills/projects/certifications to reveal and animate images with fade, movement, and rotation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive Menu&lt;/strong&gt;: Click "MENU" to toggle a dropdown navigation with smooth transitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimalist Design&lt;/strong&gt;: Dark theme with uppercase typography, no…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Sufalthakre18/javascript_projects" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;a href="https://sufalthakre18.github.io/javascript_projects/" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt;



&lt;p&gt;🔨** What I built**&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A simple portfolio clone that includes a navbar, headings, about section, and footer.&lt;/li&gt;
&lt;li&gt;Smooth animations for text, navbar, and footer on page load.&lt;/li&gt;
&lt;li&gt;A custom mouse follower that scales dynamically as you move.&lt;/li&gt;
&lt;li&gt;Interactive project sections where hovering shows an image with rotation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎯** Why I used GSAP**&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I wanted to move beyond static layouts and try animations. While CSS transitions are useful, GSAP (GreenSock Animation Platform) gave me:&lt;/li&gt;
&lt;li&gt;More control over easing and timing&lt;/li&gt;
&lt;li&gt;Sequenced animations with timelines&lt;/li&gt;
&lt;li&gt;Smooth hover and cursor effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📚 What I learned&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structuring a webpage with semantic HTML&lt;/li&gt;
&lt;li&gt;Styling layouts and typography with CSS&lt;/li&gt;
&lt;li&gt;Adding interactivity with JavaScript event listeners&lt;/li&gt;
&lt;li&gt;Using GSAP for smooth animations and transitions&lt;/li&gt;
&lt;li&gt;Importance of writing a clean, maintainable project structure&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Building small projects like this is helping me strengthen my front-end skills before diving deeper into back-end and full-stack development. Every project teaches me something new — whether it’s handling animations, making layouts responsive, or improving my code organization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🙌 Feedback welcome!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m learning step by step, and I’d love your feedback on this project. How can I make the animations smoother or the design cleaner?&lt;/p&gt;

&lt;h1&gt;
  
  
  webdev #javascript #html #css #gsap #frontend
&lt;/h1&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>fullstack</category>
    </item>
    <item>
      <title>I just finished a beginner project combining a BMI calculator, a live clock, and a GitHub profile card using the GitHub API.</title>
      <dc:creator>Sufal Thakre</dc:creator>
      <pubDate>Tue, 23 Sep 2025 16:49:08 +0000</pubDate>
      <link>https://forem.com/sufalthakre/i-just-finished-a-beginner-project-combining-a-bmi-calculator-a-live-clock-and-a-github-profile-1629</link>
      <guid>https://forem.com/sufalthakre/i-just-finished-a-beginner-project-combining-a-bmi-calculator-a-live-clock-and-a-github-profile-1629</guid>
      <description>&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%2F53sp004kephoe34el1gb.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%2F53sp004kephoe34el1gb.png" alt=" " width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Github Profile Card using API&lt;/strong&gt; (beginner)
&lt;/h1&gt;

&lt;p&gt;Small web project made by me to learn DOM, &lt;code&gt;fetch&lt;/code&gt; API and a bit of CSS.&lt;br&gt;
It shows a Github profile card (avatar, followers, repos, link) and also&lt;br&gt;
includes a simple BMI calculator and a live clock. Made to practice real stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fetches GitHub user data from &lt;code&gt;https://api.github.com/users/&amp;lt;username&amp;gt;&lt;/code&gt; using &lt;code&gt;fetch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Displays: avatar, name, bio, followers, public repos, following and a link to the GitHub profile.&lt;/li&gt;
&lt;li&gt;Has a small BMI calculator (height in cm, weight in kg).&lt;/li&gt;
&lt;li&gt;Shows your local time with a live clock.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here my code :-&lt;br&gt;
&lt;a href="https://github.com/Sufalthakre18/practice-Js/tree/d5a33ded4d02263342f0fd009aa107001f87302e/Github-Profile-Card" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>fullstack</category>
    </item>
  </channel>
</rss>
