<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Keeping Up To Date</title>
    <link>http://keepinguptodate.com</link>
    <description></description>
    <language>en-gb</language>
    <copyright>Copyright Jon Keeping</copyright>
    <lastBuildDate>Mon, 30 Aug 2021 01:00:00 GMT</lastBuildDate>
    <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
    <managingEditor>rss-managing-editor@keepinguptodate.com</managingEditor>
    <webMaster>rss-technical-issues@keepinguptodate.com</webMaster>
    <ttl>60</ttl>
    <atom:link href="https://keepinguptodate.com/rss.xml" rel="self" type="application/rss+xml" />
    
    <item>
        <title>Creating a Google Lighthouse custom audit that checks DOM elements</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>Lighthouse provides a rich set of built-in audits but can also be extended to run your own custom audits.
This can be extremely useful if you have your own specific checks that you want to perform on your site, such as checking all links to external sites have the <code>rel=&quot;noopener&quot;</code> attribute or contain a specific class to visually indicate an external link etc.</p>
<img src="http://keepinguptodate.com/pages/2021/08/lighthouse-report-summary.png" loading="lazy" alt="Lighthouse report summary">
<p>In this article, we’re going to write a custom audit to check for the well known web accessibility issue of adding a click handler to a non-interactive element and not ensuring it’s accessible for keyboard and screen reader users.
My article on
<a href="https://keepinguptodate.com/pages/2019/04/accessible-javascript-click-handlers/" target="_blank" rel="noopener">Accessible JavaScript Click Handlers</a>
covers this web accessibility issue in more detail.</p>
<hr>
<h2 id="javascript-modules-for-our-custom-audit">JavaScript modules for our custom audit</h2>
<p>There are 3 JavaScript modules we’ll need to create for our custom audit.</p>
<ol>
<li><strong>Gatherer</strong> —  Collects information about the page.
<a href="https://github.com/GoogleChrome/lighthouse/tree/master/lighthouse-core/gather/gatherers" target="_blank" rel="noopener">There are many standard gathers built-in to Lighthouse</a>,
including the
<a href="https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/gather/gatherers/anchor-elements.js" target="_blank" rel="noopener"><code>AnchorElements</code></a>
gatherer, but this doesn’t collect all the details we require so we’ll create our own.</li>
<li><strong>Audit</strong> — Converts the gatherer data into a score and  in our case, returns details to display for each element that has failed.</li>
<li><strong>Config</strong> — Provides the configuration for Lighthouse to register and run our custom gatherer and audit.</li>
</ol>
<!-- Excerpt End -->
<p><a href="https://github.com/JonUK/lighthouse-custom-audit" target="_blank" rel="noopener">The code for this article is available on GitHub</a>
and contains a
<a href="https://github.com/JonUK/lighthouse-custom-audit/blob/main/test-site/index.html" target="_blank" rel="noopener">test HTML page</a>
with a <code>&lt;div&gt;</code> element containing a click handler and no other web accessibility considerations.
This is what we want our custom audit to catch.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inaccessibleDiv<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn btn-primary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  This is an inaccessible div element with a click handler<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>This test page can be viewed locally via a static web server.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> <span class="token function">install</span><br><span class="token function">npm</span> run serve</code></pre>
<p>The command behind <code>npm run serve</code> is:</p>
<pre class="language-bashnull"><code class="language-bashnull">http-server ./test-site --port <span class="token number">8085</span> -o</code></pre>
<hr>
<h2 id="creating-the-custom-gatherer">Creating the custom gatherer</h2>
<p>Within the folder <code>gatherers</code>, create a new JavaScript file <code>non-interactive-click-handlers-gatherer.js</code>.</p>
<pre class="language-javascript code--small"><code class="language-javascript code--small"><span class="highlight-line"><span class="token keyword">const</span> <span class="token punctuation">{</span> Gatherer <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'lighthouse'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token keyword">const</span> pageFunctions <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'lighthouse/lighthouse-core/lib/page-functions'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token comment">/* global getNodeDetails */</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token keyword">class</span> <span class="token class-name">NonInteractiveClickHandlersGatherer</span> <span class="token keyword">extends</span> <span class="token class-name">Gatherer</span> <span class="token punctuation">{</span></span><br>  <span class="token comment">/**<br><span class="highlight-line">   * @param {LH.Gatherer.PassContext} options</span><br><span class="highlight-line">   * @param {LH.Gatherer.LoadData} loadData</span><br>   */</span><br><span class="highlight-line">  <span class="token keyword">async</span> <span class="token function">afterPass</span><span class="token punctuation">(</span><span class="token parameter">options<span class="token punctuation">,</span> loadData</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    <span class="token keyword">const</span> driver <span class="token operator">=</span> options<span class="token punctuation">.</span>driver<span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">    <span class="token keyword">const</span> <span class="token function-variable function">mainFn</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br><span class="highlight-line"></span><br>      <span class="token comment">/**<br><span class="highlight-line">       * Returns a boolean indicating if the element has an event listener for type</span><br><span class="highlight-line">       * @param {HTMLElement} element</span><br><span class="highlight-line">       * @param {string} type Event type e.g. 'click'</span><br><span class="highlight-line">       * @returns {boolean}</span><br>       */</span><br><span class="highlight-line">      <span class="token keyword">function</span> <span class="token function">hasEventListener</span><span class="token punctuation">(</span><span class="token parameter">element<span class="token punctuation">,</span> type</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">        <span class="token keyword">const</span> eventListeners <span class="token operator">=</span> <span class="token function">getEventListeners</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">        <span class="token keyword">return</span> <span class="token operator">!</span><span class="token operator">!</span>eventListeners<span class="token punctuation">[</span>type<span class="token punctuation">]</span><span class="token punctuation">;</span></span><br><span class="highlight-line">      <span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">      <span class="token comment">// The tag names of the non-interactive elements which we'll check</span></span><br><span class="highlight-line">      <span class="token keyword">const</span> tagNamesToCheck <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'div'</span><span class="token punctuation">,</span> <span class="token string">'span'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span><br><span class="highlight-line">      <span class="token keyword">const</span> selector <span class="token operator">=</span> tagNamesToCheck<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">', '</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">      <span class="token keyword">const</span> elements <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span>selector<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">      <span class="token keyword">const</span> failingElements <span class="token operator">=</span> elements</span><br><span class="highlight-line">        <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">element</span> <span class="token operator">=></span> <span class="token function">hasEventListener</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> <span class="token string">'click'</span><span class="token punctuation">)</span><span class="token punctuation">)</span></span><br><span class="highlight-line">        <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">element</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br><span class="highlight-line">          <span class="token comment">// Assume that any keyboard related handlers are covering web</span></span><br><span class="highlight-line">          <span class="token comment">// accessibility for keyboard users (this could be a false negative).</span></span><br><span class="highlight-line">          <span class="token keyword">const</span> hasKeyHandler <span class="token operator">=</span></span><br><span class="highlight-line">            <span class="token function">hasEventListener</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> <span class="token string">'keydown'</span><span class="token punctuation">)</span> <span class="token operator">||</span></span><br><span class="highlight-line">            <span class="token function">hasEventListener</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> <span class="token string">'keyup'</span><span class="token punctuation">)</span> <span class="token operator">||</span></span><br><span class="highlight-line">            <span class="token function">hasEventListener</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> <span class="token string">'keypress'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">          <span class="token comment">// Without the "tabindex" attribute, a non-interactive element won't be</span></span><br><span class="highlight-line">          <span class="token comment">// able to receive focus. It's only recommended to use the value 0 which</span></span><br><span class="highlight-line">          <span class="token comment">// means the element receives focus in the DOM order.</span></span><br><span class="highlight-line">          <span class="token keyword">const</span> hasValidTabIndex <span class="token operator">=</span> element<span class="token punctuation">.</span>tabIndex <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">          <span class="token keyword">const</span> hasRoleAttribute <span class="token operator">=</span> element<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'role'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">          <span class="token keyword">return</span> <span class="token operator">!</span>hasKeyHandler <span class="token operator">||</span> <span class="token operator">!</span>hasValidTabIndex <span class="token operator">||</span> <span class="token operator">!</span>hasRoleAttribute<span class="token punctuation">;</span></span><br><span class="highlight-line">        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">      <span class="token keyword">const</span> elementSummaries <span class="token operator">=</span> failingElements<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">element</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span></span><br><span class="highlight-line">        <span class="token comment">// getNodeDetails is put into scope via the "deps" array</span></span><br><span class="highlight-line">        tagName<span class="token operator">:</span> element<span class="token punctuation">.</span>tagName<span class="token punctuation">,</span></span><br><span class="highlight-line">        node<span class="token operator">:</span> <span class="token function">getNodeDetails</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span></span><br><span class="highlight-line">      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br>      <span class="token comment">/**<br><span class="highlight-line">       * @return {LH.Gatherer.PhaseResult}</span><br>       */</span><br><span class="highlight-line">      <span class="token keyword">return</span> elementSummaries<span class="token punctuation">;</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">    <span class="token keyword">return</span> driver<span class="token punctuation">.</span>executionContext<span class="token punctuation">.</span><span class="token function">evaluate</span><span class="token punctuation">(</span>mainFn<span class="token punctuation">,</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      args<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      deps<span class="token operator">:</span> <span class="token punctuation">[</span></span><br><span class="highlight-line">        pageFunctions<span class="token punctuation">.</span>getElementsInDocumentString<span class="token punctuation">,</span></span><br><span class="highlight-line">        pageFunctions<span class="token punctuation">.</span>getNodeDetailsString<span class="token punctuation">,</span></span><br><span class="highlight-line">      <span class="token punctuation">]</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> NonInteractiveClickHandlersGatherer<span class="token punctuation">;</span></span></code></pre>
<p>The gatherer will look at each <code>&lt;div&gt;</code> and <code>&lt;span&gt;</code> element that has a click handler but doesn’t have any of the following:</p>
<ul>
<li>Keyboard event handlers</li>
<li>The <code>tabindex</code> attribute with the value <code>0</code></li>
<li>The <code>role</code> attribute</li>
</ul>
<p>For each element that matches, we’ll build up an array of objects with the tag name and a
<a href="https://github.com/GoogleChrome/lighthouse/blob/master/types/artifacts.d.ts#:~:text=interface%20NodeDetails" target="_blank" rel="noopener"><code>LH.Artifacts.NodeDetails</code></a>
object. This <code>LH.Artifacts.NodeDetails</code> object will allow us to output the element HTML in the report later when we create our audit module. The array is the data returned from our custom gatherer.</p>
<hr>
<h2 id="creating-the-custom-audit">Creating the custom audit</h2>
<p>Within the folder <code>audits</code>, create a new JavaScript file <code>non-interactive-click-handlers-audit.js</code>.</p>
<pre class="language-javascript code--small"><code class="language-javascript code--small"><span class="highlight-line"><span class="token keyword">const</span> <span class="token punctuation">{</span> Audit <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'lighthouse'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token keyword">class</span> <span class="token class-name">NonInteractiveClickHandlersAudit</span> <span class="token keyword">extends</span> <span class="token class-name">Audit</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">  <span class="token keyword">static</span> <span class="token keyword">get</span> <span class="token function">meta</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br>    <span class="token comment">/**<br><span class="highlight-line">     * @return {LH.Audit.Meta}</span><br>     */</span><br><span class="highlight-line">    <span class="token keyword">return</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      id<span class="token operator">:</span> <span class="token string">'non-interactive-click-handlers'</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      title<span class="token operator">:</span> <span class="token string">'Checks that click handlers on non-interactive elements are fully accessible'</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      failureTitle<span class="token operator">:</span> <span class="token string">'One or more non-interactive elements has a click which is not fully accessible'</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      description<span class="token operator">:</span></span><br>        <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">When click handlers are added to non-interactive elements like div and span,<br><span class="highlight-line">         keyboard handling should be used to ensure the element is operable via the</span><br><span class="highlight-line">         keyboard and the tabindex attribute used so the element can receive focus.</span><br>         The element should also be given a role to assist screen reader users.</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span><br><span class="highlight-line"></span><br><span class="highlight-line">      <span class="token comment">// The name of the built-in or custom gatherer class that provides data to this audit</span></span><br><span class="highlight-line">      requiredArtifacts<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'NonInteractiveClickHandlersGatherer'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br>  <span class="token comment">/**<br><span class="highlight-line">   * @return {LH.Audit.ScoreOptions}</span><br>   */</span><br><span class="highlight-line">  <span class="token keyword">static</span> <span class="token keyword">get</span> <span class="token function">defaultOptions</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    <span class="token keyword">return</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      <span class="token comment">// See https://www.desmos.com/calculator/tsunbwqt3f</span></span><br><span class="highlight-line">      p10<span class="token operator">:</span> <span class="token number">0.5</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      median<span class="token operator">:</span> <span class="token number">1</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br>  <span class="token comment">/**<br><span class="highlight-line">   * @param {LH.Artifacts} artifacts</span><br><span class="highlight-line">   * @param {LH.Audit.Context} context</span><br>   */</span><br><span class="highlight-line">  <span class="token keyword">static</span> <span class="token function">audit</span><span class="token punctuation">(</span><span class="token parameter">artifacts<span class="token punctuation">,</span> context</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    <span class="token comment">// Get the data / elements collected by the gatherer</span></span><br><span class="highlight-line">    <span class="token keyword">const</span> elementSummaries <span class="token operator">=</span> artifacts<span class="token punctuation">.</span>NonInteractiveClickHandlersGatherer<span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">    <span class="token keyword">const</span> results <span class="token operator">=</span> elementSummaries<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">element</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span></span><br><span class="highlight-line">      node<span class="token operator">:</span> Audit<span class="token punctuation">.</span><span class="token function">makeNodeItem</span><span class="token punctuation">(</span>element<span class="token punctuation">.</span>node<span class="token punctuation">)</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      tagName<span class="token operator">:</span> element<span class="token punctuation">.</span>tagName</span><br><span class="highlight-line">    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">    <span class="token comment">/** @type {LH.Audit.Details.Table['headings']} */</span></span><br><span class="highlight-line">    <span class="token keyword">const</span> headings <span class="token operator">=</span> <span class="token punctuation">[</span></span><br><span class="highlight-line">      <span class="token punctuation">{</span> key<span class="token operator">:</span> <span class="token string">'tagName'</span><span class="token punctuation">,</span> itemType<span class="token operator">:</span> <span class="token string">'text'</span><span class="token punctuation">,</span> text<span class="token operator">:</span> <span class="token string">'Tag name'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      <span class="token punctuation">{</span> key<span class="token operator">:</span> <span class="token string">'node'</span><span class="token punctuation">,</span> itemType<span class="token operator">:</span> <span class="token string">'node'</span><span class="token punctuation">,</span> text<span class="token operator">:</span> <span class="token string">'Failing element'</span> <span class="token punctuation">}</span></span><br><span class="highlight-line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">    <span class="token keyword">const</span> score <span class="token operator">=</span> Audit<span class="token punctuation">.</span><span class="token function">computeLogNormalScore</span><span class="token punctuation">(</span></span><br><span class="highlight-line">      <span class="token punctuation">{</span> p10<span class="token operator">:</span> context<span class="token punctuation">.</span>options<span class="token punctuation">.</span>p10<span class="token punctuation">,</span> median<span class="token operator">:</span> context<span class="token punctuation">.</span>options<span class="token punctuation">.</span>median <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      results<span class="token punctuation">.</span>length</span><br><span class="highlight-line">    <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br>    <span class="token comment">/**<br><span class="highlight-line">     * @return {LH.Product}</span><br>     */</span><br><span class="highlight-line">    <span class="token keyword">return</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      score<span class="token punctuation">,</span> <span class="token comment">// Number between 0 and 1</span></span><br><span class="highlight-line">      numericValue<span class="token operator">:</span> results<span class="token punctuation">.</span>length<span class="token punctuation">,</span></span><br><span class="highlight-line">      numericUnit<span class="token operator">:</span> <span class="token string">'element'</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      displayValue<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>results<span class="token punctuation">.</span>length<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> elements</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span></span><br><span class="highlight-line">      details<span class="token operator">:</span> Audit<span class="token punctuation">.</span><span class="token function">makeTableDetails</span><span class="token punctuation">(</span>headings<span class="token punctuation">,</span> results<span class="token punctuation">)</span><span class="token punctuation">,</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> NonInteractiveClickHandlersAudit<span class="token punctuation">;</span></span></code></pre>
<p>The audit module contains the property getter <code>meta</code> which returns a set of metadata about the audit. The property <code>requiredArtifacts</code> is populated with the names of the built-in or custom gatherer classes that the audit requires data from.</p>
<p>The <code>audit</code> method reads the data from our gatherer via the <code>artifacts.NonInteractiveClickHandlersGatherer</code> property.
The gatherer array is then mapped to an array of objects with the tag name and a
<a href="https://github.com/GoogleChrome/lighthouse/blob/98eebdaf6daa82957cadd057b16ce680af226bc3/types/lhr/audit-details.d.ts#:~:text=*/-,interface%20NodeValue,-%7B" target="_blank" rel="noopener"><code>LH.Audit.Details.NodeValue</code></a>
object. The <code>audit</code> method also calculates the score for the audit (which is a number between 0 and 1) and returns this along with the mapped array and some heading details.</p>
<hr>
<h2 id="creating-the-configuration">Creating the configuration</h2>
<p>Create a new JavaScript file <code>config.js</code>.</p>
<pre class="language-javascript code--small"><code class="language-javascript code--small"><span class="highlight-line">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">  <span class="token comment">// 1. Run your custom tests along with all the default Lighthouse tests</span></span><br><span class="highlight-line">  <span class="token keyword">extends</span><span class="token operator">:</span> <span class="token string">'lighthouse:default'</span><span class="token punctuation">,</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token comment">// 2. Add gatherer to the default Lighthouse load ('pass') of the page</span></span><br><span class="highlight-line">  passes<span class="token operator">:</span> <span class="token punctuation">[</span></span><br><span class="highlight-line">    <span class="token punctuation">{</span></span><br><span class="highlight-line">      passName<span class="token operator">:</span> <span class="token string">'defaultPass'</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      gatherers<span class="token operator">:</span> <span class="token punctuation">[</span></span><br><span class="highlight-line">        <span class="token string">'./gatherers/non-interactive-click-handlers-gatherer.js'</span></span><br><span class="highlight-line">      <span class="token punctuation">]</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span></span><br><span class="highlight-line">  <span class="token punctuation">]</span><span class="token punctuation">,</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token comment">// 3. Add custom audit to the list of audits 'lighthouse:default' will run</span></span><br><span class="highlight-line">  audits<span class="token operator">:</span> <span class="token punctuation">[</span></span><br><span class="highlight-line">    <span class="token string">'./audits/non-interactive-click-handlers-audit.js'</span></span><br><span class="highlight-line">  <span class="token punctuation">]</span><span class="token punctuation">,</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token comment">// 4. Create a new 'Custom Audit' section in the default report for our results</span></span><br><span class="highlight-line">  categories<span class="token operator">:</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    custom<span class="token operator">:</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      title<span class="token operator">:</span> <span class="token string">'Custom'</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      description<span class="token operator">:</span> <span class="token string">'Custom audit for our checks on DOM element related issues'</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      auditRefs<span class="token operator">:</span> <span class="token punctuation">[</span></span><br><span class="highlight-line">        <span class="token comment">// "weight" controls how multiple audits are averaged together</span></span><br><span class="highlight-line">        <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token string">'non-interactive-click-handlers'</span><span class="token punctuation">,</span> weight<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span></span><br><span class="highlight-line">      <span class="token punctuation">]</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">;</span></span></code></pre>
<hr>
<h2 id="running-the-custom-audit">Running the custom audit</h2>
<p>With the 3 JavaScript modules in place, we’re now ready to run the custom audit against our test HTML page.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> run serve<br><span class="token function">npm</span> run lighthouse</code></pre>
<p>The command behind <code>npm run lighthouse</code> is:</p>
<pre class="language-bash code--small"><code class="language-bash code--small"><span class="highlight-line">lighthouse --config-path<span class="token operator">=</span>config.js http://localhost:8085/ --output-path ./results.html --view</span></code></pre>
<p>Once complete, you’ll see the new “Custom” section in the report which contains the failed element from the test HTML page.</p>
<img src="http://keepinguptodate.com/pages/2021/08/lighthouse-report-summary.png" loading="lazy" alt="Lighthouse report summary">
<br>
<img src="http://keepinguptodate.com/pages/2021/08/lighthouse-custom-section-results.png" loading="lazy" alt="Lighthouse custom section summary">
<p><a href="https://github.com/JonUK/lighthouse-custom-audit" target="_blank" rel="noopener">The code for this article is available on GitHub</a></p>
]]>
        </description>
        <pubDate>Mon, 30 Aug 2021 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2021/08/custom-lighthouse-audit/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2021/08/custom-lighthouse-audit/</guid>
      </item><item>
        <title>Automating Google Lighthouse audits and uploading results to Azure</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>This article covers configuring Lighthouse CI to run against a website and uploading the results to a Lighthouse CI server Docker container running both locally and in Azure.</p>
<p>Lighthouse is a great tool for auditing your website on performance, web accessibility, PWA, SEO and more. By automating Lighthouse to run nightly or with each commit and by uploading the results to a Lighthouse CI server, you can track your scores over time, see trends and detect when recent changes have had a negative impact.
<img src="http://keepinguptodate.com/pages/2021/07/lighthouse-ci-server-audit-timeline.png" loading="lazy" alt="Lighthouse showing performance timeline"></p>
<img src="http://keepinguptodate.com/pages/2021/07/lighthouse-report-comparison-summary.png" loading="lazy" alt="Lighthouse report comparison summary">
<br>
<!-- Excerpt End -->
<hr>
<h2 id="configuring-lighthouse-ci-to-audit-your-site">Configuring Lighthouse CI to audit your site</h2>
<p>Lighthouse CI is designed to have it’s configuration files exist within your project’s Git repo, so when it runs, it can include the branch name and last commit details with each report. This is especially useful with reports uploaded to a Lighthouse CI server, as you can compare results for different commits to help track down when scores have changed.</p>
<p>Note: If you don’t use Git, Lighthouse CI can be
<a href="https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#build-context" target="_blank" rel="noopener">configured for use without a Git repo</a>.</p>
<p>In this article, I’m going to create a new Git repo for an empty project but if you have an existing project you want to audit, you can skip this first step.</p>
<p>Create a folder for the empty project and initialise a Git repo</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">mkdir</span> my-website<br><span class="token builtin class-name">cd</span> my-website<br><span class="token function">git</span> init --initial-branch<span class="token operator">=</span>main</code></pre>
<p>Create the folder <code>lighthouse</code> for the configuration files.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">mkdir</span> lighthouse<br><span class="token builtin class-name">cd</span> lighthouse</code></pre>
<p>Create a new <code>package.json</code>.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> init -y</code></pre>
<p>Install <code>@lhci/cli</code>.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> <span class="token function">install</span> --save-dev @lhci/cli</code></pre>
<p>Create the configuration file <code>lighthouserc.js</code> with the following settings:</p>
<pre class="language-javascript code--medium"><code class="language-javascript code--medium"><span class="highlight-line">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">  ci<span class="token operator">:</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    collect<span class="token operator">:</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      url<span class="token operator">:</span> <span class="token punctuation">[</span></span><br><span class="highlight-line">        <span class="token string">'https://keepinguptodate.com/code-snippets/'</span></span><br><span class="highlight-line">      <span class="token punctuation">]</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      numberOfRuns<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token comment">// Set low to speed up the test runs. Default is 3.</span></span><br><span class="highlight-line">      headful<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// Show the browser which is helpful when checking the config</span></span><br><span class="highlight-line">      settings<span class="token operator">:</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">        disableStorageReset<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// Don't clear localStorage / IndexedDB / etc before loading the page</span></span><br><span class="highlight-line">        preset<span class="token operator">:</span> <span class="token string">'desktop'</span></span><br><span class="highlight-line">      <span class="token punctuation">}</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br><span class="highlight-line">    upload<span class="token operator">:</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      target<span class="token operator">:</span> <span class="token string">'temporary-public-storage'</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">;</span></span></code></pre>
<p>The <code>url</code> property should contain an array of URLs for the pages on your website that you want to audit.</p>
<p>The <code>numberOfRuns</code> has been set to <code>1</code> to speed up the runs but typically should be set to <code>3</code> or more. Lighthouse will use the median result which then accounts for some volatility in the results.</p>
<p>The upload <code>target</code> of <code>temporary-public-storage</code> means once run, Lighthouse CI will upload the results to a public temporary URL and output the URL in the console. Later, we’ll setup up a Lighthouse CI server to upload our results to.</p>
<p>If you created a new Git repo, we need to make an initial commit for Lighthouse to pickup when it runs.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">git</span> commit -m <span class="token string">"Initial commit"</span></code></pre>
<p>Now we can run Lighthouse CI</p>
<pre class="language-bashnull"><code class="language-bashnull">npx lhci autorun</code></pre>
<p>As the setting <code>headful</code> is <code>true</code>, Lighthouse CI will launch a visible Chrome instance as it runs. When Lighthouse CI has finished, the URL of the report is logged to the console.</p>
<pre class="language-text code--medium"><code class="language-text code--medium"><span class="highlight-line">Running Lighthouse 1 time(s) on https://keepinguptodate.com/code-snippets/</span><br><span class="highlight-line">Run #1...done.</span><br><span class="highlight-line">Done running Lighthouse!</span><br><span class="highlight-line"></span><br><span class="highlight-line">Uploading median LHR of https://keepinguptodate.com/code-snippets/...success!</span><br><span class="highlight-line">Open the report at https://storage.googleapis.com/xxx/reports/yyy.report.html</span></code></pre>
<p>The report can be viewed via the URL logged to the console. Alternatively, the report is available in the <code>.lighthouseci</code> folder created by Lighthouse. Note: This folder is cleared each time Lighthouse CI is run.</p>
<hr>
<h2 id="authenticating-before-running-lighthouse-audits">Authenticating before running Lighthouse audits</h2>
<p>If your website has pages that are only available to authenticated users, a puppeteer script can be created to control Chrome (e.g. enter an email address and password and click a login button) before the Lighthouse audit is run.</p>
<p>Given the following login form:</p>
<pre class="language-html code--medium"><code class="language-html code--medium"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Login<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  </span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Email<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">required</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  </span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-password<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Password<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  </span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-password<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-password<span class="token punctuation">"</span></span> <span class="token attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-password<span class="token punctuation">"</span></span>  <br>         <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span> <span class="token attr-name">minlength</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>8<span class="token punctuation">"</span></span> <span class="token attr-name">required</span><span class="token punctuation">></span></span>  <br><span class="highlight-line">  </span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>login<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Login<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>This puppeteer script will enter an email address &amp; password and click the login button.</p>
<pre class="language-javascript code--medium"><code class="language-javascript code--medium"><span class="highlight-line"><span class="token keyword">const</span> <span class="token constant">EMAIL</span> <span class="token operator">=</span> <span class="token string">'user@domain.com'</span> <span class="token comment">// Could use process.env.EMAIL for env var</span></span><br><span class="highlight-line"><span class="token keyword">const</span> <span class="token constant">PASSWORD</span> <span class="token operator">=</span> <span class="token string">'xxx'</span> <span class="token comment">// Could use process.env.PASSWORD for env var</span></span><br><span class="highlight-line"></span><br><span class="token comment">/*<br><span class="highlight-line"> * @param {import('puppeteer').Browser} browser</span><br><span class="highlight-line"> * @param { {url: string, options: LHCI.CollectCommand.Options} } context</span><br> */</span><br><span class="highlight-line">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">browser<span class="token punctuation">,</span> <span class="token punctuation">{</span> url <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br><span class="highlight-line">  <span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">setViewport</span><span class="token punctuation">(</span><span class="token punctuation">{</span> width<span class="token operator">:</span> <span class="token number">1024</span><span class="token punctuation">,</span> height<span class="token operator">:</span> <span class="token number">600</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token comment">// This login script is run for every URL so check if the user is already authenticated and if so then</span></span><br><span class="highlight-line">  <span class="token comment">// return early without doing anything.</span></span><br><span class="highlight-line">  <span class="token keyword">const</span> userDropdownElement <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#userDropdown'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token keyword">if</span> <span class="token punctuation">(</span>userDropdownElement <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">    <span class="token keyword">return</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token comment">// Enter the username and password and login</span></span><br><span class="highlight-line">  <span class="token keyword">const</span> emailInput <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#email'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token keyword">await</span> emailInput<span class="token punctuation">.</span><span class="token function">type</span><span class="token punctuation">(</span><span class="token constant">EMAIL</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token keyword">const</span> passwordInput <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#current-password'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token keyword">await</span> passwordInput<span class="token punctuation">.</span><span class="token function">type</span><span class="token punctuation">(</span><span class="token constant">PASSWORD</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token keyword">const</span> loginButton <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'button[value=login]'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token keyword">await</span> loginButton<span class="token punctuation">.</span><span class="token function">click</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">  <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">waitForNavigation</span><span class="token punctuation">(</span><span class="token punctuation">{</span> waitUntil<span class="token operator">:</span> <span class="token string">'networkidle2'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// No more then 2 network requests for half a second</span></span><br><span class="highlight-line">  <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">;</span></span></code></pre>
<p>To use this script saved with the filename <code>login-script.js</code>, first we’ll need to install puppeteer.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> <span class="token function">install</span> puppeteer --save-dev</code></pre>
<p>And then update the Lighthouse CI configuration file <code>lighthouserc.js</code> to run this puppeteer script before each URL is audited.</p>
<pre class="language-javascript code--medium"><code class="language-javascript code--medium"><span class="highlight-line">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>  </span><br><span class="highlight-line">  ci<span class="token operator">:</span> <span class="token punctuation">{</span>  </span><br><span class="highlight-line">    collect<span class="token operator">:</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      <span class="token operator">...</span></span><br><span class="highlight-line">      puppeteerScript<span class="token operator">:</span> <span class="token string">'login-script.js'</span><span class="token punctuation">,</span>  <span class="token comment">// Ensure there's an authenticated user before running Lighthouse</span></span><br><span class="highlight-line">      puppeteerLaunchOptions<span class="token operator">:</span> <span class="token punctuation">{</span>  </span><br><span class="highlight-line">        defaultViewport<span class="token operator">:</span> <span class="token keyword">null</span>  </span><br><span class="highlight-line">      <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br><span class="highlight-line">      <span class="token operator">...</span> </span><br><span class="highlight-line">    <span class="token punctuation">}</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span>  </span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">;</span></span></code></pre>
<hr>
<h2 id="uploading-results-to-a-local-lighthouse-ci-server">Uploading results to a local Lighthouse CI server</h2>
<p>We’re going to run the Lighthouse CI server in a Docker container so first ensure you have
<a href="https://www.docker.com/products/docker-desktop" target="_blank" rel="noopener">Docker Desktop</a>.
installed and running. The
<a href="https://hub.docker.com/r/patrickhulce/lhci-server/" target="_blank" rel="noopener">Lighthouse CI server image is available on Docker Hub</a>.</p>
<p>Download the latest version of the Docker image.</p>
<pre class="language-bashnull"><code class="language-bashnull">docker pull patrickhulce/lhci-server</code></pre>
<p>Create a Docker volume to store files that should be persisted. The Docker image uses SQLite for it’s database, which uses files stored on the filesystem, so using a volume ensures results are not lost when the container restarts.</p>
<pre class="language-bashnull"><code class="language-bashnull">docker volume create lhci-data</code></pre>
<p>Now we can start an instance of the Lighthouse CI server on port <code>9001</code>.</p>
<pre class="language-bash code--medium"><code class="language-bash code--medium"><span class="highlight-line">docker container run --publish <span class="token number">9001</span>:9001 --mount <span class="token assign-left variable">source</span><span class="token operator">=</span>lhci-data,target<span class="token operator">=</span>/data --detach patrickhulce/lhci-server</span></code></pre>
<p>Once the instance has started, navigate to
<a href="http://localhost:9001/" target="_blank" rel="noopener">http://localhost:9001/</a>.
where you should see the dashboard welcome page.</p>
<img src="http://keepinguptodate.com/pages/2021/07/lighthouse-without-any-projects.png" class="no-border more-vertical-spacing" loading="lazy" alt="Lighthouse welcome page">
<p>As detailed on the welcome page, we now need to create a project for our Lighthouse results to be uploaded to. We can use the Lighthouse CI wizard to walk us through these steps.</p>
<pre class="language-bashnull"><code class="language-bashnull">npx lhci wizard</code></pre>
<p>Choose the option <code>new-project</code>, enter the server URL of <code>http://localhost:9001/</code> and give the project a suitable name. The Lighthouse CI server will then output a build token and admin token. Keep these tokens safe as they will be needed later.</p>
<p>If you navigate to the Lighthouse CI server on
<a href="http://localhost:9001/" target="_blank" rel="noopener">http://localhost:9001/</a>,
you will now see the project listed.</p>
<img src="http://keepinguptodate.com/pages/2021/07/lighthouse-with-newly-created-project.png" class="no-border more-vertical-spacing" loading="lazy" alt="Lighthouse with a new project">
<p>With our Lighthouse CI server running and having created a project, we now need to update the <code>lighthouserc.js</code> configuration file so when we run Lighthouse CI, the results are uploaded to our server. Update the <code>upload</code> section as per below:</p>
<pre class="language-javascript code--medium"><code class="language-javascript code--medium"><span class="highlight-line">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>  </span><br><span class="highlight-line">  <span class="token operator">...</span></span><br><span class="highlight-line">  upload<span class="token operator">:</span> <span class="token punctuation">{</span>  </span><br><span class="highlight-line">    target<span class="token operator">:</span> <span class="token string">'lhci'</span><span class="token punctuation">,</span>  </span><br><span class="highlight-line">    serverBaseUrl<span class="token operator">:</span> <span class="token string">'http://localhost:9001'</span><span class="token punctuation">,</span>  </span><br><span class="highlight-line">    token<span class="token operator">:</span> <span class="token string">'xxx'</span> <span class="token comment">// The Lighthouse CI server build token for the project  </span></span><br><span class="highlight-line">  <span class="token punctuation">}</span>  </span><br><span class="highlight-line">  <span class="token operator">...</span> </span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">;</span></span></code></pre>
<p>And with that, when Lighthouse CI is next run, the results will be uploaded to the server.</p>
<pre class="language-bashnull"><code class="language-bashnull">npx lhci autorun</code></pre>
<pre class="language-text code--medium"><code class="language-text code--medium"><span class="highlight-line">Saved LHR to http://localhost:9001</span><br><span class="highlight-line">Done saving build results to Lighthouse CI</span><br><span class="highlight-line">View build diff at http://localhost:9001/app/projects/mywebsite/compare/xxx</span></code></pre>
<p>Navigate to
<a href="http://localhost:9001/" target="_blank" rel="noopener">http://localhost:9001/</a>
and the project to see the results.</p>
<p>Note: If you receive the error <code>Unexpected status code 422</code>, <code>Build already exists for hash</code> while performing test runs of Lighthouse CI, you can create an empty commit to bypass this.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">git</span> commit --allow-empty -m <span class="token string">"Lighthouse run"</span></code></pre>
<hr>
<h2 id="running-a-lighthouse-ci-server-in-azure">Running a Lighthouse CI server in Azure</h2>
<p>This part of the article covers running the Lighthouse CI server Docker image in Azure.
As well as creating a web app to host the Docker image, we’ll also create a PostgreSQL database to store all the results.</p>
<p>Unfortunately,
<a href="https://docs.microsoft.com/en-us/azure/app-service/configure-connect-to-azure-storage?tabs=portal&pivots=container-linux#:~:text=It%27s%20not%20recommended%20to%20use%20storage%20mounts%20for%20local%20databases%20%28such%20as%20SQLite%29%20or%20for%20any%20other%20applications%20and%20components%20that%20rely%20on%20file%20handles%20and%20locks." target="_blank" rel="noopener">
storage account file shares used with containers don’t support SQLite databases</a>,
so for this reason, we’re using a PostgreSQL database.</p>
<p>From Azure Portal, create a new resource of type “Azure Database for PostgreSQL”.
Follow the wizard and select the compute and storage you require.</p>
<p>I chose the plan <code>Single server</code> &amp; storage + compute of <code>Basic</code> <code>1 vCores, 5 GB storage</code>.
When prompted, enter a username and password for the PostgreSQL database.</p>
<img src="http://keepinguptodate.com/pages/2021/07/adding-postgresql-database-in-azure.png" class="no-border more-vertical-spacing" loading="lazy" alt="Adding the PostgreSQL database in Azure">
<p>Once the PostgreSQL database has been created, access the “Connection security” blade and enable <code>Allow access to Azure services</code> and disable <code>Enforce SSL connection</code>.</p>
<p>We’re ready to create the web app for the Lighthouse CI server.
Create a new resource of type “Web App for Container”.  Choose the operating system <code>Linux</code>, and the sku and size of <code>F1</code>, which is free. From the “Docker” tab, select an image source of <code>Docker Hub</code> and for the image and tag, enter <code>patrickhulce/lhci-server</code>. With those options in place, create the web app.
<img src="http://keepinguptodate.com/pages/2021/07/creating-the-azure-web-app.png" class="no-border more-vertical-spacing" loading="lazy" alt="Choosing the Docker image for the Azure web app"></p>
<p>Once the web app has been created, we then need to configure it to connect to the PostgreSQL database.
From the web app, access the “Configuration” blade and add the setting <code>LHCI_STORAGE__SQL_CONNECTION_URL</code> with the connection details in the following format.</p>
<pre class="language-text code--small"><code class="language-text code--small"><span class="highlight-line">postgresql://{user}%40{server}:{pass}@{server}.postgres.database.azure.com:5432/postgres</span></code></pre>
<p>So for example.</p>
<pre class="language-text code--small"><code class="language-text code--small"><span class="highlight-line">user: bob</span><br><span class="highlight-line">password: qwerty</span><br><span class="highlight-line">server: mypostgres</span><br><span class="highlight-line">postgresql://bob%40mypostgres:qwerty@mypostgres.postgres.database.azure.com:5432/postgres</span></code></pre>
<p>Additionally, add the setting <code>LHCI_STORAGE__SQL_DIALECT</code> with the value <code>postgres</code>.
As the Lighthouse CI server in the container is listening on port 9001, we need to configure the Azure web app to use this port, so add the application setting <code>WEBSITES_PORT</code> with the value <code>9001</code>.</p>
<p>And with that, you should now be able to access the web app URL and see the Lighthouse CI server welcome page. From here, follow the same steps as earlier for configuring a local Lighthouse CI server but substitute the <code>http://localhost:9001</code> server URL for the URL of the Azure web app.</p>
<hr>
<h2 id="additional-options-for-running-lighthouse-ci">Additional options for running Lighthouse CI</h2>
<p>Lighthouse CI server also
<a href="https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/server.md#:~:text=Database%20Storage" target="_blank" rel="noopener">supports MySQL databases</a>.</p>
<p>The <code>server</code>
<a href="https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/configuration.md#server" target="_blank" rel="noopener">configuration settings to use a different database are documented here</a>.</p>
<p>The format for the application settings in Azure should be all uppercase, dots replaced with 2 underscores, words separated by 1 underscore and settings prefixed with <code>LHCI_</code>.
For example:</p>
<pre class="language-textnull"><code class="language-textnull">storage.sqlConnectionUrl => LHCI_STORAGE__SQL_CONNECTION_URL</code></pre>
<p><a href="https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/configuration.md#environment-variables" target="_blank" rel="noopener">More information on environment variables naming is documented here</a></p>
<hr>
<h2 id="additional-reading">Additional reading</h2>
<p><a href="https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/recipes/docker-server/README.md#google-cloud-kubernetes" target="_blank" rel="noopener">Lighthouse CI server containers can also be run in Google Cloud Kubernetes.</a></p>
<p><a href="https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/getting-started.md#configure-your-ci-provider" target="_blank" rel="noopener">Lighthouse CI can be integrated into your Continuous Integration process so it’s run after each commit.</a></p>
<p><a href="https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/configuration.md#assert" target="_blank" rel="noopener">Assertions can be used to ensure certain scores are met else Lighthouse CI returns an appropriate status code which could be used to fail builds.</a></p>
]]>
        </description>
        <pubDate>Mon, 26 Jul 2021 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2021/07/automating-google-lighthouse-upload-to-azure/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2021/07/automating-google-lighthouse-upload-to-azure/</guid>
      </item><item>
        <title>Get Code Metrics On Your JavaScript Project Using SonarQube</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>While working on a large project, it can be very useful to keep on eye on key metrics about your codebase to see how your project is growing and review indicators that could mean you’re building up maintenance issues / technical debt.</p>
<p><a href="https://www.sonarqube.org/" target="_blank" rel="noopener">SonarQube</a>
by SonarSource is a static analysis tool that supports 27 languages including JavaScript, TypeScript, HTML &amp; CSS and can detect a range of issues in your codebase including bugs, code smells and security issues as well as provide traditional code metrics like lines of code and cyclometric complexity.</p>
<img src="http://keepinguptodate.com/pages/2021/01/sonarqube-example-of-reports.png" class="no-border more-vertical-spacing" loading="lazy" alt="SonarQube example of reports">
<p>SonarQube additionally supports React JSX and Vue.js Single File Components (that use JavaScript,
<a href="https://github.com/SonarSource/SonarJS/issues/1281#issuecomment-769839341" target="_blank" rel="noopener">TypeScript in Vue.js SFCs will hopefully be supported soon</a>.</p>
<p>SonarQube has several tiers available including a
<a href="https://www.sonarqube.org/downloads/" target="_blank" rel="noopener">Community Edition</a>
which is free.
This article covers running the Community Edition up in a Docker container and analysing a project to see the range of information SonarQube can provide.</p>
<!-- Excerpt End -->
<hr>
<h2 id="running-the-server">Running the server</h2>
<p>SonarQube consists of a server element that collates the statistics on your codebase and serves up reports as webpages as well as a scanner element that analyzes your projects code.</p>
<p>We’re going to run the server element of SonarQube in a Docker container so first ensure you have
<a href="https://www.docker.com/products/docker-desktop" target="_blank" rel="noopener">Docker Desktop</a>
installed and running.</p>
<p>Then download the latest version of the SonarQube Docker image.</p>
<pre class="language-bashnull"><code class="language-bashnull">docker pull sonarqube:latest</code></pre>
<p>And start an instance on port <code>9000</code>.</p>
<pre class="language-bash code--medium"><code class="language-bash code--medium"><span class="highlight-line">docker run -d --name sonarqube-instance -e <span class="token assign-left variable">SONAR_ES_BOOTSTRAP_CHECKS_DISABLE</span><span class="token operator">=</span>true -p <span class="token number">9000</span>:9000 sonarqube</span></code></pre>
<p>Once the instance has started, navigate to
<a href="http://localhost:9000/" target="_blank" rel="noopener">http://localhost:9000</a>
where you’ll be asked to login and change the admin password. The default credentials are:</p>
<ul>
<li>Username: admin</li>
<li>Password: admin</li>
</ul>
<img src="http://keepinguptodate.com/pages/2021/01/sonarqube-update-your-password-screen.png" class="small" loading="lazy" alt="SonarQube update your password screen">
<p>Then create a new project <code>stackedit</code> as we’ll be using the
<a href="https://github.com/benweet/stackedit" target="_blank" rel="noopener">StackEdit</a>
source code as an example project to analyse.</p>
<p>Next, create a token with the name <code>stackedit-token</code> which acts as a secret for this project and will allow the scanner to import data for the project.
Once generated, copy this token as we’ll be using it later.</p>
<img src="http://keepinguptodate.com/pages/2021/01/sonarqube-generate-project-token-screen.png" loading="lazy" alt="SonarQube generate project token screen">
<p>Now that the server is running and a project created, it’s time to setup the scanner.</p>
<hr>
<h2 id="running-the-scanner-(on-windows)">Running the scanner (on Windows)</h2>
<p>First download the
<a href="https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/" target="_blank" rel="noopener">“vanilla” SonarScanner</a>
Windows 64-bit version ZIP file. In this article, we’re not interested in static analysis for any server-side code however there are other scanners available including those that target .NET, Jenkins and Azure DevOps.</p>
<p>Next, extract this ZIP file and place the extracted <code>sonar-scanner-windows</code> folder somewhere convenient on your file system. I moved the <code>sonar-scanner-windows</code> folder to <code>C:\Program Files\sonar-scanner-windows</code>.</p>
<p>In the <code>sonar-scanner-windows\bin</code> folder is the file <code>sonar-scanner.bat</code> which executes the scanner. We’ll add the <code>bin</code> folder to the <code>PATH</code> environment variable for <strong>this instance of the command prompt</strong> so we can run <code>sonar-scanner</code> from any folder.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token builtin class-name">set</span> <span class="token assign-left variable"><span class="token environment constant">PATH</span></span><span class="token operator">=</span>%<span class="token environment constant">PATH</span>%<span class="token punctuation">;</span>C:<span class="token punctuation">\</span>Program Files<span class="token punctuation">\</span>sonar-scanner-windows<span class="token punctuation">\</span>bin</code></pre>
<p>From another appropriate folder, download the full source code of
<a href="https://github.com/benweet/stackedit" target="_blank" rel="noopener">StackEdit</a>
as the example project we’ll use to analyse.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token builtin class-name">cd</span> <span class="token punctuation">\</span>Temp<br><span class="token function">git</span> clone https://github.com/benweet/stackedit.git<br><span class="token builtin class-name">cd</span> stackedit</code></pre>
<p>Now we’re ready to run the scanner against the StackEdit source code. Run the following command ensuring you swap the token with the one you created via the SonarQube website earlier.</p>
<pre class="language-bash code--small"><code class="language-bash code--small"><span class="highlight-line">sonar-scanner.bat -D<span class="token string">"sonar.projectKey=stackedit"</span> -D<span class="token string">"sonar.sources=."</span> -D<span class="token string">"sonar.host.url=http://localhost:9000"</span> -D<span class="token string">"sonar.login=your_token_here"</span></span></code></pre>
<p>Leave the scanner to run and once complete, head back to
<a href="http://localhost:9000/" target="_blank" rel="noopener">http://localhost:9000</a>
to see the results.</p>
<hr>
<h2 id="excluding-files">Excluding files</h2>
<p>The
<a href="https://docs.sonarqube.org/latest/analysis/analysis-parameters/" target="_blank" rel="noopener">command line parameters</a>
<code>sonar.exclusions=xxx</code> and <code>sonar.inclusions=yyy</code> can be used to tell the scanner what files to target.</p>
<p>If all the source files in your project are in the folder <code>\src</code> then the following additional command line parameter will limit the scanner to just this folder.</p>
<pre class="language-bashnull"><code class="language-bashnull">-D<span class="token string">"sonar.inclusions=src/**/*.*"</span></code></pre>
<p>If you have multiple <code>dist</code> folders at different levels within your project, all with minified assets to ignore, the following additional command line parameter will exclude these files from the scanner.</p>
<pre class="language-bashnull"><code class="language-bashnull">-D<span class="token string">"sonar.exclusions=/**/dist/**/*.*"</span></code></pre>
<p>By default, the scanner excludes <code>node_modules</code> folders with the default value for another parameter <code>sonar.javascript.exclusions</code>.</p>
<p>These parameters can also be provided to the scanner via a
<a href="https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/#header-5" target="_blank" rel="noopener"><code>sonar-project.properties</code> file</a>
in the root folder of the project you’re scanning as an alternative to using command line parameters.</p>
]]>
        </description>
        <pubDate>Sat, 30 Jan 2021 00:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2021/01/code-metrics-on-your-javascript-project/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2021/01/code-metrics-on-your-javascript-project/</guid>
      </item><item>
        <title>Creating A .NET 5 ASP.NET Core App Running In Docker On A Mac</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>This article covers creating a new .NET 5 ASP.NET Core website in Visual Studio 2019 for Mac and running this website from both Visual Studio and from a Docker image.</p>
<p>Before we start, the following prerequisites need to be installed:</p>
<ul>
<li><a href="https://visualstudio.microsoft.com/vs/mac/" target="_blank" rel="noopener">Visual Studio for Mac</a> (with <a href="https://dotnet.microsoft.com/download/dotnet/5.0" target="_blank" rel="noopener">.NET 5</a> installed for old installs of VS)</li>
<li><a href="https://docs.docker.com/docker-for-mac/install/" target="_blank" rel="noopener">Docker Desktop on Mac</a></li>
</ul>
<hr>
<h2 id="create-a-new-project-and-solution">Create a new project and solution</h2>
<p>Create a new “Web Application” project.
<img src="http://keepinguptodate.com/pages/2020/12/vs2019-mac-new-project.png" class="no-border" loading="lazy" alt="Screenshot of VS2019 new project dialog box"></p>
<!-- Excerpt End -->
<p>Use the target framework of “.NET 5” and “No Authentication”.
<img src="http://keepinguptodate.com/pages/2020/12/vs2019-mac-configure-new-app.png" class="no-border" loading="lazy" alt="Screenshot of VS2019 configure new app dialog box"></p>
<p>Name the project <code>core5-website</code> in a new solution <code>core5-docker-playarea</code>.
<img src="http://keepinguptodate.com/pages/2020/12/vs2019-mac-project-name.png" class="no-border" loading="lazy" alt="Screenshot of VS2019 project and solution name dialog box"></p>
<p>Now if you run the project from Visual Studio, after accepting any development certificate prompts displayed, your browser should load the “Welcome” page on the URL
<a href="https://localhost:5001/" target="_blank" rel="noopener">https://localhost:5001</a></p>
<img src="http://keepinguptodate.com/pages/2020/12/vs2019-mac-aspnet-welcome-page.png" class="no-border" loading="lazy" alt="ASP.NET Core welcome page">
<p>The site is actually running in HTTP mode on port 5000 (<a href="http://localhost:5000/" target="_blank" rel="noopener">http://localhost:5000</a>) as well as HTTPS mode on port 5001 (<a href="https://localhost:5001/" target="_blank" rel="noopener">https://localhost:5001</a>). The website is configured to redirect HTTP requests to HTTPS via the HTTPS Redirection Middleware (<code>UseHttpsRedirection</code>) in <code>Startup.cs</code>.</p>
<p>We’ll make a change to the “Welcome” page to output the current time and to output (via an environment variable) whether the code is running in a container. Add the following code to the <code>/Pages/Index.cshtml</code> page:</p>
<pre class="language-html code--medium"><code class="language-html code--medium"><span class="highlight-line">@{</span><br><span class="highlight-line">    ViewData["Title"] = "Home page";</span><br><span class="highlight-line">    var runningInContainer = Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true";</span><br><span class="highlight-line">}</span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-center<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line">    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>display-4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Welcome<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span></span><br><span class="highlight-line">    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Learn about <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://docs.microsoft.com/aspnet/core<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>building Web apps with ASP.NET Core<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></span><br><span class="highlight-line">    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Current time: @DateTime.Now.ToLongTimeString()<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></span><br><span class="highlight-line">    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Running in container: @runningInContainer<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now when you run the site from Visual Studio, the “Welcome” page should display the time as well as detail that the website it’s not running in a container.</p>
<img src="http://keepinguptodate.com/pages/2020/12/vs2019-mac-aspnet-welcome-page2.png" class="no-border" loading="lazy" alt="ASP.NET Core welcome page with changes">  
<hr>
<h2 id="create-a-dockerfile-file">Create a <code>Dockerfile</code> file</h2>
<p>We now want to take this .NET 5 ASP.NET Core website and run it up in Docker. The first step to do this is to create a <code>Dockerfile</code> file at the solution root to hold the commands needed to build a Docker image:</p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token comment"># First stage</span><br><span class="token keyword">FROM</span> mcr.microsoft.com/dotnet/sdk<span class="token punctuation">:</span>5.0 AS build<br><span class="token keyword">WORKDIR</span> /DockerSource<br><br><span class="token comment"># Copy csproj and restore as distinct layers</span><br><span class="token keyword">COPY</span> *.sln .<br><span class="token keyword">COPY</span> core5<span class="token punctuation">-</span>website/*.csproj ./core5<span class="token punctuation">-</span>website/<br><span class="token keyword">RUN</span> dotnet restore<br><br><span class="token comment"># Copy everything else and build website</span><br><span class="token keyword">COPY</span> core5<span class="token punctuation">-</span>website/. ./core5<span class="token punctuation">-</span>website/<br><span class="token keyword">WORKDIR</span> /DockerSource/core5<span class="token punctuation">-</span>website<br><span class="token keyword">RUN</span> dotnet publish <span class="token punctuation">-</span>c release <span class="token punctuation">-</span>o /DockerOutput/Website <span class="token punctuation">-</span><span class="token punctuation">-</span>no<span class="token punctuation">-</span>restore<br><br><span class="token comment"># Final stage</span><br><span class="token keyword">FROM</span> mcr.microsoft.com/dotnet/aspnet<span class="token punctuation">:</span>5.0<br><span class="token keyword">WORKDIR</span> /DockerOutput/Website<br><span class="token keyword">COPY</span> <span class="token punctuation">-</span><span class="token punctuation">-</span>from=build /DockerOutput/Website ./<br><span class="token keyword">ENTRYPOINT</span> <span class="token punctuation">[</span><span class="token string">"dotnet"</span><span class="token punctuation">,</span> <span class="token string">"core5-website.dll"</span><span class="token punctuation">]</span></code></pre>
<p>These commands can look a bit cryptic so I’ll explain them individually.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">FROM</span> mcr.microsoft.com/dotnet/sdk<span class="token punctuation">:</span>5.0 AS build</code></pre>
<p>Specifies the base image to use for our image. In our case, we’re using the .NET 5 SDK image provided by Microsoft. The SDK image is being used so we can compile the app but we’ll actually swap to the .NET 5 runtime base image later as this is smaller so will result in smaller output image.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">WORKDIR</span> /DockerSource</code></pre>
<p>In the image we’re creating, change to the <code>/DockerSource</code> folder. <code>WORKDIR</code> will create this folder for us if it doesn’t exist.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">COPY</span> *.sln .<br><span class="token keyword">COPY</span> core5<span class="token punctuation">-</span>website/*.csproj ./core5<span class="token punctuation">-</span>website/</code></pre>
<p>Copy the solution file to the current image folder (<code>/DockerSource</code>) and copy the project file to the folder <code>/DockerSource/core5-website</code> in the image. <code>COPY</code> like <code>WORKDIR</code> will create a target folder if it doesn’t exist in the image.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">RUN</span> dotnet restore</code></pre>
<p>Restore NuGet dependencies. In the previous step, we individually copied the solution and project files to form a  separate layer to take advantage of Docker’s build cache. If neither of these files change then Docker knows <code>dotnet restore</code> will yield the same output so can safely bypass this command and use the built-in cache for the corresponding <code>dotnet restore</code> layer instead.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">COPY</span> core5<span class="token punctuation">-</span>website/. ./core5<span class="token punctuation">-</span>website/</code></pre>
<p>Copy all the files from the <code>core5-website</code> project to the  to the folder <code>/DockerSource/core5-website</code> in the image.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">WORKDIR</span> /DockerSource/core5<span class="token punctuation">-</span>website<br><span class="token keyword">RUN</span> dotnet publish <span class="token punctuation">-</span>c release <span class="token punctuation">-</span>o /DockerOutput/Website <span class="token punctuation">-</span><span class="token punctuation">-</span>no<span class="token punctuation">-</span>restore</code></pre>
<p>Swap to the <code>/DockerSource/core5-website</code> folder and compile the website with the output going to the <code>/DockerOutput/Website</code> folder in the image. The <code>--no-restore</code> option prevents the NuGet packages from being restored again as this was done previously in an earlier command.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">FROM</span> mcr.microsoft.com/dotnet/aspnet<span class="token punctuation">:</span>5.0</code></pre>
<p>Now we’ve compiled and published the website, use the .NET 5 ASP.NET Core runtime image provided by Microsoft as the base image (rather than the SDK image) to reduce the size of the final image created. The use of multiple <code>FROM</code> statements in the <code>Dockerfile</code> is known as a multi-stage build.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">WORKDIR</span> /DockerOutput/Website<br><span class="token keyword">COPY</span> <span class="token punctuation">-</span><span class="token punctuation">-</span>from=build /DockerOutput/Website ./</code></pre>
<p>Copy the artifacts from the previous stage of the multi-stage build.
<br><br></p>
<pre class="language-dockerfilenull"><code class="language-dockerfilenull"><span class="token keyword">ENTRYPOINT</span> <span class="token punctuation">[</span><span class="token string">"dotnet"</span><span class="token punctuation">,</span> <span class="token string">"core5-website.dll"</span><span class="token punctuation">]</span></code></pre>
<p>And the last command <code>ENTRYPOINT</code> takes an array of values with the first being the executable to run and the remaining values the arguments to use. When the container starts, the <code>ENTRYPOINT</code> command runs and starts the app.
<br><br></p>
<hr>
<h2 id="create-and-run-the-docker-image">Create and run the Docker image</h2>
<p>Now we have our <code>Dockerfile</code> file in place, we can create our Docker image with the following command:</p>
<pre class="language-bashnull"><code class="language-bashnull">docker build --pull -t core5-website <span class="token builtin class-name">.</span></code></pre>
<p>The <code>--pull</code> parameter ensures the latest upstream .NET 5 SDK and .NET 5 runtime images are used and the <code>-t</code> parameter provides the tag / name of the image.</p>
<p>With the Docker image created, we can run an instance of the image:</p>
<pre class="language-bash code--medium"><code class="language-bash code--medium"><span class="highlight-line">docker run --rm -it -p <span class="token number">8000</span>:80 core5-website --name core5-website-app</span></code></pre>
<p>The parameter <code>-rm</code> removes the container once it’s finished running, <code>-it</code> starts the container in interactive mode, <code>-p 8080:80</code> exposes the port 80 of the container to port 8080 on the host and <code>--name</code> provides the name of the instance.</p>
<p>With the container now running, if you browse to <a href="http://localhost:8000/" target="_blank" rel="noopener">http://localhost:8000</a> you should see the “Welcome” page and you should see that it details the app is running in a container.</p>
<p>The observant of you may be wondering why the ASP.NET Core app is running over HTTP on port 80 in the container instead of HTTPS on port 5001. Port 80 has been used because the base image from Microsoft contains the environment variable <code>ASPNETCORE_URLS</code> with the value <code>http://+:80</code>, which controls the port the app will listen on and the app is only running over HTTP as we haven’t configured a certificate for Docker to use.</p>
<hr>
<h2 id="configuring-a-certificate-to-support-https-in-docker">Configuring a certificate to support HTTPS in Docker</h2>
<p>The first step is to remove an existing self-signed certificate if there is one.</p>
<pre class="language-bashnull"><code class="language-bashnull">dotnet dev-certs https --check --verbose<br>dotnet dev-certs https --clean<br>dotnet dev-certs https --check --verbose</code></pre>
<p>Now we’ll create a new certificate (with a password of your choosing) and check that the certificate was created successfully.</p>
<pre class="language-bash code--medium"><code class="language-bash code--medium"><span class="highlight-line">dotnet dev-certs https -ep <span class="token variable">${<span class="token environment constant">HOME</span>}</span>/.aspnet/https/core5-website.pfx -p your_password_here</span><br><span class="highlight-line">dotnet dev-certs https --trust</span><br><span class="highlight-line">dotnet dev-certs https --check --verbose</span></code></pre>
<p>And with that, we can start the container with some additional parameters to provide details of the certificate we just generated and to map port 443 of the container to port 8001 on the host.</p>
<pre class="language-bash code--small"><code class="language-bash code--small"><span class="highlight-line">docker run --rm -it -p <span class="token number">8000</span>:80 -p <span class="token number">8001</span>:443 -e <span class="token assign-left variable">ASPNETCORE_URLS</span><span class="token operator">=</span><span class="token string">"https://+;http://+"</span> -e <span class="token assign-left variable">ASPNETCORE_HTTPS_PORT</span><span class="token operator">=</span><span class="token number">8001</span> -e <span class="token assign-left variable">ASPNETCORE_Kestrel__Certificates__Default__Password</span><span class="token operator">=</span><span class="token string">"your_password_here"</span> -e <span class="token assign-left variable">ASPNETCORE_Kestrel__Certificates__Default__Path</span><span class="token operator">=</span>/https/core5-website.pfx -v <span class="token variable">${<span class="token environment constant">HOME</span>}</span>/.aspnet/https:/https/ core5-website</span></code></pre>
<p>Now if you browse to <a href="http://localhost:8000/" target="_blank" rel="noopener">http://localhost:8000</a>, you will be redirected to the HTTPS version on <a href="https://localhost:8001/" target="_blank" rel="noopener">https://localhost:8001</a> with the certificate we just generated being used.</p>
<p>If you are running on a different operating system other than macOS, the previous steps for HTTPS support are slightly different. Please refer to the Microsoft <a href="https://docs.microsoft.com/en-us/aspnet/core/security/docker-https?view=aspnetcore-5.0" target="_blank" rel="noopener">Hosting ASP.NET Core images with Docker over HTTPS</a> page for more detail.</p>
]]>
        </description>
        <pubDate>Sat, 19 Dec 2020 00:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2020/12/net5-aspnet-docker-mac/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2020/12/net5-aspnet-docker-mac/</guid>
      </item><item>
        <title>BEM Class Names Checker</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->  
<p>After recently seeing a few BEM class naming issues on a project I was working on, I looked for an online site that would check a CSS stylesheet for BEM naming issues.
I couldn’t find one… and what with being on lockdown here in England and having a fair amount of free time on my hands, I thought I’d write one!</p>
<p>Introducing the
<a href="https://bem-class-names-checker.netlify.app/" target="_blank" rel="noopener">BEM Class Names Checker</a>
— An online tool to check a CSS stylesheet for common BEM naming issues.</p>
<img src="http://keepinguptodate.com/pages/2020/05/10-bem-class-names-checker.png" loading="lazy" alt="Screenshot of BEM Class Names Checker website">
<br>
<!-- Excerpt End -->
<p><a href="https://bem-class-names-checker.netlify.app/" target="_blank" rel="noopener">https://bem-class-names-checker.netlify.app/</a></p>
<p>As you make changes to the CSS stylesheet, in real-time any BEM class naming issues are displayed. I hope this online tool will help as a way of experimenting with BEM as well as validating existing stylesheets.</p>
]]>
        </description>
        <pubDate>Sun, 31 May 2020 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2020/05/bem-class-names-checker/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2020/05/bem-class-names-checker/</guid>
      </item><item>
        <title>BEM Visually Explained</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->  
<p>BEM (<code>Block</code>, <code>Element</code>, <code>Modifier</code>) is a methodology for naming CSS styles in a modular and maintainable manner. If you’ve worked on large websites, I’m sure you’ll have come across indecipherable class names and felt the pain of changing a style in one place and that breaking the styling elsewhere on something that seemingly isn’t related. These are issues that BEM will help you to avoid.</p>
<p>In this article, we’re going to use the example of a product card to explain BEM.</p>
<figure>
    <img src="http://keepinguptodate.com/pages/2020/05/01-product-card.svg" class="small" loading="lazy" alt="Product card showing a toilet roll">
    <figcaption class="center">Product card example</figcaption>
</figure>
<p>BEM relies solely on CSS class names to apply styles to HTML so there are no HTML tag or ID selectors. BEM class names can consist of 3 parts <code>[block]__[element]--[modifier]</code>.</p>
<figure>
    <img src="http://keepinguptodate.com/pages/2020/05/05-bem-naming-structure.svg" loading="lazy" alt="Structure of BEM class names">
    <figcaption class="center">Example BEM class name</figcaption>
</figure>
<!-- Excerpt End -->
<hr>
<h2 id="block">Block</h2>
<p>A <code>Block</code> is a component part of a website that can exist in it’s own right. We’re using a product card as our example <code>Block</code> in this article. Other examples of a <code>Block</code> are a website header &amp; footer, a login form and a check list.</p>
<img src="http://keepinguptodate.com/pages/2020/05/02-bem-block-example.svg" loading="lazy" alt="Product card is the Block">
<p><code>Block</code> names are in lowercase and each word is separated by a single dash (<code>-</code>) e.g. <code>check-list</code> and <code>site-header</code>.</p>
<p>When you can, try and name <code>Block</code>s as generically as possible so they can naturally be reused. In this example, we have a product card but if you had other cards on your website that all follow a similar style and layout, it would be better to have a <code>card</code>  <code>Block</code> that could be used for other cards like offer cards and latest product cards etc. For the purposes of this article, we’ll assume that the product card is highly specialised so a generic <code>card</code>  <code>Block</code> has purposefully not been used.</p>
<hr>
<h2 id="element">Element</h2>
<p>An <code>Element</code> is a child aspect of a <code>Block</code> that only exists within that <code>Block</code>. In our product card example, <code>image</code>, <code>title</code>, <code>price</code> and <code>like-button</code> are all examples of <code>Element</code>s within the product card <code>Block</code>.</p>
<img src="http://keepinguptodate.com/pages/2020/05/03-bem-element-examples.svg" loading="lazy" alt="Image, title, price and the like button are all elements">
<p><code>Element</code> class names all begin with the <code>Block</code> name followed by 2 underscores (<code>__</code>) and the <code>Element</code> name in lowercase with words separated by a dash e.g. <code>product-card__title</code> and <code>site-header__logo-image</code>.</p>
<h3 id="element-common-mistake-1-%E2%80%94-element-chaining">Element common mistake 1 — Element chaining</h3>
<p>One thing to note with <code>Element</code>s is they shouldn’t be chained. If we take the example of a shopping list, it has items and each item has a product image. At first glance, you may think the <code>Block</code> is <code>shopping-list</code>, <code>Element</code> 1 is <code>item</code> and <code>Element</code> 2 is <code>product-image</code> but this is wrong as there should only be one <code>Element</code> in the class name.</p>
<pre class="language-css prism--wrong"><code class="language-css prism--wrong"><span class="highlight-line"><span class="token selector">.shopping-list__item__product-image</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">  ...</span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Instead, the product image should be thought of as an <code>Element</code> within the <code>shopping-list</code> <code>Block</code> and the class name named as such.</p>
<pre class="language-css prism--correct"><code class="language-css prism--correct"><span class="highlight-line"><span class="token selector">.shopping-list__product-image</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">  ...</span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="element-common-mistake-2-%E2%80%94-element-names-based-on-style">Element common mistake 2 — Element names based on style</h3>
<p><code>Elements</code> should be named based on the “type of thing being displayed” rather than the “styling to be applied”. With our product card, we have a title which is styled larger and in bold text but the <code>Element</code> should not be named based on it’s style.</p>
<pre class="language-css prism--wrong"><code class="language-css prism--wrong"><span class="highlight-line"><span class="token selector">.product-card__large-bold</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">  ...</span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Some issues with this class name is by just reading it, it’s hard to know where it’s being used and hard to know where it can / should be re-used in future. Also, if this style was being used for the title, the price, the availability and other information in the product card, if a developer changed this class because they wanted a larger font for the title, they will actually increase the font size for all the other elements which may not be their intention.</p>
<pre class="language-css prism--correct"><code class="language-css prism--correct"><span class="highlight-line"><span class="token selector">.product-card__title</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">  ...</span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="block-vs-element">Block vs Element</h3>
<p>With our made up product card example, we’ve created the <code>like-button</code> <code>Element</code> within the <code>product-card</code> <code>Block</code> which implies that we only ever show like buttons in product cards. If that isn’t the case and like buttons can be shown elsewhere on the site, the like button should become it’s own standalone <code>Block</code>.</p>
<hr>
<h2 id="modifier">Modifier</h2>
<p>A <code>Modifier</code> can be applied to either a <code>Block</code> or an <code>Element</code> and is used to adjust the default look or behaviour of the <code>Block</code> or  <code>Element</code>.  With our product card example, the <code>Modifier</code> <code>liked</code> exists and can be applied to both the product card <code>Block</code> and the <code>like-button</code> <code>Element</code> when a product has been liked.</p>
<img src="http://keepinguptodate.com/pages/2020/05/04-bem-modifier-examples.svg" class="" loading="lazy" alt="The modifier liked can be applied to the block and to an element">
<p><code>Modifier</code> class names are separated from the <code>Block</code> or <code>Element</code> by 2 dashes (<code>--</code>) and should be in lowercase and words separated by a dash e.g. <code>product-card--unavailable</code> and <code>shopping-list__item--limited-stock</code>.</p>
<p><code>Modifier</code> classes should always be used in conjunction with original <code>Block</code> or <code>Element</code> class and just override the attributes to be different. It’s also possible to use multiple <code>Modifier</code> class names on the same HTML tag.</p>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn--primary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line">    Download</span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn btn--primary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line">    Download</span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn btn--primary btn--disabled<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line">    Download</span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<hr>
<h2 id="what-are-the-benefits-of-bem%3F">What are the benefits of BEM?</h2>
<p>So you’ve got your head around the slightly odd syntax of BEM (once you’ve seen it a few times, it does become very easy to read), and now you’re probably asking why would I use it? Here’s what I think are the clear benefits:</p>
<ul>
<li><strong>Consistent naming of CSS selectors</strong> — With a large project being worked on by multiple developers, there’s often different levels of experience and different approaches to CSS. With BEM, every developer should very quickly be able to understand where each CSS class is used and understand how they should name new classes.</li>
<li><strong>Less unexpected broken styles</strong> — As BEM classes always target a <code>Block</code>, in theory, any style changes related to that block will not affect other <code>Block</code>s.</li>
<li><strong>No class name clashes</strong> — With BEM, there shouldn’t be any class name collisions.</li>
<li><strong>Any HTML tags can be used</strong> — As BEM only uses class names and not tag or ID CSS selectors, we can use any HTML tags we see fit.</li>
<li><strong>HTML structure can change without everything breaking</strong> — As BEM classes are all discrete and not nested, the structure of our HTML can be however we like and it can be changed over time without breaking due to CSS cascading issues.</li>
<li><strong>Promotes modular, component based design</strong> — BEM pushes you to think about your site being composed of discrete areas which promotes abstraction and reuse.</li>
</ul>
]]>
        </description>
        <pubDate>Sun, 10 May 2020 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2020/05/bem-visually-explained/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2020/05/bem-visually-explained/</guid>
      </item><item>
        <title>5 Common Web App Accessibility Issues And How To Fix Them</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->    
<p>Over the years, I’ve worked on many large web app projects with developers of varying levels of experience and often see the same web accessibility issues crop up.  In this article, I detail the 5 most common web accessibility issues I’ve come across and offer solutions for them.</p>
<p>Are you making / have you made these mistakes in your projects?</p>
<hr>
<h2 id="1)-using-an-input-without-an-associated-label">1) Using An Input Without An Associated Label</h2>
<h3 id="wrong---using-plain-text-next-to-the-input">Wrong - Using plain text next to the input</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line">Username <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>The text “Username” is not associated with the <code>&lt;input&gt;</code> so when a screen reader announces the <code>&lt;input&gt;</code>, it won’t announce that it’s for entering a username. The <code>&lt;input&gt;</code> will be announced along the lines of “<strong>input, edit text</strong>” so a screen reader user would have no idea what this <code>&lt;input&gt;</code> is for or what to enter.</p>
<p>The text “Username” also won’t act as a click / touch target to set focus to the <code>&lt;input&gt;</code>.</p>
<!-- Excerpt End -->
<h3 id="wrong---using-a-label-but-not-linking-it-to-the-input">Wrong - Using a label but not linking it to the input</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  </span></code></pre>
<p>When using a <code>&lt;label&gt;</code>, it must be associated to it’s corresponding <code>&lt;input&gt;</code>. This can be done either with the <code>for</code> attribute or by wrapping the <code>&lt;label&gt;</code> around the <code>&lt;input&gt;</code>. As neither of these approaches has been used here, the <code>&lt;label&gt;</code> is not associated to the <code>&lt;input&gt;</code>.</p>
<p>Also note that a <code>&lt;label&gt;</code> has a specific semantic meaning which is to provide a label for a corresponding <code>&lt;input&gt;</code>. A <code>&lt;label&gt;</code> must therefore not be used for any general text content that is not acting as a label for an <code>&lt;input&gt;</code>. I have seen <code>&lt;label&gt;</code> elements used just to visually make some general text bold which is wrong.</p>
<h3 id="wrong---using-a-label-with-for-attribute-but-the-input-id-is-missing-%2F-wrong">Wrong - Using a label with <code>for</code> attribute but the input <code>id</code> is missing / wrong</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>firstName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>firstName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lastName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  </span></code></pre>
<p>With the “First name”, the <code>&lt;input&gt;</code> doesn’t contain the <code>id</code> attribute and with the “Last name”, the input contains the <code>id</code> attribute but with the value <code>name</code> and not <code>lastName</code>. In both cases, the <code>&lt;label&gt;</code> is not correctly associated with it’s <code>&lt;input&gt;</code>.</p>
<p>Also note that attribute values are case-sensitive so a <code>for=&quot;Username&quot;</code> attribute will not match an <code>&lt;input&gt;</code> with the attribute <code>id=&quot;username&quot;</code>.</p>
<hr>
<h3 id="correct---using-a-label-and-input-with-correct-for-%26-id-attribute-values">Correct - Using a label and input with correct <code>for</code> &amp; <code>id</code> attribute values</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  </span></code></pre>
<h3 id="correct---using-a-label-wrapped-around-the-input">Correct - Using a label wrapped around the input</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  Username</span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  </span></code></pre>
<h3 id="correct---using-the-aria-labelledby-attribute">Correct - Using the <code>aria-labelledby</code> attribute</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>resetHeading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Reset Password<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>resetUsername<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>resetHeading resetUsername<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  </span></code></pre>
<p>If there are one or more existing elements that contain text suitable to act as a label for an <code>&lt;input&gt;</code>, the IDs of these elements can be referenced using the <code>aria-labelledby</code> attribute. In this example, the <code>&lt;input&gt;</code> will be announced along the lines of “<strong>reset password username, edit text</strong>”.</p>
<p>Providing the extra context of “reset password” may be extremely useful in some scenarios for example on a sign up page that consists a “Username” input in a section for creating a new account and a “Username” input in another section for resetting a forgotten password.</p>
<h3 id="correct---using-the-aria-label-attribute-for-a-non-visual-label">Correct - Using the <code>aria-label</code> attribute for a non-visual label</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>searchTerm<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Search<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Search<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>In some very specific cases, a visual label is not required for an <code>&lt;input&gt;</code> as the purpose of the <code>&lt;input&gt;</code> is clear from the surrounding content. In this example, for a fully sighted user, it’s clear what purpose the <code>&lt;input&gt;</code> serves due the “Search” button next to it. For screen reader users though, this visual connection cannot be made. The <code>aria-label</code> attribute can be used to provide a non-visual label for the search <code>&lt;input&gt;</code>.</p>
<h3 id="further-reading-on-labels-for-user-input">Further reading on labels for user input</h3>
<p>WCAG 2.1 (Level A) - 3.3.2 Labels or Instructions
<a href="https://www.w3.org/WAI/WCAG21/quickref/#labels-or-instructions" target="_blank" rel="noopener">https://www.w3.org/WAI/WCAG21/quickref/#labels-or-instructions</a></p>
<hr>
<h2 id="2)-using-an-icon-font-without-text-alternative">2) Using An Icon Font Without Text Alternative</h2>
<p>Icon fonts provide a convenient way to add scalable, styleable icons to a website (although now arguably SVG icons provide a better approach). The markup to display an icon is typically a <code>&lt;i&gt;</code> or <code>&lt;span&gt;</code> tag with a class applied to it which includes a <code>:before</code> or <code>:after</code> selector to output a specific Unicode character. This Unicode character happens to visually display an icon in the font file but a screen reader either won’t announce this character or will announce a literal description of the icon, neither of which is ideal.</p>
<p>With this in mind, let’s have a look at some common web accessibility issues when using an icon font.</p>
<h3 id="wrong---icon-with-no-text-alternative">Wrong - Icon with no text alternative</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">></span></span></span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fas fa-download<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>i</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="wrong---hiding-text-on-small-viewports">Wrong - Hiding text on small viewports</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">  <br><span class="highlight-line">  <span class="token selector">.download-text</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span>  </span><br><span class="highlight-line"> </span><br><span class="highlight-line">  <span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 768px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    <span class="token selector">.download-text</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      <span class="token property">display</span><span class="token punctuation">:</span> inline<span class="token punctuation">;</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span>  <br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">></span></span></span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>download-text<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Download<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fas fa-download<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>i</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<hr>
<h3 id="correct---icon-with-text-alternative">Correct - Icon with text alternative</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">></span></span></span><br><span class="highlight-line">  Download  </span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fas fa-download<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>i</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">></span></span> </span></code></pre>
<p>Note that the attribute <code>aria-hidden=&quot;true&quot;</code> has been applied to the <code>&lt;i&gt;</code> to ensure the screen reader doesn’t announce the Unicode character.</p>
<h3 id="correct---having-text-alternative-still-available-when-displayed-text-is-hidden-on-small-viewports">Correct - Having text alternative still available when displayed text is hidden on small viewports</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">  <br><span class="highlight-line">  <span class="token selector">.download-text</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line">  <span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 768px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br><span class="highlight-line">    <span class="token selector">.download-text</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">      <span class="token property">display</span><span class="token punctuation">:</span> inline<span class="token punctuation">;</span></span><br><span class="highlight-line">    <span class="token punctuation">}</span></span><br><span class="highlight-line">  <span class="token punctuation">}</span></span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span>  <br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">></span></span></span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Download<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>download-text<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Download<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span></span><br><span class="highlight-line">  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fas fa-download<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>i</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span>  </span></code></pre>
<p>The class <code>sr-only</code> (screen reader only) has been applied to the first <code>&lt;span&gt;</code> which positions the text outside of the viewport so it can’t visually be seen but still exists in the DOM and is announced by a screen reader.</p>
<p>Both the text that is only displayed on larger viewports and the icon <code>&lt;i&gt;</code> tag have the attribute <code>aria-hidden=&quot;true&quot;</code> so they are not announced by a screenreader.</p>
<h3 id="further-reading-on-icon-font-accessibility">Further reading on icon font accessibility</h3>
<p><a href="https://fontawesome.com/how-to-use/on-the-web/other-topics/accessibility" target="_blank" rel="noopener">https://fontawesome.com/how-to-use/on-the-web/other-topics/accessibility</a></p>
<hr>
<h2 id="3)-adding-a-click-handler-to-a-non-interactive-element">3) Adding A Click Handler To A Non-Interactive Element</h2>
<h3 id="wrong---adding-click-handlers-to-a-div-and-span-in-react-and-vue">Wrong - Adding click handlers to a <code>div</code> and <code>span</code> in React and Vue</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{this.addToBasket}</span><span class="token punctuation">></span></span>Add To Basket<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  </span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">@click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkout<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Checkout<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span>  </span></code></pre>
<p>Unlike a native  <code>&lt;button&gt;</code> or <code>&lt;a&gt;</code>, a keyboard user can’t interact with and invoke  click handlers on <code>&lt;div&gt;</code> or <code>&lt;span&gt;</code> elements and a screen reader user isn’t made aware that the <code>&lt;div&gt;</code> or <code>&lt;span&gt;</code> elements can be interacted with.</p>
<hr>
<h3 id="correct---use-click-handlers-with-interactive-elements">Correct - Use click handlers with interactive elements</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{this.addToBasket}</span><span class="token punctuation">></span></span>Add To Basket<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span>  </span><br><span class="highlight-line">  </span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span> <span class="token attr-name">@click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkout<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Checkout<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>  </span></code></pre>
<h3 id="further-reading-on-accessible-javascript-click-handlers">Further reading on accessible JavaScript click handlers</h3>
<p><a href="https://keepinguptodate.com/pages/2019/04/accessible-javascript-click-handlers/" target="_blank" rel="noopener">https://keepinguptodate.com/pages/2019/04/accessible-javascript-click-handlers/</a></p>
<hr>
<h2 id="4)-not-using-a-live-region-for-important-information">4) Not Using A Live Region For Important Information</h2>
<p>With Progressive Web Apps, Single Page Applications and client-side rich webpages, the content on a webpage can change dynamically without the need for a full page reload. When content in part of a page is dynamically changed, a fully-sighted user can visually see these changes but a screen reader user is not aware of the changes.</p>
<p>Live regions provide a way to have dynamic content changes announced by screen readers.</p>
<h3 id="wrong---important-information-will-not-be-announced">Wrong - Important information will not be announced</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>Your message has been sent<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">></span></span>Unable to place order as you're offline<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="correct---live-region-used-to-announce-important-information">Correct - live-region used to announce important information</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>status<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line">  Your message has been sent</span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>assertive<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line">  Unable to place order as you're offline</span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>   </span></code></pre>
<h3 id="further-reading-on-live-regions">Further reading on live regions</h3>
<p><a href="https://developers.google.com/web/fundamentals/accessibility/semantics-aria/hiding-and-updating-content#aria-live" target="_blank" rel="noopener">https://developers.google.com/web/fundamentals/accessibility/semantics-aria/hiding-and-updating-content#aria-live</a></p>
<hr>
<h2 id="5)-not-providing-the-href-attribute-on-an-anchor-tag">5) Not Providing The <code>href </code>Attribute On An Anchor Tag</h2>
<p>An anchor element should have a <code>href</code> attribute with a value to ensure the element can receive focus by a keyboard user and to ensure the link is announced correctly by screen readers.</p>
<h3 id="wrong---no-href-attribute">Wrong - No <code>href</code> attribute</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{this.upload}</span><span class="token punctuation">></span></span>Upload<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>  </span></code></pre>
<h3 id="wrong---empty-href-attribute">Wrong - Empty <code>href</code> attribute</h3>
<pre class="language-html prism--wrong"><code class="language-html prism--wrong"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{this.upload}</span><span class="token punctuation">></span></span>Upload<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>  </span></code></pre>
<h3 id="correct---a-href-attribute-with-a-value-is-provided">Correct - A <code>href</code> attribute with a value is provided</h3>
<pre class="language-html prism--correct"><code class="language-html prism--correct"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span> <span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{this.upload}</span><span class="token punctuation">></span></span>Upload<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>  </span></code></pre>
]]>
        </description>
        <pubDate>Mon, 24 Feb 2020 00:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2020/02/common-web-accessibility-issues/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2020/02/common-web-accessibility-issues/</guid>
      </item><item>
        <title>Using TypeScript With Knockout</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>Knockout is a minimalist, mature and proven library for creating web applications.
It isn’t as feature rich as some of the more modern libraries &amp; frameworks but it does what it does well, primarily being binding HTML elements against a data model.
Even in 2019, I believe Knockout has its place and is still used on some very large projects, including Microsoft’s Azure Portal.</p>
<p>As of v3.5.0 (released Feb 2019), Knockout has built-in TypeScript definitions.
<a href="https://github.com/knockout/knockout/blob/master/build/types/knockout.d.ts" target="_blank" rel="noopener">https://github.com/knockout/knockout/blob/master/build/types/knockout.d.ts</a></p>
<p>TypeScript provides a huge amount of benefits over JavaScript including type safety, improved IDE tooling and additional constructs like interfaces and enums.
If you have an existing project that uses Knockout, you can move over to TypeScript now and start enjoying these benefits.</p>
<p>This article will start with covering using TypeScript and Knockout’s type definitions to create view models, components and bindings that are strongly typed.
We’ll then cover creating a new project from scratch that uses webpack and Babel to transpile TypeScript down to JavaScript (ES5).</p>
<!-- Excerpt End -->
<p>The code for this article is available on the GitHub repo:
<a href="https://github.com/JonUK/knockout-typescript" target="_blank" rel="noopener">https://github.com/JonUK/knockout-typescript</a></p>
<hr>
<h2 id="using-strong-types-with-knockout">Using strong types with Knockout</h2>
<p>We’re going to create a <code>Person</code> class that will be used by a read-only and an editable component to display &amp; allow editing of a person’s details.</p>
<p>Create the file <code>src/models/person.ts</code>.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> ko <span class="token keyword">from</span> <span class="token string">'knockout'</span><span class="token punctuation">;</span>  <br><span class="token keyword">import</span> <span class="token punctuation">{</span><br>  Observable<span class="token punctuation">,</span> ObservableArray<span class="token punctuation">,</span> PureComputed<br><span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'knockout'</span><span class="token punctuation">;</span>  <br>  <br><span class="token keyword">class</span> <span class="token class-name">Person</span> <span class="token punctuation">{</span>  <br>  firstName<span class="token operator">:</span> Observable<span class="token operator">&lt;</span>string<span class="token operator">></span><span class="token punctuation">;</span>  <br>  lastName<span class="token operator">:</span> Observable<span class="token operator">&lt;</span>string<span class="token operator">></span><span class="token punctuation">;</span>  <br>  favouriteFilms<span class="token operator">:</span> ObservableArray<span class="token operator">&lt;</span>string<span class="token operator">></span><span class="token punctuation">;</span>  <br>  <br>  fullName<span class="token operator">:</span> PureComputed<span class="token operator">&lt;</span>string<span class="token operator">></span> <span class="token operator">=</span> ko<span class="token punctuation">.</span><span class="token function">pureComputed</span><span class="token punctuation">(</span><br>    <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">firstName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">' '</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">lastName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <br>  <br>  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">firstName<span class="token operator">:</span> string<span class="token punctuation">,</span> lastName<span class="token operator">:</span> string<span class="token punctuation">,</span> favouriteFilms<span class="token operator">:</span> string<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">|</span> <span class="token keyword">null</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>  <br>    <span class="token keyword">this</span><span class="token punctuation">.</span>firstName <span class="token operator">=</span> ko<span class="token punctuation">.</span><span class="token function">observable</span><span class="token punctuation">(</span>firstName<span class="token punctuation">)</span><span class="token punctuation">;</span>  <br>    <span class="token keyword">this</span><span class="token punctuation">.</span>lastName <span class="token operator">=</span> ko<span class="token punctuation">.</span><span class="token function">observable</span><span class="token punctuation">(</span>lastName<span class="token punctuation">)</span><span class="token punctuation">;</span>  <br>    <span class="token keyword">this</span><span class="token punctuation">.</span>favouriteFilms <span class="token operator">=</span> ko<span class="token punctuation">.</span><span class="token function">observableArray</span><span class="token punctuation">(</span>favouriteFilms <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token keyword">export</span> <span class="token keyword">default</span> Person<span class="token punctuation">;</span></code></pre>
<p>The types <code>Observable</code>, <code>ObservableArray</code> and <code>Purecomputed</code> have all been pulled in from <code>knockout</code>.
First name is an <code>Observable</code> of type <code>string</code> so if you try to set the <code>firstName</code> observable to a <code>null</code>, a <code>boolean</code>, a <code>number</code> etc, you’ll receive a TypeScript error.
Similarly, the <code>PureComputed</code> must always return a <code>string</code> and <code>favouriteFilms</code> can only contain an array of <code>string</code>.</p>
<p>Let’s now create the read-only component to show the person’s details.
Create the file <code>src/components/PersonReadOnly.ts</code>.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">import</span> Person <span class="token keyword">from</span> <span class="token string">'../models/person'</span><span class="token punctuation">;</span>  <br><span class="token keyword">import</span> template <span class="token keyword">from</span> <span class="token string">'./PersonReadOnly.html'</span><span class="token punctuation">;</span>  <br>  <br><span class="token keyword">class</span> <span class="token class-name">PersonReadOnly</span> <span class="token punctuation">{</span>  <br>  person<span class="token operator">:</span> Person<span class="token punctuation">;</span>  <br>  <br>  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">params<span class="token operator">:</span> <span class="token punctuation">{</span> person<span class="token operator">:</span> Person <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>  <br>    <span class="token keyword">this</span><span class="token punctuation">.</span>person <span class="token operator">=</span> params<span class="token punctuation">.</span>person<span class="token punctuation">;</span>  <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token comment">// The default export returns the component details object to register with KO  </span><br><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> viewModel<span class="token operator">:</span> PersonReadOnly<span class="token punctuation">,</span> template<span class="token operator">:</span> template <span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>The template (which we’ll create next) is imported which will result in a HTML string being included in the webpack bundle.</p>
<p>Create the template <code>src/components/PersonReadOnly.html</code> for the read-only component.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>h4 mt-4 mb-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>PersonReadOnly Component<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>table</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>table<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>thead</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span> <span class="token attr-name">scope</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>col<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Item<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span> <span class="token attr-name">scope</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>col<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Value<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>thead</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tbody</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span> <span class="token attr-name">scope</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span> <span class="token attr-name">data-bind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text: person.firstName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span> <span class="token attr-name">scope</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span> <span class="token attr-name">data-bind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text: person.lastName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span> <span class="token attr-name">scope</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Full name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span> <span class="token attr-name">data-bind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text: person.fullName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span> <span class="token attr-name">scope</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Favourite films<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span> <span class="token attr-name">data-bind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>films: person.favouriteFilms<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tbody</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>table</span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The first name observable, last name observable and full name computed are all bound using the built-in <code>text</code> binding.
The favourite films observable array is however bound with a custom <code>films</code> binding which we’ll create next.</p>
<p>Create the file <code>src/bindings/filmsBinding.ts</code>.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> ko <span class="token keyword">from</span> <span class="token string">'knockout'</span><span class="token punctuation">;</span><br><span class="token keyword">import</span> <span class="token punctuation">{</span> BindingHandler<span class="token punctuation">,</span> ObservableArray <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'knockout'</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> filmsBinding <span class="token operator">=</span> <span class="token punctuation">{</span><br>  init<span class="token operator">:</span> <span class="token punctuation">(</span>element<span class="token operator">:</span> HTMLElement<span class="token punctuation">,</span> <span class="token function-variable function">valueAccessor</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> ObservableArray<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token parameter"><span class="token keyword">void</span></span> <span class="token operator">=></span> <span class="token punctuation">{</span><br>    <span class="token keyword">const</span> valueUnwrapped <span class="token operator">=</span> ko<span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token function">valueAccessor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>    <span class="token keyword">const</span> isPopulatedArray <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>valueUnwrapped<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> valueUnwrapped<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">;</span><br>    <span class="token keyword">const</span> text <span class="token operator">=</span> isPopulatedArray <span class="token operator">?</span> valueUnwrapped<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">', '</span><span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token string">'Unknown'</span><span class="token punctuation">;</span><br>  <br>    element<span class="token punctuation">.</span>textContent <span class="token operator">=</span> text<span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span> <span class="token keyword">as</span> BindingHandler<span class="token punctuation">;</span><br><br><span class="token keyword">export</span> <span class="token keyword">default</span> filmsBinding<span class="token punctuation">;</span></code></pre>
<p>Now we’ll create the editable component.
Create the file <code>src/components/PersonEditable.ts</code>.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">import</span> Person <span class="token keyword">from</span> <span class="token string">'../models/person'</span><span class="token punctuation">;</span>  <br><span class="token keyword">import</span> template <span class="token keyword">from</span> <span class="token string">'./PersonEditable.html'</span><span class="token punctuation">;</span>  <br>  <br><span class="token keyword">class</span> <span class="token class-name">PersonReadOnly</span> <span class="token punctuation">{</span>  <br>  person<span class="token operator">:</span> Person<span class="token punctuation">;</span>  <br>  <br>  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">params<span class="token operator">:</span> <span class="token punctuation">{</span> person<span class="token operator">:</span> Person <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>  <br>    <span class="token keyword">this</span><span class="token punctuation">.</span>person <span class="token operator">=</span> params<span class="token punctuation">.</span>person<span class="token punctuation">;</span>  <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token comment">// The default export returns the component details object to register with KO  </span><br><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> viewModel<span class="token operator">:</span> PersonReadOnly<span class="token punctuation">,</span> template<span class="token operator">:</span> template <span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>And create the editable component template <code>src/components/PersonEditable.html</code>.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>h4 mt-4 mb-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>PersonEditable Component<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>form-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>firstName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">data-bind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>textInput: person.firstName<span class="token punctuation">"</span></span><br>        <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>firstName<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>form-control<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>form-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lastName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">data-bind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>textInput: person.lastName<span class="token punctuation">"</span></span><br>        <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lastName<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>form-control<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>We can now reference and use these components in <code>index.ts</code>.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> ko <span class="token keyword">from</span> <span class="token string">'knockout'</span><span class="token punctuation">;</span>  <br>  <br><span class="token keyword">import</span> Person <span class="token keyword">from</span> <span class="token string">"./models/person"</span><span class="token punctuation">;</span>  <br><span class="token keyword">import</span> PersonReadOnly <span class="token keyword">from</span> <span class="token string">'./components/PersonReadOnly'</span><span class="token punctuation">;</span>  <br><span class="token keyword">import</span> PersonEditable <span class="token keyword">from</span> <span class="token string">'./components/PersonEditable'</span><span class="token punctuation">;</span>  <br>  <br><span class="token keyword">import</span> filmsBinding <span class="token keyword">from</span> <span class="token string">'./bindings/filmsBinding'</span><span class="token punctuation">;</span>  <br>  <br>ko<span class="token punctuation">.</span>components<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'person-read-only'</span><span class="token punctuation">,</span> PersonReadOnly<span class="token punctuation">)</span><span class="token punctuation">;</span>  <br>ko<span class="token punctuation">.</span>components<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'person-editable'</span><span class="token punctuation">,</span> PersonEditable<span class="token punctuation">)</span><span class="token punctuation">;</span>  <br>  <br>ko<span class="token punctuation">.</span>bindingHandlers<span class="token punctuation">.</span>films <span class="token operator">=</span> filmsBinding<span class="token punctuation">;</span>  <br>  <br><span class="token keyword">class</span> <span class="token class-name">AppViewModel</span> <span class="token punctuation">{</span>  <br>  person<span class="token operator">:</span> Person<span class="token punctuation">;</span>  <br>  <br>  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>  <br>  <br>    <span class="token comment">// These values are hard-coded but could come from a server API request with JSON response  </span><br>  <span class="token keyword">this</span><span class="token punctuation">.</span>person <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span>  <br>      <span class="token string">'Jon'</span><span class="token punctuation">,</span>  <br>      <span class="token string">'Keeping'</span><span class="token punctuation">,</span>  <br>      <span class="token punctuation">[</span><span class="token string">'The Matrix'</span><span class="token punctuation">,</span> <span class="token string">'The Shawshank Redemption'</span><span class="token punctuation">,</span> <span class="token string">'Upgrade'</span><span class="token punctuation">]</span>  <br>    <span class="token punctuation">)</span>  <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span>  <br>  <br>  <br>ko<span class="token punctuation">.</span><span class="token function">applyBindings</span><span class="token punctuation">(</span><br>  <span class="token keyword">new</span> <span class="token class-name">AppViewModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br>  document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'app'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Here we’re importing and then registering the components and the custom binding.
We’re also creating a instance of the <code>Person</code> class on the <code>person</code> property of <code>AppViewModel</code>.
All that’s left to do now is update the webpack template to use these 2 components.</p>
<p>Update the <code>&lt;div id=&quot;app&quot;&gt;</code> element in <code>webpack-template/index.html</code> to contain:</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>app<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>col-sm-12 col-lg-7<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>person-read-only</span> <span class="token attr-name">params</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>person: person<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>person-read-only</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>col-sm-12 col-lg-5 pl-lg-5<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>person-editable</span> <span class="token attr-name">params</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>person: person<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>person-editable</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>Now when you run <code>npm run serve</code> and reload the browser you’ll see these strongly typed Knockout components in action.</p>
<p>The code for this article is available on the GitHub repo:
<a href="https://github.com/JonUK/knockout-typescript" target="_blank" rel="noopener">https://github.com/JonUK/knockout-typescript</a></p>
<hr>
<h2 id="using-knockout-validation-with-types">Using Knockout Validation with types</h2>
<p>The package <a href="https://github.com/Knockout-Contrib/Knockout-Validation" target="_blank" rel="noopener">Knockout Validation</a>
is very popular with Knockout but unfortunately hasn’t been updated with the latest type definitions.</p>
<p><a href="https://github.com/Knockout-Contrib/Knockout-Validation/pull/670" target="_blank" rel="noopener">A GitHub pull request has been created to provide the latest types</a>
but at this time (April 2021), this pull request has yet to be merged.</p>
<p>For us, this means we’ll need to add a TypeScript type definition file to our project.
Create the file <code>src/knockout.validation.d.ts</code> and copy the content from the
<a href="https://github.com/Knockout-Contrib/Knockout-Validation/blob/ac04d921a2e76d237e3e244a799f4248d9477808/dist/knockout.validation.d.ts" target="_blank" rel="noopener">GitHub pull request file <code>knockout.validation.d.ts</code></a></p>
<p>With the TypeScript type definitions in place, we can install the actual package.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> <span class="token function">install</span> --save knockout.validation</code></pre>
<p>From our main entry point <code>src/main.ts</code>, include this package and configure it as required.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> ko <span class="token keyword">from</span> <span class="token string">'knockout'</span><span class="token punctuation">;</span><br><span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'knockout.validation'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token operator">...</span><br><br>ko<span class="token punctuation">.</span>validation<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br>  errorElementClass<span class="token operator">:</span> <span class="token string">'is-invalid'</span><span class="token punctuation">,</span><br>  errorMessageClass<span class="token operator">:</span> <span class="token string">'invalid-feedback'</span><span class="token punctuation">,</span><br>  decorateInputElement<span class="token operator">:</span> <span class="token boolean">true</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token operator">...</span></code></pre>
<p>Within our components, we can now apply validation rules.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull">  name<span class="token operator">:</span> Observable<span class="token operator">&lt;</span>string<span class="token operator">></span> <span class="token operator">=</span> ko<span class="token punctuation">.</span><span class="token function">observable</span><span class="token punctuation">(</span><span class="token string">'Bananas'</span><span class="token punctuation">)</span><br>    <span class="token punctuation">.</span><span class="token function">extend</span><span class="token punctuation">(</span><span class="token punctuation">{</span> required<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>  productCode<span class="token operator">:</span> Observable<span class="token operator">&lt;</span>string<span class="token operator">></span> <span class="token operator">=</span> ko<span class="token punctuation">.</span><span class="token function">observable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br>    <span class="token punctuation">.</span><span class="token function">extend</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br>      required<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br>      minLength<span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span><br>      pattern<span class="token operator">:</span> <span class="token punctuation">{</span><br>        message<span class="token operator">:</span> <span class="token string">'Please enter letters and digits only'</span><span class="token punctuation">,</span><br>        params<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">^[A-Za-z0-9]*$</span><span class="token regex-delimiter">/</span></span><br>      <span class="token punctuation">}</span><br>    <span class="token punctuation">}</span><span class="token punctuation">)</span><br><br>  stockCount<span class="token operator">:</span> Observable<span class="token operator">&lt;</span>number<span class="token operator">></span> <span class="token operator">=</span> ko<span class="token punctuation">.</span><span class="token function">observable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br>    <span class="token punctuation">.</span><span class="token function">extend</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br>      min<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br>      max<span class="token operator">:</span> <span class="token number">100</span><br>    <span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<hr>
<h2 id="creating-a-new-knockout-project-with-typescript-support">Creating a new Knockout project with TypeScript support</h2>
<p>Fire up your terminal / command prompt and let’s get stuck in!</p>
<p>Create a new folder for our project.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">mkdir</span> knockout-typescript<br><span class="token builtin class-name">cd</span> knockout-typescript</code></pre>
<p>From within the new folder, initialize a new <code>package.json</code>.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> init -y</code></pre>
<p>Now we have our newly created <code>package.json</code> we can install Knockout and TypeScript.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> <span class="token function">install</span> --save knockout<br><span class="token function">npm</span> <span class="token function">install</span> --save-dev typescript</code></pre>
<p>TypeScript is installed with the <code>--save-dev</code> argument to save it as a development dependency as it’s not needed in production.</p>
<p>Our project is going to use webpack to:</p>
<ul>
<li>Handle transpiling TypeScript to Javascript and bundling into a single file</li>
<li>Generate a HTML page that includes a reference to the output bundle</li>
<li>During development, provide a web server that supports live reloading</li>
</ul>
<p>Let’s install webpack and the other related packages we’re going to use.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> <span class="token function">install</span> --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin</code></pre>
<p>We’ll use Babel (with webpack) to actually transpile our TypeScript to JavaScript which can then  be run by web browsers.
Let’s install Babel and other related packages.</p>
<pre class="language-bashnull"><code class="language-bashnull"><span class="token function">npm</span> <span class="token function">install</span> --save-dev @babel/core @babel/preset-env @babel/preset-typescript @babel/plugin-proposal-class-properties babel-loader</code></pre>
<p>We’re going to use Knockout components with HTML templates and want these templates to be bundled up by webpack.
The effect this will have is when the page loads, the component templates will be ready immediately without any additional calls to the server.
We’ll use the webpack loader <code>html-loader</code> to include our HTML in the webpack bundle.
<code>npm install --save-dev html-loader</code></p>
<p>We now need to add the webpack configuration file <code>webpack.config.js</code>.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'path'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <br><span class="token keyword">const</span> HtmlWebpackPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'html-webpack-plugin'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <br>  <br>module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>  <br>  mode<span class="token operator">:</span> <span class="token string">'development'</span><span class="token punctuation">,</span>  <br>  entry<span class="token operator">:</span> <span class="token punctuation">{</span>  <br>    app<span class="token operator">:</span> <span class="token string">'./src/index.ts'</span>  <br>  <span class="token punctuation">}</span><span class="token punctuation">,</span>  <br>  devtool<span class="token operator">:</span> <span class="token string">'source-map'</span><span class="token punctuation">,</span> <span class="token comment">// Generate separate source map files  </span><br>  devServer<span class="token operator">:</span> <span class="token punctuation">{</span>  <br>    contentBase<span class="token operator">:</span> <span class="token string">'./dist'</span><span class="token punctuation">,</span>  <br>    overlay<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token comment">// Show errors in overlay on the website  </span><br>  <span class="token punctuation">}</span><span class="token punctuation">,</span>  <br>  module<span class="token operator">:</span> <span class="token punctuation">{</span>  <br>    rules<span class="token operator">:</span> <span class="token punctuation">[</span>  <br>      <span class="token punctuation">{</span>  <br>        test<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.(js|ts)$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>  <br>        exclude<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">node_modules</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>  <br>        loader<span class="token operator">:</span> <span class="token string">'babel-loader'</span>  <br>      <span class="token punctuation">}</span><span class="token punctuation">,</span>  <br>      <span class="token punctuation">{</span>  <br>        test<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.html$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span> <span class="token comment">// All Knockout.js component HTML templates  </span><br>        use<span class="token operator">:</span> <span class="token string">'html-loader'</span> <span class="token comment">// Adds the component templates to the bundle  </span><br>      <span class="token punctuation">}</span>  <br>    <span class="token punctuation">]</span>  <br>  <span class="token punctuation">}</span><span class="token punctuation">,</span>  <br>  plugins<span class="token operator">:</span> <span class="token punctuation">[</span>  <br>    <span class="token keyword">new</span> <span class="token class-name">HtmlWebpackPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>  <br>      template<span class="token operator">:</span> <span class="token string">'./webpack-template/index.html'</span>  <br>    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>  <br>  <span class="token punctuation">]</span><span class="token punctuation">,</span>  <br>  resolve<span class="token operator">:</span> <span class="token punctuation">{</span>  <br>    extensions<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'.js'</span><span class="token punctuation">,</span> <span class="token string">'.ts'</span><span class="token punctuation">]</span>  <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>This configuration file instructs webpack to use <code>babel-loader</code> to transpile TypeScript (and ES6+) files down to a JavaScript (ES5) bundle which is output to the <code>dist</code> folder.
HTML files are also included in the bundle by <code>html-loader</code>.
The app entry file <code>src/index.ts</code> and the webpack template HTML file <code>webpack-template/index.html</code> used to generate <code>dist/index.html</code> don’t exist yet so lets create these now.</p>
<p>Create the file <code>src/index.ts</code>.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'An alert from index.ts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This is just some test code which will allow us to check everything is working correctly.
Later we’ll add some real code here.</p>
<p>Create the file <code>webpack-template/index.html</code>.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>Knockout TypeScript<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span>  <br>  <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span>  <br>  <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css<span class="token punctuation">"</span></span>  <br>  <span class="token attr-name">integrity</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm<span class="token punctuation">"</span></span>  <br>  <span class="token attr-name">crossorigin</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>anonymous<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>mt-4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Using TypeScript With Knockout<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token comment">&lt;!-- The element Knockout will bind to --></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>app<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>As well as the configuration file for webpack, we also need to add configuration files for Babel TypeScript.</p>
<p>Create the file Babel configuration file  <code>.babelrc</code>.</p>
<pre class="language-jsonnull"><code class="language-jsonnull"><span class="token punctuation">{</span>  <br>  <span class="token property">"presets"</span><span class="token operator">:</span> <span class="token punctuation">[</span>  <br>    <span class="token string">"@babel/preset-env"</span><span class="token punctuation">,</span>  <br>    <span class="token string">"@babel/typescript"</span>  <br>  <span class="token punctuation">]</span><span class="token punctuation">,</span>  <br>  <span class="token property">"plugins"</span><span class="token operator">:</span> <span class="token punctuation">[</span>  <br>    <span class="token string">"@babel/proposal-class-properties"</span>  <br>  <span class="token punctuation">]</span>  <br><span class="token punctuation">}</span></code></pre>
<p>Create the TypeScript configuration file <code>tsconfig.json</code></p>
<pre class="language-jsonnull"><code class="language-jsonnull"><span class="token punctuation">{</span>  <br>  <span class="token property">"compilerOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span>  <br>    <span class="token comment">/* Basic Options */</span>  <br>    <span class="token property">"target"</span><span class="token operator">:</span> <span class="token string">"esnext"</span><span class="token punctuation">,</span> <br>    <span class="token property">"module"</span><span class="token operator">:</span> <span class="token string">"commonjs"</span><span class="token punctuation">,</span><br>    <span class="token property">"allowJs"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <br>    <span class="token property">"noEmit"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br>    <span class="token property">"isolatedModules"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br>  <br>    <span class="token comment">/* Strict Type-Checking Options */</span><br>    <span class="token property">"strict"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br>   <br>    <span class="token comment">/* Module Resolution Options */</span><br>    <span class="token property">"moduleResolution"</span><span class="token operator">:</span> <span class="token string">"node"</span><span class="token punctuation">,</span><br>    <span class="token property">"baseUrl"</span><span class="token operator">:</span> <span class="token string">"./src"</span><span class="token punctuation">,</span><br>  <br>    <span class="token comment">/* Advanced Options */</span> <br>    <span class="token property">"forceConsistentCasingInFileNames"</span><span class="token operator">:</span> <span class="token boolean">true</span><br>  <span class="token punctuation">}</span><span class="token punctuation">,</span>  <br>  <span class="token property">"include"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"src"</span><span class="token punctuation">]</span>  <br><span class="token punctuation">}</span></code></pre>
<p>We’re almost ready to test our page out by running the webpack dev server.
Just before we do that though, let’s update the <code>scripts</code> section in <code>package.json</code> to include a new entries for starting the webpack dev server and for performing a production build.</p>
<pre class="language-jsonnull"><code class="language-jsonnull"><span class="token punctuation">{</span><br>  ...<br>  <span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>  <br>    <span class="token property">"serve"</span><span class="token operator">:</span> <span class="token string">"webpack-dev-server --open"</span><span class="token punctuation">,</span>  <br>    <span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"webpack --mode production"</span><span class="token punctuation">,</span>  <br>  <span class="token punctuation">}</span><span class="token punctuation">,</span><br>  ...<br><span class="token punctuation">}</span></code></pre>
<p>Good work! Now you can run <code>npm run serve</code> to start dev server and load the site.
You should see the JavaScript alert that we added in <code>index.ts</code>.</p>
<p>The code for this article is available on the GitHub repo:
<a href="https://github.com/JonUK/knockout-typescript" target="_blank" rel="noopener">https://github.com/JonUK/knockout-typescript</a></p>
]]>
        </description>
        <pubDate>Sat, 14 Dec 2019 00:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2019/12/using-typescript-with-knockout/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2019/12/using-typescript-with-knockout/</guid>
      </item><item>
        <title>Gradually Migrating To Typescript</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>This article covers how to start using TypeScript modules in an existing project alongside JavaScript modules.
If you have a large project, it may not be feasible to move to TypeScript all in one go.
Gradually migrating provides more flexibility and allows you to, for example, use TypeScript just for new code or “upgrade” modules when there are changes to existing code.</p>
<p>TypeScript provides some huge benefits like types support, better IDE tooling and additional constructs like interfaces and enums so why not start making use of these now.</p>
<!-- Excerpt End -->
<p>This articles assumes you’re using webpack with Babel to transpile ES6+ down to ES5.
The code for this article is available on the GitHub repo:
<a href="https://github.com/JonUK/javascript-with-typescript" target="_blank" rel="noopener">https://github.com/JonUK/javascript-with-typescript</a></p>
<hr>
<h2 id="steps-to-configure-typescript-support">Steps to configure TypeScript support</h2>
<ol>
<li>
<p>Install TypeScript:<br>
<code>npm install --save-dev typescript</code></p>
</li>
<li>
<p>Install the Babel TypeScript preset along with class property support plugin:<br>
<code>npm install --save-dev @babel/preset-typescript</code><br>
<code>npm install --save-dev @babel/plugin-proposal-class-properties</code></p>
</li>
<li>
<p>Edit  <code>.babelrc</code>  to include the new Babel preset and plugin:</p>
</li>
</ol>
<pre class="language-jsonnull"><code class="language-jsonnull"><span class="token punctuation">{</span>  <br>  <span class="token property">"presets"</span><span class="token operator">:</span> <span class="token punctuation">[</span>  <br>    ...<br>    <span class="token string">"@babel/typescript"</span>  <br>  <span class="token punctuation">]</span><span class="token punctuation">,</span>  <br>  <span class="token property">"plugins"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br>    ...  <br>    <span class="token string">"@babel/proposal-class-properties"</span>  <br>  <span class="token punctuation">]</span>  <br><span class="token punctuation">}</span></code></pre>
<ol start="4">
<li>Update <code>webpack.config</code> so Babel additionally compiles TypeScript files:</li>
</ol>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token operator">...</span><br><span class="token punctuation">{</span><br>  test<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.(js|ts)$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>  <br>  exclude<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">node_modules</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>  <br>  loader<span class="token operator">:</span> <span class="token string">'babel-loader'</span>  <br><span class="token punctuation">}</span><span class="token punctuation">,</span><br><span class="token operator">...</span></code></pre>
<ol start="5">
<li>Update <code>webpack.config</code> to additionally resolve files with the <code>.ts</code> extension:</li>
</ol>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token operator">...</span><br>resolve<span class="token operator">:</span> <span class="token punctuation">{</span>  <br>  extensions<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'.js'</span><span class="token punctuation">,</span> <span class="token string">'.ts'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br><span class="token punctuation">}</span><span class="token punctuation">,</span><br><span class="token operator">...</span></code></pre>
<ol start="6">
<li>Create a <code>tsconfig.json</code> file to provide configuration for  the TypeScript compiler:</li>
</ol>
<pre class="language-jsonnull"><code class="language-jsonnull"><span class="token punctuation">{</span>  <br>  <span class="token property">"compilerOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span>  <br>    <span class="token comment">/* Basic options */</span>  <br>    <span class="token property">"target"</span><span class="token operator">:</span> <span class="token string">"esnext"</span><span class="token punctuation">,</span><br>    <span class="token property">"module"</span><span class="token operator">:</span> <span class="token string">"commonjs"</span><span class="token punctuation">,</span><br>    <span class="token property">"allowJs"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br>    <span class="token property">"noEmit"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <br>    <span class="token property">"isolatedModules"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br>  <br>    <span class="token comment">/* Strict type-checking Options */</span><br>    <span class="token property">"strict"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br>  <br>    <span class="token comment">/* Module resolution options */</span><br>    <span class="token property">"moduleResolution"</span><span class="token operator">:</span> <span class="token string">"node"</span><span class="token punctuation">,</span><br>    <span class="token property">"baseUrl"</span><span class="token operator">:</span> <span class="token string">"./src"</span><span class="token punctuation">,</span><br>  <br>    <span class="token comment">/* Advanced options */</span><br>    <span class="token property">"forceConsistentCasingInFileNames"</span><span class="token operator">:</span> <span class="token boolean">true</span><br>  <span class="token punctuation">}</span><span class="token punctuation">,</span>  <br>  <span class="token property">"include"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"src"</span><span class="token punctuation">]</span>  <br><span class="token punctuation">}</span></code></pre>
<hr>
<h2 id="steps-for-eslint-support">Steps for ESLint support</h2>
<ol start="7">
<li>
<p>Install the ESLint TypeScript parser and plugin:<br>
<code>npm install --save-dev @typescript-eslint/parser</code><br>
<code>npm install --save-dev @typescript-eslint/eslint-plugin</code></p>
</li>
<li>
<p>Add an overrides section to <code>.eslintrc</code> to handle TypeScript files:</p>
</li>
</ol>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token punctuation">{</span><br>  <span class="token operator">...</span><br>  <span class="token string">"overrides"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br>    <span class="token punctuation">{</span><br>      <span class="token string">"extends"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"plugin:@typescript-eslint/recommended"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br>      <span class="token string">"files"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"*.ts"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br>      <span class="token string">"parser"</span><span class="token operator">:</span> <span class="token string">"@typescript-eslint/parser"</span><span class="token punctuation">,</span><br>      <span class="token string">"parserOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br>        <span class="token string">"project"</span><span class="token operator">:</span> <span class="token string">"./tsconfig.json"</span><br>      <span class="token punctuation">}</span><span class="token punctuation">,</span><br>      <span class="token string">"plugins"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br>        <span class="token string">"@typescript-eslint"</span><br>      <span class="token punctuation">]</span><span class="token punctuation">,</span><br>      <span class="token string">"rules"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br>        <span class="token string">"@typescript-eslint/no-inferrable-types"</span><span class="token operator">:</span> <span class="token string">"off"</span><span class="token punctuation">,</span><br>        <span class="token string">"@typescript-eslint/no-explicit-any"</span><span class="token operator">:</span> <span class="token string">"off"</span><span class="token punctuation">,</span><br>        <span class="token string">"@typescript-eslint/ban-ts-ignore"</span><span class="token operator">:</span> <span class="token string">"warn"</span><span class="token punctuation">,</span><br>        <span class="token string">"@typescript-eslint/no-use-before-define"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"error"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token string">"functions"</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br>        <span class="token string">"no-invalid-this"</span><span class="token operator">:</span> <span class="token string">"off"</span><br>      <span class="token punctuation">}</span><br>    <span class="token punctuation">}</span><br>  <span class="token punctuation">]</span><br>  <span class="token operator">...</span><br><span class="token punctuation">}</span>  </code></pre>
<ol start="9">
<li>Update <code>webpack.config</code> so ESLint additionally checks TypeScript files:</li>
</ol>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token operator">...</span><br><span class="token punctuation">{</span>  <br>  enforce<span class="token operator">:</span> <span class="token string">'pre'</span><span class="token punctuation">,</span>  <br>  test<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.(js|ts)$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>  <br>  exclude<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">node_modules</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>  <br>  loader<span class="token operator">:</span> <span class="token string">'eslint-loader'</span><span class="token punctuation">,</span>  <br>  options<span class="token operator">:</span> <span class="token punctuation">{</span>  <br>    emitError<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>  <br>    emitWarning<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>  <br>    failOnError<span class="token operator">:</span> <span class="token boolean">true</span>  <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span><span class="token punctuation">,</span>  <br><span class="token operator">...</span></code></pre>
<hr>
<h2 id="using-typescript-modules-(github-code-example)">Using TypeScript modules (GitHub code example)</h2>
<p>Having followed the steps above, you can now add TypeScript files in your project.</p>
<p>To see all this is action, I’ve created an example in GitHub for a simple webpage that calculates the character count, word count and longest word length for some text.
The example uses webpack, Babel &amp; ESLint along with Karma and Jasmine for unit tests.
<img src="http://keepinguptodate.com/pages/2019/12/word-count.png" loading="lazy" alt="Screenshot of word count webpage example"></p>
<p>The repository has two branches for before and after the TypeScript support changes:</p>
<p><strong>JavaScript webpage GitHub branch before changes</strong><br>
<a href="https://github.com/JonUK/javascript-with-typescript/tree/01_javascript_only" target="_blank" rel="noopener">https://github.com/JonUK/javascript-with-typescript/tree/01_javascript_only</a></p>
<p><strong>JavaScript and TypeScript webpage branch</strong><br>
<a href="https://github.com/JonUK/javascript-with-typescript/tree/02_typescript_support" target="_blank" rel="noopener">https://github.com/JonUK/javascript-with-typescript/tree/02_typescript_support</a></p>
<p>Review the TypeScript file <code>src/wordCountCalculator.ts</code> and it’s corresponding tests <code>wordCountCalculator_test.js</code> which are still in JavaScript.</p>
<hr>
<p>Now go forth and add TypeScript support! 😄</p>
]]>
        </description>
        <pubDate>Sun, 01 Dec 2019 00:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2019/12/gradually-migrating-to-typescript/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2019/12/gradually-migrating-to-typescript/</guid>
      </item><item>
        <title>Vuex Getter With Parameters In TypeScript</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>When using Vuex with TypeScript, you typically use the <code>@Getter</code> decorator from the
<a href="https://github.com/ktsn/vuex-class" target="_blank" rel="noopener">binding helpers package <code>vuex-class</code></a>.
This is a more declarative approach over using <code>this.$store.getters.*</code>.</p>
<p><strong>Vuex <code>@Getter</code> example</strong></p>
<pre class="language-javascriptnull"><code class="language-javascriptnull">@<span class="token function">Getter</span><span class="token punctuation">(</span><span class="token string">'books'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> namespace<span class="token operator">:</span> <span class="token string">'library'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><br>books<span class="token operator">!</span><span class="token operator">:</span> Book<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre>
<p>The <code>getter</code> above retrieves all books from the Vuex module <code>library</code> but what if you wanted to retrieve just a single book by it’s ID?</p>
<!-- Excerpt End -->
<hr>
<h2 id="method-style-getters">Method Style <code>Getters</code></h2>
<p>Vuex supports “method-style” <code>getters</code> which are <code>getters</code> that return a function with one or more parameters.</p>
<p><strong>Vuex “method-style” getter example</strong></p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token operator">...</span><br><br><span class="token keyword">const</span> state<span class="token operator">:</span> LibraryState <span class="token operator">=</span> <span class="token punctuation">{</span><br>  books<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br><span class="token keyword">export</span> <span class="token keyword">const</span> getters<span class="token operator">:</span> GetterTree<span class="token operator">&lt;</span>LibraryState<span class="token punctuation">,</span> RootState<span class="token operator">></span> <span class="token operator">=</span> <span class="token punctuation">{</span><br>  <span class="token function-variable function">getBookById</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">theState<span class="token operator">:</span> LibraryState</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>id<span class="token operator">:</span> string<span class="token punctuation">)</span><span class="token operator">:</span> Book <span class="token operator">|</span> <span class="token parameter"><span class="token keyword">null</span></span> <span class="token operator">=></span> <span class="token punctuation">{</span><br>    <span class="token keyword">const</span> book <span class="token operator">=</span> theState<span class="token punctuation">.</span>books<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token parameter">item</span> <span class="token operator">=></span> item<span class="token punctuation">.</span>id <span class="token operator">===</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span><br>    <span class="token keyword">return</span> book <span class="token operator">||</span> <span class="token keyword">null</span><span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br><span class="token operator">...</span></code></pre>
<p>In this example, the <code>getter</code> returns another function which accepts the book <code>id</code> argument and performs a query on the array in state to find the item.
With this “method-style” <code>getter</code> in place, we can now get a handle to it from within a component via the <code>@Getter</code> decorator as follows.</p>
<p><strong><code>@Getter</code> decorator with argument example</strong></p>
<pre class="language-javascriptnull"><code class="language-javascriptnull">@<span class="token function">Getter</span><span class="token punctuation">(</span><span class="token string">'getBookById'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> namespace<span class="token operator">:</span> <span class="token string">'library'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><br>getBookById<span class="token operator">!</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">id<span class="token operator">:</span> string</span><span class="token punctuation">)</span> <span class="token operator">=></span> Book<span class="token punctuation">;</span></code></pre>
<p>The <code>getBookById</code> function can now be called with the book <code>id</code> parameter to retrieve a single book from state.</p>
<p><strong><code>mounted</code> hook and route param example</strong></p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">class</span> <span class="token class-name">BookDetails</span> <span class="token keyword">extends</span> <span class="token class-name">Vue</span> <span class="token punctuation">{</span><br>  @<span class="token function">Getter</span><span class="token punctuation">(</span><span class="token string">'getBookById'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> namespace<span class="token operator">:</span> <span class="token string">'library'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><br>  getBookById<span class="token operator">!</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">id<span class="token operator">:</span> string</span><span class="token punctuation">)</span> <span class="token operator">=></span> Book<span class="token punctuation">;</span><br><br>  book<span class="token operator">:</span> Book <span class="token operator">|</span> <span class="token keyword">null</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span><br><br>  <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span><br>    <span class="token keyword">const</span> id <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$route<span class="token punctuation">.</span>params<span class="token punctuation">.</span>id<span class="token punctuation">;</span><br>    <span class="token keyword">this</span><span class="token punctuation">.</span>book <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getBookById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre>
]]>
        </description>
        <pubDate>Thu, 05 Sep 2019 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2019/09/vuex-getter-parameter-typescript/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2019/09/vuex-getter-parameter-typescript/</guid>
      </item><item>
        <title>Designing And Developing A Responsive Weather App Webpage</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->  
<p>This article covers designing and developing a Weather App webpage using Responsive Web Design (RWD) techniques.</p>
<p>This article will cover creating wireframes for our page so we can visually see how the app will be laid out on different devices and then we’ll create some high-fidelity designs.
Next we’ll develop the responsive webpage making use of CSS media queries to alter the layout and design to fit any device screen size from a mobile phone to a large desktop.</p>
<!-- Excerpt End -->  
<img src="http://keepinguptodate.com/pages/2019/08/01-on-different-devices.png" class="no-border" loading="lazy" alt="Preview of how the page looks on different devices">
<p>The code for this article is available on GitHub:
<a href="https://github.com/JonUK/responsive-web-weather-app" target="_blank" rel="noopener">https://github.com/JonUK/responsive-web-weather-app</a></p>
<p>A demo of the responsive Weather App webpage is hosted on Netlify:
<a href="https://clever-cray-449315.netlify.com/" target="_blank" rel="noopener">https://clever-cray-449315.netlify.com/</a></p>
<hr>
<h2 id="project-brief">Project Brief</h2>
<p>Before we start, let’s review the fictitious brief for our Weather App page so we have a clear idea of what we want to achieve.</p>
<p><strong>Requirements</strong></p>
<ul>
<li>Current weather conditions are displayed at a large size</li>
<li>Weather conditions for the next 5 days are displayed</li>
<li>Weather conditions at different times throughout the current day are displayed (if space available)</li>
<li>The location of the weather conditions is displayed</li>
<li>The page looks good on all devices from mobile phones through to large desktops</li>
<li>Ideally no scrolling is required on mobile phones and tablets in portrait orientation</li>
</ul>
<hr>
<h2 id="creating-wireframes">Creating Wireframes</h2>
<p>A great starting point for a web page that is highly visual is to create wireframes. This is especially true for pages that need to work across different device sizes.</p>
<p>We’ll take a mobile first design approach where we create wireframes for a mobile phone and then work up to tablets and desktops. In many ways, a mobile phone is the hardest device to design for due to the limited space available so will force us to consider the essential features and content.</p>
<p>I created the wireframes using Adobe XD (a free version is available).</p>
<p><strong>Mobile Phone</strong> — 375 x 667 pixels
<img src="http://keepinguptodate.com/pages/2019/08/02-wireframe-mobile.svg" class="small" loading="lazy" alt="Wireframe for a mobile phone"></p>
<p><strong>Tablet</strong> — 768 x 1024 pixels
<img src="http://keepinguptodate.com/pages/2019/08/03-wireframe-tablet.svg" class="medium" loading="lazy" alt="Wireframe for a tablet"></p>
<p><strong>Desktop</strong> — 1920 x 1080 pixels
<img src="http://keepinguptodate.com/pages/2019/08/04-wireframe-desktop.svg" alt="alt text" title="Wireframe for a tablet" loading="lazy"></p>
<p>As you’ll notice, there are no colors or icons on the wireframes and they look very plain. That’s intentional and later we’ll come back and create high-fidelity designs. The purpose of these wireframes is to understand the layout and content for our page on different devices.</p>
<p>On a mobile phone, we won’t show the “Weather by hour” section as there’s not enough space (without introducing vertical scrolling) and we show a condensed version of the “Next 5 days” section.</p>
<p>On a tablet, we’ll move the “Current stats” section up and to the right the “Current temperature” section and we show a extended table version of the “Next 5 days” section.</p>
<p>On a desktop, we essentially have the same version as the tablet but with more spacing and some larger font sizes.</p>
<p>Although our wireframes have been created at typical mobile phone (375 x 667), tablet (768 x 1024) and desktop  (1920 x 1080) sizes, these won’t necessarily be the breakpoints we’ll use to swap layouts. Later when we’re developing our page, we’ll review the breakpoints to use based on the space our content needs to show.</p>
<hr>
<h2 id="creating-high-fidelity-designs">Creating High-Fidelity Designs</h2>
<p>Now we understand the page layout for each device and we understand what content we want to show, we can move on to creating high-fidelity designs. These designs will let us explore exactly how we want to present the UI to our users.</p>
<p>A caveat to add at this point is that I’m a developer rather than designer, so I’ll give this my best shot!</p>
<p><strong>Mobile Phone</strong> — 375 x 667 pixels<br>
<img src="http://keepinguptodate.com/pages/2019/08/05-hi-fidelity-mobile@2x.png" class="small" loading="lazy" alt="High-fidelity designs for a mobile phone"></p>
<p><strong>Tablet</strong> — 768 x 1024 pixels
<img src="http://keepinguptodate.com/pages/2019/08/06-hi-fidelity-tablet.png" class="medium" loading="lazy" alt="High-fidelity designs for a tablet"></p>
<p>I used the free
<a href="http://emske.com/25-outline-weather-icons/" target="_blank" rel="noopener">25 Outline Weather Icons from Emske.com</a>
which look fantastic and really help give a nice “clean” look.</p>
<hr>
<h2 id="developing-the-page-layout">Developing The Page Layout</h2>
<p>Now we’ve created the wireframes and designs, we’re ready to get our hands dirty and start developing the Weather App page. As we’ve seen, there’s 2 main layouts we need to support.</p>
<p><strong>Mobile And Tablet / Desktop Layouts</strong>
<img src="http://keepinguptodate.com/pages/2019/08/07-page-layout.png" alt="alt text" title="Page layout across mobile, tablet and desktop" loading="lazy"></p>
<p>We’ll take a mobile first development approach to creating our web page which means we’ll initially create CSS just to  support mobile phone devices and then add media queries to adjust these styles for larger devices.</p>
<p>Let’s start development with the HTML page. Create the HTML file <code>index.html</code> with the following content:</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>Weather App<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&amp;display=swap<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>site.css<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>main-container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>location-and-date<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    A) Location and date  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-temperature<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    B) Current temperature  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-stats<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    C) Current stats  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>weather-by-hour<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    D) Weather by hour  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>next-5-days<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    E) Next 5 days  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span>  <br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>Next create the file <code>site.css</code> with the following content:</p>
<pre class="language-cssnull"><code class="language-cssnull"><span class="token selector">*</span> <span class="token punctuation">{</span>  <br>  <span class="token property">-webkit-font-smoothing</span><span class="token punctuation">:</span> antialiased<span class="token punctuation">;</span>  <br>  <span class="token property">-moz-osx-font-smoothing</span><span class="token punctuation">:</span> grayscale<span class="token punctuation">;</span>  <br>  <span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span><br><br><span class="token selector">body</span> <span class="token punctuation">{</span>  <br>  <span class="token property">margin</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span>  <br>  <span class="token property">font-size</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br>  <span class="token property">background-color</span><span class="token punctuation">:</span> gray<span class="token punctuation">;</span><br>  <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">'Open Sans'</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span><br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.main-container</span> <span class="token punctuation">{</span>  <br>  <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>  <br>  <span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.location-and-date</span> <span class="token punctuation">{</span>  <br>  <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br>  <span class="token property">border</span><span class="token punctuation">:</span> 3px solid blue<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.current-temperature</span> <span class="token punctuation">{</span>  <br>  <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br>  <span class="token property">border</span><span class="token punctuation">:</span> 3px solid yellow<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.current-stats</span> <span class="token punctuation">{</span>  <br>  <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br>  <span class="token property">border</span><span class="token punctuation">:</span> 3px solid red<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.weather-by-hour</span> <span class="token punctuation">{</span>  <br>  <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>  <br>  <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br>  <span class="token property">border</span><span class="token punctuation">:</span> 3px solid blue<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.next-5-days</span> <span class="token punctuation">{</span>  <br>  <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br>  <span class="token property">border</span><span class="token punctuation">:</span> 3px solid yellow<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span></code></pre>
<p>We’re using flexbox on the <code>&lt;main&gt;</code> element via the use of the attribute <code>display: flex;</code> on the class <code>main-container</code> and this element is acting as the flexbox parent. Each flexbox child item (each <code>&lt;div&gt;</code> element) is set to a width of <code>100%</code> and the items are allowed to wrap because of the parent’s attribute <code>flex-wrap: wrap;</code>.  The end result is that each <code>&lt;div&gt;</code> element is displayed at full width one below the other which is what we want on a mobile phone device.</p>
<p>The “Weather by hour” section is hidden due to the attribute <code>display: none;</code>. As we saw in our designs, only on viewports larger than a mobile phone is this section displayed.</p>
<p>The HTML contains a <code>&lt;link&gt;</code> element to load the Google Fonts “Open Sans” font which we then apply to all elements via the <code>body</code> selector in the CSS.</p>
<hr>
<h2 id="adjusting-the-layout-for-a-tablet">Adjusting The Layout For A Tablet</h2>
<p>We do want to show the “Weather by hour” section on a tablet device or any device larger than a tablet. A typical tablet in portrait orientation is 768 pixels wide so we’ll use a media query to apply different styles to the “Weather by hour” section when the screen is 768 pixels or greater.</p>
<p>Add the following CSS below the <code>.weather-by-hour</code> selector:</p>
<pre class="language-cssnull"><code class="language-cssnull"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 768px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>  <br>  <span class="token selector">.weather-by-hour</span> <span class="token punctuation">{</span>  <br>    <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>  <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span></code></pre>
<p>Now as you resize the browser wider and narrower than 768 pixels, the “Weather by hour” section will show and hide.</p>
<p>The other change we want on a tablet device or any device larger than a tablet is to show the “Current temperature” and “Current stats” sections next to each other. To achieve this, we’ll use another media query to set the width of the 2 <code>&lt;div&gt;</code> elements to <code>50%</code> when the screen is 768 pixels or greater.</p>
<p>Add the following CSS under the <code>.current-stats</code> selector:</p>
<pre class="language-cssnull"><code class="language-cssnull"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 768px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>  <br>  <span class="token selector">.current-temperature,  <br>  .current-stats</span> <span class="token punctuation">{</span>  <br>    <span class="token property">width</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span>  <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span></code></pre>
<p>To test how your webpage looks on different devices, most modern browsers have developer tools where you can view your site at different viewport sizes. You can read about how to do this with Chrome on the
<a href="https://developers.google.com/web/tools/chrome-devtools/device-mode/" target="_blank" rel="noopener">Google Developers website article Simulate Mobile Devices with Device Mode in Chrome DevTools</a>.</p>
<hr>
<h2 id="adjusting-the-layout-for-a-desktop">Adjusting The Layout For A Desktop</h2>
<p>On a desktop, we’ll increase the text size slightly and restrict the maximum width of the <code>&lt;main&gt;</code> element to 1120 pixels. Add the following CSS below the <code>.main-container</code> selector:</p>
<pre class="language-cssnull"><code class="language-cssnull"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 1000px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>  <br>  <span class="token selector">.main-container</span> <span class="token punctuation">{</span>  <br>    <span class="token property">width</span><span class="token punctuation">:</span> 980px<span class="token punctuation">;</span>  <br>    <span class="token property">margin</span><span class="token punctuation">:</span> 16px auto<span class="token punctuation">;</span><br>    <span class="token property">font-size</span><span class="token punctuation">:</span> 1.1em<span class="token punctuation">;</span> <br>  <span class="token punctuation">}</span>  <br><span class="token punctuation">}</span></code></pre>
<p>By setting the width to 980 pixels and setting the left and right margins to <code>auto</code> ensures the <code>&lt;main&gt;</code> element is centered in the viewport when the screen is at or is wider than 1000 pixels. A 20 pixel (1000 - 980) tolerance has been used in case a browser scrollbar is being displayed.</p>
<hr>
<h2 id="setting-the-background-gradient">Setting The Background Gradient</h2>
<p>In our high-fidelity designs, we have a nice blue gradient background. To achieve this, we’ll apply a <code>linear-gradient</code> to the <code>&lt;body&gt;</code> element. As the blue gradient is quite dark, we’ll swap the foreground <code>color</code> to <code>white</code> so text is legible.</p>
<pre class="language-cssnull"><code class="language-cssnull"><span class="token selector">body</span> <span class="token punctuation">{</span>  <br>  <span class="token property">margin</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span>  <br>  <span class="token property">font-size</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br>  <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">'Open Sans'</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span>  <br>  <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span>  <br>  <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br>  <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>to bottom<span class="token punctuation">,</span> <span class="token function">rgb</span><span class="token punctuation">(</span>43<span class="token punctuation">,</span>50<span class="token punctuation">,</span>178<span class="token punctuation">)</span> 0%<span class="token punctuation">,</span> <span class="token function">rgb</span><span class="token punctuation">(</span>20<span class="token punctuation">,</span>136<span class="token punctuation">,</span>204<span class="token punctuation">)</span> 100%<span class="token punctuation">)</span><span class="token punctuation">;</span>  <br>  <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span>  <br>  <span class="token property">background-attachment</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span></code></pre>
<hr>
<h2 id="location-and-date-section">Location And Date Section</h2>
<p>The first section on the webpage we’ll tackle is the “Location And Date” section. In the HTML, swap the existing  <code>&lt;div&gt;</code> with the class <code>location-and-date</code> with the following:</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>location-and-date<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>location-and-date__location<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>London, UK<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>Sunday 4th August<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>Then in the CSS, swap the <code>location-and-date</code> class style with the following:</p>
<pre class="language-cssnull"><code class="language-cssnull"><span class="token selector">.location-and-date</span> <span class="token punctuation">{</span>  <br>  <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span><br><br><span class="token selector">.location-and-date__location</span> <span class="token punctuation">{</span>  <br>  <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>  <br>  <span class="token property">font-size</span><span class="token punctuation">:</span> 2em<span class="token punctuation">;</span>  <br>  <span class="token property">font-weight</span><span class="token punctuation">:</span> 600<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span></code></pre>
<p>To give our webpage some semantic meaning, we’ve used a level 1 heading for the location text and styled the text to the size and weight we require.</p>
<p>The CSS class names used follow a Block, Element, Modifier (BEM) naming approach to help isolate styles and make the CSS more maintainable.
You can read more about BEM on the
<a href="https://www.smashingmagazine.com/2018/06/bem-for-beginners/#non-evident-advantages-of-the-methodology" target="_blank" rel="noopener">Smashing website article BEM For Beginners: Why You Need BEM</a>.</p>
<hr>
<h2 id="current-temperature-section">Current Temperature Section</h2>
<p>The current temperature section consists of a large weather icon, the temperature and a short summary (e.g. “Mostly Cloudy”). Let’s change the HTML to add these elements and then via CSS we’ll style them.</p>
<p>In <code>index.html</code> replace the <code>&lt;div&gt;</code> with the class <code>current-temperature</code> with the following:</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-temperature<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-temperature__icon-container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icons/mostly-sunny.svg<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-temperature__icon<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-temperature__content-container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-temperature__value<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>21<span class="token entity named-entity" title="&deg;">&amp;deg;</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>current-temperature__summary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Mostly Sunny<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>As the image is a visual representation of the short summary text, we don’t need this to be announced separately by a screen reader. We can safely add an <code>alt</code> attribute with an empty string so screens reader won’t announce anything for this image.</p>
<p>Next replace the existing <code>current-temperature</code> class and add the other new classes  to <code>site.css</code>:</p>
<pre class="language-cssnull"><code class="language-cssnull"><span class="token selector">.current-temperature</span> <span class="token punctuation">{</span>  <br>  <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>  <br>  <span class="token property">margin-top</span><span class="token punctuation">:</span> 0.25em<span class="token punctuation">;</span>  <br>  <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.current-temperature__icon-container</span> <span class="token punctuation">{</span>  <br>  <span class="token property">flex-grow</span><span class="token punctuation">:</span> 1.25<span class="token punctuation">;</span>  <br>  <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.current-temperature__content-container</span> <span class="token punctuation">{</span>  <br>  <span class="token property">flex-grow</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>  <br>  <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.current-temperature__icon</span> <span class="token punctuation">{</span>  <br>  <span class="token property">width</span><span class="token punctuation">:</span> 10.5em<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.current-temperature__value</span> <span class="token punctuation">{</span>  <br>  <span class="token property">font-size</span><span class="token punctuation">:</span> 5.25em<span class="token punctuation">;</span>  <br>  <span class="token property">font-weight</span><span class="token punctuation">:</span> 300<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span>  <br>  <br><span class="token selector">.current-temperature__summary</span> <span class="token punctuation">{</span>  <br>  <span class="token property">margin-top</span><span class="token punctuation">:</span> -0.5em<span class="token punctuation">;</span>  <br>  <span class="token property">margin-left</span><span class="token punctuation">:</span> -0.6em<span class="token punctuation">;</span>  <br>  <span class="token property">font-size</span><span class="token punctuation">:</span> 1.125em<span class="token punctuation">;</span>  <br><span class="token punctuation">}</span></code></pre>
<p>The <code>&lt;div&gt;</code> with the class <code>current-temperature</code> is now acting as a flexbox child of the <code>&lt;main&gt;</code> element as well as a flexbox parent for the <code>&lt;div&gt;</code> elements with the classes <code>current-temperature__icon-container</code> and <code>current-temperature__content-container</code>.</p>
<p>The 2 child <code>container</code> <code>&lt;divs&gt;</code> have a <code>flex-grow</code> attribute which controls how much space they will take up as the available width increases. The <code>current-temperature__icon-container</code> has a grow factor of <code>1.25</code> so will take up slightly more width than the <code>current-temperature__content-container</code> which has a grow factor of <code>1</code>.</p>
<p>Some custom margins have been added to the temperature summary text to pull the text up closer to the temperature value text and to help align the text horizontally.</p>
<hr>
<p><strong>Screenshot On A Mobile Phone</strong>
<img src="http://keepinguptodate.com/pages/2019/08/08-part-1-final-mobile.png" class="small" loading="lazy" alt="The webpage partially complete"></p>
<p>Well done! You’ve made some great progress. Come back soon for part 2 where we’ll develop the rest of sections of the webpage including the “Next 5 days” section which will involve some much more advanced responsive techniques.</p>
<p>The code for this article is available on GitHub:
<a href="https://github.com/JonUK/responsive-web-weather-app" target="_blank" rel="noopener">https://github.com/JonUK/responsive-web-weather-app</a></p>
]]>
        </description>
        <pubDate>Sun, 04 Aug 2019 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2019/08/design-and-develop-web-app/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2019/08/design-and-develop-web-app/</guid>
      </item><item>
        <title>Creating A Blog With Eleventy</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>This article covers creating a blog from scratch using the Node.js powered static site generator
<a href="https://www.11ty.io/" target="_blank" rel="noopener">Eleventy</a>
(aka 11ty).
Eleventy is a relatively new project that came out in 2018 and is quickly gaining traction. It’s up to an impressive 5,000 stars on GitHub already.
Eleventy keeps things simple and as you’ll see, really enables you to quickly create a fully functional site.</p>
<p>The blog we’ll create will contain a homepage with excerpts from our posts  that can be clicked on to read the full post. Once we have our blog up and running, adding new posts will be as simple as adding new Markdown files. Eleventy will take care of generating HTML pages seamlessly for us as well as updating the homepage.</p>
<!-- Excerpt End -->
<p>The code for this article is available on GitHub:<br>
<a href="https://github.com/JonUK/eleventy-blog" target="_blank" rel="noopener">https://github.com/JonUK/eleventy-blog</a></p>
<p>A demo of the blog is hosted on Netlify:<br>
<a href="https://dazzling-almeida-ca0492.netlify.com/" target="_blank" rel="noopener">https://dazzling-almeida-ca0492.netlify.com/</a></p>
<p><img src="http://keepinguptodate.com/pages/2019/06/01-homepage.png" alt="alt text" title="A visual of how the final blog site looks" loading="lazy"></p>
<hr>
<h2 id="creating-the-project">Creating the project</h2>
<p>Create the folder <code>eleventy-blog</code> for our project and then within this folder, create a new <code>package.json</code>.</p>
<pre class="language-textnull"><code class="language-textnull">mkdir eleventy-blog<br>cd eleventy-blog<br>npm init</code></pre>
<p>When the <code>npm init</code> command is run, you will be asked a series of questions which can all be answered with the default values.</p>
<p>With the <code>package.json</code> file in place, we can now install Eleventy locally as a development dependency.</p>
<pre class="language-textnull"><code class="language-textnull">npm install @11ty/eleventy --save-dev</code></pre>
<p>Note that the package is within the scope <code>@11ty</code>. Another completely different npm package exists with the name <code>eleventy</code>.</p>
<p>Now let’s create the Markdown file <code>About.md</code> for the about page on our blog.</p>
<pre class="language-markdownnull"><code class="language-markdownnull"><span class="token title important"><span class="token punctuation">#</span> About This Blog </span><br>  <br>This is my special place on the big World Wide Web</code></pre>
<p>And with these steps alone, we are ready to have Eleventy generate our first HTML page. Run <code>npx eleventy</code>.</p>
<pre class="language-textnull"><code class="language-textnull">npx eleventy<br> <br>Writing _site/About/index.html from ./About.md.<br>Processed 1 file in 0.26 seconds</code></pre>
<p>You should now have a new folder <code>_site</code> created for you by Eleventy which contains the folder <code>About</code> and inside this folder, the HTML file <code>index.html</code>.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>About This Blog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span> <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This is my special place on big World Wide Web<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre>
<p>When Eleventy finds a template content file like a Markdown file, it generates an <code>index.html</code> under a folder named the same as the original filename but without the file extension. E.g. the file <code>Pages\Help.md</code> will result in <code>index.html</code> being created under the folder <code>_site\Pages\Help</code>.</p>
<p>The default output folder is <code>_site</code> and the default input folder is the root directory. These can be configured via an <code>.eleventy.js</code> config file in the root directory. Later in this article, we’ll create this file but for other reasons.
<a href="https://www.11ty.io/docs/config/" target="_blank" rel="noopener">You can read more about configuration in the official documentation</a>.</p>
<hr>
<h2 id="serving-up-the-site">Serving up the site</h2>
<p>Having Eleventy generate the HTML for our blog posts is great but while we’re developing and adding content, we really want to run a web server so we can see our pages in the browser and even better than that, have the browser update whenever we make changes.  Well guess what… Eleventy has got this covered!</p>
<p>First we’ll add some script entries to our <code>package.json</code> for the different Eleventy commands we’re going to use.</p>
<pre class="language-textnull"><code class="language-textnull">...<br>"scripts": {<br>  "build": "npx eleventy",<br>  "serve": "npx eleventy --serve",<br>  "debug": "DEBUG=* npx eleventy"<br>},<br>...</code></pre>
<p>Next we’ll create a basic layout file that will contain HTML markup to wrap around the HTML that Eleventy generates for each Markdown file. The layout file will ensure that the final output is valid HTML with <code>&lt;html&gt;</code> and <code>&lt;body&gt;</code> tags. The layout file will also contain standard elements we’d like on every page such as a header and footer.</p>
<p>Create the folder <code>_includes</code> and within this folder create the file <code>base-layout.njk</code> with the following content.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span> <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>Eleventy Blog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span> <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span> <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span> <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br>    Site Header<br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/About<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>About<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span> <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span><span class="token punctuation">></span></span> <br>    {{ content | safe }}<br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span> <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>footer</span><span class="token punctuation">></span></span>Site Footer<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>footer</span><span class="token punctuation">></span></span> <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span> <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>Layouts are searched for in the “includes folder”, which is <code>_includes</code> by default.  This can be configured to another location if required.</p>
<p>A <code>.njk</code> file is a Nunjucks template that can contain, among other things, loops, conditional logic, special formatting and variable handling. With the line <code>{{ content | safe }}</code>, the template will take the HTML generated from the Markdown file and combine this into the HTML generated for the layout template. The <code> | safe</code> part runs a filter that ensures the HTML from the Markdown is not escaped.
<a href="https://mozilla.github.io/nunjucks/" target="_blank" rel="noopener">You can learn more about Nunjucks on the official site</a></p>
<p>To use this layout, we need to add some front matter metadata at the top of our <code>About.md</code> file.</p>
<pre class="language-markdownnull"><code class="language-markdownnull"><span class="token hr punctuation">---</span> <br><span class="token title important">layout: base-layout.njk <br><span class="token punctuation">---</span></span> <br><span class="token title important"><span class="token punctuation">#</span> About this blog </span><br>  <br>This is my special place on the big World Wide Web<br><br></code></pre>
<p>We can now start the local web server with hot-reloading via the following command.</p>
<pre class="language-textnull"><code class="language-textnull">npm run serve</code></pre>
<p>Going to
<a href="http://localhost:8080/About" target="_blank" rel="noopener">http://localhost:8080/About</a>
in a browser (your port may vary) shows our first page in all it’s glory!</p>
<p>If you change the Markdown file or the layout template, Eleventy will generate the new HTML and update the browser almost immediately. Similarly, if you create a new Markdown file, Eleventy will detect this and automatically generate a new HTML file.</p>
<hr>
<h2 id="creating-the-homepage">Creating the homepage</h2>
<p>What we’d like on our homepage is to list the recent blog posts we’ve added to our site. Let’s first create some dummy blog post Markdown files so we have some content to show. In a new folder <code>posts\2019</code> create the files <code>blog-post-1.md</code> and <code>blog-post-2.md</code> with following content.</p>
<p><strong>blog-post-1.md</strong></p>
<pre class="language-markdownnull"><code class="language-markdownnull"><span class="token hr punctuation">---</span><br>layout: base-layout.njk <br>title: This Is My First Ever Post<br>date: 2019-05-30<br><span class="token title important">tags: ['post']<br><span class="token punctuation">---</span></span><br>This is the first post on my blog.<br> <br>Eleventy is super fresh init.</code></pre>
<p><strong>blog-post-2.md</strong></p>
<pre class="language-markdownnull"><code class="language-markdownnull"><span class="token hr punctuation">---</span><br>layout: base-layout.njk <br>title: How To Get Rich Quick<br>date: 2019-06-01<br><span class="token title important">tags: ['post']<br><span class="token punctuation">---</span></span><br>Buy the latest and greatest cryptocurrency that no-one has heard of.<br> <br><span class="token title important"><span class="token punctuation">##</span> Legal Stuff</span><br>We do not accept any liability for any loss or damage.</code></pre>
<p>Note that each blog post is using the <code>base-layout.njk</code> Nunjucks template and the title for each blog article is in the front matter metadata.</p>
<p>We’ll now create an <code>index.njk</code> Nunjucks template for the homepage of our site. Create <code>index.njk</code> in the root directory with the following content.</p>
<pre class="language-htmlnull"><code class="language-htmlnull">---<br>layout: base-layout.njk<br>pagination:<br>  data: collections.post<br>  size: 10<br>  reverse: true<br>  alias: posts<br>---<br>{% for post in posts %}<br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ post.url | url }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ post.data.title }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br>    {{ post.date }}<br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span><br>{% endfor %}</code></pre>
<p>Each of our blog articles has the tag of <code>post</code> assigned to it in the front matter metadata. Eleventy provides a collection for each tag that’s been assigned, so we can access all our blog posts via the collection <code>collections.post</code>. The <code>pagination</code> metadata specifies that the variable <code>posts</code> will be available in the template and will contain the last 10 blog posts. In the template, we’re then iterating through each post and outputting a link to the post and outputting the date.</p>
<p><img src="http://keepinguptodate.com/pages/2019/06/02-dates-not-formatted.png" alt="alt text" title="Dates are displayed in a long format that is not easy to read" loading="lazy"></p>
<p>Looking at the homepage, the date isn’t formatted in a particularly nice way and should be formatted to be easier to read. Eleventy has no in-built mechanism for formatting dates, so we’ll create our own “filter” to handle this. We’re going to use the npm package “moment” to handle date formatting so let get that installed.</p>
<pre class="language-textnull"><code class="language-textnull">npm install moment --save-dev</code></pre>
<p>We’ll then create the Eleventy configuration file <code>.eleventy.js</code> where we’ll add some date filters.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">const</span> moment <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'moment'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <br>moment<span class="token punctuation">.</span><span class="token function">locale</span><span class="token punctuation">(</span><span class="token string">'en'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <br>module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <br>  eleventyConfig<span class="token punctuation">.</span><span class="token function">addFilter</span><span class="token punctuation">(</span><span class="token string">'dateIso'</span><span class="token punctuation">,</span> <span class="token parameter">date</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br>    <span class="token keyword">return</span> <span class="token function">moment</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toISOString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <br>  eleventyConfig<span class="token punctuation">.</span><span class="token function">addFilter</span><span class="token punctuation">(</span><span class="token string">'dateReadable'</span><span class="token punctuation">,</span> <span class="token parameter">date</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br>    <span class="token keyword">return</span> <span class="token function">moment</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">utc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string">'LL'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// E.g. May 31, 2019</span><br>  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>This will make the filters <code>dateIso</code> and <code>dateReadable</code> available for us to use within our Nunjucks templates. We’ll use both of these date formats as we’re going to output a <code>&lt;time&gt;</code> element which uses the ISO format to assist with search engines understanding the date.</p>
<p>Update <code>index.njk</code> to use the new filters.</p>
<pre class="language-htmlnull"><code class="language-htmlnull">...<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ post.date | dateIso }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ post.date | dateReadable }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>time</span><span class="token punctuation">></span></span><br>...</code></pre>
<p>This results in our dates being much more readable.</p>
<p><img src="http://keepinguptodate.com/pages/2019/06/03-readable-dates.png" alt="alt text" title="Dates are formatted and easy to read" loading="lazy"></p>
<hr>
<h2 id="displaying-excerpts-on-the-homepage">Displaying excerpts on the homepage</h2>
<p>When people view the homepage of our blog, we’d like to entice them to read a post by displaying the first part of it for them to see.
As of Eleventy <code>v0.9.0</code>, the <code>gray-matter</code> npm package that Eleventy uses to parse front matter can be configured to use an excerpt separator which makes an excerpt available in your template as <code>page.excerpt</code>.</p>
<p><a href="https://www.11ty.dev/docs/data-frontmatter-customize/#example-parse-excerpts-from-content" target="_blank" rel="noopener">https://www.11ty.dev/docs/data-frontmatter-customize/#example-parse-excerpts-from-content</a></p>
<p>For the purposes of this article, we’re not going to use that approach and we’re going to take full control of excerpt handling ourselves via a “shortcode”. Add the following function at the very bottom of <code>.eleventy.js</code> (after the <code>module.exports</code> function).</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">function</span> <span class="token function">extractExcerpt</span><span class="token punctuation">(</span><span class="token parameter">article</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>article<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">'templateContent'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>    console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">'Failed to extract excerpt: Document has no property "templateContent".'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>    <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br> <br>  <span class="token keyword">let</span> excerpt <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span><br>  <span class="token keyword">const</span> content <span class="token operator">=</span> article<span class="token punctuation">.</span>templateContent<span class="token punctuation">;</span><br> <br>  <span class="token comment">// The start and end separators to try and match to extract the excerpt</span><br>  <span class="token keyword">const</span> separatorsList <span class="token operator">=</span> <span class="token punctuation">[</span><br>    <span class="token punctuation">{</span> start<span class="token operator">:</span> <span class="token string">'&lt;!-- Excerpt Start -->'</span><span class="token punctuation">,</span> end<span class="token operator">:</span> <span class="token string">'&lt;!-- Excerpt End -->'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br>    <span class="token punctuation">{</span> start<span class="token operator">:</span> <span class="token string">'&lt;p>'</span><span class="token punctuation">,</span> end<span class="token operator">:</span> <span class="token string">'&lt;/p>'</span> <span class="token punctuation">}</span><br>  <span class="token punctuation">]</span><span class="token punctuation">;</span><br> <br>  separatorsList<span class="token punctuation">.</span><span class="token function">some</span><span class="token punctuation">(</span><span class="token parameter">separators</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br>    <span class="token keyword">const</span> startPosition <span class="token operator">=</span> content<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>separators<span class="token punctuation">.</span>start<span class="token punctuation">)</span><span class="token punctuation">;</span><br>    <span class="token keyword">const</span> endPosition <span class="token operator">=</span> content<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>separators<span class="token punctuation">.</span>end<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <br>    <span class="token keyword">if</span> <span class="token punctuation">(</span>startPosition <span class="token operator">!==</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">&amp;&amp;</span> endPosition <span class="token operator">!==</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>      excerpt <span class="token operator">=</span> content<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>startPosition <span class="token operator">+</span> separators<span class="token punctuation">.</span>start<span class="token punctuation">.</span>length<span class="token punctuation">,</span> endPosition<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>      <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token comment">// Exit out of array loop on first match</span><br>    <span class="token punctuation">}</span><br>  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <br>  <span class="token keyword">return</span> excerpt<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Then from within the <code>module.exports</code> function, register the <code>excerpt</code> “shortcode” with Eleventy.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull">eleventyConfig<span class="token punctuation">.</span><span class="token function">addShortcode</span><span class="token punctuation">(</span><span class="token string">'excerpt'</span><span class="token punctuation">,</span> <span class="token parameter">article</span> <span class="token operator">=></span> <span class="token function">extractExcerpt</span><span class="token punctuation">(</span>article<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This “shortcode” will take the HTML output from each article and return just the content between the HTML comments <code>&lt;!-- Excerpt Start --&gt;</code> and <code>&lt;!-- Excerpt End --&gt;</code>. If these comments aren’t found, it will instead return the content in the first paragraph.</p>
<p>Update the blog post Markdown files to wrap the <code>&lt;!-- Excerpt Start --&gt;</code> and <code>&lt;!-- Excerpt End --&gt;</code> HTML comments around the content you want to show as the excerpt for each post on the homepage. For example:</p>
<pre class="language-markdownnull"><code class="language-markdownnull"><span class="token comment">&lt;!-- Excerpt Start --></span><br>This is included on the homepage.<br><span class="token comment">&lt;!-- Excerpt End --></span><br> <br>This will not be on the homepage.</code></pre>
<p>Now we just need to use the <code>excerpt</code> “shortcode” in the <code>index.njk</code> homepage template. We’ll also add a “Read more” link at the bottom of each excerpt.</p>
<pre class="language-htmlnull"><code class="language-htmlnull">...<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ post.date | dateIso }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ post.date | dateReadable }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>time</span><span class="token punctuation">></span></span><br><br>{% excerpt post %}<br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ post.url | url }}<span class="token punctuation">"</span></span><br>   <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Read more on {{ post.data.title }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Read more<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br>...</code></pre>
<p>The blog is starting to take shape nicely.</p>
<p><img src="http://keepinguptodate.com/pages/2019/06/04-excerpts.png" alt="alt text" title="Excerpts are displayed on the homepage for each post" loading="lazy"></p>
<br>
<hr>
<h2 id="blog-post-nested-layout-template">Blog post nested layout template</h2>
<p>If we look at each of our blog posts, they don’t display the title or the date available in their front matter metadata. We can easily solve this by creating a nested layout template for use with blog post Markdown files. Create a new layout template  <code>post-layout.njk</code> in the folder <code>_includes</code> with the following content.</p>
<pre class="language-htmlnull"><code class="language-htmlnull">---<br>layout: base-layout.njk<br>---<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>{{ title }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span> <br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ date | dateIso }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ date | dateReadable }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>time</span><span class="token punctuation">></span></span> <br>  {{ content | safe }}<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span></code></pre>
<p>This will output the post title and date for each post. This layout template itself uses <code>base-layout.njk</code> layout, so it’s a nested layout template. To use this layout template, update the front matter metadata in each blog post Markdown file to <code>layout: post-layout.njk</code>.</p>
<p>Congratulations, you’ve got yourself a blog setup where adding new posts and having the homepage update is as simple as creating a new Markdown file.</p>
<hr>
<h2 id="show-me-the-code!">Show me the code!</h2>
<p>The code for this article is available on GitHub:<br>
<a href="https://github.com/JonUK/eleventy-blog" target="_blank" rel="noopener">https://github.com/JonUK/eleventy-blog</a></p>
<p>The latest version of the code in <code>master</code> has a few additional changes to use Google Fonts and use a CSS file to smarten the site up.</p>
<p>A demo of the blog is hosted on Netlify:<br>
<a href="https://dazzling-almeida-ca0492.netlify.com/" target="_blank" rel="noopener">https://dazzling-almeida-ca0492.netlify.com/</a></p>
]]>
        </description>
        <pubDate>Sat, 01 Jun 2019 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2019/06/creating-blog-with-eleventy/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2019/06/creating-blog-with-eleventy/</guid>
      </item><item>
        <title>One Scenario Where The Medium.com Import Tool Fails</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>Some missing metadata on your site could be the cause of not being able to import your story into medium.com.</p>
<p>For the last few hours, I’ve been pulling my hair out trying to understand why the Import Tool on medium.com wasn’t working for an article on my blog.</p>
<!-- Excerpt End -->
<p><img src="http://keepinguptodate.com/pages/2019/05/import-failed.png" alt="alt text" title="Medium.com import failed error message" loading="lazy"></p>
<p>After much trial and error, it turned out that adding the
<a href="http://ogp.me/" target="_blank" rel="noopener">Open Graph protocol</a>
metadata <code>og:type</code> resolved my issue.</p>
<hr>
<h2 id="the-fix">The fix</h2>
<p>The changes I made were to first register the Open Graph prefix of <code>og:</code> by adding the prefix attribute to my <code>&lt;html&gt;</code> tag.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span> <span class="token attr-name">prefix</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>og: http://ogp.me/ns#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>Then within the <code>&lt;head&gt;</code> tag, I ensured the <code>og:type</code> metadata was set along with <code>og:title</code> and <code>og:description</code>.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">property</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>og:type<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>article<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">property</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>og:title<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">property</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>og:description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>After these changes, the Import Tool worked fine.
I hope this information might help prevent someone else from the same headache I’ve had with this issue!</p>
<hr>
<h2 id="other-known-causes">Other known causes</h2>
<p>If you have a web page that uses client-side JavaScript to generate your markdown and content, it’s unlikely the Import Tool will work on your page.</p>
]]>
        </description>
        <pubDate>Tue, 21 May 2019 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2019/05/medium.com-import-tool-failed/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2019/05/medium.com-import-tool-failed/</guid>
      </item><item>
        <title>Correct MIME Type Not Always Being Returned In ASP.NET On Azure</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>I recently faced an issue where the MIME type for a <code>.mp4</code> file was being returned from an Azure Web App as <code>application/octet-stream</code> and not the <code>video/mp4</code> I was seeing locally.</p>
<p><strong>Code</strong></p>
<pre class="language-csharpnull"><code class="language-csharpnull"><span class="token class-name"><span class="token keyword">var</span></span> filename <span class="token operator">=</span> <span class="token string">"test.mp4"</span><span class="token punctuation">;</span><br><span class="token class-name"><span class="token keyword">var</span></span> mimeType <span class="token operator">=</span> MimeMapping<span class="token punctuation">.</span><span class="token function">GetMimeMapping</span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p><strong>Output</strong></p>
<pre class="language-textnull"><code class="language-textnull">Locally:  video/mp4<br>In Azure: application/octet-stream</code></pre>
<!-- Excerpt End -->
<hr>
<h2 id="what%E2%80%99s-going-on%3F!">What’s going on?!</h2>
<p>After some head scratching as to how a static class with a static method could return different results given the same inputs, I decided to dig into the source code.</p>
<a href="https://github.com/Microsoft/referencesource/blob/master/System.Web/MimeMapping.cs" target="_blank" rel="noopener">
  MimeMapping.cs in the GitHub repo for the Microsoft .NET Reference Source
</a>
<p>In the file <code>MimeMapping.cs</code>, you can see mappings of file extension to MIME type but there’s no mapping for <code>.mp4</code> files.</p>
<pre class="language-csharpnull"><code class="language-csharpnull"><span class="token comment">// MimeMapping.cs around line 246</span><br><span class="token function">AddMapping</span><span class="token punctuation">(</span><span class="token string">".mp2"</span><span class="token punctuation">,</span> <span class="token string">"video/mpeg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">AddMapping</span><span class="token punctuation">(</span><span class="token string">".mp3"</span><span class="token punctuation">,</span> <span class="token string">"audio/mpeg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">AddMapping</span><span class="token punctuation">(</span><span class="token string">".mpa"</span><span class="token punctuation">,</span> <span class="token string">"video/mpeg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">AddMapping</span><span class="token punctuation">(</span><span class="token string">".mpe"</span><span class="token punctuation">,</span> <span class="token string">"video/mpeg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>How was <code>video/mp4</code> being returned locally for an <code>.mp4</code> file extension then? The answer lies further down in the code in the file <code>MimeMapping.cs</code>.</p>
<pre class="language-csharp code--small"><code class="language-csharp code--small"><span class="highlight-line"><span class="token comment">// MimeMapping.cs around line 448</span></span><br><span class="highlight-line"><span class="token comment">// This can provide mappings from the actual applicationHost.config file in IIS7 integrated mode</span></span><br><span class="highlight-line"><span class="token keyword">private</span> <span class="token keyword">sealed</span> <span class="token keyword">class</span> <span class="token class-name">MimeMappingDictionaryIntegrated</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">MimeMappingDictionaryBase</span></span> <span class="token punctuation">{</span></span><br><span class="highlight-line">	<span class="token keyword">private</span> <span class="token keyword">readonly</span> <span class="token class-name">IntPtr</span> _applicationContext<span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">	<span class="token keyword">public</span> <span class="token function">MimeMappingDictionaryIntegrated</span><span class="token punctuation">(</span><span class="token class-name">IntPtr</span> applicationContext<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">		_applicationContext <span class="token operator">=</span> applicationContext<span class="token punctuation">;</span></span><br><span class="highlight-line">	<span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">	<span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">PopulateMappings</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">		<span class="token class-name">IntPtr</span> mimeMapCollection <span class="token operator">=</span> IntPtr<span class="token punctuation">.</span>Zero<span class="token punctuation">;</span></span><br><span class="highlight-line">		<span class="token keyword">try</span> <span class="token punctuation">{</span></span><br><span class="highlight-line">			<span class="token class-name"><span class="token keyword">int</span></span> result<span class="token punctuation">;</span></span><br><span class="highlight-line">			<span class="token class-name"><span class="token keyword">int</span></span> mimeMapCount<span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">			<span class="token comment">// Read the collection</span></span><br><span class="highlight-line">			result <span class="token operator">=</span> UnsafeIISMethods<span class="token punctuation">.</span><span class="token function">MgdGetMimeMapCollection</span><span class="token punctuation">(</span>IntPtr<span class="token punctuation">.</span>Zero<span class="token punctuation">,</span> _applicationContext<span class="token punctuation">,</span> <span class="token keyword">out</span> mimeMapCollection<span class="token punctuation">,</span> <span class="token keyword">out</span> mimeMapCount<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">            <span class="token range operator">..</span><span class="token punctuation">.</span></span></code></pre>
<p>The class <code>MimeMappingDictionaryIntegrated</code> can provide mappings based on IIS configuration.
If <code>MimeMapping</code> is hosted within an ASP.NET application, the internal method <code>SetIntegratedApplicationContext</code> is called and additional MIME type mappings from the IIS configuration are available.</p>
<p>On my local machine, the IIS Express root configuration file <code>applicationhost.config</code> in the folder <code>C:\Program Files (x86)\IIS Express</code> (location can vary) contains the following:</p>
<pre class="language-xmlnull"><code class="language-xmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mimeMap</span> <span class="token attr-name">fileExtension</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.mp4<span class="token punctuation">"</span></span> <span class="token attr-name">mimeType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>video/mp4<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>Bingo! That explains why locally the <code>.mp4</code> extension is mapped to <code>video/mp4</code>.
It appears the default Azure Web App configuration does not include this mapping and therefore the default MIME type of <code>application/octet-stream</code> is returned instead.</p>
<p>Let’s have a look at a couple of solutions to resolve this issue of missing MIME type mappings.</p>
<hr>
<h2 id="solution-1-%E2%80%94-add-a-mime-mapping-in-web.config">Solution 1 — Add a MIME mapping in web.config</h2>
<p>The first solution is to add the MIME type mapping for the extension <code>.mp4</code> to your project’s <code>web.config</code>.</p>
<pre class="language-xmlnull"><code class="language-xmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>system.webServer</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>staticContent</span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>remove</span> <span class="token attr-name">fileExtension</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.mp4<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mimeMap</span> <span class="token attr-name">fileExtension</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.mp4<span class="token punctuation">"</span></span> <span class="token attr-name">mimeType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>video/mp4<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>staticContent</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>system.webServer</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span></code></pre>
<hr>
<h2 id="solution-2-%E2%80%94-use-a-different-mime-type-mappings-source">Solution 2 — Use a different MIME type mappings source</h2>
<p>The
<a href="https://github.com/samuelneff/MimeTypeMap" target="_blank" rel="noopener">
GitHub project MimeTypeMap
</a>
provides a much more extensive mapping of file extensions to MIME types (which includes <code>.mp4</code>).
The code is packaged up and available via the
<a href="https://www.nuget.org/packages/MimeTypeMap.List/" target="_blank" rel="noopener">
NuGet package MimeTypeMap.List
</a>.</p>
]]>
        </description>
        <pubDate>Sun, 19 May 2019 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2019/05/correct-mime-type-asp-net-azure/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2019/05/correct-mime-type-asp-net-azure/</guid>
      </item><item>
        <title>Accessible JavaScript Click Handlers</title>
        <description>
          <![CDATA[<!-- Excerpt Start -->
<p>The Internet is a fantastic medium where people can share information, knowledge… and cute cat photos freely and inclusively.
HTML is one of the key technologies for the World Wide Web and when used correctly, largely ensures content is accessible to all.
Unfortunately, with the rush to create sophisticated JavaScript rich web pages, the fundamentals of good semantic markup are often overlooked to the detriment of web accessibility.</p>
<p>One way to drastically exclude users from your site is to add JavaScript click handlers to non-interactive elements such as <code>&lt;div&gt;</code> and <code>&lt;span&gt;</code> tags.</p>
<!-- Excerpt End -->
<hr>
<h2 id="when-is-a-button-not-a-button%3F">When is a button not a button?</h2>
<p>Can you spot the difference between the two “buttons” below?</p>
<iframe width="100%" height="275" src="http://jsfiddle.net/KeepingUpToDate/nx5L7j01/embedded/result,html,js,css/" allowfullscreen="allowfullscreen" allowpaymentrequest="" frameborder="0"></iframe>
<p>To a fully-sighted mouse or touch device user, these “buttons” would appear to function and look the same.
For keyboard users or users of assistive technology though, it’s a very different story.</p>
<p>Let’s review these “buttons” as a keyboard user and a screen reader user.</p>
<div class="video-responsive">
  <iframe width="100%" height="315" src="https://www.youtube.com/embed/SnF0ZQ7JOb8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
<p>The difference between the “buttons” is one is a native <code>&lt;button&gt;</code> while the other is a <code>&lt;div&gt;</code> tag.
Unlike a native <code>&lt;button&gt;</code>, a keyboard user can’t interact with and invoke the click handler on the inaccessible <code>&lt;div&gt;</code> “button” and a screen reader user isn’t made aware that the inaccessible “button” is an element that could be interacted with.</p>
<p>Clearly, the simple fix for the inaccessible button is to swap the <code>&lt;div&gt;</code> for a <code>&lt;button&gt;</code> and job done… but this blog article would be a very short if we left it there!
Instead, let’s explore the differences between these “buttons” further and see how we can replicate native <code>&lt;button&gt;</code> behaviour with a <code>&lt;div&gt;</code> element to highlight what using the correct semantic markup gives us out of the box.</p>
<hr>
<h2 id="focusable-element">Focusable element</h2>
<p>The tab key allows a keyboard user to move focus through all the interactive elements on your page. Our <code>&lt;div&gt;</code> button is not focusable though, so let’s resolve that by adding the <code>tabindex</code> attribute.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Download<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>By setting the <code>tabindex</code> to <code>0</code>, we’re telling the browser that the element can receive focus and should do so in the same order it appears in the DOM, as a user tabs though all the interactive elements.</p>
<p>Web browsers provide a standard focus indicator in the form of an outline / focus ring around the focused element. The outline can be styled differently as required to match the design of your site but the outline should <strong>never</strong> just be removed.</p>
<pre class="language-cssnull"><code class="language-cssnull"><span class="token selector">button:focus</span> <span class="token punctuation">{</span><br>  <span class="token property">outline</span><span class="token punctuation">:</span> 2px dashed red<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>An element’s <code>tabindex</code> can be set to a positive integer which means the DOM order is ignored for the element.
The browser will initially move focus through the elements with a <code>tabindex</code> greater than <code>0</code> (in the order of lowest <code>tabindex</code> to highest) before then moving focus through all the other interactive elements in DOM order.
In general though, using <code>tabindex</code> to define an explicit tab order is <strong>not recommended</strong>.</p>
<hr>
<h2 id="keyboard-interaction">Keyboard interaction</h2>
<p>A keyboard user can now tab and set focus on the <code>&lt;div&gt;</code> button but they still can’t operate the button.
With a native <code>&lt;button&gt;</code> element, when the button has focus, both <code>Space</code> and <code>Enter</code> keys will trigger the click event handler.
To replicate this behaviour on our <code>&lt;div&gt;</code> button, we’ll need to add an event listener to <code>onkeypress</code> to determine if <code>Space</code> or <code>Enter</code> was pressed and if so manually trigger the click handler.</p>
<pre class="language-javascriptnull"><code class="language-javascriptnull"><span class="token keyword">const</span> button <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'divButton'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>button<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br>  <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'Time to start downloading'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>button<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'keydown'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br>  <span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>code <span class="token operator">===</span> <span class="token string">'Space'</span> <span class="token operator">||</span> event<span class="token punctuation">.</span>code <span class="token operator">===</span> <span class="token string">'Enter'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>    button<span class="token punctuation">.</span><span class="token function">click</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<hr>
<h2 id="screen-readers">Screen readers</h2>
<p>We’ll now turn our focus to the differences between a native <code>&lt;button&gt;</code> and our <code>&lt;div&gt;</code> button for screen reader users.
When the native button received focus, it was announced with “Download, Button”.
The screen reader announcing the word “Button” gives a visually impaired user more context to know the type of control that has focus and therefore to know it can be interacted with.
The reason the screen reader announced the word “Button” is because a native button intrinsically has the attribute <code>role</code> of <code>button</code>.
If we apply this attribute to our <code>&lt;div&gt;</code> button then the screen reader will announce in the same manner.</p>
<pre class="language-htmlnull"><code class="language-htmlnull"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Download<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>Note that adding the <code>role</code> of <code>button</code> just affects how an element appears to a screen reader.
We still need to make the <code>&lt;div&gt;</code> focusable and react to keyboard events as detailed above.</p>
<p>Now that our <code>&lt;div&gt;</code> button is focusable, reacts to keyboard events and is announced correctly by screen readers, we have successfully replicated a native <code>&lt;button&gt;</code>.</p>
<hr>
<h2 id="accessibility-checking">Accessibility checking</h2>
<p>There are a number of tools available to help check your site for common accessibility issues.
Unfortunately, none of the tools I tried (including the
<a href="https://chrome.google.com/webstore/detail/axe/lhdoppojpmngadmnindnejefpokejbdd" target="_blank" rel="noopener">
axe Chrome extension
</a>
and
<a href="https://developers.google.com/web/updates/2018/05/lighthouse3" target="_blank" rel="noopener">
Google’s Lighthouse
</a>
identified the <code>&lt;div&gt;</code> “button” as an accessibility issue.
I suspect this is due to them not evaluating for JavaScript related web accessibility issues.</p>
<p>Automated accessibility tools can be very useful but, as evidenced here, should be considered a part of your wider strategy around checking for accessibility issues.
Arguably, one of the best checks you can perform is to unplug your mouse, turn off your screen and with the keyboard and a screen reader, ensure you are able to perceive and interact with your pages.</p>
<hr>
<h2 id="summary">Summary</h2>
<p>In almost all cases, click handlers should be applied to natively interactive elements like <code>&lt;button&gt;</code> and <code>&lt;a&gt;</code>.
Only in exceptional cases should you consider adding click handlers to non-interactive elements and if you do go down this road, ensure you cover accessibility.</p>
<iframe width="100%" height="275" src="http://jsfiddle.net/KeepingUpToDate/qgavpjmh/embedded/result,html,js,css/" allowfullscreen="allowfullscreen" allowpaymentrequest="" frameborder="0"></iframe>
]]>
        </description>
        <pubDate>Tue, 09 Apr 2019 01:00:00 GMT</pubDate>
        <link>
        http://keepinguptodate.com/pages/2019/04/accessible-javascript-click-handlers/</link>
        <guid isPermaLink="true">
        http://keepinguptodate.com/pages/2019/04/accessible-javascript-click-handlers/</guid>
      </item>
  </channel>
</rss>







