HTML Refresher and Detailed Study - Table of Contents for Post Series
Summary
This summary has been prepared by Gemini based on the details I shared with it, and then reviewed and edited by me.
Phase 1: The Foundational Refresher
Dates: 11 April to 4 May 2026 (part-time and including documentation periods)
Blog posts: Part 1 (TOC) to Part 5 (TOC)
This initial phase began as a refresher on core web concepts. While standard React and Next.js architectures were already familiar territory, this period focused heavily on document-level structure and semantics.
- Key Work: The study systematically reviewed HTML5 semantic elements (such as
footer,aside, andfigure), form data handling, character encoding (UTF-8), and data attributes. - Security & APIs: DOM security boundaries were explored, specifically noting the differences and XSS vulnerabilities between
innerHTMLandtextContent. Standard, front-facing Web APIs such as Geolocation, Canvas, Storage, and modern Fetch mechanics were also reviewed. - The Pivot: The study then moved on to advanced Web APIs provided by the browser engine.
Phase 2: Transitioning to Real-Time & Offline Architectures
Dates: 3 May to 20 May 2026 (part-time and including documentation periods)
Blog posts: Part 6 (TOC) to Part 9 (Up to Stage 4 of chat tutorial app) (TOC)
The study focused on the browser's native real-time and offline capabilities. A chat application was selected as the ideal testbed for these stateful APIs.
- Real-Time Data: Work commenced with a soft real-time data visualization app to establish a firm understanding of the WebSocket protocol handshake and framed TCP sockets.
- The App Shell: The uppercase echo chat tutorial was initiated, implementing a basic Service Worker to manage a Network First caching strategy.
- Message Queuing: IndexedDB was integrated to handle offline message queuing, bringing the application to Stage 4, where a resilient WebSocket auto-reconnect logic featuring exponential backoff was successfully implemented.
Phase 3: Advanced PWA Features & Local Resilience
Dates: 21 May to 2 June 2026 (part-time and including documentation periods)
Blog posts: Part 9 (Latter part) (TOC) to Part 12 (Stages 5 to 7 of chat tutorial app) (TOC)
During this phase, the basic chat application was transformed into a sophisticated Progressive Web App (PWA) entirely within a local environment. The resilience mechanisms typically reserved for native mobile applications were engineered natively in the browser.
- Offline State: Chat history persistence was finalized through IndexedDB.
- Background Sync: The Service Worker was wired to handle network on/off situations natively using the Background Sync API.
- Push Notifications: The Web Push API was integrated, requiring the management of VAPID key architecture and the complexities of browser notification permissions, including edge cases related to Chrome's prerendering quirks.
Phase 4: Cloud Deployment & Systems Engineering
Dates: 3 June to 21 June 2026 (part-time, including documentation periods and includes a general break from project work from 14–16 June)
Blog posts: Part 13 (TOC) to Part 15 (TOC)
Moving the application from localhost to the cloud exposed the codebase to production realities. This phase required defensive programming and advanced repository management.
- Cloud Proxy Defusal: Deploying the backend to Render revealed that cloud load balancers can delay WebSocket close events or mask close codes. Application-level rejections and 100ms timer hacks were engineered to bypass this infrastructure interference.
- The Outbox Pattern: It was discovered that a dropped internet connection will not trigger a browser
offlineevent if the local LAN remains active. To prevent data loss during these "Silent Drop" scenarios, an enterprise-grade Outbox pattern was implemented. - Build Tooling: After finding Netlify's built-in asset optimization deprecated, Vite was adopted for JavaScript minification. The
sw.jsfile was carefully maintained as a classic script to preserve the tutorial's zero-magic architecture. - DevOps: Advanced Git operations using
git filter-repowere executed to extract thechat-appsdirectory into its own clean monorepo while preserving specific commit histories.
Phase 5: Architectural Polish, Final Review & Public Announcement
Dates: 22 June to 9 July 2026 (part-time and including documentation periods)
Blog posts: Part 16 (TOC) to Part 17 (TOC)
The final phase focused on rigorous architectural refinement, handling cross-tab state, improving the user experience, and finalizing the educational documentation.
- MPA Migration: The app was refactored from a Single Page Application (SPA) to a Multi-Page Application (MPA) to cleanly serve the "About" page via the Service Worker cache, avoiding Blogger CORS restrictions. The MPA shift introduced the need in Service Worker Background Sync and Push handlers to differentiate between main chat tab (index.html) and about page (about.html).
- Mobile UX: Dynamic Viewport Height (
dvh) was utilized to prevent mobile on-screen keyboards from breaking the layout, and the connection status bar UI was refined. - Finalization: Exhaustive edge-case testing was conducted, Git commits were squashed to create a clean history, companion blog posts were written, and the Stage 8 completion snapshot folder was finalized on 5 July.
- Public Announcement: Public announcement was done on this blog and on LinkedIn.
Introduction
This is a refresher on HTML topics that I had quickly studied perhaps in end 2024 and early 2025, and then referred to when needed for software development learning or work I was doing.
For this refresher, initially I used w3schools.com HTML tutorial. But there were many HTML topics covered by w3schools.com which I had not got into in detail earlier on. This time around I did study most of these topics in some detail.
I also studied some advanced Web API topics like Service Worker, WebSocket and Push notifications which are either not covered or have limited coverage in the w3schools tutorial. The last phase of this study resulted in this advanced echo chat tutorial PWA app deployed on Netlify.
So it is a refresher cum detailed study. I have put up a series of longish blog posts related to this HTML refresher and detailed study, with each post having a Contents section with internal links to the sections. This post lists all the Contents of all the posts in the series with the internal links going directly to the related section in the post.
HTML Refresher and Detailed Study - Part 1
Contents
Misc: title attr, formatting elements, link rel (and more)
- title attribute
- HTML Formatting Elements
- Investing time for Accessibility
- HTML Formatting Elements Continued
- link rel
- _parent and _top values for target attribute
- href mailto
- Confusion Due To React and Next.js Components That Replace HTML elements
img element, Image Maps, picture element
- img element width and height attributes
- Image Maps - map and area tags not used nowadays
- picture element
Misc: Description lists, boolean attributes, meta viewport, base element (and more)
- link type attribute
- HTML Description Lists
- Using CSS property float:left to display a list horizontally not used nowadays
- class and id attribute values are case sensitive
- Boolean attributes like disabled
- title attribute and title element
- meta viewport typical statement
- base element
- When to use href attribute and when to use src attribute
Misc elements: footer, aside, figure, figcaption, details, summary, time (and more)
- footer element
- var, kbd and samp elements
- aside element
- figure and figcaption elements
- Most MERN software I saw Had Limited Use of HTML Semantic elements (like figure, footer etc.)
- details and summary elements
- time element
- Quick overview of ARIA
Misc: UTF-8, nbsp, Unicode, Emojis (and more)
- meta charset UTF-8 - recommended
- UTF-8 is comprehensive. So why have UTF-16
- UTF-8 is good choice even for Devanagari, Chinese or Japanese web documents
- nbsp (Non-Breaking Space)
- Unicode characters can be copy-pasted to VS Code. Unicode character list sites
- XHTML is not so relevant now
- Emojis history. XML related tech less used in web nowadays
- UTF-8 has no versions typically but UNICODE has versions
- What to look at if some UTF-8 characters (like Devanagari characters) are not rendered on web page
HTML Refresher and Detailed Study - Part 2
Contents
Forms
- About HTML Forms
- name attribute in Forms
- FormData in Client and Server side
- Scenarios where using same string for id and name attributes will not work
- Scenario where name attribute is useful for input button elements
Some elements not typically used in React projects: datalist, output, datatime-local, input type image
- datalist is not used often in React projects
- output element is not typically used in React projects
- output element is not so popular in plain JavaScript projects
- datetime-local is not used much in React projects
- input type image is not commonly used in React projects
Misc elements: input type number, input range, search
- input type number with step handles decimals
- input range does not show selected value
- search element
React, Astro and HTML
- React impact on HTML elements usage
- Plain HTML dev today is rare. It will typically be with framework like React or Astro
Misc: autocomplete, input form, formaction, formmethod
- autocomplete attribute default value is On
- input form attribute is rarely used
- formaction, formmethod attributes are rarely used
canvas and SVG
- canvas element usage
- SVG more commonly used in React apps than canvas
- canvas is used when svg struggles due to scale (over 1000 objects)
- getContext method
- getContext method holds keys to graphics card (GPU)
Misc: video..source element, html plugins, geolocation API, Storage API
- In video .. source element, providing type attribute is recommended
- html plugins: object and embed elements are rarely used nowadays
- geolocation success function time interval
- geolocation API is universally supported
- Drag and Drop API is typically not used in React apps
- typeof Storage used in early 2010s for feature detection
- Better to use getItem and setItem methods of localStorage instead of JavaScript properties shorthand
HTML Refresher and Detailed Study - Part 3
Contents
Web Workers and Service Workers
HTML Standards and who controls them
- HTML standard is massive now
- HTML Living Standard
- W3C is no longer Boss of HTML; Browser vendors are in charge
- Chromium (Google/Microsoft), WebKit (Apple) and Gecko (Mozilla) are bosses of the web now
- Living Standard is followed by Platform Engineering (Browsers). Product Engineering (.NET, Java) uses versions
- Big Tech has great power over web but Mozilla provides some balance
Misc Web APIs: Server-Sent Events (EventSource) API, Notifications API, History API, Intersection Observer API
- HTML Server-Sent Events API
- Push Notifications (Firebase Cloud Messaging)
- HTML Living Standard Notifications Support
- Basic Web APIs not mentioned in W3Schools
- History API and IntersectionObserver API
Fetch Web API
- fetch API is Web API and not JavaScript language feature
- Node.js 18 onwards provides fetch API
- Historical background of fetch API
Misc: dialog element, Accessibility, search element, Client-Side Web Communication APIs (and more)
- W3Schools is a good site for HTML refresher and detailed study from typical web dev perspective
- dialog and ARIA
- a11y short form for accessibility
- click here is a problem for a11y
- search element
- dialog closedby attribute
- dialog with command attribute
- dialog popover attribute
- The "Local File" Security Barrier for iframe with youtube video and Web Workers
- Client-Side Web Communication APIs like postMessage API
General discussion on multi-threading features of Browser and how Browser has evolved into Virtual Operating System
- Introduction
- Web Workers provide Fat Client possibility which can be hosted on free static sites like GitHub Pages
- Structured Cloning, Transferrable Objects and IndexedDB: Options for large data chunks transfer between Web Worker and Main Thread
- Transferable objects provide bi-directional mechanism for some heavy data objects like ArrayBuffer and ImageBitmap
- Transferables are different from Shared Memory (SharedArrayBuffer) in Web API
- Stunning Evolution of Browser from simple "document viewer" to Virtual Operating System; Atomics API
- Innocuous looking web page may result in heavy compute happening on PC CPU with many threads created; Browser guardrails to prevent PC 'hijack'
- Browser terminates almost all threads of an app once app's Main Thread terminates; Exception is Shared Worker thread
HTML Refresher and Detailed Study - Part 4
Contents
Data attributes
- Data attributes in HTML
- Some React libraries use data attributes
- W3Schools HTML tutorial has limited coverage of data attributes
- MDN guide page for data attributes is recommended by Gemini
- Dynamically generated HTML elements may be more easily targetted via data attributes
- Backend server populates elements with data attributes to identify them
- State tracking using data attributes
- Passing small bits of configuration using data attributes
- Some React libraries using data attributes to identify elements it interacts with
- React Styling libraries like Headless UI using data attributes to signal state to CSS
- React library using data attributes for metadata avoiding prop drilling
- Data attributes store data in DOM itself in contrast to JavaScript variables
- Data attributes are more easily visible in Chrome Inspector/DevTools as compared to JavaScript variables
- Small data-attributes.html sample test file
XSS security vulnerability of innerHTML; textContent is safe
- textContent needs newline escape character and CSS white-space pre-line for line breaks
- XSS security vulnerability of innerHTML
- innerHTML security flagging is a problem. textContent avoids that
- Use textContent with specific CSS instead of innerHTML for multi-line content to avoid security flagging
Misc: Notifications API, IndexedDB, Cookies, Authentication, Fetch API
- HTML Notifications API are for system notifications and so useful only when combined with service worker
- IndexedDB
- Video: IndexedDB - What is it, and when you should choose it
- Official Google Video: IndexedDB - Progressive Web App Training
- Cookies rarely used by App Dev nowadays but HttpOnly Cookie used for better security for tokens like JWT
- Google services access: API Key and OAuth 2.0 / OpenID
- Plain JS libraries for cookies
- Header-based Authentication common for React SPAs
- More detailed: Cookies rarely used at App dev level now. Libraries use HttpOnly cookie for JWT as it is more secure
- Fetch API Refresher Using Modern Async Await for Promises
- In React/Nextjs apps I typically used Axios library for fetch type operations
- Handling Fetch Promises: Modern Async/Await is preferred over Legacy .then() Chaining
- Promise.all can be used with Async/Await; Avoiding overloading mind with .then() promise chaining syntax details
HTML Refresher and Detailed Study - Part 5
Contents
Misc: <noscript>, aside element not used for sidebar, code and pre elements used together, figure element used with picture element
- Gemini Summary of the <noscript> Discussion
- Gemini Summary of aside element not being used for sidebar as nav element is enough for semantics and a11y
- Gemini Summary about <code> and <pre> Elements Used Together
- Can figure element be used with picture element?
Misc: HTML entities, symbols and emojis, URL ASCII limitation, URL Encoding, autocomplete enumerated attribute
- HTML entities, symbols and emojis
- URL ASCII limitation
- URL Encoding
- autocomplete is an enumerated attribute and not a boolean attribute
Misc: Select dropdown size, React apps use custom combobox, picture element, thead, tbody and tfoot
- HTML Select dropdown size cannot be specified
- React apps use custom combobox type components instead of input with datalist HTML elements
- Picture Element - Detailed Look
- Using thead, tbody and tfoot is good practice
addEventListener safer than inline JS; Vanilla JS vs React security; package.json enables GitHub Dependabot security
- In Plain JS, using addEventListener() is best practice for wiring event handlers
- Inline JavaScript: Vulnerable to XSS in plain JS, but protected in React's event system
- Plain JS app security has to be handled by dev. React provides security by default.
- Dependency Manager model (like package.json) enables security systems like GitHub's Dependabot
HTML Refresher and Detailed Study - Part 6
Contents
WebSockets Intro
- WebSockets Basic Exposure
- MDN docs for WebSockets is too complex
- WS/WSS are protocols but use a hack to piggyback over HTTP protocol for initial handshake/setup
- After HTTP piggyback handshake, WebSocket connection is somewhat similar to BSD TCP socket connection
- WebSocket communication is called real-time by Web devs
- Technical precision wise, WebSockets are 'soft real-time' and not 'hard real-time'
- 'real-time' terminology drift between early 2000s and now
Vanilla JS real-time data visualization app using Web Sockets, Canvas API and JSON data
- Plain JS (soft) real-time data visualization app using Web Sockets, Canvas API and JSON data
- Performance aspects of Canvas API draw functions including specific case of Windows 11
- Short overview of WebGL
- Implementation of 'high-performance live dashboard' WebSockets, Canvas and JSON plain JS app
- Server-Sent Events vs. WebSockets for Live Telemetry Dashboard app
- Using Two-way communication over WebSockets for Live Telemetry Dashboard app
- Global/Broadcast architecture over WebSockets when serving thousands of concurrent clients
- Common practice to stringify JSON data before sending on WebSockets using Text Frames; Massive scale may need Binary Frames
HTML Refresher and Detailed Study - Part 7
Contents
Top-Level design of chat application with offline support using WebSockets and Service Worker
Very Basic Service Worker
- Very basic Service Worker example with only console logs
- Calling register() on every page load is not a problem
- skipWaiting() forcibly removes old service worker version but has risk of mismatch with other app JS code
- Activate event is fired when browser promotes installed script to active worker
- if ('serviceWorker' in navigator) is best practice for feature detection
- Automatic Service Worker uninstallation (unregistration) is rare
- Large number of registered Service Workers in Chrome is normal and not a performance drain
- Unused service worker should be unregistered manually as automatic cleanup may not happen
- Service Worker status change from Running to Stopped is based on idle time
- Chrome DevTools SW panel keeps Running SW in Running state; chrome://serviceworker-internals/ does not do that
- Testing Service Worker Running and Stopped states
- On Win 11 Service Worker is a separate OS thread; Stateless architecture due to transient lifetime of thread
- Service Worker (transient) lifetime is very different from Web Worker lifetime
- self.clients.claim() immediately gets control of all active pages
- Activate event is fired only once for a particular version of Service Worker JS file
Service Worker with Offline Caching
- Service Worker with Network First caching example - Brad Traversy
- UI does not show whether app is showing live data or offline data
- waitUntil() prevents browser from killing thread until promise passed is settled (resolved or rejected)
- Deciphering chained invocations and nested code of Traversy example
- For top-level navigation, when offline, Browser usually will show network error even if it has required files in cache
- waitUntil() code in Traversy example omits error handling
- In waitUntil() code, catching errors for logging purposes and passing it up
- In JavaScript, throw does not need to be inside a try-catch block
- Service Worker with Cache First example that has Zombie app risk; Network First and Stale-While-Revalidate strategies
- Cache management strategies; Google Workbox
- MDN Service Worker sample code using async await which uses Cache First strategy
- Standard browser HTTP cache comes into play in Network First Service Worker caching strategy
- When online, browser will serve top-level navigation page directly from HTTP cache if file is present and within cache-expiry
- Even with no-cache, browser will save resource in cache but check with server for updated version; no-store results in resource not being saved
- Heuristic caching (guesswork based on some factor(s)) is used if server does not send any Cache-Control or Expires headers
- GitHub Pages and Netlify default Cache-Control headers
- For standard static sites, plain refresh (F5) usually gives latest page; Hard refresh (Ctrl+F5) bypasses HTTP cache and Service Worker cache
- Duplication of cache by browser and Service Worker
- Service Worker API does not have cache expiry support; Google Workbox provides expiration support
- Industry Standard is to avoid plain vanilla Service Worker caching and use Google Workbox or similar for SW caching
Service Worker Misc: Next.js, React, PWA hype
- My Next.js Gita app could get offline functionality if Service Worker caching is added
- By default, Next.js app does NOT have Service Worker functionality but it can be added
- Create React App (CRA) (deprecated) earlier versions added Service Worker functionality by default but was later discontinued
- PWA marketing hype in 2018-2022
HTML Refresher and Detailed Study - Part 8
Contents
Web Push API
- How Service Workers get Push Notifications (Web Push) from a server like in a chat application
- How small browser vendors handle Web Push infrastructure needs
- Tutorial Videos on Push Notifications (Web Push)
- JavaScript does not have interface keyword; Web IDL uses interface term which does not map directly to Java/C++ interface
- Akhilesh Rao Web Push, Service Worker and Notification tutorial
- Running and testing app
- Push Services queues messages for TTL if destination device is disconnected from Internet
Vanilla JS Chat with WebSockets
- Difficult to get tutorial combining vanilla JS, WebSockets and Service Worker
- Vanilla JS Real-Time Chat with WebSockets Tutorial Text Articles
- socket.io package is very popular for JS real-time applications; socket.io overview
- Vanilla JS Real-Time Chat with WebSockets Tutorial Video
- Broadcast functionality is not suitable for this task's objective
- Limit server to uppercase echo functionality to keep it simple
- Production (real world) chat app architecture
- In uppercase echo server app, use case of user sending message when app is offline
- In uppercase echo server app, use case of server having message to send to user but client app is offline
- Service Worker is needed to provide cached client files when client device is offline
- Service Worker in context of chat app is worth knowing
- Chat app use case (details): App is offline when user sends a message
- Chat app use case (details): User closes tab with pending message - Background Sync
- Chat app use case (details): Server sends Web Push Notification as client app is offline
- Concise capture of technologies involved in chat app with offline capability
Vaibhav Thakur vanilla JS Chat and WebSockets tutorial
- Vaibhav Thakur Chat with WebSockets tutorial
- Chat client WebSocket auto-reconnect code is mandatory in production
- Debouncing the sendTyping(true) message
Thomas Sentre vanilla JS Chat and WebSockets tutorial
- Thomas Sentre Chat with WebSockets tutorial
- Explanation of client blob message reading code - by Gemini
- Server-Side Message Handling and Binary Data (by Gemini)
Dave Gray vanilla JS Chat and WebSockets tutorial
- Dave Gray Chat tutorial - Intro to WebSockets
- WebSocket Node.js server Buffer.from() defensive programming; Implicit Coercion of Buffer to string using Template Literal
Node.js third party ws package (WebSockets) returns Buffers not strings
HTML Refresher and Detailed Study - Part 9
Contents (sections and/or jump-links)
Implementation of uppercase Echo Chat application with offline support using WebSockets and Service Worker
Stage 1: Simple delayed uppercase Echo Chat server and client with WebSockets but without Service Worker
Stage 2: Adding Service Worker Network First Caching (Offline App Shell)
- Stage 2: Adding Service Worker Network First Caching (Offline App Shell)
- Why have Pre-caching when we already have dynamic caching?
- Stage 2 Testing
- How to prevent Service Worker deleting another Service Worker's cache for projects having subfolders with separate Service Workers
- Adding network online/offline detection code (navigator.onLine)
- Stage 2: Further Testing
Stage 3A: Offline Message Queuing with IndexedDB implemented with custom IndexedDB wrapper functions
- Stage 3A: Offline Message Queuing with IndexedDB implemented with custom IndexedDB wrapper functions
- Stage 3: Test plan
- IndexedDB tutorials
- Custom wrapper functions provided by Gemini for IndexedDB code
- Stage 3 idb variant possibility
- Test Results for Stage 3A: Offline Message Queuing with IndexedDB (custom IndexedDB wrapper fns)
Stage 3B: Offline Message Queuing with IndexedDB implemented with idb package
- Stage 3B: Offline Message Queuing with IndexedDB implemented with idb package
- Better to use idb with ES modules (type module in script element) and not with legacy global variables (iife)
- LiveServer origin (http://127.0.0.1:5501) local storage, IndexedDB and Service Workers data shared by all apps run at that origin; Cleaning this data
- Separate subdomain apps have unique origins; Same subdomain apps (GitHub pages) have same origin (cross-app browser data leak danger)
- Custom Subdomains (purchased) mapped to GitHub Pages (free) provides unique Origins across Subdomains
- Netlify and Vercel use separate subdomains for separate apps and so provide unique Origins
- Stage 3B: Testing
Plan for WebSocket auto-reconnect and advanced Progressive Web App features in uppercase Echo Chat app
Stage 4: WebSocket auto-reconnect
- Stage 4: WebSocket auto-reconnect
- Stage 4: Testing
- WebSocket auto reconnect attempts with exponential backoff
- Service Worker cache interference with update of web page on refresh
- Shared dbPromise object holding IndexedDB database connection leverages JS Promise settled value caching
Disabling Live Server browser auto refresh for a project
Implementation of advanced (PWA) version of uppercase Echo Chat application
- Plan for advanced Progressive Web App features in uppercase Echo Chat app (Stages 5 to 7)
- Tested advanced version app baseline in new folder adv-uppercase-echo-chat initialized as copy of Stage 4 app
- Small bug possibly in Gemini web chat UI related to network connectivity resilience
- Stage 5 Chat History Persistence
- Stage 5 snapshot folder created
HTML Refresher and Detailed Study - Part 10
Contents (sections and/or jump-links)
Stage 6 Background Sync API (Offline Sending) Server refactor
- Initial Code Changes for Stage 6 Background Sync API Server refactor
- ws library Server method port and server parameters
- Testing stage 6 server with POST requests using VS Code "REST Client" Extension and PowerShell Invoke-RestMethod
Stage 6 Background Sync API Client refactor
- Initial suggested code changes for Stage 6 Background Sync API Client refactor
- Background Sync API limited availability - Not supported on Firefox and Safari
- register method for sync is functionally different from register for Service Worker
- Duplicate register calls for sync are ignored
- MDN documentation for Background Sync API is poor (as it is not an official standard)
- Background Sync spec site has 'UNOFFICIAL DRAFT' plastered over it
- MDN vendor neutrality
- MDN is now jointly sponsored by Mozilla, Google, Microsoft, and others; MDN history
- Microsoft and Google both multi-trillion dollar companies seem to be main financiers of Core Browser level Web Dev Reference documentation
- Stage 6 Client refactor implementation
- Initial client testing and bugfixing
- Preventing client race condition for syncing offline messages between UI thread and Service Worker
- Using IIFE inside event.waitUntil()
- MDN documentation for Clients API is good (as it is ratified W3C standard)
- Testing prevention of race condition for offline sync in client between UI thread and Service Worker
- Checking Service Worker console logs when no associated client tab is open
- Service Worker fetch does not automatically retry like browser and so results in LiveServer fetch network failure at times
- Stage 6 completion snapshot folder created
- 2002 vs 2026 Global Top Market Cap Companies
HTML Refresher and Detailed Study - Part 11
Contents (sections and/or jump links)
Revisiting Web Push and understanding its usage for real life scenarios
- Introduction
- What exactly does webpush.setVapidDetails() do?
- VAPID Key Generation and usage in code
- How are VAPID keys used to set up a Push service facility specific to client page
- Creation of Push subscription endpoint needs only public key and not private key - seems counterintuitive at first
- Public key is like a snap-shut padlock; Client to client Push notifications may be possible but very risky due to lack of security for Private key
- Server plays vital roles of holding Public and Private VAPID keys, storing subscription endpoints and sending Push notifications
- Single VAPID public/private key pair is typically shared across all client-initiated Push service subscription endpoints for an app
- Example Weather app with 1000 Push subscription endpoints
- Server clearing 'dead endpoints' when Push service returns specific errors
- Explicit unsubscription by client can be done using unsubscribe() method
- Client (same web app) can have only one Push subscription endpoint
- Client can easily ask Browser for existing Push subscription endpoint
- On Windows PC for different user logins, each app for same Browser will have separate subscription endpoint
- Android will typically have only one endpoint for an app for same Browser (exception is Android system-level Multiple Users feature)
Stage 7: Web Push Notifications Overview
Stage 7 Server Refactoring
- Intro
- Plain JavaScript Object for subscriptions as it has to be serialized; More efficient Map for activeSockets
- When is Map superior choice over plain JavaScript object for dictionary object
- Nested JavaScript Closure used in WebSocket server code
- Separate Closure "Backpack" data for each WebSocket connection makes WebSocket server code simple
- Standard Express REST API servers (stateless and ephemeral) don't need Closure "Backpacks"; WebSocket servers (stateful and long-lived) need them
- Heavy Closure can suck up WebSocket server RAM and may even crash it
- Single Node.js Express server can comfortably juggle thousands of concurrent REST requests
- Clustering (Horizontal Scaling/Load Balancing) helps to improve Node REST API server performance on multi-core CPUs
- Early 2000s Load Balancing was financially expensive; Now Node JS Load Balancing is free open source software based
- Shell for Stage 7 Client refactoring
- Free Load Balancing Node JS software knowledge helps from Web Dev solution provider perspective
- At WebSocket connection time, URL Query String is the only way to pass metadata to server
- Modern JavaScript engines (like V8 used in Node.js) optimize Closure data to only what is used by inner function except when eval is used
- JavaScript Template Literals (${}) can have any expression inside them
- Stage 7 server code is backward compatible; Minor improvement in anonymous client server console messages
- JavaScript Bracket Notation: obj[dynprop] (Dynamic) vs Dot Notation: obj.statprop (Static/Literal)
- Stage 7 server code ready for testing
- Stage 7 server backward compatibility testing with stage 6 client
Stage 7 Client Refactoring
- Gemini provided Stage 7 Client refactored code
- Generation of unique ClientId: Math.random() vs crypto.randomUUID()
- Server handling of clientId collisions; In current code, two browser tabs on same client will have same clientId
- With current code, two separate browser tabs having same chat frontend will not work properly
- Possible to have unique clientId per client tab but IndexedDB is shared
- In our tutorial code, simplify app by enforcing single tab chat only
- JavaScript hoisting allows functions to be defined after code that calls them
- Review of this round of code changes
- Testing this round of incremental changes
- server.js comment on client multi-tab separate chat support needing 1-to-Many mapping for clientId and WebSocket
- Client race condition between hydrateUI() method and connectWebSocket() method's WebSocket open handler; Related fix
- Optimization of code for pre-existing subscription entry in server
- Testing optimization of code for pre-existing subscription entry in server
HTML Refresher and Detailed Study - Part 12
Contents (sections and/or jump links)
Stage 7 Client Refactoring (Continued)
- Gemini provided client service worker push event listener code
- Notifications permissions prompt not showing even after settings of ask for permission
- Notifications permissions prompt related User Gesture rule
- Notifications permission prompt Browser block after permission prompt has been ignored several times
- Clicking button on some transient Chrome UI resulted in Notifications prompt being shown
- Notifications blocked issue without prompt can be handled by support providing solution of clicking padlock icon in browser address bar
- Browser destroys old subscription endpoint when notifications permission is blocked/disabled.
- In tutorial implementation, old unused clientId and associated subscription entries in Server data file have to be manually cleaned
- Client UI should show notifications button or notifications enabled message based on permission state
- Client should have a clear history and pending messages button
- Testing Notifications permissions prompt related UI
- UI Improvements: Enable notifications button related, Clear Chat button etc.
- If Push event handler is absent, subscription endpoint may be marked as dead by Push service
- Fixing tutorial code to handle case of Browser deleted subscription
- Having await at top-level in an ES module script blocks module and so is not preferable
- Notifications.permission, at times (prerendering), initially gives wrong value of 'default'
- As tutorial script.js is an ES module, waiting for DOMContentLoaded is not needed
- Isolated prerendering as condition when Notifications.permission initially gives wrong value of 'default'
- Asynchronous Permissions API may be a solution for prerendering initially giving Notifications.permission wrong 'default' value
- MDN view on Notification.permission and Permissions API; MDN does not mention Chromium prerendering quirk
- Developer community approach for handling prerendering related issues
- document.prerendering and 'prerenderingchange' event intro
- Trying out Notification.requestPermission() on visibilitychange event to fix prerendering issue
- Using document.prerendering and prerenderingchange event to fix prerendering related initially wrong 'default' Notification permission
- Testing document.prerendering and prerenderingchange event fix to prerendering permission value issue
- Chrome client side silent Push penalty budget
- Adding push event handler to Service Worker and initial testing
- Push event handler: Writing server response to IndexedDB; On notification click, opening client
- Solving edge case issue with BroadcastChannel API message from Service Worker to UI thread
- Client app code runs partially with old Service Worker and then with updated Service Worker
- self.skipWaiting() in SW install is dangerous in production; "Update Available" Safe Pattern: SW delays activation till client gives go-ahead
- In tutorial app, skipWaiting() continues to be used for simplicity but with comment about risk for production
- Testing BroadcastChannel API message from Service Worker to UI thread solution for edge case
- Use case of client allowing notifications initially but after some usage, denying/disabling it
- Stage 7 completion snapshot folder created
- Main Thread vs UI Thread term for web app client; JS Main Thread term history
- All devices that run browsers like Chrome support multi-threading
HTML Refresher and Detailed Study - Part 13
Contents (sections and/or jump links)
Stage 8: Cloud deployment of tutorial app
Stage 8: Cloud deployment of Delayed uppercase chat app server
- Using render.com as free tier cloud backend for tutorial app server
- Render has Monorepo Support. Root Directory of web server can be subfolder in repo
- Render does support multiple web services in a project in free tier but defaults to paid tier
- Quotes vs. No Quotes in .env conventions
- Render sniffed that server app was using 3000 as PORT instead of picking it up from env var PORT and adapted suitably
- Render detects GET request is from browser and so shows service start up message
- Wake up route for render server
- No issue in exposing VAPID public key via an unprotected GET request
- Testing server deployed on render
Stage 8: Refactoring chat app client (still local) to work with Cloud (Render) server; Some server changes
- Using config.js on client for swapping between local and cloud backend servers as plain vanilla JS does not support .env file
- Refactoring client to work with local or cloud server
- Network first strategy with cache update on every successful fetch does not have stale cache issue
- Bumped cache version from v1 to v2 as we added new config.js
- Tutorial sw.js attempts to atomically overwrite ASSETS_TO_CACHE array entry files on every SW install
- Version Skew danger in network first strategy with non-atomic cache update on every successful fetch
- Quite a few web apps use push notifications but very few web apps seem to use offline caching
- Web apps that do use offline caching: excalidraw.com, youtube.com
- Android Gmail and WhatsApp apps have extensive offline capability in contrast to zero offline features for their web apps
- IndexedDB data on Windows PC is easily accessible to administrator login which would be risky for apps like Gmail and WhatsApp
- Google Search AI on why Service Worker offline caching seems to be rarely used by big web apps
- MDN docs gives rosy view of Service Worker offline caching which is not realistic
- Testing client with cloud server (Render)
- Using config.js on client to switch easily between local and cloud backend; git update-index --skip-worktree command
- Testing Render backend url.parse() deprecation and port number issues fix
- Tested Render server ephemeral disk related subscription recreation when server is shut down and then restarted
- Testing push notification from render server
- Due to ephemeral disk on Cloud server, Subscription has to be sent again by client on WebSocket reconnect
- Push notification is an Ephemeral Signaling Protocol where losing a notification in some edge cases is acceptable
- Fetching subscription directly from the browser on every reconnect is recommended
- WebSocket timeout in Chrome is typically around 60 to 90 seconds; Render server restarts in less than 15 seconds
- Render server restart from dashboard results in WebSocket close being delayed till after server has been restarted (Graceful Shutdowns and Rolling Restarts)
- Render server Inactivity Shutdown sends WebSocket close at shutdown time itself and restarts server on client sending new WebSocket connect request
- Render Cloud server changes WebSocket close code sent by node server to client (Cloud Proxy Masking); Application-Level Rejection solution
- Testing Application-Level Rejection solution for Render Cloud server masking WebSocket close code issue
- Offline IndexedDB queue is core resilience mechanism of tutorial app; Web Push Notification is out-of-band signaling mechanism that complements it
- Final Testing round of local client before deployment to Cloud
HTML Refresher and Detailed Study - Part 14
Contents (sections and/or jump links)
Stage 8: Cloud client deployment of Delayed uppercase chat app; Some server changes
- Client deployment to Netlify
- Internet being down and so Cloud server being unreachable does not trigger Offline event as LAN is still up
- Client ws.send() does not report error if LAN is still up but Internet is down and server is unreachable
- Windows OS actively probes Internet connectivity for its Taskbar Internet online/offline status icon
- WebSocket timeout varies for Idle Tab and Active Tab (has outstanding message(s))
- WebSocket API does not have mechanism to check if message is received by server; Application-Level Acknowledgements; Watchdog Timers
- Changing Server duplicate clientId handling to Last-In Wins
- Testing Last-In Wins approach on server
Stage 8: Client refactoring with Outbox pattern to fix lost user messages for server 'Silent Drop' case
- Introduction
- Fixing client side lost message when server is unreachable but Browser does not send Offline event
- Fetch socket is different from WebSocket socket and so GET / would not be suitable as pre-flight server WebSocket alive check; Ultra-Lightweight Watchdog Alternative
- Application-Level Ping (with watchdog timer) as pre-flight check for Server WebSocket
- Adopted approach of putting user message in outbox first and moving it to history only after server response (Outbox pattern)
- Client UI improvements
- Background Sync seems to be broken; 'visibilitychange' vs 'beforeunload' events for Background Sync registration
- Fixing duplicate server response in client UI when push notification is received; More UI improvements
- Watchdog timer not implemented as usually WebSocket disconnect fires within 20 seconds; Ignoring "Silent Drop" edge case not yet encountered
- Watchdog timer approach code for any future implementation need to fix "Silent Drop" type edge cases
- Note explaining refactoring of app code to handle server becoming unreachable due to Internet going down but browser not sending Offline event as LAN is still up
- Deleting outbox entries one message id at a time vs. clearing entire outbox at one go
- Initial testing of refactored client with Cloud deployment
- Background Sync event gets sent on Netlify client but was not getting sent on Live Server local client
- Removed old version comments and commented code from client to improve readability
Stage-8: Minification of Client JavaScript code
- Introduction
- Netlify Optimization did not work out for tutorial app JS code
- package.json and build scripts seem to be necessary for tutorial app JS minification
- Netlify deprecated its built-in Asset Optimization which did JS minification, in late 2023; Netlify Site password protection not available for free tier
- Using Vite build tool for tutorial app JavaScript minification
- git --skip-worktree for config.js interfered with git switch from dev to main branch; Solution
- Using .env with Vite on client side instead of config.js; Service Worker provided config as URL parameter by script.js file
- Universal .env quoting rule (Node & Vite)
- Why sw.js is used as vanilla JS and not ES module in our app with Vite build
- Testing app locally - dev and build
- dev branch new files which are in gitignore get shown as untracked files on switching to main branch
- Long-lived branches vs. Ephemeral (short-lived) feature (dev) branches
- Netlify Build command and publish directory changes for Vite build tool
- Vite config for minifying script.js without filename change
- Checking Service Worker cache content
- Netlify app minifies JavaScript sources; Also handles missing server URL env var (config) correctly; Google Gemini web client sources are well protected
Stage-8: Testing of app after successful Vite minification of JavaScript sources
- Netlify client testing; Background Sync event registration on Close tab may result in duplicate server responses shown to user
- 6 second timeout in Sync handler is not a good idea as Service Worker handlers are expected to be ephemeral
- Commented out Close tab handler and so Sync event is not registered on tab close
- Background Sync event is highly reliable for mobile users as losing cell towers triggers OS-level offline state
HTML Refresher and Detailed Study - Part 15
Contents (sections and/or jump links)
Stage-8: Render server delaying WebSocket close event till after ws.send is used
- Introduction
- Render Load Balancer may delay sending close event to node.js server container as it is viewed as low priority
- 4-second delay for Render server to send WebSocket close event is possible
- Localhost server behaves as expected but same server on Cloud could behave differently and in unexpected ways
- On Render server ws.send callback does not report WS error for disconnected WS; Immediately after ws.send, close event is sent
- Cannot use ws.ping to check if WebSocket is alive before calling ws.send on Render server
- 100 ms timer hack after ws.send to check if close event fires immediately after ws.send
- Capturing millisecond time intervals for WebSocket events on Render server; Only 1 ms interval between ws.send log and subsequent close log
- Successful testing after adding send push notification code in WebSocket close within 100ms of send edge case for Render Cloud server
Stage-8: Further app testing
- Testing Netlify client on Android mobile; Push notifications need Chrome app to be open
- Possibility of skipping reconnect attempts when Browser says we are offline; Reconnect is attempted when Browser sends online event
- Turning off auto-deploy for monorepo projects
chat-apps repo created by extracting related folder from larger monorepo
- Splitting large monorepo into two separate monorepos
- Preparation for chat-apps repo extraction
- git-filter-repo installation
- git-filter-repo used to extract chat-apps folder as repo from original repo copy and drop other contents; Additional manual steps
- Nested git repos were ignored by top-level parent original repo
- WinMerge comparison to confirm only expected differences between new chat-apps folder and related folder in original larger repo
- git log comparison between chat-apps repo and original repo
- VS Code source control shows chat-apps commits as expected
- Using WinMerge to copy over wanted untracked files from original repo to chat-apps folder
- Installing and testing uppercase chat apps locally; Creating new GitHub repo for chat-apps
- Changing deployment settings in Render server and Netlify client to pick up new GitHub repo and testing Cloud app
Original repo restructured to remove chat apps folder and commits
- Intro
- Removing chat apps related folder and commits with git filter-repo --invert-paths --path htmlcssjs/chat-app/
- Examining commits log of modified repo
- Procedure to compress last 7 sequential commits into a single commit using git reset --soft
- Resetting git history to drop last 7 sequential commits using git reset --hard
- WinMerge comparison of new repo folder and original folder to verify results
- Deletion of two old nested trivial .git folders to incorporate related files in (get tracked by) top-level git repo
- Compressing last two sequential commits into one by git soft reset followed by new commit
- New private GitHub repo created and linked to this local git repo; Migration of new repo is now complete
- Tested some test apps in repo. All of them worked as expected
Stage-8: Continuing further tutorial chat app testing and improvement
- Intro
- When offline, halting reconnect loop; Minor UI improvements
- Zombie Socket: In go offline and quickly back online case, online event triggers new socket creation followed by delayed close event on old socket
- Ways to handle zombie WebSocket issue on client side
- Implemented and tested simple solution for zombie websocket issue
- Repeated server response at times issue
- IndexedDB delete does not return whether it actually deleted a record or not
- Debugging workaround to check whether IndexedDB delete actually deletes a record or not
- Analysis of why repeated server responses happen at times
- Solution to occasional repeated server response issue: Resetting in-memory array of pending message (outbox) ids on websocket open
- Chrome DevTools Offline on app refresh results in navigator.onLine being wrongly true! Turning off Wi-Fi adapter is sensed correctly
- Refactored app to do setup of push subscription on every successful websocket connect
- Netlify app shows warning about request notification permission used outside user gesture response; No such issue with localhost app
- Testing Netlify app on desktop: offline sync, background sync
- Testing Netlify app on Android mobile; Push notifications are unreliable on Android
- Mobile app service worker: USB debugging from PC connected to mobile is best practice; IndexedDB logger inappropriate for tutorial
- Mobile app in Chrome tab with push disabled: Receives server response though tab was closed few seconds earlier
- Mobile full screen app with push disabled: Does not receive server response when app is closed immediately after sending message
- When mobile Chrome itself is open, app tabs that are closed seem to remain alive for grace period which explains unusual behaviour
- With Chrome on Android, socket close is not sent to server on full screen app or Chrome app itself being closed by user
- No good way to close socket on web app close on Android; With Chrome on Windows, socket close sent to server on app close
HTML Refresher and Detailed Study - Part 16
Contents (sections and/or jump links)
Stage-8: Polishing tutorial chat app and creating support documentation
- In setupWebPush ask for notification permission only if not already granted
- About page: Intro
- About page as blog post - external link - will not get offline-cached
- About Page: (Discussion only) Fetching HTML from Blogger blog post using fetch will hit CORS problem
- About Page: (Discussion only) Fetching HTML from GitHub Pages static JSON site will not encounter CORS problem
- About Page solution that works with offline cache: MPA with About opened in new window and having external link to Details section in blog post
- Vite npm run preview is SPA server; Vite build config has to include about.html
- Companion blog post having details like procedure for testing app features
- PWA app opens About page like a modal
- Conditionally change focus to input field only for desktop; Pointer detection method to detect mobile
- Adding manifest.json to make app proper Progressive Web App (PWA)
- (Dynamic Viewport Height) dvh to fix scrolling need when On-Screen Keyboard appears
- PWA standalone installation on Windows 11 as well as Android mobile; Disabling swipe-to-refresh on PWA
- Testing PWA on mobile using Vite dev server on desktop PC
- PWA testing using local Vite: ngrok needs installation
- PWA testing using local Vite: localtunnel zero-install method; Cloudflare needs small installation
- PWA testing using local Vite: localtunnel method eventually worked; swipe-to-refresh on PWA disabled
- PWA testing using local Vite: (Discussion only) USB debugging OR Remote debugging with Chrome
- Improving connection status bar UI messages
- Dropped timestamp from server response
- Push notifications on mobile are much more reliable with proper PWA installed app (WebAPK transformation)
- web.dev by Chrome DevRel team has great PWA docs including Chrome and Android specific matters; MDN web docs is primary reference for PWA but vendor-neutral
- Proper PWA installation on Android improves app startup UX as well as push notifications reliability; Google Play Store possibility for PWA
- Render server timestamp removal tested; 'About This App' title justified
- 'PWA app' term commonly used in industry
- Further improvement in Connection status bar messages
- Padding on body element norms for desktop and mobile; Google Material Design uses 600px as boundary between mobile and tablet
- Dynamic Viewport Height (dvh) ensures mobile keyboard does not cover input box
- About page vertical margin and padding for desktop are appropriate; Has to be drastically reduced for mobile
- Non-invasive Chrome 'Tap to copy the URL for this app' notification every time PWA app is opened on mobile
- Reducing edge cases of ignored user messages by deleting Outbox entry only when it matches server response
HTML Refresher and Detailed Study - Part 17
Contents (sections and/or jump links)
Stage-8: Review rounds followed by snapshot folder creation
- Review rounds for client and server
- At Browser API level, Service Workers see unique ids for every tab/PWA but main script does not; Ping-Pong approach to identify active tab
- Broadcast Channel API does not provide list of clients subscribed to it
- Show push notification if multiple (chat) tabs are open
- clients.matchAll() list of clients is in order of most recently focused as per MDN
- git command to show when a particular line of code was added
- Multi-App workspace with Live Server resulted in URL check for 'client' which would fail in Netlify app
- When using Live Server for PWA apps in monorepo, change server root to client folder instead of monorepo root
- Discussion on ways for Service Worker to identify active tab and duplicate connection tab
- Testing notification being shown when two (chat) tabs are open
- const and let variables are not hoisted in usable way like var variables; Temporal Dead Zone (TDZ) and fatal ReferenceError
- Quite difficult to test whether a const or let variable is in Temporal Dead Zone (TDZ) to avoid fatal ReferenceError possibility
- Avoiding TDZ fatal ReferenceError possibility by moving const variable definition to before it is first referenced in source code
- Further testing of notification and notification click
- Squashing temporary commits on feature (WIP) branch before merging feature branch with main
- Squashing commits on main (primary) branch is not recommended
- VS Code UI for squashing commits using built-in visual editor
- VS Code UI would have provided warning for delete of local WIP branch if commits on WIP branch did not exist on current (main) branch
- Fix to differentiate between About and Chat tabs in Background Sync, Push and notification handlers in sw.js; matchAll() returns unique client ids for each open tab/PWA
- Testing and bug fixing for various edge cases took lot of time
- Duplicate connection tab should ignore online and offline events
- Duplicate connection tab's send UI is frozen but user can do other ops like view chat history and visit About page
- Duplicate connection tab may be useful for power users to view chat history while typing in a new message in active connection tab
- Timestamp for squashed git commit: Default and how to override default
- Stage 8 completion snapshot folder created
Stage-8: Public announcement of tutorial echo chat client app
- Public announcement
- My 2024 React Blog FS Netlify app and Next.js Gita app are both NOT PWA apps
- PWA app has to have a service worker; Even microsoft.com and about.google do not have offline func.
- Guides and articles on PWA offline functionality design and suitability
- My gita app could have selective offline facility for some pages like Home, Chapter Summaries, About and Settings
- No offline func.: WhatsApp, Gmail, Google Drive, Google Docs; Decent offline func.: stackedit.io, YouTube
- My March 2024 first exposure to PWA through default minimal PWA functionality provided by CRA in React app
- Gemini validation of browser as cross-platform virtual operating system dense paragraph in public announcement
- LinkedIn standard feed posts get more visibility and engagement than long-form articles
- My Dec 2024 first exposure to implementing push notifications using Firebase Cloud Messaging in test React app
- Main development points about implementation of Firebase push notifications in test React app
- Quick overview of how Firebase was used in above examples for push notifications
- Firebase Cloud Messaging is for Native messaging as well as web; Web Push API is used only for web
- Firebase Cloud Messaging doc page for web mentions w3.org Push API; My lack of knowledge about web-push package led to wrong impression
- Added echo in app description; Navigational badge link
- PWA apps need HTML and JavaScript even if HTML usage is minimal
- Showing explicit URL vs. embedding link in descriptive text
Comments
Post a Comment