<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jan Nicklas</title>
    <description>The latest articles on Forem by Jan Nicklas (@jantimon).</description>
    <link>https://forem.com/jantimon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F351856%2Fa1917b33-800c-489b-8b01-44dd1fe84f25.jpg</url>
      <title>Forem: Jan Nicklas</title>
      <link>https://forem.com/jantimon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jantimon"/>
    <language>en</language>
    <item>
      <title>Focus Containment: The Broken State of Keyboard Navigation</title>
      <dc:creator>Jan Nicklas</dc:creator>
      <pubDate>Thu, 22 May 2025 16:44:13 +0000</pubDate>
      <link>https://forem.com/jantimon/focus-containment-the-broken-state-of-keyboard-navigation-45l1</link>
      <guid>https://forem.com/jantimon/focus-containment-the-broken-state-of-keyboard-navigation-45l1</guid>
      <description>&lt;p&gt;As web developers, we strive to create experiences that work for everyone. &lt;br&gt;
Yet when it comes to keyboard navigation which is a critical component of web accessibility, we're still struggling with fundamental challenges. &lt;br&gt;
Even the largest tech companies with substantial resources fail to deliver consistent keyboard experiences.&lt;/p&gt;

&lt;p&gt;The central problem? &lt;strong&gt;Focus containment&lt;/strong&gt;.&lt;br&gt;
It's one of the most complex aspects of keyboard navigation and with the current browser APIs it feels impossible to get right.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Importance of Keyboard Navigation
&lt;/h2&gt;

&lt;p&gt;Before diving into the technical aspects, let's take a look why this matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt;: Many users with motor disabilities rely entirely on keyboards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power users&lt;/strong&gt;: Keyboard shortcuts enable efficient navigation for all users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screen reader users&lt;/strong&gt;: Rely on consistent focus management to understand page context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A broken focus experience isn't just an inconvenience - it can render a website completely unusable for a significant portion of your audience.&lt;/p&gt;
&lt;h2&gt;
  
  
  Baseline
&lt;/h2&gt;

&lt;p&gt;To be fair, defining a common expaction for keyboard navigation is tricky. But many users expect web content to behave as predictably as the browser UI itself. Think about it when you hit Tab while in the URL bar, you know exactly what's going to happen. Focus moves to the next element in a logical sequence, with clear visual feedback. No surprises.. it just works: &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/jEqaiA_kats"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The Current State: A Case Study in Frustration
&lt;/h2&gt;

&lt;p&gt;Let's look at some high-profile examples of focus management failures:&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 1: The Disappearing Focus State
&lt;/h3&gt;

&lt;p&gt;By default overlays will not contain focus which leads to a frustrating experience for keyboard users.&lt;br&gt;
As you can see on Gitlab opening the search bar and then pressing tab causes focus to move completely off-screen. &lt;br&gt;
The user is essentially "lost" with no visual indication of where they are in the page:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/NvEJPtncS74"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 2: Inconsistent Controls
&lt;/h3&gt;

&lt;p&gt;Many sites use very inconsistent key controls for focus traps.&lt;br&gt;
Like many other sites Mozilla's website implements focus traps that use tab keys in some components and arrow keys in others - even on the same page next to each other.&lt;br&gt;&lt;br&gt;
The search behaviour is different from the native browser url bar and often leads to the frustrating experience where users must guess which key to use in each context&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/RfN5BPuR9Vk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 2: Unreachable Elements on Amazon, Bing and YouTube
&lt;/h3&gt;

&lt;p&gt;Amazon, Bing and YouTube contain interface elements that are completely unreachable via keyboard in certain states, effectively blocking keyboard users from core functionality&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/SUDViPZ_Rn8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/9hdZ-maQZb4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZvPjk0eA_a8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If organizations with dedicated accessibility teams and substantial resources struggle with these basics, what hope do smaller teams have?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Inadequate Approaches We Currently Use
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. No Solution At All
&lt;/h3&gt;

&lt;p&gt;Many sites simply don't address focus containment. This results in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Focus disappearing off-screen&lt;/li&gt;
&lt;li&gt;Users tabbing into invisible or irrelevant areas&lt;/li&gt;
&lt;li&gt;Complete loss of context for keyboard and screen reader users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This "solution" is really the absence of one, creating a poor experience that's unfortunately still common even on major platforms like GitLab.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. JavaScript Focus Traps
&lt;/h3&gt;

&lt;p&gt;The most common approach today involves complex JavaScript to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create and maintain lists of focusable elements&lt;/li&gt;
&lt;li&gt;Handle tab/shift+tab at boundaries&lt;/li&gt;
&lt;li&gt;Restore focus when traps are closed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But these implementations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are difficult to maintain&lt;/li&gt;
&lt;li&gt;Often contain bugs&lt;/li&gt;
&lt;li&gt;Rarely account for dynamically added content&lt;/li&gt;
&lt;li&gt;Create inconsistent experiences across websites&lt;/li&gt;
&lt;li&gt;Frequently fail to handle edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical focus trap implementation requires 100+ lines of JavaScript with numerous edge cases. Here's just a small sample of what's needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified example - real implementations are much more complex&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createFocusTrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;firstElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;lastElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;lastElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;firstElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even the best implementations are brittle and fail in complex scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; Element
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element is a step forward, providing native focus containment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"myDialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Dialog Content&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"closeDialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Close&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myDialog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Activates native focus containment&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser-handled focus management&lt;/li&gt;
&lt;li&gt;Ability to access browser UI (a significant improvement)&lt;/li&gt;
&lt;li&gt;Built-in backdrop and stacking context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it still has critical limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inflexible single-container approach&lt;/strong&gt;: Only works for simple dialogs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can't handle multiple interactive layers&lt;/strong&gt;: No way to allow focus on both a dialog and a toast notification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No simple way to create custom dialog-like components&lt;/strong&gt;: Forces using the dialog element even when it's not semantically appropriate&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. The &lt;code&gt;inert&lt;/code&gt; Attribute
&lt;/h3&gt;

&lt;p&gt;The HTML &lt;code&gt;inert&lt;/code&gt; attribute is our most powerful option today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;Navigation here&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;inert&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This content is not interactive&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;This content remains interactive&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;inert&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Footer content here&lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets us mark specific parts of the page as non-interactive, which is a reversal of the focus trap approach. Instead of containing focus within an element, we remove interactivity from everything else.&lt;/p&gt;

&lt;p&gt;While powerful, it has a fatal flaw: &lt;strong&gt;no support for "donut holes" of interactivity&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Donut Hole Problem
&lt;/h2&gt;

&lt;p&gt;Imagine a common UI pattern: a modal with a toast notification that appears on top. The modal should trap focus, but users should also be able to interact with the toast:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwh8dl1j5mnpui8lv39ed.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwh8dl1j5mnpui8lv39ed.jpg" alt="auction with toast mockup" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or a search bar which has to be inside the header to receive focus but should also allow focus in its flyout:&lt;/p&gt;

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

&lt;p&gt;With current solutions, this is nearly impossible to implement correctly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Using &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/strong&gt;: The toast/input would be outside the dialog's focus trap, making it unreachable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using JavaScript traps&lt;/strong&gt;: Requires complex coordination between multiple traps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using &lt;code&gt;inert&lt;/code&gt;&lt;/strong&gt;: You'd need to mark everything except the modal as inert, then somehow exempt the toast/input.. This is quite complex and has various edge cases. For example you would have to watch the entire DOM for new appearing elements&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This "donut hole" problem (where you need interactive elements inside otherwise non-interactive areas) appears in various UI patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modals with toast notifications&lt;/li&gt;
&lt;li&gt;Search overlays with interactive results&lt;/li&gt;
&lt;li&gt;Dropdown menus with nested interactive components&lt;/li&gt;
&lt;li&gt;Tutorial overlays with interactive elements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Missed Opportunity: CSS &lt;code&gt;interactivity: inert&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The CSS Working Group developed a promising solution in the interactivity property draft.&lt;br&gt;
&lt;a href="https://github.com/w3c/csswg-drafts/issues/10711" rel="noopener noreferrer"&gt;https://github.com/w3c/csswg-drafts/issues/10711&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And Chrome 135 has already shipped the new CSS interactivity property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Make the main content inert */&lt;/span&gt;
&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;interactivity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inert&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/* Create another "donut hole" of interactivity */&lt;/span&gt;
&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="nc"&gt;.active-component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;interactivity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach could revolutionise focus management by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Making focus containment declarative rather than imperative&lt;/li&gt;
&lt;li&gt;Allowing for nested interactive zones (solving the donut hole problem)&lt;/li&gt;
&lt;li&gt;Separating behavior from structure&lt;/li&gt;
&lt;li&gt;Enabling responsive focus containment through media queries&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But critically, according to discussions in the draft, the working group &lt;a href="https://github.com/w3c/csswg-drafts/issues/10711#issuecomment-2884252218" rel="noopener noreferrer"&gt;decided to remove support for "interactive donut holes" again&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This would essentially reduce this new css property to be equivalent to the existing HTML &lt;code&gt;inert&lt;/code&gt; attribute. &lt;br&gt;
So the latest plan is going back to the status quo of the &lt;code&gt;inert&lt;/code&gt; html attribute instead of improving APIs for authors to build better keyboard user experiences.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Call for Change
&lt;/h2&gt;

&lt;p&gt;I believe we should keep the CSS &lt;code&gt;interactivity&lt;/code&gt; property with support for donut holes like shipped in Chrome 135. &lt;/p&gt;

&lt;p&gt;It will help developers to solve complex focus containment problems with simpler, more declarative code.&lt;/p&gt;

&lt;p&gt;Theoretically, you could still hack around these limitations with some creative CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.active-component&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.active-component&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;.active-component&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="py"&gt;interactivity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inert&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;   
&lt;span class="nc"&gt;.active-component&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   
  &lt;span class="py"&gt;interactivity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   
&lt;span class="p"&gt;}&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;But let's be honest&lt;/strong&gt; this is a hack.&lt;br&gt;&lt;br&gt;
The browser would need to recalculate styles for &lt;strong&gt;every single element&lt;/strong&gt; on the page, causing FPS drops during interactions. And very often these kinds of hacks tend to become maintenance issues or buggy over time..&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Need Better Standards
&lt;/h2&gt;

&lt;p&gt;With the current APIs before Chrome 135 web authors weren't able to fix their keyboard interactions&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Current solutions have too many gaps for modern web applications&lt;/li&gt;
&lt;li&gt;Developers need declarative ways to control focus&lt;/li&gt;
&lt;li&gt;The "donut hole" feature is a real-world requirement&lt;/li&gt;
&lt;li&gt;Core accessibility shouldn't require JavaScript focus hacks&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What You Can Do Today
&lt;/h2&gt;

&lt;p&gt;While we advocate for better standards, here are some best practices to implement today:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element when appropriate&lt;/strong&gt;&lt;br&gt;It's not perfect, but it's better than custom JavaScript for simple modals&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage the &lt;code&gt;inert&lt;/code&gt; attribute&lt;/strong&gt;&lt;br&gt;For simple cases, this can provide good focus management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test aggressively with keyboards&lt;/strong&gt;&lt;br&gt;Verify that users can always see where focus is and access all functionality&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consider alternative patterns&lt;/strong&gt;&lt;br&gt;Sometimes simplifying your UI is an option and can eliminate complex focus management needs&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Focus containment remains one of web development's most challenging accessibility problems. The current tools force developers to choose between poor user experiences and complex, fragile implementations.&lt;/p&gt;

&lt;p&gt;If we want a more accessible web, we need better primitives. The decision to limit the CSS &lt;code&gt;interactivity&lt;/code&gt; property might become a missed opportunity to solve a critical problem. Let's ask for revisiting this decision and providing developers with the tools they need to create truly accessible experiences.&lt;/p&gt;

&lt;p&gt;Because when even the biggest tech companies struggle to get this right, the problem might not be with the developers - it's probably with our tools&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>a11y</category>
      <category>frontend</category>
    </item>
    <item>
      <title>html-webpack-plugin 4 has been released!</title>
      <dc:creator>Jan Nicklas</dc:creator>
      <pubDate>Mon, 23 Mar 2020 11:42:58 +0000</pubDate>
      <link>https://forem.com/jantimon/html-webpack-plugin-4-has-been-released-125d</link>
      <guid>https://forem.com/jantimon/html-webpack-plugin-4-has-been-released-125d</guid>
      <description>&lt;p&gt;It took way too long but finally the new major version of the &lt;a href="https://www.npmjs.com/package/html-webpack-plugin" rel="noopener noreferrer"&gt;&lt;code&gt;html-webpack-plugin&lt;/code&gt;&lt;/a&gt; is making it's leap from beta to a stable release! &lt;/p&gt;

&lt;h2&gt;
  
  
  Performance!
&lt;/h2&gt;

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

&lt;p&gt;One big goal for this release was to improve the performance during development and production builds.&lt;/p&gt;

&lt;p&gt;The performance boost was gained mainly by dropping the usage of &lt;code&gt;compilation.getStats().toJson()&lt;/code&gt; thanks to a new API provided by the webpack core team around &lt;a href="https://twitter.com/wSokra" rel="noopener noreferrer"&gt;&lt;strong&gt;@sokra&lt;/strong&gt;&lt;/a&gt;. This new API provides all information necessary to inject the scripts and styles into the html code.&lt;br&gt;&lt;br&gt;
Unfortunately relying on that API means that &lt;strong&gt;webpack 1-3 can not be supported anymore&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To further increase the performance the entire caching approach was rebuilt from scratch to decrease the compilation efforts.&lt;/p&gt;

&lt;p&gt;The alpha tester feedback was great!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jantimon/html-webpack-plugin/pull/953#issuecomment-417408140" rel="noopener noreferrer"&gt;#953&lt;/a&gt; "The best of 5 total build time goes down from 10.41s (with 4.0.0-alpha) to 10.29s - which is now &lt;strong&gt;only 130ms slower than when not using the plugin at all&lt;/strong&gt; :-)"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jantimon/html-webpack-plugin/issues/962#issuecomment-409070361" rel="noopener noreferrer"&gt;#962&lt;/a&gt;: "For reference, my project (which has grown substantially since #962) builds in &lt;strong&gt;~8000ms without and ~1000ms with these changes&lt;/strong&gt;."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmsz8fmtz7d5zsmr2w32i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmsz8fmtz7d5zsmr2w32i.png" alt="CompileTime" width="495" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While working on those performance improvements I wrote a &lt;a href="https://github.com/jantimon/cpuprofile-webpack-plugin" rel="noopener noreferrer"&gt;cpuprofile-webpack-plugin&lt;/a&gt; - a small util to analyse your webpack build performance for production build but even more importantly for recompilations during development.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1149466691627130881-19" src="https://platform.twitter.com/embed/Tweet.html?id=1149466691627130881"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1149466691627130881-19');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1149466691627130881&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Feel free to give it a try and let me know what you think :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Template language support
&lt;/h2&gt;

&lt;p&gt;Since html-webpack-plugin 2.x has been able to use the loaders specified inside the webpack config file. Therefore it is not only capable of compiling &lt;code&gt;.ejs&lt;/code&gt; templates but &lt;a href="https://github.com/webpack/docs/wiki/list-of-loaders#templating" rel="noopener noreferrer"&gt;any code which can be transpiled with a webpack loader&lt;/a&gt; (&lt;code&gt;hbs&lt;/code&gt;, &lt;code&gt;ejs&lt;/code&gt;, &lt;code&gt;twig&lt;/code&gt;, &lt;code&gt;dust&lt;/code&gt;, &lt;code&gt;pug&lt;/code&gt;, &lt;code&gt;htl&lt;/code&gt;, &lt;code&gt;js,&lt;/code&gt; &lt;code&gt;ts,&lt;/code&gt; &lt;code&gt;jsx&lt;/code&gt;, &lt;code&gt;tsx&lt;/code&gt; ...).  &lt;/p&gt;

&lt;p&gt;A javascript or jsx template allows even to generate a static server side rendered version of your application.  &lt;/p&gt;

&lt;p&gt;html-webpack-plugin template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOMServer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;!doctype html&amp;gt;
    &amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
      &amp;lt;meta charset="utf-8"&amp;gt;
      &amp;lt;title&amp;gt;Server Side Rendering Demo&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;div id="root"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ReactDOMServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For further details on how to connect loaders to the html-webpack-plugin checkout the &lt;a href="https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md" rel="noopener noreferrer"&gt;template option docs&lt;/a&gt; or take a look at the &lt;a href="https://codesandbox.io/s/html-webpack-plugin--jsx-sjbcq" rel="noopener noreferrer"&gt;html-webpack-plugin jsx codesandbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another way to use the html-webpack-plugin to prerender a static page out of your app is the &lt;a href="https://github.com/GoogleChromeLabs/prerender-loader" rel="noopener noreferrer"&gt;prerender-loader&lt;/a&gt; from &lt;a href="https://twitter.com/_developit" rel="noopener noreferrer"&gt;@developit&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Meta Tags
&lt;/h2&gt;

&lt;p&gt;The html-webpack-plugin is now able to inject meta-tags without writing custom templates:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width=device-width, initial-scale=1, shrink-to-fit=no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Base Tags
&lt;/h2&gt;

&lt;p&gt;Similar to the meta tags it is now also possible to add a base tag:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Custom Template with Zero Config
&lt;/h2&gt;

&lt;p&gt;Customizing the template is now possible without configuration.&lt;br&gt;&lt;br&gt;
From version 4 the html-webpack-plugin will look for a local &lt;code&gt;src/index.ejs&lt;/code&gt; file. If such a file can be found it will be used as a template:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F08wr7cj5ty8nu8qot69y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F08wr7cj5ty8nu8qot69y.png" alt="Zero Config Template" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Minification by default
&lt;/h2&gt;

&lt;p&gt;Thanks to the work by &lt;a href="https://github.com/edmorley" rel="noopener noreferrer"&gt;&lt;strong&gt;@edmorley&lt;/strong&gt;&lt;/a&gt; we were able to enable html minification by default if webpack is running in &lt;code&gt;production&lt;/code&gt; mode. This behaviour can be disabled by adding &lt;code&gt;minification: false&lt;/code&gt; to the html-webpack-plugin configuration.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/jantimon/html-webpack-plugin/pull/1048" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Enable minification by default when 'mode' is production
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#1048&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/edmorley" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars3.githubusercontent.com%2Fu%2F501702%3Fv%3D4" alt="edmorley avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/edmorley" rel="noopener noreferrer"&gt;edmorley&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/jantimon/html-webpack-plugin/pull/1048" rel="noopener noreferrer"&gt;&lt;time&gt;Sep 16, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Previously minification was disabled by default. Now, if &lt;code&gt;minify&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt; and &lt;code&gt;mode&lt;/code&gt; is &lt;code&gt;'production'&lt;/code&gt;, then it is enabled using the following options:&lt;/p&gt;
&lt;div class="highlight highlight-source-js js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;collapseWhitespace&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;removeComments&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;removeRedundantAttributes&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;removeScriptTypeAttributes&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;removeStyleLinkTypeAttributes&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;useShortDoctype&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;These options were based on the settings used by create-react-app, Neutrino and vue-cli, and are hopefully fairly conservative. See:
&lt;a href="https://github.com/jantimon/html-webpack-plugin/issues/1036#issuecomment-421408841" rel="noopener noreferrer"&gt;https://github.com/jantimon/html-webpack-plugin/issues/1036#issuecomment-421408841&lt;/a&gt;
&lt;a href="https://github.com/kangax/html-minifier#options-quick-reference" rel="noopener noreferrer"&gt;https://github.com/kangax/html-minifier#options-quick-reference&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These same defaults can enabled regardless of &lt;code&gt;mode&lt;/code&gt;, by setting &lt;code&gt;minify&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; (which previously passed an empty object to html-minifier, meaning most minification features were disabled). Similarly, minification can be disabled even in production, by setting &lt;code&gt;minify&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This change has no effect on users who pass an object to &lt;code&gt;minify&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Fixes #1036.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jantimon/html-webpack-plugin/pull/1048" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frtvdfwc2whva4ay9vlr4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frtvdfwc2whva4ay9vlr4.png" alt="minification demo" width="540" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Allow template variable modification
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/youyuxi" rel="noopener noreferrer"&gt;Evan You&lt;/a&gt; asked for a better way to modify which values are sent down to the template:&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/jantimon/html-webpack-plugin/pull/830" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        feat(template): support custom template params
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#830&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/yyx990803" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars1.githubusercontent.com%2Fu%2F499550%3Fv%3D4" alt="yyx990803 avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/yyx990803" rel="noopener noreferrer"&gt;yyx990803&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/jantimon/html-webpack-plugin/pull/830" rel="noopener noreferrer"&gt;&lt;time&gt;Jan 01, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;This allows the user to inject custom variables to be used in the template interpolation, e.g. simplifying &lt;code&gt;webpackConfig.output.publicPath&lt;/code&gt; to something shorter.&lt;/p&gt;
&lt;p&gt;Docs/tests are not included, but if this sounds like a good idea I can add those upon request.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jantimon/html-webpack-plugin/pull/830" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The outcome was a new &lt;code&gt;templateVariables&lt;/code&gt; option which allows to add additional data e.g. &lt;code&gt;process.env&lt;/code&gt; to the values which are sent down to the template:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5xmkkukzdikgpn7npmyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5xmkkukzdikgpn7npmyp.png" alt="TemplateVariables" width="751" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Nonblocking script loading
&lt;/h2&gt;

&lt;p&gt;Until now all script tags were added at the end of the body tag.&lt;br&gt;&lt;br&gt;
However now that all modern browsers allow to load javascript in parallel without pausing the html parsing the &lt;code&gt;scriptLoading&lt;/code&gt; option can speed up your users page loads.&lt;/p&gt;

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

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;scriptLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;defer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  New Hooks
&lt;/h2&gt;

&lt;p&gt;The webpack core team asked to upgrade to the new hook system to further increase the webpack build speed.&lt;/p&gt;

&lt;p&gt;These hooks allow plugin developers to change the default behaviour of the html-webpack-plugin. The following chart shows the flow and the hooks (&lt;code&gt;beforeAssetTagGeneration&lt;/code&gt;, &lt;code&gt;alterAssetTags&lt;/code&gt;, &lt;code&gt;alterAssetTagGroups&lt;/code&gt;, &lt;code&gt;afterTemplateExecution&lt;/code&gt;, &lt;code&gt;beforeEmit&lt;/code&gt;, &lt;code&gt;afterEmit&lt;/code&gt;):&lt;/p&gt;

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

&lt;p&gt;Here is an example for a plugin which manipulates the generated html file in the &lt;code&gt;beforeEmit&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HtmlWebpackPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyPlugin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;apply &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compiler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;compiler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compilation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compilation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Static Plugin interface |compilation |HOOK NAME | register listener &lt;/span&gt;
      &lt;span class="nx"&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compilation&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;beforeEmit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tapAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Set a meaningful name here for stacktraces&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Manipulate the content&lt;/span&gt;
          &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The Magic Footer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="c1"&gt;// Tell webpack to move on&lt;/span&gt;
          &lt;span class="nf"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MyPlugin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For more information please take a look at the &lt;a href="https://github.com/jantimon/html-webpack-plugin#events" rel="noopener noreferrer"&gt;readme events section&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Contributors
&lt;/h2&gt;

&lt;p&gt;People around the world are approaching me to ask for help, suggest and work on new features, fix a typo or even fix entire problems. Thank you so much and please keep it up! :)&lt;/p&gt;

&lt;p&gt;If you have any feedback for this release &lt;a href="https://github.com/jantimon/html-webpack-plugin/" rel="noopener noreferrer"&gt;create an issue&lt;/a&gt; or contact me on twitter &lt;a href="https://twitter.com/jantimon" rel="noopener noreferrer"&gt;@jantimon&lt;/a&gt; (direct messages are open).&lt;/p&gt;
&lt;h2&gt;
  
  
  Sponsors
&lt;/h2&gt;

&lt;p&gt;Big thanks to all the sponsors who supported the development over the last years.&lt;br&gt;&lt;br&gt;
Especially &lt;a href="https://tipe.io/" rel="noopener noreferrer"&gt;TipeIO&lt;/a&gt; and &lt;a href="https://www.principal.com/" rel="noopener noreferrer"&gt;Principal Financial Services, Inc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opencollective.com/html-webpack-plugin" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxh6i2jrqqv96ht8te17s.png" alt="Sponsors" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Full changelog
&lt;/h2&gt;

&lt;p&gt;The changelog with all changes can be found directly on &lt;a href="https://github.com/jantimon/html-webpack-plugin/blob/master/CHANGELOG.md" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is coming up next?
&lt;/h2&gt;

&lt;p&gt;The next goal is to be fully compatible with the Webpack 5. Especially with the new Webpack 5 &lt;code&gt;FileSystemInfo&lt;/code&gt; API to solve &lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/jantimon/html-webpack-plugin/issues/1269" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Webpack 5 Support Plan?
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#1269&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/septs" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars0.githubusercontent.com%2Fu%2F3842474%3Fv%3D4" alt="septs avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/septs" rel="noopener noreferrer"&gt;septs&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/jantimon/html-webpack-plugin/issues/1269" rel="noopener noreferrer"&gt;&lt;time&gt;Sep 09, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;The webpack 5.0 changelog
&lt;a href="https://github.com/webpack/changelog-v5/blob/master/README.md" rel="noopener noreferrer"&gt;https://github.com/webpack/changelog-v5/blob/master/README.md&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/jantimon"&gt;@jantimon&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jantimon/html-webpack-plugin/issues/1269" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;Another goal would be to further improve browser load times. For browsers with support for preloading &lt;a href="https://twitter.com/wSokra" rel="noopener noreferrer"&gt;&lt;strong&gt;@sokra&lt;/strong&gt;&lt;/a&gt; proposed an even faster approach than &lt;code&gt;scriptLoading: 'defer'&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>webpack</category>
      <category>html</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
