<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://patrickweaver.net/blog/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    
    <title>Patrick Weaver: Blog</title>
    <link>https://patrickweaver.net/blog/</link>
    <atom:link href="https://patrickweaver.net/rss.xml" rel="self" type="application/rss+xml" />
    <description></description>
    <language>en</language>
    <item>
      <title>Building a Web Page That Edits Itself</title>
      <link>https://patrickweaver.net/blog/one-pager-self-editing-html/</link><description>&lt;p&gt;The original vision for the world wide web was a read/write medium. A few years ago I thought, “what if an HTML file could update itself?”&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/portfolio/one-pager/demo.gif&quot; alt=&quot;An animated screen capture of using One Pager to make a web page about a birthday party.&quot; style=&quot;max-width: 100%; width: 403px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;When I first started making goofy web pages many years ago, the &lt;code&gt;index.html&lt;/code&gt; file felt magical. It felt like the file &lt;strong&gt;&lt;em&gt;was&lt;/em&gt;&lt;/strong&gt; the website, and a few &lt;code&gt;.html&lt;/code&gt; files in a directory were a little community of pages. Over time, working with things like PHP and WordPress, then Django, Rails, and Node.js, “web pages” faded into the background, even my small personal websites felt like systems that just generated ephemeral files by necessity.&lt;/p&gt;
&lt;p&gt;After moving my personal website (this one!) to &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;11ty&lt;/a&gt; in 2020, and inspired by ideas like the “file-first” approach of Omar Rizwan’s &lt;a href=&quot;https://omar.website/tabfs/&quot;&gt;TabFS&lt;/a&gt; and the “read/write web” revitalization of &lt;a href=&quot;https://x.com/xhfloz&quot;&gt;XH’s&lt;/a&gt; &lt;a href=&quot;https://mmm.page/&quot;&gt;mmm.page&lt;/a&gt; and &lt;a href=&quot;https://github.com/beakerbrowser&quot;&gt;Beaker Browser&lt;/a&gt;, I had the idea: &lt;em&gt;“A web page that can update itself!”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I quickly threw together a few &lt;code&gt;getElementById()&lt;/code&gt; and &lt;code&gt;.innerHTML&lt;/code&gt;s and had a &lt;a href=&quot;https://github.com/patrickweaver/one-pager/tree/7b9491c4c00ba90933f7e460c298d3404b6e4baf&quot;&gt;working prototype&lt;/a&gt; of an &lt;code&gt;index.html&lt;/code&gt; file that could update its own DOM via the UI, and then save a copy of itself to a local file.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/portfolio/one-pager/prototype-1.gif&quot; alt=&quot;An animated screen capture of using an early prototype of the web page.&quot; style=&quot;max-width: 100%; width: 450px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Next, realizing that I needed a way to reach beyond the UI in the browser to update things like the page title, or global styles, I added a control panel to update metadata.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/portfolio/one-pager/prototype-2.png&quot; alt=&quot;A screenshot of an early prototype with a control panel to update page metadata.&quot; style=&quot;max-width: 100%; width: 450px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;After adding controls for global styles like the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; width I realized I needed to rethink the UX if I wanted the project to be easy to use. For example, shrinking the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; width also squished the control panel to the same width, after all, it was just another element in the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/portfolio/one-pager/prototype-3.png&quot; alt=&quot;A screenshot of a prototype where the metadata control panel is very narrow because the &lt;body&gt; width has been set to 200px.&quot; style=&quot;max-width: 100%; width: 450px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I had started the prototype with the classic trio of &lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;script.js&lt;/code&gt; and &lt;code&gt;style.css&lt;/code&gt;, but after a few commits switched to a single file with &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags within the HTML. Coming back to the project a few months after that initial prototype, the code was getting to be quite repetitive and hard to follow. It was hard to keep track of logic, there were &lt;em&gt;a lot&lt;/em&gt; of event listeners. I decided to refactor all of the JavaScript into modules using Vite as a bundler, and followed that with another refactor in TypeScript.&lt;/p&gt;
&lt;p&gt;One early design choice was that when switching an element like an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; into editing mode, I would hide the original element and insert a &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; with the original element’s content. At first this made things like canceling the edit easier because I could just un-hide the “real” element, but I needed lots of custom logic for saving the change for different types of elements.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/portfolio/one-pager/prototype-4.png&quot; alt=&quot;A screenshot of the UI for editing a single element.&quot; style=&quot;max-width: 100%; width: 450px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;The TypeScript refactor had helped me finally pin down some of these bugs and I realized that it would be easier to use &lt;code&gt;contenteditable=&amp;quot;true&amp;quot;&lt;/code&gt; to edit the “real” element in-place. With this change, I was able to avoid any synchronization on save, and simplify the editing UI to just a set of buttons that I inserted below the element in the DOM. I was still able to restore the original content after a “cancel” action by storing it in &lt;code&gt;data-&lt;/code&gt; attributes.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/portfolio/one-pager/prototype-5.png&quot; alt=&quot;A screenshot of improved single element editing UI.&quot; style=&quot;max-width: 100%; width: 450px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;My original intention was to add a few more element types, things like &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt;, but I decided it had been long enough and I should finish up with my current feature set of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Text elements: &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; through &lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Images: &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; (these are stored as Data URLs to avoid breaking out of the one file)&lt;/li&gt;
&lt;li&gt;Links: Selections of text, or images can be wrapped in &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I cleaned up my prototype CSS, added some fun early-2000s button styling, made a quick attempt to make the concept work on mobile (it’s not great), and at the suggestion of an early &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt; playtester, moved the control panel to the right-hand side on desktop.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/portfolio/one-pager/prototype-6.png&quot; alt=&quot;A screenshot of the final prototype.&quot; style=&quot;max-width: 100%; width: 450px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;You can try it out now at &lt;a href=&quot;https://doodles.patrickweaver.net/one-pager/&quot;&gt;doodles.patrickweaver.net/one-pager&lt;/a&gt;. The editing UI only displays under two conditions, the file is opened locally with a &lt;code&gt;file:///&lt;/code&gt; protocol, or being viewed at the specific URL above on my “Doodles” subdomain. Anywhere else a web page made with One Pager will not be editable, until you download the &lt;code&gt;index.html&lt;/code&gt;! Below is an example web page I made about my &lt;a href=&quot;https://doodles.patrickweaver.net/one-pager/examples/20s-kids/&quot;&gt;24 Hour kid-appropriate-non-kids-songs playlist&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/one-pager/20s-kids-example.png&quot; alt=&quot;A screenshot of a web page about a 24 hour playlist.&quot; style=&quot;max-width: 100%; width: 450px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;If you make any web pages with One Pager, let me know, and I will add it to the &lt;a href=&quot;https://doodles.patrickweaver.net/one-pager/examples/&quot;&gt;Example Gallery&lt;/a&gt;. Good luck building some HTML!&lt;/p&gt;
</description><pubDate>Mon, 13 Apr 2026 00:00:00 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/one-pager-self-editing-html/</guid>
    </item>
    <item>
      <title>IRL HTML Is the Place For Your Most Hand-Written Websites</title>
      <link>https://patrickweaver.net/blog/irl-html-is-the-place-for-your-most-hand-written-websites/</link><description>&lt;p&gt;Last year before &lt;a href=&quot;https://html.energy&quot;&gt;HTML Energy&lt;/a&gt;&#39;s &lt;abbr title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/abbr&gt; Day in &lt;a href=&quot;https://www.are.na/bay-area-95sssqibasq/w-s-www-gathering&quot;&gt;San Francisco&lt;/a&gt; I had the idea to iterate on some of my previous projects like &lt;a href=&quot;https://www.patrickweaver.net/portfolio/whiteboard-email/&quot;&gt;Whiteboard Email&lt;/a&gt;, and make both an app that would do &lt;abbr title=&quot;Optical character recognition&quot;&gt;OCR&lt;/abbr&gt; on HTML, and a website to host truly “hand-written” HTML. That became &lt;a href=&quot;https://doodles.patrickweaver.net/irl-html/&quot;&gt;IRL HTML&lt;/a&gt; and it was good enough to make some &lt;a href=&quot;https://doodles.patrickweaver.net/irl-html/pages/acc8/&quot;&gt;very simple&lt;/a&gt; websites!&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;display: flex; max-width: 100%; align-items: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/irl-html/simple-website.png&quot; alt=&quot;A screenshot of a website made with IRL HTML. It says Park, With Grass&quot; style=&quot;max-width: 50%; margin-right: 2.5px;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/irl-html/notebook-park.jpg&quot; alt=&quot;A photograph of the hand-written code for the website above in a notebook laying in grass.&quot; style=&quot;max-width: 50%; margin-left: 2.5px;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;The published website and the hand written source code, we realized that some letters were more recognizable as upper case, some as lower case.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The first prototype of IRL HTML used &lt;a href=&quot;https://tesseract.projectnaptha.com/&quot;&gt;Tesseract.js&lt;/a&gt; which is only reliable for typewritten text. Given my success with &lt;a href=&quot;https://www.patrickweaver.net/portfolio/record-player/&quot;&gt;album covers&lt;/a&gt; I tried the Google Vision API, which worked well enough for &lt;em&gt;some&lt;/em&gt; (carefully) hand-written HTML, but still made a lot of errors, which might prevent the page from rendering at all.&lt;/p&gt;
&lt;p&gt;A lot of the projects I’ve done over the years have used computer vision in creative ways, and I’ve always been searching for more reliable ways to have computers read hand-written text. At a &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt; meetup in SF in January 2024 someone suggested that the ChatGPT API might be capable of it. We made a &lt;a href=&quot;https://doodles.patrickweaver.net/irl-html/pages/0e98/&quot;&gt;simple website on IRL HTML&lt;/a&gt; to commemorate the occasion.&lt;/p&gt;
&lt;p&gt;To get ready for the &lt;a href=&quot;https://html.energy/events.html&quot;&gt;2024 HTML Day&lt;/a&gt; I decided to update IRL HTML using both the ChatGPT and Anthropic Claude APIs. Both were able to read my hand-written HTML code surprisingly well! They do sometimes output text that isn’t really there (notice the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; tags were turned into &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags in the example below), and sometimes it adds some description of the output, which for this case isn’t useful. One benefit of the multi-modal nature of the LLMs is that I can inform the model that the output is supposed to be HTML, I can add instructions to fix any minor syntax errors. I could probably improve the accuracy even more by combining OCR methods, or making multiple requests to the LLMs.&lt;/p&gt;
&lt;p&gt;The prompt I’m using for both LLM APIs is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This is an image of a piece of paper with HTML code on it. If there are any syntax errors, fix them with the most likely valid HTML. Respond with just the HTML code property formatted, not wrapped in markdown or any description of what is in the response.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is probably some room to iterate on the prompt for better output, but the cycle of testing (especially with different hand-written code) is long and not free, so I may stick with what is working reasonably well.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;display: flex; max-width: 100%; align-items: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/irl-html/about-irl-html-notebook.jpg&quot; alt=&quot;A photograph of a notebook with hand-written HTML code for an “About IRL HTML” website&quot; style=&quot;max-width: 50%; margin-right: 2.5px;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/irl-html/about-irl-html-website.png&quot; alt=&quot;A screenshot of the website generated from the hand-written code above&quot; style=&quot;max-width: 50%; margin-left: 2.5px;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;More complex hand-written source code, and the webiste (with a picture of itself)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Overall, though I’m skeptical of the utility of LLMs more generally, it’s exciting to more easily and reliably make the kinds of quirky computer vision projects I’ve been dreaming of for a long time. I’d be interested in smaller handwriting OCR specific models that I might be able to run locally someday!&lt;/p&gt;
</description><pubDate>Wed, 10 Jul 2024 00:00:00 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/irl-html-is-the-place-for-your-most-hand-written-websites/</guid>
    </item>
    <item>
      <title>Unravelling an iMessage URL Parsing Mystery</title>
      <link>https://patrickweaver.net/blog/imessage-mystery/</link><description>&lt;p&gt;I have long been a fan of frontend-only websites, usually single-page apps, that are able to persist state in sharable URLs despite not having a database or a backend. One of the more complex projects I used this technique for was my &lt;a href=&quot;https://doodles.patrickweaver.net/crossword/editor&quot;&gt;Crossword Puzzle Editor&lt;/a&gt;, which stores an entire puzzle, both clues and correct answers in the URL hash.&lt;/p&gt;
&lt;p&gt;However, when I composed a small puzzle and shared it with a friend via iMessage I was greeted with a link to the app with empty state above a long string of random characters in a blue bubble:&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/imessage/broken-link.png&quot; alt=&quot;A screenshot of an iMessage conversation with a broken link in it&quot; style=&quot;max-width: 100%; width: 300px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I tried again with a smaller puzzle and got the same kind of broken link, but it couldn’t just be the length of the URL because surely all of the tracking URLs used in every marketing email wouldn’t break like this. I tried one of a similar length, it sent flawlessly and rendered as a clean link preview, not the jumble of query string params it really was. I looked closer and noticed that the tracking URL had one visible difference from my crossword URL: it had “-” characters every so often.&lt;/p&gt;
&lt;p&gt;I wrote a quick function to insert intermittent “-”s in the URLs, and remove them on parsing the state from the URL, then tried sending the original crossword. It worked!&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/imessage/fixed-link.png&quot; alt=&quot;A screenshot of an iMessage conversation with a not broken link in it&quot; style=&quot;max-width: 100%; width: 300px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;At the time I couldn’t find anyone else discussing the issue online, but my guess is that this quirk of iMessage URL parsing is due to security concerns with parsing text. A significant proportion of the zero day vulnerabilities in iOS are exploited through iMessage, and I would guess that a lot of the techniques used to create them are encoding malicious code in Base64 strings, just like I am with non malicious data.&lt;/p&gt;
&lt;p&gt;I would guess that because “-” is not a valid Base64 character, the parser used by iMessage can review each section separately. I finished up my crossword app adding “-” as the 64th character in every URL hash (I didn’t think about the length being serendipitous with the Base64 encoding until now). In the back of my mind I was curious about how long the sections of Base64 encoded text could be but didn’t look any further into it.&lt;/p&gt;
&lt;p&gt;A few years passed and I saw the following post on Mastodon:&lt;/p&gt;
&lt;blockquote class=&quot;social-post&quot;&gt;i built a little webapp that saves state as an unruly URI fragment hash but it looks like some clients (like Signal and Apple Messages) don’t want to recognize the whole things as a complete URL. is there anything I can do about that?&lt;/blockquote&gt;
&lt;p&gt;It was time for my weird Base64 URL tidbit to come to the rescue! I replied with the tip about breaking up with “-”s and the original poster replied that it had worked for them as well!&lt;/p&gt;
&lt;p&gt;After being reminded about the technique I was curious to figure out some of the details like, how long exactly could the sections be, and what other characters would work as separators. Using the technique I built &lt;a target=&quot;_blank&quot; href=&quot;https://doodles.patrickweaver.net/imessage-url-state-example/#eyIxIjp7InIiOiIyMjMiLCJnIjoiMjIiLCJiIjoi-MTU3In0sIjIiOnsiciI6MjEwLCJnIjoiMzEiLCJi-IjoiODcifSwiMyI6eyJyIjoiMjA1IiwiZyI6NzMs-ImIiOiIzNiJ9LCI0Ijp7InIiOiIyNTUiLCJnIjoi-MTk3IiwiYiI6MzZ9LCI1Ijp7InIiOiIyNTIiLCJn-IjoiMjQwIiwiYiI6IjExOSJ9LCI2Ijp7InIiOiI2-OSIsImciOiIxNzkiLCJiIjowfSwiNyI6eyJyIjoi-NjEiLCJnIjoiMjAzIiwiYiI6MjMyfSwiOCI6eyJy-IjoiNjEiLCJnIjoiMTA5IiwiYiI6IjI0MiJ9LCI5-Ijp7InIiOiIxOTAiLCJnIjoiMTQ4IiwiYiI6IjIy-OCJ9LCIxMCI6eyJyIjoiMjE0IiwiZyI6IjgxIiwi-YiI6IjE5NCJ9fQ==&quot;&gt;a small app that would store 10 RGB values in the URL&lt;/a&gt; (this example has a rainbow), but would also let the user control how frequent the “-” characters were, or replace them with other characters not used in Base64.&lt;/p&gt;
&lt;figure&gt;
  &lt;div style=&quot;text-align: center;&quot;&gt;
    &lt;a target=&quot;_blank&quot; href=&quot;https://doodles.patrickweaver.net/imessage-url-state-example/#eyIxIjp7InIiOiIyMjMiLCJnIjoiMjIiLCJiIjoi-MTU3In0sIjIiOnsiciI6MjEwLCJnIjoiMzEiLCJi-IjoiODcifSwiMyI6eyJyIjoiMjA1IiwiZyI6NzMs-ImIiOiIzNiJ9LCI0Ijp7InIiOiIyNTUiLCJnIjoi-MTk3IiwiYiI6MzZ9LCI1Ijp7InIiOiIyNTIiLCJn-IjoiMjQwIiwiYiI6IjExOSJ9LCI2Ijp7InIiOiI2-OSIsImciOiIxNzkiLCJiIjowfSwiNyI6eyJyIjoi-NjEiLCJnIjoiMjAzIiwiYiI6MjMyfSwiOCI6eyJy-IjoiNjEiLCJnIjoiMTA5IiwiYiI6IjI0MiJ9LCI5-Ijp7InIiOiIxOTAiLCJnIjoiMTQ4IiwiYiI6IjIy-OCJ9LCIxMCI6eyJyIjoiMjE0IiwiZyI6IjgxIiwi-YiI6IjE5NCJ9fQ==&quot;&gt;
      &lt;img src=&quot;https://patrickweaver.net/images/blog/imessage/colors-app.png&quot; alt=&quot;A screenshot of the web app to test long URLs&quot; style=&quot;max-width: 100%; margin: 0 auto;&quot;&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Here is an example with the same 10 colors as the link above, but with a “+” every 10th character:&lt;/p&gt;
&lt;blockquote class=&quot;long-link&quot;&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://doodles.patrickweaver.net/imessage-url-state-example/#eyIxIjp7I+nIiOiIyMj+MiLCJnIjo+iMjIiLCJi+IjoiMTU3I+n0sIjIiOn+siciI6MjE+wLCJnIjoi+MzEiLCJiI+joiODcifS+wiMyI6eyJ+yIjoiMjA1+IiwiZyI6N+zMsImIiOi+IzNiJ9LCI+0Ijp7InIi+OiIyNTUiL+CJnIjoiMT+k3IiwiYiI+6MzZ9LCI1+Ijp7InIiO+iIyNTIiLC+JnIjoiMjQ+wIiwiYiI6+IjExOSJ9L+CI2Ijp7In+IiOiI2OSI+sImciOiIx+NzkiLCJiI+jowfSwiNy+I6eyJyIjo+iNjEiLCJn+IjoiMTA0I+iwiYiI6Mj+MyfSwiOCI+6eyJyIjoi+MTI3IiwiZ+yI6IjkwIi+wiYiI6IjI+0MiJ9LCI5+Ijp7InIiO+iIxOTAiLC+JnIjoiMTQ+4IiwiYiI6+IjIyOCJ9L+CIxMCI6ey+JyIjoiMjE+0IiwiZyI6+IjgxIiwiY+iI6IjE5NC+J9fQ==&quot;&gt;https://doodles.patrickweaver.net/imessage-url-state-example/#eyIxIjp7I+nIiOiIyMj+MiLCJnIjo+iMjIiLCJi+IjoiMTU3I+n0sIjIiOn+siciI6MjE+wLCJnIjoi+MzEiLCJiI+joiODcifS+wiMyI6eyJ+yIjoiMjA1+IiwiZyI6N+zMsImIiOi+IzNiJ9LCI+0Ijp7InIi+OiIyNTUiL+CJnIjoiMT+k3IiwiYiI+6MzZ9LCI1+Ijp7InIiO+iIyNTIiLC+JnIjoiMjQ+wIiwiYiI6+IjExOSJ9L+CI2Ijp7In+IiOiI2OSI+sImciOiIx+NzkiLCJiI+jowfSwiNy+I6eyJyIjo+iNjEiLCJn+IjoiMTA0I+iwiYiI6Mj+MyfSwiOCI+6eyJyIjoi+MTI3IiwiZ+yI6IjkwIi+wiYiI6IjI+0MiJ9LCI5+Ijp7InIiO+iIxOTAiLC+JnIjoiMTQ+4IiwiYiI6+IjIyOCJ9L+CIxMCI6ey+JyIjoiMjE+0IiwiZyI6+IjgxIiwiY+iI6IjE5NC+J9fQ==&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;Or one with a “*” every 300th character (there is only one):&lt;/p&gt;
&lt;blockquote class=&quot;long-link&quot;&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://doodles.patrickweaver.net/imessage-url-state-example/#eyIxIjp7InIiOiIyMjMiLCJnIjoiMjIiLCJiIjoiMTU3In0sIjIiOnsiciI6MjEwLCJnIjoiMzEiLCJiIjoiODcifSwiMyI6eyJyIjoiMjA1IiwiZyI6NzMsImIiOiIzNiJ9LCI0Ijp7InIiOiIyNTUiLCJnIjoiMTk3IiwiYiI6MzZ9LCI1Ijp7InIiOiIyNTIiLCJnIjoiMjQwIiwiYiI6IjExOSJ9LCI2Ijp7InIiOiI2OSIsImciOiIxNzkiLCJiIjowfSwiNyI6eyJyIjoiNjEiLCJnIjoiMTA0Iiw*iYiI6MjMyfSwiOCI6eyJyIjoiMTI3IiwiZyI6IjkwIiwiYiI6IjI0MiJ9LCI5Ijp7InIiOiIxOTAiLCJnIjoiMTQ4IiwiYiI6IjIyOCJ9LCIxMCI6eyJyIjoiMjE0IiwiZyI6IjgxIiwiYiI6IjE5NCJ9fQ==&quot;&gt;https://doodles.patrickweaver.net/imessage-url-state-example/#eyIxIjp7InIiOiIyMjMiLCJnIjoiMjIiLCJiIjoiMTU3In0sIjIiOnsiciI6MjEwLCJnIjoiMzEiLCJiIjoiODcifSwiMyI6eyJyIjoiMjA1IiwiZyI6NzMsImIiOiIzNiJ9LCI0Ijp7InIiOiIyNTUiLCJnIjoiMTk3IiwiYiI6MzZ9LCI1Ijp7InIiOiIyNTIiLCJnIjoiMjQwIiwiYiI6IjExOSJ9LCI2Ijp7InIiOiI2OSIsImciOiIxNzkiLCJiIjowfSwiNyI6eyJyIjoiNjEiLCJnIjoiMTA0Iiw*iYiI6MjMyfSwiOCI6eyJyIjoiMTI3IiwiZyI6IjkwIiwiYiI6IjI0MiJ9LCI5Ijp7InIiOiIxOTAiLCJnIjoiMTQ4IiwiYiI6IjIyOCJ9LCIxMCI6eyJyIjoiMjE0IiwiZyI6IjgxIiwiYiI6IjE5NCJ9fQ==&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;300 character long sections of Base64 was longer that I expected would work, but as soon as I increased the “distance” between the separator characters to 302, which is a “-” (or “+”...) every 303rd character the links started to break.&lt;/p&gt;
&lt;figure&gt;
  &lt;div style=&quot;text-align: center;&quot;&gt;
    &lt;img src=&quot;https://patrickweaver.net/images/blog/imessage/broken-link-colors.png&quot; alt=&quot;A screenshot of an iMessage conversation with a broken link because the separator is the 303rd character&quot; style=&quot;max-width: 100%; margin: 0 auto;&quot;&gt;
  &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Interestingly, the limit was 301 character long sections of Base64 on both iMessage and Signal!&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/imessage/signal.jpg&quot; alt=&quot;A screenshot of a signal conversation with a workign link and a broken link&quot; style=&quot;max-width: 100%; width: 300px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;A working link (301 distance) above a broken link (302 distance) in Signal&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now that I know the exact cut off of 301 characters I’ve been able to find &lt;a href=&quot;https://blog.jonschneider.com/2023/06/the-mystery-of-broken-jwt-magic-link.html&quot;&gt;one other blog post&lt;/a&gt;, but I’m still surprised that this isn’t more widely known.&lt;/p&gt;
&lt;p&gt;Try out the app yourself or experiment with other platforms, I would love to learn more about the details behind this! This link without a hash will randomly generate 10 colors (and then store them in the URL hash): &lt;a target=&quot;_blank&quot; href=&quot;https://doodles.patrickweaver.net/imessage-url-state-example&quot;&gt;doodles.patrickweaver.net/imessage-url-state-example&lt;/a&gt;.&lt;/p&gt;
</description><pubDate>Mon, 05 Feb 2024 00:00:00 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/imessage-mystery/</guid>
    </item>
    <item>
      <title>A Blog Post With Every HTML Element</title>
      <link>https://patrickweaver.net/blog/a-blog-post-with-every-html-element/</link><description>&lt;!-- markdownlint-disable MD033 MD013 --&gt;
&lt;style&gt;
    section &gt; ul {
        padding-left: 0;
    }
    section &gt; ul &gt; li {
        display: inline-block;
    }

    section &gt; ul &gt; li::after {
        content: &#39;, &#39;;
    }

    section &gt; ul &gt; li:last-child::after {
        content: &#39;&#39;;
    }

    .confetti {
        position: fixed;
        font-size: 3rem;
        top: 50px;
    }

    #pw-links-portal {
    width: 300px;
    height: 200px;
    margin: 0.5rem;
    }

    .very-long-url-wrapper {
        max-width: 100%;
        overflow: scroll;
        padding-bottom: 1rem;
    }

    #weird-table {
        border: 5px double #8f8f9f;
        padding: 5px;
    }

    #weird-table .low-numbers {
        background-color: #ff4f2f4f;
    }

    #weird-table .high-numbers {
        background-color: #2f4fff4f;
    }

    #weird-table th {
        background-color: #2fff2f1f;
    }

    #weird-table th[scope=&#39;row&#39;] {
    background-color: #2fffff1f;
    text-align: left;
}

    #weird-table th,
    #weird-table td {
        border: 2px solid black;
        padding: 5px;
        min-width: 40px;
        text-align: center;
    }

    #weird-table th:nth-child(1),
    #weird-table td:nth-child(1) {
        border: none;
    }

    #weird-table th:nth-child(2),
    #weird-table td:nth-child(2) {
        border-radius: 3px;
    }

    #weird-table th:nth-child(3),
    #weird-table td:nth-child(3) {
        border-radius: 6px;
    }

    #weird-table th:nth-child(4),
    #weird-table td:nth-child(4) {
        border-radius: 9px;
    }

    #weird-table th:nth-child(5),
    #weird-table td:nth-child(5) {
        border-radius: 12px;
    }

    #weird-table th:nth-child(6),
    #weird-table td:nth-child(6) {
        border-radius: 15px;
    }

    #weird-table th:nth-child(7),
    #weird-table td:nth-child(7) {
        border-radius: 18px;
    }

    #everything-form {
        border: 5px double #080f18;
        border-radius: 20px;
        padding: 10px;
    }

    #everything-form label {
        display: block;
    }

    #everything-form {
        display: grid;
        grid-template-columns: 20% 20% 20% 20% 20%;
        grid-template-rows: 20% 20% 20% 20% 20%;
    }

    #everything-form &gt; * {
        margin: 2px;
    }

    #eform-type {
        grid-column-start: 1;
        grid-column-end: 3;
        grid-row-start: 1;
        grid-row-end: 3;
        background-color: #f8fffa;
        border-radius: 3px;
        border: 1px solid #d8dfda;
        text-align: right;
        padding: 4px;
    }

    #eform-size {
        grid-column-start: 1;
        grid-column-end: 3;
        grid-row-start: 3;
        grid-row-end: 7;
        text-align: center;
        margin: 5px 10px 5px 5px;
    }

    #eform-size-meter {
        width: 100%;
    }

    #eform-size &gt; fieldset {
        width: 100%;
        background-color: #faeaff;
    }

    #eform-size &gt; fieldset &gt; legend {
        width: 100%;
        background-color: #fcf2ff;
        border-radius: 10px;
        border: 1px solid #dcd2df;
    }

    #eform-size &gt; * {
        display: inline-block;
    }

    #eform-color {
        grid-column-start: 3;
        grid-column-end: 5;
        grid-row-start: 1;
        grid-row-end: 3;
        text-align: right;
        background-color: #fafaef;
        padding: 6px;
        border-radius: 5px;
        border: 2px solid #bf6ff8;
    }

    #eform-dream {
        grid-column-start: 5;
        grid-column-end: 6;
        grid-row-start: 1;
        grid-row-end: 6;
        position: relative;
        border-radius: 3px;
        background-color: #e8ffe8;
        display: grid;
        grid-template-columns: 25% 25% 25% 25%;
        grid-template-rows: 33% 34% 33%;
    }

    #eform-dream &gt; label {
        writing-mode: vertical-lr;
        text-orientation: upright;
        vertical-align: middle;
        padding: 1rem 0.5rem;
        grid-column-start: 1;
        grid-column-end: 3;
        grid-row-start: 1;
        grid-row-end: 4;
    }

    #dream-secret {
        text-orientation: sideways;
        font-size: 0.5rem;
    }

    #dream-input {
        resize: none;
        font-family: cursive;
        font-size: 2rem;
        display: block;
        margin: 4px;
        font-color: #38789f;
        writing-mode: vertical-lr;
        text-orientation: sideways;
        grid-column-start: 3;
        grid-column-end: 5;
        grid-row-start: 1;
        grid-row-end: 4;
    }

    #form-completion-label {
        display: inline-block;
        margin-top: 3rem;
        width: 100%;
        text-align: center;
        grid-column-start: 3;
        grid-column-end: 5;
        grid-row-start: 3;
        grid-row-end: 4;
    }

    #form-completion {
        grid-column-start: 3;
        grid-column-end: 5;
        grid-row-start: 4;
        grid-row-end: 5;
        width: 100%;
        margin-top: 1rem;
    }

    #eform-submit-button {
        border: 4px solid #6faf38;
        background-color: #8fcf58;
        color: #ffeff8;
        grid-column-start: 3;
        grid-column-end: 5;
        grid-row-start: 5;
        grid-row-end: 6;
        border-radius: 4px;
        box-shadow: -4px -4px 3px #bfbfef;
        margin: 5px;
        font-size: 2rem;
        font-family: serif;
        position: relative;
        cursor: pointer;
    }

    #eform-submit-button:hover {
        border-color: #51b578;
        background-color: #47775A;
        color: #F0F6FB;
        border-radius: 6px;
        font-size: 2.25rem;
    }

    #eform-submit-button:active {
        border-color: #54c480;
        background-color: #4E7E52;
        color: #F4FAFF;
        border-radius: 6px;
        top: -2px;
        left:-2px;
        box-shadow: -2px -2px 1px #bfbfef;
        border-radius: 3px;
        font-size: 2.5rem;
    }

    #everything-output {
        border: 4px dotted #f47a9e;
        border-radius: 10px;
        width: 300px;
        max-width: 90%;
        height: 100px;
        display: block;
        margin: 2rem 1rem;
        text-align: center;
        padding: 1rem;
    }

    #palm-sheriff {
        font-size: 0.75rem;
        line-height: 0.75rem;
        margin: 0 40%;
    }

    #menu-button {
        border: 2px solid #2f2f2f;
        border-radius: 10px;
        width: 200px;
        height: 3rem;
        padding: 0.5rem;
        background-color: #2f8fdf;
        color: #ffffff;
        text-align: center;
        box-shadow: -3px -3px 10px #9fcff8;
    }

    #menu-button:hover {
        box-shadow: -3px -3px 15px #9fcff8;
        background-color: #2888d0;
        cursor: pointer;
    }

    #menuitem-example {
        width: 300px;
        border: 2px solid #2f2f2f;
        padding: 0;
        background-color: #78c8fd;
        margin: 1px 0 0;
        border-radius: 4px;
    }

    #menuitem-example &gt; menuitem {
        display: inline-block;
        width: 100%;
        padding: 10px;
        border-bottom: 1px solid #000000;
    }

    #menuitem-example &gt; menuitem:last-child {
        border: none;
    }

    #nobr-box {
        width: 170px;
        border: 2px solid #2f2f2f;
        border-radius: 3px;
        padding: 5px;
        background-color: #fdfaf7;
        overflow: scroll;
        margin: 1rem 0;
    }

    footer {
        margin: 2rem 0 1rem;
        border: 1px solid #dfd8df;
        border-radius: 5px;
        padding: 1rem 10px;
        background-color: #fefcfe;
    }

    @media (max-width: 600px) {
        #everything-form {
        }

        #everything-form {
            display: grid;
            grid-template-columns: 20% 20% 20% 20% 20%;
            grid-template-rows: 25% 25% 35% 5% 10%;
        }

        #everything-form label {
            font-size: 1rem;
        }

        #eform-type {
            grid-column-start: 1;
            grid-column-end: 5;
            grid-row-start: 1;
            grid-row-end: 2;
        }

        #eform-color {
            grid-column-start: 1;
            grid-column-end: 5;
            grid-row-start: 2;
            grid-row-end: 3;
        }

        #eform-size {
            grid-column-start: 1;
            grid-column-end: 5;
            grid-row-start: 3;
            grid-row-end: 4;
            text-align: center;
            margin: 5px 10px 5px 5px;
        }

        #eform-dream {
            grid-column-start: 5;
            grid-column-end: 6;
            grid-row-start: 1;
            grid-row-end: 4;
        }

        #eform-dream &gt; label {
            grid-column-start: 1;
            grid-column-end: 5;
            grid-row-start: 1;
            grid-row-end: 2;
        }

        #dream-secret {
            text-orientation: sideways;
            font-size: 0.5rem;
        }

        #dream-input {
            grid-column-start: 1;
            grid-column-end: 5;
            grid-row-start: 2;
            grid-row-end: 4;
        }

        #form-completion-label {
            grid-column-start: 1;
            grid-column-end: 5;
            grid-row-start: 4;
            grid-row-end: 5;
            font-size: 0.75rem;
            margin: 0;
            text-align: left;
        }

        #form-completion {
            grid-column-start: 1;
            grid-column-end: 5;
            grid-row-start: 5;
            grid-row-end: 6;
            margin-top: 0;
        }

        #eform-submit-button {
            grid-column-start: 5;
            grid-column-end: 6;
            grid-row-start: 4;
            grid-row-end: 6;
            border: 4px solid #6faf38;
            border-radius: 2px;
            box-shadow: -4px -4px 3px #bfbfef;
            margin: 5px;
            font-size: 1rem;
            font-family: serif;
            position: relative;
            writing-mode: vertical-lr;
            text-orientation: sideways;
        }

        #eform-submit-button:hover {
            border-radius: 4px;
            font-size: 1rem;
            writing-mode: vertical-rl;
        }

        #eform-submit-button:active {
            border-radius: 6px;
            top: -2px;
            left:-2px;
            box-shadow: -2px -2px 1px #bfbfef;
            border-radius: 2px;
            font-size: 1.25rem;
        }

        #palm-sheriff {
        font-size: 0.75rem;
        line-height: 0.75rem;
        margin: 0 30%;
    }
}
&lt;/style&gt;
&lt;section&gt;
&lt;p&gt;After learning a little bit more about web accessibility last year I had been exploring some of the less common &lt;abbr title=&quot;Hyper Text Markup Language&quot;&gt;HTML&lt;/abbr&gt; elements, and making changes to this website, like wrapping the text of the posts on this blog in &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; tags and adding a &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; tag in the website’s layout templates (this website is built using &lt;a href=&quot;https://www.11ty.dev/&quot; target=&quot;_blank&quot;&gt;Eleventy&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I had previously done some work to make sure that &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; elements were layed out nicely for images with associated captions, and I had been impressed with various &lt;a href=&quot;https://www.recurse.com/&quot; target=&quot;_blank&quot;&gt;Recurser’s&lt;/a&gt; implementation of footnotes or sidenotes&lt;sub id=&quot;footnote-1-link&quot;&gt;&lt;a href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#footnote-1&quot;&gt;1&lt;/a&gt;&lt;/sub&gt;, and have been thinking it would be interesting to see what other interesting layouts were possible with just HTML.&lt;/p&gt;
&lt;p&gt;I could, element by element, continue to add support (mostly by making &lt;abbr title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/abbr&gt; updates for each element to fit in with the rest of my style choices) as I came across specific needs for them, but not one to shy away from an exhaustive exploration, I decided to write this post and attempt to use every element.&lt;/p&gt;
&lt;p&gt;A goal of the post, was to avoid delaying other future posts with CSS updates on a previously unused element, but in reality it took a year and a half to make all the updates for just this post! I am using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element&quot; target=&quot;_blank&quot;&gt;MDN Web Docs list of HTML elements&lt;/a&gt; as a reference which has more than 100 tags divided into a few categories, which I will also use in this post. Many of the tags like &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; don’t make sense to include in the text of a blog post, but if you’re viewing this post on &lt;a href=&quot;https://www.patrickweaver.net&quot;&gt;patrickweaver.net&lt;/a&gt;, then every one of the elements is used somewhere on this page.&lt;/p&gt;
&lt;/section&gt;
&lt;hr&gt;
&lt;h2&gt;The Elements&lt;/h2&gt;
&lt;section id=&quot;main-root&quot;&gt;
&lt;h3&gt;Main Root&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I didn’t have to make any changes to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag for this post, but one thing I don’t always remember to include is the &lt;code&gt;lang&lt;/code&gt; property (in this case &lt;code&gt;lang=&amp;quot;en&amp;quot;&lt;/code&gt;).&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;document-metadata&quot;&gt;
&lt;h3&gt;Document Metadata&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base&quot;&gt;&lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link&quot;&gt;&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta&quot;&gt;&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style&quot;&gt;&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title&quot;&gt;&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wasn’t familiar with the &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag before writing this post, though I’ve now added one with relative links to my layout templates. This caused a few issues with things like local development, and relative links, though they were easily resolved. The rest of the metadata tags are familiar and were already here.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;sectioning-root&quot;&gt;
&lt;h3&gt;Sectioning Root&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body&quot;&gt;&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Like the Document metadata tags, in the layout, though it was interesting to read the documentation and learn about attributes, like &lt;code&gt;onbeforeprint&lt;/code&gt; that provides functionality I’ve used more hacky methods to accomplish previously, and &lt;code&gt;onblur&lt;/code&gt;, whose primary utility seems to be annoying popups.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;content-sectioning&quot;&gt;
&lt;hgroup&gt;
&lt;h3&gt;Content Sectioning&lt;/h3&gt;
&lt;p&gt;Headers and Document Organization&lt;/p&gt;
&lt;/hgroup&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address&quot;&gt;&lt;code&gt;&amp;lt;address&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article&quot;&gt;&lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside&quot;&gt;&lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer&quot;&gt;&lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header&quot;&gt;&lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements&quot;&gt;&lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h4&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h5&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup&quot;&gt;&lt;code&gt;&amp;lt;hgroup&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main&quot;&gt;&lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav&quot;&gt;&lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section&quot;&gt;&lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/search&quot;&gt;&lt;code&gt;&amp;lt;search&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I first looked at the list I assumed that &lt;code&gt;&amp;lt;address&amp;gt;&lt;/code&gt; would be designed exclusively for mailing addresses, but was surprised to see that it can be used for email addresses, and even links. I updated the email address on the About page of site site, but I’ll add an &lt;code&gt;&amp;lt;address&amp;gt;&lt;/code&gt; below also:&lt;/p&gt;
&lt;address&gt;
    &lt;p&gt;Email Patrick:&lt;/p&gt;
    &lt;a href=&quot;mailto:hello.patrickw@gmail.com&quot;&gt;hello.patrickw@gmail.com&lt;/a&gt;
&lt;/address&gt;
&lt;p&gt;As I mentioned above, I updated the blog post page template to use the &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; tag, but reading the documentation, I’m now wondering if it would fit on every page of the site.&lt;/p&gt;
&lt;p&gt;I’m now using &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt; elements, which represent, &lt;q cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside&quot;&gt;a portion of a document whose content is only indirectly related to the document’s main content&lt;/q&gt;, to wrap around my footnotes at the bottom of this page (though I may try to style them as sidenotes in the future). The current design of this website doesn’t have a &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;, but I’ve added one to this blog post, and while I had a &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; element on the page header previously, I now know that more than one is acceptable so there is one around the header section of each blog posts as well.&lt;/p&gt;
&lt;h4&gt;Heading elements&lt;/h4&gt;
&lt;p&gt;As part of the style update and cleanup of this site that inspired this post I realized I was often using headers with incorrect hierarchy, which I cleaned up. Though I never reached 6 levels of headers, the closest I got was &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; in some of the blog posts with multiple sections and subsections.&lt;/p&gt;
&lt;h5&gt;How to use top level headings&lt;/h5&gt;
&lt;p&gt;It is a little bit unclear how to use &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tags in a post like this from just the MDN documentation. This &lt;a href=&quot;https://www.w3.org/QA/Tips/Use_h1_for_Title&quot;&gt;tip from the W3C&lt;/a&gt; suggests different implementations for pages with standalone, or collection content, which would make it difficult to use the same templates and styles for different pages on this site.&lt;/p&gt;
&lt;h6&gt;How I decided to handle it&lt;/h6&gt;
&lt;p&gt;Before writing this post I had updated the site to use &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; for the name of the site (my name) at the top, &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; for the section name, for this page, “Blog”, and &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; for the title of what would be the &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; on a page. However, after reading the documentation and the tip above, I decided to update the hierarchy and use &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; for different things on different pages, and use classes for styles, which is probably more in line with the separation of concerns of HTML and CSS (which means this paragraph is below an &lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Sometime between when I started this experiment in early 2022 and when I published it in Summer 2023 &lt;code&gt;&amp;lt;hgroup&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;search&amp;gt;&lt;/code&gt; were added to the MDN documentation (which I realize is not the official spec). I’ve added an &lt;code&gt;&amp;lt;hgroup&amp;gt;&lt;/code&gt; around the heading of this section, with a subtitle &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; element. &lt;code&gt;&amp;lt;search&amp;gt;&lt;/code&gt; is a semantic element that indicates that an input can be used for search, not for search results (with the exception of quick results that populate within a form ). While a &lt;code&gt;&amp;lt;search&amp;gt;&lt;/code&gt; element that contains a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; works with just HTML on a website that can generate search results on a server, because this website is statically generated the example below requires JavaScript. I haven’t seen a &lt;code&gt;&amp;lt;search&amp;gt;&lt;/code&gt; element anywhere else, neither MDN’s header search, or google.com use it in 2023.&lt;/p&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;search&gt;
&lt;form id=&quot;search-form&quot;&gt;
    &lt;label for=&quot;text-search&quot;&gt;Search this post: &lt;/label&gt;
    &lt;input type=&quot;search&quot; id=&quot;text-search&quot;&gt;
    &lt;button id=&quot;search-button&quot; type=&quot;button&quot;&gt;Search&lt;/button&gt;
&lt;/form&gt;
&lt;/search&gt;
&lt;output id=&quot;search-output&quot; form=&quot;search-form&quot; for=&quot;text-search&quot; name=&quot;search-output&quot; style=&quot;display: none; margin: 1rem; border: 1px solid #555; padding: 0.5rem;&quot;&gt;
&lt;ul id=&quot;search-output-list&quot;&gt;&lt;/ul&gt;
&lt;/output&gt;
&lt;script&gt;
    const searchInput = document.getElementById(&quot;text-search&quot;);
    const searchButton = document.getElementById(&quot;search-button&quot;);
    const searchOutput = document.getElementById(&quot;search-output&quot;);
    const searchOutputList = document.getElementById(&quot;search-output-list&quot;);
    searchButton.addEventListener(&#39;click&#39;, searchCb);
    searchInput.addEventListener(&#39;keydown&#39;, searchCb);
    function searchCb(event) {
        const { keyCode } = event;
        if (keyCode &amp;&amp; keyCode !== 13) return;
        event.preventDefault()
        const text = searchInput.value;
        searchOutput.style.display = text ? &quot;block&quot; : &quot;none&quot;;
        if (!text) return;
        const a = document.getElementsByTagName(&quot;article&quot;)[0].innerText;
        let index = 0
        let count = 0;
        while (index !== -1 &amp;&amp; count &lt; 5) {
            count++;
            index = a.indexOf(text, index);
            if (index !== -1) {
                const newResult = findStringInArticle(index, text)
                searchOutputList.innerHTML += `&lt;li&gt;&lt;code class=&quot;code-regular&quot;&gt;${newResult}&lt;/code&gt;&lt;/li&gt;`
                index++;
            }
        }
        function escapeHtml(unsafe) {
            // https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
            return unsafe
                .replace(/&amp;/g, &quot;&amp;amp;&quot;)
                .replace(/&lt;/g, &quot;&amp;lt;&quot;)
                .replace(/&gt;/g, &quot;&amp;gt;&quot;)
                .replace(/&quot;/g, &quot;&amp;quot;&quot;)
                .replace(/&#39;/g, &quot;&amp;#039;&quot;);
        }
        function findStringInArticle(index, text) {
            if (index &gt;= 0) {
                let start = index - 100;
                if (start &lt; 0 ) start = 0;
                let end = index + 100;
                if (end &gt; a.length) end = a.length;
                return `${start &gt; 0 ? &#39;…&#39; : &#39;&#39;}${escapeHtml(a.slice(start, index))}&lt;mark&gt;${escapeHtml(a.slice(index, index + text.length))}&lt;/mark&gt;${escapeHtml(a.slice(index + text.length, end))}${end &lt; a.length ? &#39;…&#39; : &#39;&#39;}`;
            }
        }
    }
&lt;/script&gt;
&lt;noscript&gt;
    &lt;p&gt;This search functionality will only be interactive when JavaScript is enabled.&lt;/p&gt;
&lt;/noscript&gt;
&lt;!-- markdownlint-enable --&gt;
&lt;!-- markdownlint-disable MD033 MD013 --&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; element was one of the initial curiosities that led me down the path of reading about and implementing every element, though it wasn’t until I read through the MDN list that I added a &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; element around the menu at the top of this page. I’ve added &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; elements to this post, but I’m not sure how often I will use them elsewhere. One reason is that it makes it harder to mix and match HTML and markdown with visually clear nesting in the &lt;a href=&quot;https://github.com/patrickweaver/pw20/blob/main/build/blog/a-blog-post-with-every-html-element.md&quot;&gt;document where I am writing this post&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;text-content&quot;&gt;
&lt;h3&gt;Text Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote&quot;&gt;&lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd&quot;&gt;&lt;code&gt;&amp;lt;dd&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div&quot;&gt;&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl&quot;&gt;&lt;code&gt;&amp;lt;dl&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt&quot;&gt;&lt;code&gt;&amp;lt;dt&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption&quot;&gt;&lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure&quot;&gt;&lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr&quot;&gt;&lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li&quot;&gt;&lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu&quot;&gt;&lt;code&gt;&amp;lt;menu&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol&quot;&gt;&lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre&quot;&gt;&lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul&quot;&gt;&lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had previously used &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; for embedding tweets into blog posts, but for this post I decided to add styles for standalone quotes. I didn’t know about the &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; &lt;code&gt;cite&lt;/code&gt; property or &lt;code&gt;&amp;lt;cite&amp;gt;&lt;/code&gt; element until reading the docs more closely:&lt;/p&gt;
&lt;figure class=&quot;blockquote-figure&quot;&gt;
    &lt;blockquote cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote#attributes&quot;&gt;
        &lt;h4&gt;&lt;code&gt;cite&lt;/code&gt;&lt;/h4&gt;
        &lt;p&gt;A URL that designates a source document or message for the information quoted. This attribute is intended to point to information explaining the context or the reference for the quote.&lt;/p&gt;
    &lt;figcaption&gt;&lt;cite&gt;MDN&lt;/cite&gt; on &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt;&lt;/figcaption&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;dd&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;dl&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;dt&amp;gt;&lt;/code&gt; are elements that, after finding out about them when first looking into more obscure HTML elements, I was very surprised to have not known about sooner. Although, it’s unclear from the documentation whether lists like the links on the current version of my &lt;a href=&quot;https://web.archive.org/web/20220628123804/https://www.patrickweaver.net/portfolio/&quot;&gt;portfolio page&lt;/a&gt; should use &lt;code&gt;&amp;lt;dl&amp;gt;&lt;/code&gt; (I’m currently using &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;). I am curious how &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; became part of almost every “Intro to HTML” class, but &lt;code&gt;&amp;lt;dl&amp;gt;&lt;/code&gt; is relatively obscure. It’s also strange that &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; have default margins, but for &lt;code&gt;&amp;lt;dl&amp;gt;&lt;/code&gt; the margin is on &lt;code&gt;&amp;lt;dd&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;HTML list elements&lt;/h4&gt;
&lt;dl&gt;
    &lt;dt&gt;&lt;code&gt;dl&lt;/code&gt;&lt;/dt&gt;&lt;dd&gt;Description list&lt;/dd&gt;
    &lt;dt&gt;&lt;code&gt;ol&lt;/code&gt;&lt;/dt&gt;&lt;dd&gt;Ordered list&lt;/dd&gt;
    &lt;dt&gt;&lt;code&gt;ul&lt;/code&gt;&lt;/dt&gt;&lt;dd&gt;Unordered list&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; has acquired a bad reputation on the modern web due to overuse, so I was surprised to be using relatively few &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements, though once I started using elements that I wanted to style together, I ended up with around 20. It shows how unnecessary most &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s probably are, though this site has minimal generic “sections”.&lt;/p&gt;
&lt;p&gt;Among the first more obscure elements that I added to this site were &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt;. I was adding some blog posts that had originally been published on Medium, and wanted to add captions below images in a web-semantically correct way. Though after reading the documentation, which says &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; can be used for, &lt;q cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure#usage_notes&quot;&gt;image, illustration, diagram, code snippet, etc.,&lt;/q&gt; I realized that there are a lot of places where I currently have code blocks that I could be using them.&lt;/p&gt;
&lt;p&gt;I have always liked &lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt; elements a lot, but I’m never sure when to use them. The documentation says, &lt;q cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr#try_it&quot;&gt;While it may still be displayed as a horizontal rule in visual browsers, this element is now defined in semantic terms, rather than presentational terms, so if you wish to draw a horizontal line, you should do so using appropriate CSS&lt;/q&gt; which made me revisit the &lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt; styles on this site and I decided to include an emoji in an &lt;code&gt;hr:after&lt;/code&gt; rule, though I should check how that works on a screen reader.&lt;/p&gt;
&lt;p&gt;The items in the (inline styled) lists of elements on this page are of course &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; elements, though I had never looked at the documentation until now. It’s interesting that the same &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; element is used in both &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; lists, with quirks like &lt;q cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li#attributes&quot;&gt;The value attribute has no meaning for unordered lists&lt;/q&gt;, but is not used to wrap the &lt;code&gt;&amp;lt;dt&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;dd&amp;gt;&lt;/code&gt; elements in a &lt;code&gt;&amp;lt;dl&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I had not encountered &lt;code&gt;&amp;lt;menu&amp;gt;&lt;/code&gt; before writing this post, and I was initially surprised that it survived to HTML 5 (while &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menuitem&quot;&gt;&lt;code&gt;&amp;lt;menuitem&amp;gt;&lt;/code&gt;&lt;/a&gt; didn’t) because modern browsers treat it as essentially a &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;. Researching further &lt;a href=&quot;https://en.wikipedia.org/wiki/HTML_element#Basic_text&quot;&gt;on Wikipedia&lt;/a&gt; I read:&lt;/p&gt;
&lt;figure class=&quot;blockquote-figure&quot;&gt;
    &lt;blockquote cite=&quot;https://en.wikipedia.org/wiki/HTML_element#Basic_text&quot;&gt;MENU existed in HTML Tags, and was standardized in HTML 2.0; deprecated in HTML 4.0 Transitional; invalid in HTML 4.0 Strict; then redefined in HTML5, but removed in HTML 5.2,&lt;/blockquote&gt;
&lt;/figure&gt;and now I don’t know what to think, but here’s a &lt;code&gt;&amp;lt;menu&amp;gt;&lt;/code&gt;:&lt;p&gt;&lt;/p&gt;
&lt;menu id=&quot;interactive-menu&quot;&gt;
    &lt;script type=&quot;text/javascript&quot;&gt;
        const b = (id) =&gt; `
            &lt;div
                id=&quot;${id}&quot;
                class=&quot;confetti&quot;
                style=&quot;left: ${Math.random() * 1000}px&quot;
            &gt;
                🎈
            &lt;/div&gt;
        `;
        const s = (id) =&gt; `
            &lt;div
                id=&quot;${id}&quot;
                class=&quot;confetti&quot;
                style=&quot;left: ${Math.random() * 1000}px&quot;
            &gt;
                🧽
            &lt;/div&gt;
        `;
        const m = document.getElementById(&#39;interactive-menu&#39;);
        function balloons() { confetti(b) };
        function sponges() { confetti(s) };
        function confetti(f) {
            const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
            for (let i = 0; i &lt; 3; i++) {
                const id = `c-${String(Math.random()).slice(2, 9)}`
                m.insertAdjacentHTML(&#39;beforeend&#39;, f(id))
                sink(id)
            }
            async function sink(id) {
                const e = document.getElementById(id);
                const startTop = getComputedStyle(e)?.top ?? &#39;0px&#39;;
                let top = parseInt(startTop.slice(0, startTop.indexOf(&#39;p&#39;)), 10);
                while (top &lt; vh + 100) {
                    top = top + 2;
                    e.style.top = `${top}px`;
                    await new Promise(r =&gt; setTimeout(r, 16.67));
                }
                e.remove();
            }
        }
    &lt;/script&gt;
    &lt;li&gt;&lt;button onclick=&quot;balloons()&quot;&gt;Balloons&lt;/button&gt;&lt;/li&gt;
    &lt;li&gt;&lt;button onclick=&quot;sponges()&quot;&gt;Sponges&lt;/button&gt;&lt;/li&gt;
    &lt;noscript&gt;
        &lt;p&gt;This menu will only be interactive when JavaScript is enabled.&lt;/p&gt;
    &lt;/noscript&gt;
&lt;/menu&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; are some of the HTML elements I used in my earliest web pages, and more recently I try to use &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; with CSS in places that are semantically lists, but might not be styled like a traditional list.&lt;/p&gt;
&lt;p&gt;I was surprised not to find more guidance on using &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p&quot;&gt;the MDN docs&lt;/a&gt;, something I wonder often when adding non long form text to a website is, “is this really a paragraph?” But, it seems like as far as HTML is concerned, if it’s not a heading, then probably!&lt;/p&gt;
&lt;p&gt;Before reading the documentation I had really only considered using &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tags along with &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; tags for code blocks, but the example used on MDN is used to show how &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; can display meaningful whitespace for things like:&lt;/p&gt;
&lt;figure class=&quot;no-padding-figure&quot;&gt;&lt;pre role=&quot;img&quot; aria-label=&quot;An ASCII picture of a house with the label, &#39;in this house we write semantic HTML&#39;&quot;&gt;
  ┏┓
  ┃┃╱╲ in
  ┃╱╱╲╲ this
  ╱╱╭╮╲╲house
  ▔▏┗┛▕▔ we
  ╱▔▔▔▔▔▔▔▔▔▔╲
write semantic HTML
  ╱╱┏┳┓╭╮┏┳┓ ╲╲
  ▔▏┗┻┛┃┃┗┻┛▕▔
&lt;/pre&gt;&lt;/figure&gt;
&lt;/section&gt;
&lt;section id=&quot;inline-text-semantics&quot;&gt;
&lt;h3&gt;Inline Text Semantics&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a&quot;&gt;&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr&quot;&gt;&lt;code&gt;&amp;lt;abbr&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b&quot;&gt;&lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi&quot;&gt;&lt;code&gt;&amp;lt;bdi&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo&quot;&gt;&lt;code&gt;&amp;lt;bdo&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br&quot;&gt;&lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite&quot;&gt;&lt;code&gt;&amp;lt;cite&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code&quot;&gt;&lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data&quot;&gt;&lt;code&gt;&amp;lt;data&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn&quot;&gt;&lt;code&gt;&amp;lt;dfn&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em&quot;&gt;&lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i&quot;&gt;&lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd&quot;&gt;&lt;code&gt;&amp;lt;kbd&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark&quot;&gt;&lt;code&gt;&amp;lt;mark&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q&quot;&gt;&lt;code&gt;&amp;lt;q&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp&quot;&gt;&lt;code&gt;&amp;lt;rp&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt&quot;&gt;&lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby&quot;&gt;&lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s&quot;&gt;&lt;code&gt;&amp;lt;s&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp&quot;&gt;&lt;code&gt;&amp;lt;samp&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small&quot;&gt;&lt;code&gt;&amp;lt;small&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span&quot;&gt;&lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong&quot;&gt;&lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub&quot;&gt;&lt;code&gt;&amp;lt;sub&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup&quot;&gt;&lt;code&gt;&amp;lt;sup&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time&quot;&gt;&lt;code&gt;&amp;lt;time&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u&quot;&gt;&lt;code&gt;&amp;lt;u&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var&quot;&gt;&lt;code&gt;&amp;lt;var&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr&quot;&gt;&lt;code&gt;&amp;lt;wbr&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As with other very common tags I was curious to get to the documentation for &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; to see if there was anything I was unaware of, or had been using incorrectly. I was surprised to discover that &lt;code&gt;&amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt;link&amp;lt;/a&amp;gt;&lt;/code&gt; links to the top of the page after years as just using it as a placeholder when I didn’t know the URL yet.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;abbr&amp;gt;&lt;/code&gt; seems likely to be the least used tag, especially because the &lt;abbr title=&quot;MDN Web Docs, previously Mozilla Developer Network and formerly Mozilla Developer Center&quot;&gt;MDN&lt;/abbr&gt; documentation doesn’t make a great case for it &lt;q cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr#default_styling&quot;&gt;the purpose of this element is purely for the convenience of the author.&lt;/q&gt;&lt;/p&gt;
&lt;p&gt;The distinction between &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt;, as the docs have it, is not what I had previously thought (that &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; was the HTML5 replacement for &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt;). I think the distinction is more nuanced and overlapping than the technical nature of the documentation is really able to convey. The way I will probably explain it to other people in the future is that, for the most part, you should use &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; for &lt;b&gt;single words&lt;/b&gt; (or compound words), &lt;strong&gt;and &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; for whole sentences or phrases&lt;/strong&gt;. This is more a rule of thumb and bypasses the actual distinction. The documentation reads:&lt;/p&gt;
&lt;figure&gt;
    &lt;blockquote&gt;
        The &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; element is for content that is of greater importance, while the &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; element is used to draw attention to text without indicating that it’s more important.
    &lt;/blockquote&gt;
    &lt;figcaption&gt;
        &lt;cite&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong#b_vs._strong&quot;&gt;MDN on “&lt;code class=&quot;code-regular&quot;&gt;&amp;lt;b&amp;gt;&lt;/code&gt; vs. &lt;code class=&quot;code-regular&quot;&gt;&amp;lt;strong&amp;gt;&lt;/code&gt;”&lt;/a&gt;&lt;/cite&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;But I can’t think of many instances where I would want to draw attention to a word, where that word is not more important than other words. The recommendation seems to be from a reality where, for the most part, all words are of equal importance, but deserve different amounts of attention, which doesn’t seem very common.&lt;/p&gt;
&lt;p&gt;I’ll probably think of &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; from now on as the HTML tag with a similar effect as the quotation marks used for “emphasis” you might see on signage that can cause intergenerational confusion.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;bdi&amp;gt;&lt;/code&gt; is a tag for a concept that I hadn’t considered until now, text whose direction might (or might not) differ from the direction of the surrounding text. The examples on the documentation are all names, but could stand in for any user inputted text. In the future I will probably try to wrap any tags, like the ones controlled by the input below, that might contain arbitrary user inputted unicode characters in a &lt;code&gt;&amp;lt;bdi&amp;gt;&lt;/code&gt; tag just to be safe.&lt;/p&gt;
&lt;form&gt;
&lt;p&gt;&lt;label for=&quot;bdi-name-input&quot;&gt;What is your name?
&lt;input type=&quot;text&quot; id=&quot;bdi-name-input&quot;&gt;&lt;/label&gt;&lt;/p&gt;
&lt;/form&gt;
&lt;p&gt;Hello, &lt;bdi id=&quot;bdi-name-display&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/bdi&gt;, thanks for reading!&lt;/p&gt;
&lt;div&gt;
&lt;button id=&quot;rtl-fill&quot; type=&quot;button&quot;&gt;Fill with a RTL language name&lt;/button&gt; (&lt;a href=&quot;https://en.wikipedia.org/wiki/Abdul_Rahman_Munif&quot; target=&quot;_blank&quot;&gt;عَبْد الرَّحْمٰن بِن إِبْرَاهِيم المُنِيف&lt;/a&gt;)
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    const bdi_i = document.getElementById(&quot;bdi-name-input&quot;);
    const bdi_o = document.getElementById(&quot;bdi-name-display&quot;);
    const bdi_b = document.getElementById(&quot;rtl-fill&quot;);
    bdi_i.addEventListener(&quot;input&quot;, function(event) {
        bdi_o.innerHTML = event.target?.value || &quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;;
    })
    bdi_b.addEventListener(&quot;click&quot;, function(event) {
        const rtl_name = &#39;عَبْد الرَّحْمٰن بِن إِبْرَاهِيم المُنِيف&#39;;
        bdi_o.innerHTML = rtl_name;
        bdi_i.value = rtl_name;
    })
&lt;/script&gt;
&lt;noscript&gt;
    &lt;p&gt;This form will only be interactive when JavaScript is enabled.&lt;/p&gt;
&lt;/noscript&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;bdo&amp;gt;&lt;/code&gt; I will probably use less often because I don’t work with RTL languages often, but it is still good to know how to handle small amounts of RTL text, like this link to the page for HTML on the Farsi Wikipedia: &lt;a href=&quot;https://fa.wikipedia.org/wiki/%D8%A7%DA%86%E2%80%8C%D8%AA%DB%8C%E2%80%8C%D8%A7%D9%85%E2%80%8C%D8%A7%D9%84&quot; target=&quot;_blank&quot;&gt;&lt;bdo dir=&quot;rtl&quot;&gt;اچ‌تی‌ام‌ال&lt;/bdo&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; is interesting to me, because for a time (a long time ago) it seemed to be as misused as &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; has been recently, but, as CSS has gotten more robust that is probably less often the case; though I wonder if it is still taught early in HTML classes. It’s interesting that the example use case in the docs is poetry because it makes me wonder how the grey area between a paragraph and a standalone line was considered in creating the HTML spec. It also seems like a &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag might be a better fit in some cases.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;cite&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;q&amp;gt;&lt;/code&gt; are interesting because they try to add HTML semantic elements for meaning that is also conveyed by visible punctuation in most languages. It’s the kind of redundancy that underscores repetitiousness of working with computers (the default style for &lt;cite&gt;cite&lt;/cite&gt; uses italics while &lt;q cite=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/this%20is%20not%20really%20a%20quotation&quot;&gt;q&lt;/q&gt; adds quotation marks not in the text.).&lt;/p&gt;
&lt;p&gt;I’ve clearly used many &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; elements in this post so far, but reading the documentation made me wonder whether the recently released Markdown support in Google Docs uses &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; for text, but on inspecting the HTML of a Doc, I remembered that &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; is used to render the document now.&lt;/p&gt;
&lt;p&gt;Occasionally the MDN documentation examples are difficult to mentally translate to real-world use cases. &lt;code&gt;&amp;lt;data&amp;gt;&lt;/code&gt; is one of these, where the only examples show a &lt;code&gt;&amp;lt;data&amp;gt;&lt;/code&gt; element with a &lt;code&gt;value&lt;/code&gt; property with product IDs wrapped around product names In that case the IDs seem either, user facing, in which case it would probably be better to display them to the user, or non user facing, in which case, I’m not sure who the “data” is for. Interestingly, &lt;code&gt;&amp;lt;data&amp;gt;&lt;/code&gt; doesn’t seem to appear in the &lt;a href=&quot;https://www.w3.org/TR/2011/WD-html5-20110405/&quot;&gt;W3C HTML5 specification&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;dfn&amp;gt;&lt;/code&gt; is another strange element because it seems like it is misnamed, as it wraps, not the definition of a term, but instead that term within the definition. It does &lt;a href=&quot;http://www.martinrinehart.com/frontend-engineering/engineers/html/html-tag-history.html&quot;&gt;only seem to have been part of the never officially adopted HTML 2.0 proposal&lt;/a&gt;, and it exemplifies the academic nature of the early web. The &lt;a href=&quot;http://www.martinrinehart.com/frontend-engineering/engineers/html/html-tag-history.html&quot;&gt;HTML Tags: Past, Present, Proposed&lt;/a&gt; page on martinrinehart.com defines &lt;dfn title=&quot;HyperText Markup Language 2.0&quot;&gt;HTML 2.0&lt;/dfn&gt; as &lt;q cite=&quot;http://www.martinrinehart.com/frontend-engineering/engineers/html/html-tag-history.html&quot;&gt;There never was an HTML 2.0 standard, but these all shaped browser development in the late 20th century&lt;/q&gt; with references to several RFC documents.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; bring up similar usage questions as &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt;, but in my opinion with even more nuance, especially, as the documentation notes, with other more specific tags like &lt;code&gt;&amp;lt;cite&amp;gt;&lt;/code&gt; that also by default italicize text in most browsers. The rule of thumb from above also for the most part fits here, &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; for single words, and &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; for sentences or phrases, though the examples in the documentation seem to mostly use &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; for drawing attention to potential confusion, as in, &lt;q cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em#i_vs._em&quot;&gt;The word &lt;i&gt;the&lt;/i&gt; is an article&lt;/q&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;kbd&amp;gt;&lt;/code&gt; is another tag that makes me wonder about the conceptual boundaries of the usage of the tag. It is intended for specifying keys on a computer keyboard, for example: to type the &lt;code&gt;&amp;lt;&lt;/code&gt; character used for (the non escaped) version of the tags in this post, I press &lt;kbd&gt;Shift&lt;/kbd&gt; + &lt;kbd&gt;,&lt;/kbd&gt; (the styles here on &lt;code&gt;&amp;lt;kbd&amp;gt;&lt;/code&gt; are applied through custom CSS). But I’m curious if it would also be appropriate to put a &lt;code&gt;&amp;lt;kbd&amp;gt;&lt;/code&gt; around something like &lt;i&gt;Right click&lt;/i&gt; (in this case I used &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; instead).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;mark&amp;gt;&lt;/code&gt; is interesting because it suggests a 2-way authoring web that was originally envisioned, but failed to come to fruition, with usage notes like, &lt;q&gt;Think of this like using a &lt;mark&gt;highlighter pen&lt;/mark&gt; in a book to mark passages that you find of interest.&lt;/q&gt; The yellow here is the default style in all major browsers.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;rp&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; all relate to rendering &lt;a href=&quot;https://en.wikipedia.org/wiki/Agate_(typography)&quot;&gt;“ruby” or “agate” fonts&lt;/a&gt;, which are the smallest legible text used in print. They are used in HTML to, &lt;q&gt;provide pronunciation, translation, or transliteration information for East Asian typography.&lt;/q&gt; Because I don’t read any East Asian languages, I’ll use the same example as the MDN docs do below. Interestingly, &lt;code&gt;&amp;lt;rp&amp;gt;&lt;/code&gt; is used to hide parentheses characters, which are included in the source. It’s surprising to me that there is an element to hide these characters in a very rare instance, but we still rely on CSS to &lt;a href=&quot;https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html#hiding-content-visually&quot;&gt;hide content visually&lt;/a&gt; (but still show it to screen readers).&lt;/p&gt;
&lt;ruby&gt;
漢 &lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;kan&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;
字 &lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;ji&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;
&lt;/ruby&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;s&amp;gt;&lt;/code&gt; is the strikethrough element, which should be used to indicate text that is not accurate or relevant, but was previously. Along with &lt;code&gt;&amp;lt;menu&amp;gt;&lt;/code&gt; it is one of two elements (or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u#usage_notes&quot;&gt;according to MDN 3 with &lt;code&gt;&amp;lt;u&amp;gt;&lt;/code&gt;&lt;/a&gt;) that were &lt;a href=&quot;http://www.martinrinehart.com/frontend-engineering/engineers/html/html-tag-history.html&quot;&gt;deprecated in HTML 4.01, and un-deprecated and redefined in HTML5&lt;/a&gt;. The redefinition changes the use of the tag from presentational, which should now be achieved with CSS, to relevant to the context of the text. However, some screen readers don’t announce the strikethrough, which seems potentially confusing. I’ve added the CSS recommended by the MDN docs to my website, so the strikethrough should be announced here.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;s&gt;HTML 3.0: &lt;code&gt;&amp;lt;s&amp;gt;&lt;/code&gt; is used for presentation&lt;/s&gt;&lt;/li&gt;
  &lt;li&gt;HTML 5: &lt;code&gt;&amp;lt;s&amp;gt;&lt;/code&gt; is used for removed text&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;samp&amp;gt;&lt;/code&gt; is another element I wasn’t aware of before reading the MDN docs. It is used for rendering the output of a computer program, which I had previously just used &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; for (I just updated my &lt;a href=&quot;https://patrickweaver.net/blog/how-to-raspberry-pi-server/&quot;&gt;Raspberry Pi blog post&lt;/a&gt; to use &lt;code&gt;&amp;lt;samp&amp;gt;&lt;/code&gt;). Another example is below:&lt;/p&gt;
&lt;figure class=&quot;figure-with-outline&quot;&gt;
&lt;figcaption&gt;A “Hello, World” program in JavaScript:&lt;/figcaption&gt;
&lt;code&gt;
console.log(&quot;Hello, World&quot;)
&lt;/code&gt;
&lt;br&gt;&lt;br&gt;
&lt;samp&gt;
Hello, World
&lt;/samp&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;small&amp;gt;&lt;/code&gt; is used to render smaller text, which is something I would usually have done with CSS. The MDN docs don’t provide much clarity about which strategy to use: &lt;q&gt;Authors are encouraged to use their best judgement when determining whether to use &lt;code&gt;&amp;lt;small&amp;gt;&lt;/code&gt; or CSS.&lt;/q&gt;. &lt;small&gt;I will probably continue to use CSS for the most part since the effect seems to be presentational for the most part.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; is a very familiar element, but reading the documentation made me wonder for the first time why HTML and CSS allow contradictory element styles like a &lt;/p&gt;&lt;div style=&quot;display: inline&quot;&gt;&lt;code&gt;&amp;lt;div style=&quot;display: inline&quot; /&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;p&gt;
&lt;/p&gt;&lt;p&gt; or a &lt;span style=&quot;display: block&quot;&gt;&lt;code&gt;&amp;lt;span style=&quot;display: block&quot; /&amp;gt;&lt;/code&gt;&lt;/span&gt;. Though the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; breaks the parent &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag, while the &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; does not.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;sub&amp;gt;&lt;/code&gt; (used for footnotes here) and &lt;code&gt;&amp;lt;sup&amp;gt;&lt;/code&gt; (used in the equation below) are more elements I probably have used the CSS implementations of previously (though it is appropriate for presentation only super or subscript.)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;time&amp;gt;&lt;/code&gt; seems like a helpful element for creating semantic HTML, but even Google doesn’t seem to use it in &lt;a href=&quot;https://www.google.com/search?q=%22Patrick+Weaver%22+%22HTML%22&quot;&gt;search results&lt;/a&gt;. I’ve updated the dates on my blog posts to use it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;u&amp;gt;&lt;/code&gt; has probably my favorite quote from the MDN docs, &lt;q&gt;Most of the time, you actually don’t want to use &lt;code&gt;&amp;lt;u&amp;gt;&lt;/code&gt;.&lt;/q&gt; Their strongest recommendation seems to be indicating &lt;u class=&quot;u-sp&quot;&gt;spellling&lt;/u&gt; errors.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;var&amp;gt;&lt;/code&gt; is a way to indicate semantically that a string is a variable for either math or programming. For example, rendering the pythagorean theorem uses both &lt;code&gt;&amp;lt;var&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;sup&amp;gt;&lt;/code&gt;: &lt;var&gt;a&lt;/var&gt;&lt;sup&gt;2&lt;/sup&gt; + &lt;var&gt;b&lt;/var&gt;&lt;sup&gt;2&lt;/sup&gt; = &lt;var&gt;c&lt;/var&gt;&lt;sup&gt;2&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;wbr&amp;gt;&lt;/code&gt; was also a new element until now, but it solves a problem I have hit many times before, breaking long “words” at certain points, specifically URLs. If I put a &lt;code&gt;&amp;lt;wbr&amp;gt;&lt;/code&gt; element at clear break points in this URL: &lt;a href=&quot;https://www.patrickweaver.net/pages/averylongurlwithoutanypunctuationbutstillmadeupofwords/&quot;&gt;https://www.patrickweaver.net&lt;wbr&gt;/pages&lt;wbr&gt;/a&lt;wbr&gt;very&lt;wbr&gt;long&lt;wbr&gt;url&lt;wbr&gt;without&lt;wbr&gt;any&lt;wbr&gt;punctuation&lt;wbr&gt;but&lt;wbr&gt;still&lt;wbr&gt;made&lt;wbr&gt;up&lt;wbr&gt;of&lt;wbr&gt;words/&lt;/a&gt; then it will break at clear points at different screen widths, which is is more readable compared to the same link without the &lt;code&gt;&amp;lt;wbr&amp;gt;&lt;/code&gt; elements:&lt;/p&gt;
&lt;p class=&quot;very-long-url-wrapper&quot;&gt;&lt;a href=&quot;https://www.patrickweaver.net/pages/averylongurlwithoutanypunctuationbutstillmadeupofwords/&quot;&gt;https://www.patrickweaver.net/pages/averylongurlwithoutanypunctuationbutstillmadeupofwords/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Though including hyphen characters in the URL also creates clear breakpoints in modern browsers. I did have to wrap the URL above in a special scrollable &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; to prevent it from breaking the layout of the rest of the page.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;image-and-multimedia&quot;&gt;
&lt;h3&gt;Image and Multimedia&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area&quot;&gt;&lt;code&gt;&amp;lt;area&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio&quot;&gt;&lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img&quot;&gt;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map&quot;&gt;&lt;code&gt;&amp;lt;map&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track&quot;&gt;&lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video&quot;&gt;&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;area&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;map&amp;gt;&lt;/code&gt; are elements that I hadn’t been familiar with previously, even though I had made a few image map type websites in the days before CSS 3. It seems like an indication of how seldom they are used these days that the tools for debugging the boundaries of &lt;code&gt;&amp;lt;area&amp;gt;&lt;/code&gt; elements are hard to use. One &lt;code&gt;&amp;lt;area&amp;gt;&lt;/code&gt; border at a time will display while using tab focus, but styling the elements does not work (unless there is a &lt;code&gt;display&lt;/code&gt; hack I couldn’t figure out). It’s also somewhat strange that &lt;code&gt;&amp;lt;area&amp;gt;&lt;/code&gt; is essentially an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; with a shape.&lt;/p&gt;
&lt;p&gt;Here’s a &lt;code&gt;&amp;lt;map&amp;gt;&lt;/code&gt; (with &lt;code&gt;&amp;lt;area&amp;gt;&lt;/code&gt;s on each tag linking to MDN) of a handwritten HTML document I made for June 3rd’s &lt;a href=&quot;https://html.energy/events.html&quot;&gt;HTML Day Freewrite&lt;/a&gt; in SF.&lt;/p&gt;
&lt;figure&gt;
&lt;map name=&quot;park-html-map&quot;&gt;
    &lt;area shape=&quot;rect&quot; coords=&quot;38 20 150 55&quot; alt=&quot;HTML tag&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html&quot;&gt;
    &lt;area shape=&quot;rect&quot; coords=&quot;32 62 143 98&quot; alt=&quot;head tag&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head&quot;&gt;
    &lt;area shape=&quot;rect&quot; coords=&quot;32 107 170 140&quot; alt=&quot;title tag&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title&quot;&gt;
    &lt;area shape=&quot;rect&quot; coords=&quot;28 195 195 230&quot; alt=&quot;title closing tag&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title&quot;&gt;
    &lt;area shape=&quot;rect&quot; coords=&quot;25 240 178 275&quot; alt=&quot;head closing tag&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head&quot;&gt;
    &lt;area shape=&quot;rect&quot; coords=&quot;22 287 130 325&quot; alt=&quot;body tag&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body&quot;&gt;
    &lt;area shape=&quot;rect&quot; coords=&quot;201 285 297 320&quot; alt=&quot;body closing tag&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body&quot;&gt;
    &lt;area shape=&quot;rect&quot; coords=&quot;30 334 160 374&quot; alt=&quot;html tag&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html&quot;&gt;
&lt;/map&gt;
&lt;img usemap=&quot;#park-html-map&quot; src=&quot;https://patrickweaver.net/images/blog/html/park-html.jpg&quot; alt=&quot;A processed photograph of handwritten HTML in a notebook&quot; style=&quot;width: 300px;&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; is one of the classic multimedia HTML 5 tags so I’ve used it before, but thinking about it now, it’s surprising I don’t see them more often in the 2020s. I would guess that there are more in use than I thought, for example, the &lt;a href=&quot;https://bandcamp.com&quot;&gt;Bandcamp&lt;/a&gt; player uses a hidden &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; element even though the UI is a custom mix of &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s inside a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; oddly enough.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://patrickweaver.net/images/blog/html/audio-element.m4a&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;figcaption&gt;A recording of me reading the paragraph above. (Excuse my scratchy late night voice)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; is of course one of the first elements I used, but I was curious what properties could be used with it that I might not have heard of. &lt;code&gt;srcset&lt;/code&gt;, used for specifying multiple sizes of the same image to load at different screen resolutions, was one of those. I’ve created a demo below, which goes against the spirit of &lt;code&gt;srcset&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The image below has 3 images provided to its &lt;code&gt;srcset&lt;/code&gt; property:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A yellow background and a monospace font that is 300 pixels wide and should render when the page is less than 520 pixels wide&lt;/li&gt;
&lt;li&gt;A blue background and a serifed font that should render when the page is between 520 and 800 pixels wide&lt;/li&gt;
&lt;li&gt;A green background and a script font that should render when the page is greater than 800 pixels wide.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickweaver.net/images/blog/html/srcset-700.png&quot; srcset=&quot;https://patrickweaver.net/images/blog/html/srcset-300.png 300w, https://patrickweaver.net/images/blog/html/srcset-500.png 500w, https://patrickweaver.net/images/blog/html/srcset-700.png 700w&quot; sizes=&quot;(max-width: 520px) 300px, (max-width: 800px) 500px, 700px&quot; style=&quot;max-width: 700px; width: 100%;&quot; alt=&quot;A demo of the srcset property of the HTML &lt;img&gt; tag that loads different images at different sizes&quot; id=&quot;image-with-srcset&quot;&gt;&lt;/p&gt;
&lt;p&gt;I have tested it, and it does work, but it takes a lot of forethought to make sure that you will be able to load the correct image. Things like the browser’s cache, and scaled displays will change the behavior. If you want to see the effect you will probably need to load the page in a private window on a non scaled or “retina” display.&lt;/p&gt;
&lt;p&gt;It’s interesting that while both &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; are less widely used than &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; even after their introduction in HTML 5, the distribution of videos online seems to be centralized, mostly on YouTube, while audio is more decentralized, with people serving individual files when distributing things like podcasts, even though the audio player that is often used is not on the web.&lt;/p&gt;
&lt;figure&gt;
&lt;video controls=&quot;&quot; height=&quot;300&quot; muted=&quot;&quot; poster=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags-poster.jpg&quot; preload=&quot;metadata&quot; width=&quot;495&quot; id=&quot;audio-and-video-tags-video&quot;&gt;
    &lt;source src=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags.mp4&quot; type=&quot;video/mp4&quot;&gt;
    &lt;track default=&quot;&quot; kind=&quot;captions&quot; srclang=&quot;en&quot; src=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags.vtt&quot;&gt;
    &lt;a href=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags.mp4&quot;&gt; Download the video&lt;/a&gt;
&lt;/video&gt;
&lt;embed type=&quot;video/mp4&quot; src=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags.mp4&quot; width=&quot;495&quot; height=&quot;300&quot; title=&quot;A screen recording of my dev setup while writing the paragraph above.&quot; style=&quot;display: none&quot; id=&quot;audio-and-video-tags-embed&quot;&gt;
&lt;object type=&quot;video/mp4&quot; data=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags.mp4&quot; width=&quot;495&quot; height=&quot;300&quot; style=&quot;display: none;&quot; id=&quot;audio-and-video-tags-object&quot;&gt;
&lt;img src=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags-poster.jpg&quot; alt=&quot;a still of a screen recording&quot;&gt;
&lt;/object&gt;
&lt;figcaption&gt;A screen recording of my dev setup while writing the paragraph above. &lt;/figcaption&gt;
&lt;fieldset&gt;
    &lt;legend&gt;Select below to control if the video above is shown in a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; element:&lt;/legend&gt;
    &lt;div&gt;
        &lt;input type=&quot;radio&quot; id=&quot;audio-and-video-tags-video-option&quot; name=&quot;audio-and-video-tags&quot; value=&quot;video&quot; checked=&quot;&quot;&gt;
        &lt;label for=&quot;audio-and-video-tags-video-option&quot;&gt;&lt;code class=&quot;code-regular&quot;&gt;&amp;lt;video&amp;gt;&lt;/code&gt;&lt;/label&gt;
    &lt;/div&gt;
    &lt;div&gt;
        &lt;input type=&quot;radio&quot; id=&quot;audio-and-video-tags-embed-option&quot; name=&quot;audio-and-video-tags&quot; value=&quot;embed&quot;&gt;
        &lt;label for=&quot;audio-and-video-tags-embed-option&quot;&gt;&lt;code class=&quot;code-regular&quot;&gt;&amp;lt;embed&amp;gt;&lt;/code&gt;&lt;/label&gt;
    &lt;/div&gt;
    &lt;div&gt;
        &lt;input type=&quot;radio&quot; id=&quot;audio-and-video-tags-object-option&quot; name=&quot;audio-and-video-tags&quot; value=&quot;object&quot;&gt;
        &lt;label for=&quot;audio-and-video-tags-object-option&quot;&gt;&lt;code class=&quot;code-regular&quot;&gt;&amp;lt;object&amp;gt;&lt;/code&gt;&lt;/label&gt;
    &lt;/div&gt;
&lt;/fieldset&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    const viEl = document.getElementById(&#39;audio-and-video-tags-video&#39;);
    const emEl = document.getElementById(&#39;audio-and-video-tags-embed&#39;);
    const obEl = document.getElementById(&#39;audio-and-video-tags-object&#39;);
    document.querySelectorAll(&quot;input[name=&#39;audio-and-video-tags&#39;]&quot;).forEach((input) =&gt; {
        input.addEventListener(&#39;change&#39;, (event) =&gt; {
            const shouldDisplay = event.target.value;
            if (shouldDisplay === &#39;video&#39;) {
                viEl.style.display = &#39;block&#39;;
                emEl.style.display = &#39;none&#39;;
                obEl.style.display = &#39;none&#39;;
            } else if (shouldDisplay === &#39;embed&#39;) {
                viEl.style.display = &#39;none&#39;;
                emEl.style.display = &#39;block&#39;;
                obEl.style.display = &#39;none&#39;;
            } else if (shouldDisplay === &#39;object&#39;) {
                viEl.style.display = &#39;none&#39;;
                emEl.style.display = &#39;none&#39;;
                obEl.style.display = &#39;block&#39;;
            }
        });
    });
&lt;/script&gt;
&lt;noscript&gt;
    &lt;p&gt;The embedded video elements will only be toggleable when JavaScript is enabled.&lt;/p&gt;
&lt;/noscript&gt;
&lt;/figure&gt;
&lt;p&gt;One reason for this may be that there are still some quirks with &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements. For example, there is a &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; accompanying the video above, but when I initially tried creating the element with a self closing tag it did not render. Also, as I’m writing this, Safari does not support the video at all, likely because of the development server I am using not supporting the “Range” request header. I am curious to find out whether the hosted version of the site (on GitHub pages) will support playing the video in Safari. I thought that I might have a similar issue with &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt;, but that just turned out to be me not setting up my &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; build correctly for &lt;code&gt;.vtt&lt;/code&gt; files.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;embedded-content&quot;&gt;
&lt;h3&gt;Embedded Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed&quot;&gt;&lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe&quot;&gt;&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object&quot;&gt;&lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture&quot;&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/portal&quot;&gt;&lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source&quot;&gt;&lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is somewhat surprising that &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; have not been deprecated since most of their uses have been superseded by specific tags like &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;. Maybe eventually they will be, or they will just live on in the name of backwards compatibility.&lt;/p&gt;
&lt;p&gt;Speaking of &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; it’s also interesting that the very 90s name “iframe” (“inline frame”, not “iPod Frame” or something, and I was surprised to learn does render &lt;code&gt;display: inline&lt;/code&gt;) has survived while &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; was deprecated in HTML 5. I thought that &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; might be a way to force the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; &lt;code&gt;srcset&lt;/code&gt; example above to render the smaller images on a high DPI display, but even at 150px wide on my device the large image still renders:&lt;/p&gt;
&lt;figure&gt;
&lt;iframe width=&quot;150&quot; height=&quot;200&quot; src=&quot;https://patrickweaver.net/pages/srcset&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;An embed of a page with just the srcset example  from above at 150px wide.
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; is a tag that is, with &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; actually intended for layout tricks like the &lt;code&gt;srcset&lt;/code&gt; experiment I tried above, rendering different versions of an image in different situations. The image below should show an icon of a computer when used with a mouse/trackpad, or an icon of a phone when used with a touch screen (using the &lt;code&gt;pointer&lt;/code&gt; media query to determine), and should fall back to an image of a red circle with a line through it when neither media query is appropriate. The responsive design developer tools in your browser should trick it, though interestingly, not when inspecting an element.&lt;/p&gt;
&lt;picture&gt;
    &lt;source srcset=&quot;https://patrickweaver.net/images/blog/html/no.png&quot; media=&quot;(pointer:none)&quot; type=&quot;image/png&quot;&gt;
    &lt;source srcset=&quot;https://patrickweaver.net/images/blog/html/phone.png&quot; media=&quot;(pointer:coarse)&quot; type=&quot;image/png&quot;&gt;
    &lt;source srcset=&quot;https://patrickweaver.net/images/blog/html/computer.png&quot; media=&quot;(pointer:fine)&quot; type=&quot;image/png&quot;&gt;
    &lt;img src=&quot;https://patrickweaver.net/images/blog/html/no.png&quot; alt=&quot;an icon of a computer when used with a mouse/trackpad, an icon of a phone when used with a touch screen, or an image of a red circle with a line through otherwise&quot;&gt;
&lt;/picture&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; is one of the more mysterious sounding elements, and seems intended for the iPad-style link previews that I’ve seen implemented on some websites on hover, &lt;a href=&quot;https://web.dev/hands-on-portals/&quot;&gt;but also come with SPA-like performance benefits&lt;/a&gt;. It unfortunately is still an experimental feature and isn’t (as of 2023) enabled by default in any browsers. It seems that it &lt;a href=&quot;https://medium.com/swlh/portals-in-chrome-going-on-a-test-drive-66b16971fb19&quot;&gt;used to be an available experimental flag in Chrome&lt;/a&gt;, but today in 2023 it is not available at &lt;a href=&quot;chrome://flags&quot;&gt;chrome://flags&lt;/a&gt;. I’ll include one below in case it works for future readers! I did try it in an very old Chromium version I still had installed. &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; doesn’t seem to be part of the &lt;a href=&quot;https://www.w3.org/TR/2011/WD-html5-20110405/&quot;&gt;HTML 5 Spec&lt;/a&gt;, I’m not sure why it’s included in the MDN documentation.&lt;/p&gt;
&lt;figure&gt;
&lt;portal id=&quot;pw-links-portal&quot; src=&quot;https://www.patrickweaver.net/links/#links&quot;&gt;&lt;/portal&gt;
&lt;figcaption&gt;
A &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; element that should display the &lt;a href=&quot;https://www.patrickweaver.net/links/#links&quot;&gt;“Links”&lt;/a&gt; page on patrickweaver.net, if the feature is ever implemented in browsers.
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id=&quot;svg-and-mathml&quot;&gt;
&lt;h3&gt;SVG and MathML&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/svg&quot;&gt;&lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/math&quot;&gt;&lt;code&gt;&amp;lt;math&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is an interesting section of the documentation because it pairs one of the most ubiquitous elements with one of the most obscure. &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; elements are everywhere, especially that tools like Figma and Sketch have made them easy to prototype and export. Pairing them with &lt;code&gt;&amp;lt;math&amp;gt;&lt;/code&gt; in the documentation evokes a more artisanal, hand-crafted &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; that is relatively uncommon these days. I’ve gone hand-crafted here and drawn some &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; circles.&lt;/p&gt;
&lt;figure&gt;
&lt;svg viewBox=&quot;0 0 100 100&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; stroke=&quot;white&quot; fill=&quot;white&quot; width=&quot;200&quot;&gt;&lt;circle cx=&quot;50&quot; cy=&quot;50&quot; r=&quot;49&quot; fill=&quot;red&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;47&quot; cy=&quot;50&quot; r=&quot;42&quot; fill=&quot;orange&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;50&quot; cy=&quot;53&quot; r=&quot;35&quot; fill=&quot;yellow&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;55&quot; cy=&quot;52&quot; r=&quot;28&quot; fill=&quot;green&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;52&quot; cy=&quot;48&quot; r=&quot;21&quot; fill=&quot;indigo&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;54&quot; cy=&quot;50&quot; r=&quot;14&quot; fill=&quot;blue&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;52&quot; cy=&quot;55&quot; r=&quot;7&quot; fill=&quot;violet&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;
&lt;figcaption&gt;An svg that shows overlapping circles each with a different color of the rainbow.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;math&amp;gt;&lt;/code&gt; is really a wrapper element for other non HTML elements from the &lt;a href=&quot;https://www.w3.org/TR/MathML3/&quot; target=&quot;_blank&quot;&gt;MathML&lt;/a&gt; namespace, so I guess I don’t have to include every possible child element here. I’ll stick to something simple: &lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;inline&quot;&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/mrow&gt;&lt;/math&gt;
&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;scripting&quot;&gt;
&lt;h3&gt;Scripting&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas&quot;&gt;&lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript&quot;&gt;&lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I did a deep dive into &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;, specifically &lt;a href=&quot;https://doodles.patrickweaver.net/canvas-lines/&quot;&gt;drawing crisp lines&lt;/a&gt; in 2020 while I was at &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt;. One thing I wasn’t familiar with before reading the MDN docs though, was that there is a maximum size for a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element in each browser (though all modern browsers it is about 32 thousand pixels in each dimension). Below is a re-implementation of &lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt; with &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; (and &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;canvas id=&quot;marquee-canvas&quot; width=&quot;600&quot; height=&quot;100&quot; style=&quot;width: 300px; height: 50px;&quot;&gt;&lt;/canvas&gt;&lt;/p&gt;
&lt;p&gt;&lt;label for=&quot;toggleMarqueeButton&quot;&gt;Start or Stop the fake &lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt;:&lt;/label&gt;
&lt;button onClick=&quot;toggleMarquee()&quot; id=&quot;toggleMarqueeButton&quot;&gt;Stop&lt;/button&gt;&lt;/p&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    const canvasWidth = 300;
    const canvasHeight = 50;
    var dpr = window.devicePixelRatio || 1;
    var canvas = document.getElementById(&quot;marquee-canvas&quot;);
    var context = canvas.getContext(&quot;2d&quot;);
    canvas.width = canvasWidth*dpr;
    canvas.height = canvasHeight*dpr;
    context.scale(dpr, dpr);
    context.fillStyle = &quot;black&quot;;
    context.font = &quot;bold 40px Arial&quot;;

    const defaultTimer = 16.6;
    let timer = defaultTimer;
    let xVal = canvasWidth;
    const text = &quot;&lt;marquee&gt;&quot;;

    const toggleMarqueeButton = document.getElementById(&quot;toggleMarqueeButton&quot;);
    let marqueeStatus = true;
    function toggleMarquee() {
        toggleMarqueeButton.innerHTML = marqueeStatus ? &quot;Start&quot; : &quot;Stop&quot;;
        timer = marqueeStatus ? 0 : defaultTimer;
        marqueeStatus = !marqueeStatus;
        if (marqueeStatus) draw();
    }

    function drawText(text, x, y) {
        context.fillText(text, x, y);
    }

    function draw() {
        setTimeout(() =&gt; {
            if (xVal &lt; 0) {
                xVal = canvasWidth;
            }
            context.clearRect(0, 0, canvasWidth, canvasHeight);
            drawText(text, xVal, 42);
            drawText(text, xVal - canvasWidth, 42);
            xVal--;
            if (timer &gt; 0) draw();
        }, timer);
    }
    draw();
&lt;/script&gt;
&lt;p&gt;&lt;noscript&gt;&lt;p&gt;The &lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt; like animation above will only be render when JavaScript is enabled.&lt;/p&gt;&lt;/noscript&gt;&lt;/p&gt;
&lt;p&gt;There were a lot of tags that I expected to feel a little out of date when I started this post, but &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; wasn’t one of them, but now that I have written most of the post, and have created a few &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags, I’m realizing how little I use them now. For most of the code that I write, even though most of it is JavaScript, all of the “scripting” gets put into on &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag by build tools and it ends up feeling more like boilerplate than a “markup” tag.&lt;/p&gt;
&lt;p&gt;On the other hand, for most of the interactive websites I’ve made, I’ve rarely included a &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; tag with the exception of those minified and compiled single page apps where the user would likely get a blank page if it weren’t for the &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; tag (and even then, probably less often than I should have, though I’ve made sure to include one with every &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag here).&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;demarcating-edits&quot;&gt;
&lt;h3&gt;Demarcating Edits&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del&quot;&gt;&lt;code&gt;&amp;lt;del&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins&quot;&gt;&lt;code&gt;&amp;lt;ins&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another set of &lt;ins&gt;new to me&lt;/ins&gt; elements &lt;del&gt;I had never come across before&lt;/del&gt; &lt;ins&gt;on&lt;/ins&gt; reading the MDN documentation, &lt;code&gt;&amp;lt;del&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;ins&amp;gt;&lt;/code&gt; seem to be intended for &lt;del&gt;creating&lt;/del&gt; &lt;ins&gt;use in&lt;/ins&gt; a word processor. Reading about them led to yet another round of, “&lt;del&gt;I wonder if these are used in rendering Google Docs?&lt;/del&gt; &lt;ins&gt;Oh wait, it’s all &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;&lt;/ins&gt;.”&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;table-content&quot;&gt;
&lt;h3&gt;Table Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption&quot;&gt;&lt;code&gt;&amp;lt;caption&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col&quot;&gt;&lt;code&gt;&amp;lt;col&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup&quot;&gt;&lt;code&gt;&amp;lt;colgroup&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table&quot;&gt;&lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody&quot;&gt;&lt;code&gt;&amp;lt;tbody&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td&quot;&gt;&lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot&quot;&gt;&lt;code&gt;&amp;lt;tfoot&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th&quot;&gt;&lt;code&gt;&amp;lt;th&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead&quot;&gt;&lt;code&gt;&amp;lt;thead&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr&quot;&gt;&lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s not a new insight that &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; elements were overused for layout purposes on the early web, but an irony that I’m only realizing now is that recently, as the web has gotten more and more populated by data, that &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; elements have become rarely used, probably mostly due to their clunky default design, but probably also because one main goal of creating a web UI for the data that is otherwise probably stored in database tables, is to create a &lt;i&gt;different&lt;/i&gt; view of the same data.&lt;/p&gt;
&lt;p&gt;I don’t have any tabular data as this is an exclusively &lt;em&gt;document&lt;/em&gt; based blog post, but below I’ve added a table with some custom CSS and “data” on emoji that is likely only accurate for the Apple emoji set in 2023. I wasn’t familiar with &lt;code&gt;&amp;lt;col&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;colgroup&amp;gt;&lt;/code&gt; before, but I don’t know that there are many cases where I would use them rather than a more custom, non &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; design.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
&lt;table id=&quot;weird-table&quot;&gt;
    &lt;caption&gt;
        A table with a custom CSS
    &lt;/caption&gt;
    &lt;colgroup&gt;
        &lt;col&gt;
        &lt;col span=&quot;2&quot; class=&quot;low-numbers&quot;&gt;
        &lt;col span=&quot;2&quot;&gt;
        &lt;col span=&quot;2&quot; class=&quot;high-numbers&quot;&gt;
    &lt;/colgroup&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th scope=&quot;row&quot;&gt;Number&lt;/th&gt;
            &lt;th&gt;1&lt;/th&gt;
            &lt;th&gt;2&lt;/th&gt;
            &lt;th&gt;3&lt;/th&gt;
            &lt;th&gt;4&lt;/th&gt;
            &lt;th&gt;5&lt;/th&gt;
            &lt;th&gt;6&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;th scope=&quot;row&quot;&gt;Emoji&lt;/th&gt;
            &lt;td&gt;1️⃣&lt;/td&gt;
            &lt;td&gt;2️⃣&lt;/td&gt;
            &lt;td&gt;3️⃣&lt;/td&gt;
            &lt;td&gt;4️⃣&lt;/td&gt;
            &lt;td&gt;5️⃣&lt;/td&gt;
            &lt;td&gt;6️⃣&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th scope=&quot;row&quot;&gt;Emoji with leaves&lt;/th&gt;
            &lt;td rowspan=&quot;2&quot;&gt;🥭&lt;/td&gt;
            &lt;td&gt;🌱&lt;/td&gt;
            &lt;td&gt;🍂&lt;/td&gt;
            &lt;td&gt;🍀&lt;/td&gt;
            &lt;td&gt;🪴&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th scope=&quot;row&quot;&gt;Emoji with fruit&lt;/th&gt;
            &lt;td&gt;🍒&lt;/td&gt;
            &lt;td&gt;🫐&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th scope=&quot;row&quot;&gt;Emoji with slices&lt;/th&gt;
            &lt;td&gt;🍰&lt;/td&gt;
            &lt;td&gt;🥪&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;🥒&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;tfoot&gt;
        &lt;tr&gt;
            &lt;th scope=&quot;row&quot;&gt;Totals&lt;/th&gt;
            &lt;td&gt;4&lt;/td&gt;
            &lt;td&gt;4&lt;/td&gt;
            &lt;td&gt;3&lt;/td&gt;
            &lt;td&gt;3&lt;/td&gt;
            &lt;td&gt;2&lt;/td&gt;
            &lt;td&gt;1&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tfoot&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;forms&quot;&gt;
&lt;h3&gt;Forms&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist&quot;&gt;&lt;code&gt;&amp;lt;datalist&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset&quot;&gt;&lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input&quot;&gt;&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label&quot;&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend&quot;&gt;&lt;code&gt;&amp;lt;legend&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter&quot;&gt;&lt;code&gt;&amp;lt;meter&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup&quot;&gt;&lt;code&gt;&amp;lt;optgroup&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option&quot;&gt;&lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output&quot;&gt;&lt;code&gt;&amp;lt;output&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress&quot;&gt;&lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select&quot;&gt;&lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea&quot;&gt;&lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have usually used &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; and it’s associated elements when submitting the form would redirect to a new page, not for in-page user interaction, but either is valid. I would guess the form elements are probably the most important for accessibility so I will likely refer to the documentation whenever implementing one in the future.&lt;/p&gt;
&lt;p&gt;&lt;label for=&quot;everything-form&quot;&gt;Fill out this form and scroll down to get an emoji surprise below!&lt;/label&gt;&lt;/p&gt;
&lt;form id=&quot;everything-form&quot;&gt;
&lt;div id=&quot;eform-type&quot;&gt;
&lt;label for=&quot;type-choice&quot;&gt;Choose an emoji type:&lt;/label&gt;
&lt;input list=&quot;emoji-types&quot; id=&quot;type-choice&quot; name=&quot;type-choice&quot;&gt;
&lt;datalist id=&quot;emoji-types&quot;&gt;
&lt;option value=&quot;Fruit&quot;&gt;
&lt;/option&gt;&lt;option value=&quot;Dessert&quot;&gt;
&lt;/option&gt;&lt;option value=&quot;Drinks&quot;&gt;
&lt;/option&gt;&lt;option value=&quot;Animals&quot;&gt;
&lt;/option&gt;&lt;/datalist&gt;
&lt;/div&gt;
&lt;div id=&quot;eform-size&quot;&gt;
&lt;fieldset id=&quot;size-choice&quot;&gt;
&lt;legend&gt;Select a size:&lt;/legend&gt;
&lt;input type=&quot;radio&quot; name=&quot;size-choice&quot; id=&quot;small-size-choice&quot; value=&quot;small&quot;&gt;
&lt;label for=&quot;small-size-choice&quot;&gt;Small&lt;/label&gt;
&lt;input type=&quot;radio&quot; name=&quot;size-choice&quot; id=&quot;large-size-choice&quot; value=&quot;large&quot;&gt;
&lt;label for=&quot;large-size-choice&quot;&gt;Large&lt;/label&gt;
&lt;/fieldset&gt;
&lt;label for=&quot;eform-size-meter&quot;&gt;Size Meter:&lt;/label&gt;
&lt;meter id=&quot;eform-size-meter&quot; min=&quot;0&quot; max=&quot;2&quot; low=&quot;1&quot; high=&quot;2&quot; optimum=&quot;2&quot;&gt;
&lt;/meter&gt;
&lt;/div&gt;
&lt;div id=&quot;eform-color&quot;&gt;
&lt;label for=&quot;color-choice&quot;&gt;Color:&lt;/label&gt;
&lt;select id=&quot;color-choice&quot;&gt;
    &lt;option value=&quot;&quot; disabled=&quot;&quot; selected=&quot;&quot;&gt;--Please choose an option--&lt;/option&gt;
    &lt;optgroup label=&quot;Hot&quot;&gt;
        &lt;option&gt;Orange&lt;/option&gt;
        &lt;option&gt;Red&lt;/option&gt;
        &lt;option&gt;Yellow&lt;/option&gt;
    &lt;/optgroup&gt;
    &lt;optgroup label=&quot;Cool&quot;&gt;
        &lt;option&gt;Blue&lt;/option&gt;
        &lt;option&gt;Green&lt;/option&gt;
    &lt;/optgroup&gt;
&lt;/select&gt;
&lt;/div&gt;
&lt;div id=&quot;eform-dream&quot;&gt;
&lt;label for=&quot;dream-input&quot;&gt;Share a dream &lt;span id=&quot;dream-secret&quot;&gt;(secret!)&lt;/span&gt;&lt;/label&gt;
&lt;textarea id=&quot;dream-input&quot; cols=&quot;1&quot; rows=&quot;10&quot; wrap=&quot;hard&quot; name=&quot;dream-input&quot; placeholder=&quot;😶‍🌫️&quot;&gt;&lt;/textarea&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;label for=&quot;form-completion&quot; id=&quot;form-completion-label&quot;&gt;Form completion:&lt;/label&gt;
&lt;progress id=&quot;form-completion&quot; max=&quot;4&quot; value=&quot;0&quot;&gt;
       0/4
&lt;/progress&gt;
&lt;br&gt;
&lt;button id=&quot;eform-submit-button&quot; type=&quot;submit&quot; disabled=&quot;&quot;&gt;submit&lt;/button&gt;
&lt;/form&gt;
&lt;h4&gt;Your emoji:&lt;/h4&gt;
&lt;output id=&quot;everything-output&quot; form=&quot;everything-form&quot; for=&quot;type-choice small-size-choice large-size-choice color-choice&quot; name=&quot;everything-output&quot;&gt; &lt;/output&gt;
&lt;p&gt;&lt;noscript&gt;Without JavaScript enabled the form above will not be interactive.&lt;/noscript&gt;&lt;/p&gt;
&lt;script&gt;
    const eform = document.getElementById(&quot;everything-form&quot;);
    const eOutput = document.getElementById(&quot;everything-output&quot;);
    const typeChoice = document.getElementById(&quot;type-choice&quot;);
    const sizeChoice = document.getElementById(&quot;size-choice&quot;);
    const sizeMeter = document.getElementById(&quot;eform-size-meter&quot;)
    const colorChoice = document.getElementById(&quot;color-choice&quot;);
    const dreamInput = document.getElementById(&quot;dream-input&quot;);
    const formCompletion = document.getElementById(&quot;form-completion&quot;);
    const eformSubmit = document.getElementById(&quot;eform-submit-button&quot;);
    let selectedEmoji = &quot;❌&quot;;
    let selectedEmojiName = &quot;Form Incomplete&quot;;
    eform.addEventListener(&quot;submit&quot;, (event) =&gt; {
        event.preventDefault();
        const el = event?.target?.elements;
        const type = el?.[&quot;type-choice&quot;]?.value;
        const size = el?.[&quot;size-choice&quot;]?.value;
        const color = el?.[&quot;color-choice&quot;]?.value;
        const selected = selectEmoji(type, size, color);
        selectedEmoji = selected.emoji;
        selectedEmojiName = selected.name;
        eOutput.value = `${selectedEmoji}: ${selectedEmojiName}`;
    })
    const fields = {
        &quot;type-choice&quot;: 0,
        &quot;small-size-choice&quot;: 1,
        &quot;large-size-choice&quot;: 1,
        &quot;color-choice&quot;: 2,
        &quot;dream-input&quot;: 3,
    }
    const complete = [0, 0, 0, 0];
    eformSubmit.disabled = false;
    function fillForm(event) {
        const value = event?.target?.value
        const idShort = event?.target?.id?.slice(0, 3) ?? null;
        complete[fields[event?.target?.id]] = event?.target?.value ? 1 : 0;
        const newCompletion = complete.reduce((a,c) =&gt; a + c, 0);
        formCompletion.value = newCompletion
        formCompletion.innerHTML = `${newCompletion}/4`
    }
    typeChoice.addEventListener(&quot;input&quot;, fillForm);
    sizeChoice.addEventListener(&quot;input&quot;, fillForm);
    colorChoice.addEventListener(&quot;input&quot;, fillForm);
    dreamInput.addEventListener(&quot;input&quot;, fillForm);
    sizeChoice.addEventListener(&quot;input&quot;, (event) =&gt; {
        const idShort = event.target.id.slice(0, 3);
        const v = { sma: 1, lar: 2 }
        sizeMeter.value = v?.[idShort] ?? 0;
    })
    function selectEmoji(type, size, color) {
        if (type === &quot;Fruit&quot;) {
            if (color === &quot;Orange&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍊&quot;, name: &quot;tangerine&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍑&quot;, name: &quot;peach&quot; }
                }
            } else if (color === &quot;Red&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍓&quot;, name: &quot;Strawberry&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍎&quot;, name: &quot;Apple&quot; }
                }
            } else if (color === &quot;Yellow&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍋&quot;, name: &quot;Lemon&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍌&quot;, name: &quot;Banana&quot; }
                }
            } else if (color === &quot;Blue&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🫐&quot;, name: &quot;Blueberry&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🥥&quot;, name: &quot;Coconut&quot; }
                }
            } else if (color === &quot;Green&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🥝&quot;, name: &quot;Kiwi&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍐&quot;, name: &quot;Pear&quot; }
                }
            }
        } else if (type === &quot;Dessert&quot;) {
            if (color === &quot;Orange&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🥮&quot;, name: &quot;Mooncake&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🥧&quot;, name: &quot;Pie&quot; }
                }
            } else if (color === &quot;Red&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍫&quot;, name: &quot;Chocolate&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍧&quot;, name: &quot;Shaved Ice&quot; }
                }
            } else if (color === &quot;Yellow&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍨&quot;, name: &quot;Ice Cream&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍰&quot;, name: &quot;Cake&quot; }
                }
            } else if (color === &quot;Blue&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍬&quot;, name: &quot;Candy&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍭&quot;, name: &quot;Lollipop&quot; }
                }
            } else if (color === &quot;Green&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍡&quot;, name: &quot;Dango&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🥗&quot;, name: &quot;Dessert Salad&quot; }
                }
            }
        } else if (type === &quot;Drinks&quot;) {
            if (color === &quot;Orange&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍺&quot;, name: &quot;Beer&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍻&quot;, name: &quot;Two Beers&quot; }
                }
            } else if (color === &quot;Red&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍷&quot;, name: &quot;Wine&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🥤&quot;, name: &quot;Big Soda Cup&quot; }
                }
            } else if (color === &quot;Yellow&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🥂&quot;, name: &quot;Champagne Flute&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍹&quot;, name: &quot;Cocktail&quot; }
                }
            } else if (color === &quot;Blue&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🍶&quot;, name: &quot;Sake&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍶&quot;, name: &quot;Sake&quot; }
                }
            } else if (color === &quot;Green&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🧃&quot;, name: &quot;Juice Box&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🍾&quot;, name: &quot;Champagne Bottle&quot; }
                }
            }
        } else if (type === &quot;Animals&quot;) {
            if (color === &quot;Orange&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🦊&quot;, name: &quot;Fox&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🐅&quot;, name: &quot;Tiger&quot; }
                }
            } else if (color === &quot;Red&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🦀&quot;, name: &quot;Crab&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🦑&quot;, name: &quot;Squid&quot; }
                }
            } else if (color === &quot;Yellow&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🐝&quot;, name: &quot;Bee&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🐱&quot;, name: &quot;Cat&quot; }
                }
            } else if (color === &quot;Blue&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🦋&quot;, name: &quot;Butterfly&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🐋&quot;, name: &quot;Whale&quot; }
                }
            } else if (color === &quot;Green&quot;) {
                if (size === &quot;small&quot;) {
                    return { emoji: &quot;🐸&quot;, name: &quot;Frog&quot; }
                } else if (size === &quot;large&quot;) {
                    return { emoji: &quot;🐊&quot;, name: &quot;Crocodile&quot; }
                }
            }
        }
        return { emoji:&quot;❌&quot;, name: &quot;Invalid Submission&quot; };
    }
&lt;/script&gt;
&lt;/section&gt;
&lt;section id=&quot;interactive-elements&quot;&gt;
&lt;h3&gt;Interactive Elements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details&quot;&gt;&lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog&quot;&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary&quot;&gt;&lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; combination is probably the most common, “You don’t need JavaScript to…” element (another &lt;a href=&quot;https://catskull.net/html.html&quot; target=&quot;_blank&quot;&gt;popular blog&lt;/a&gt; post making this point prompted me to finally finish this one). Making this kind of expand/collapse interactive view was one of the first things I used JavaScript for (it was probably jQuery). I remember it being somewhat confusing at the time (2006?), so it’s funny to me to see it so easy with just a couple HTML tags.&lt;/p&gt;
&lt;details&gt;
  &lt;summary&gt;Speculation about &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;&lt;/summary&gt;
  &lt;div&gt;I do wonder if &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; were being considered for the HTML spec today, if they would be withdrawn in favor of a similar effect using CSS.&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;I wasn’t aware of the HTML only &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; before using it here. It requires JavaScript to be useful beyond just its stylistic impact, but it’s an interesting way to create interactions that are more custom than &lt;code&gt;alert()&lt;/code&gt;, but still simple, because while &lt;code&gt;alert()&lt;/code&gt; is styled by the browser &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; can be styled with CSS.&lt;/p&gt;
&lt;dialog open=&quot;&quot;&gt;
  &lt;p&gt;Guess who?!&lt;/p&gt;
  &lt;form method=&quot;dialog&quot;&gt;
    &lt;button&gt;Reveal!&lt;/button&gt;
  &lt;/form&gt;
&lt;/dialog&gt;
&lt;pre id=&quot;palm-sheriff&quot; role=&quot;img&quot; aria-label=&quot;A sheriff whose body is made of palm trees.&quot;&gt;
       🤠
     🌴🌴🌴
   🌴  🌴  🌴
   👇 🌴 🌴 👇
　 　 🌴 🌴
　 　 🌴 🌴
　 　 👢 👢
  the palm sheriff
&lt;/pre&gt;
&lt;/section&gt;
&lt;section id=&quot;web-components&quot;&gt;
&lt;h3&gt;Web Components&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot&quot;&gt;&lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Web components had been on a (long) list of “things I should learn more about” for a while, so I was glad to get to this section of the MDN documentation. I was surprised that JavaScript (and &lt;code&gt;Class&lt;/code&gt; syntax at that) was necessary to get web components to render, but after reading more about the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM&quot;&gt;Shadow DOM&lt;/a&gt;, it makes sense, because HTML has no built in way to encapsulate styles, though it does seem to be somewhat similar conceptually to &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;
&lt;p&gt;I’ve used web components below to create a table of contents for this post below, though I’m not making the best use of them. One thing I couldn’t quickly figure out how to do, when compared to components from frontend frameworks, is pass in properties that are themselves used as properties of elements in the child template, so in the list below I have to pass a full &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element into each child (which renders the &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;), to be able to set the correct &lt;code&gt;href&lt;/code&gt; property. Someday after finishing this post I will read more about web components and maybe figure out something I’m missing now.&lt;/p&gt;
&lt;template id=&quot;table-of-contents-item&quot;&gt;
    &lt;li&gt;&lt;slot name=&quot;emoji&quot;&gt;✅&lt;/slot&gt; &lt;slot name=&quot;table-of-contents-link&quot;&gt;End of List&lt;/slot&gt;
&lt;/li&gt;&lt;/template&gt;
&lt;template id=&quot;table-of-contents&quot;&gt;
    &lt;style&gt;
        .table-of-contents {
            border: 2px solid var(--text-2);
            border-radius: 5px;
            padding: 5px 10px;
            background: rgb(255,252,234);
            background: linear-gradient(324deg, rgba(255,252,234,1) 0%, rgba(249,239,255,1) 21%, rgba(255,239,231,1) 100%);
            display: inline-block;
            min-width: 300px;
            width: 80%;
            margin: 0 10%;
        }
        .table-of-contents h4 {
            margin: 0.5rem 0;
            font-size: 1.6rem;
            line-height: 1.8rem;
        }
    &lt;/style&gt;
    &lt;div class=&quot;table-of-contents&quot;&gt;
        &lt;h4&gt;Table of Contents&lt;/h4&gt;
        &lt;ul&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;📜&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#main-root&quot;&gt;Main Root&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;📑&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#document-metadata&quot;&gt;Document Metadata&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;📂&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#sectioning-root&quot;&gt;Sectioning Root&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🗂&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#content-sectioning&quot;&gt;Content Sectioning&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🔡&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#text-content&quot;&gt;Text Content&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;📝&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#inline-text-semantics&quot;&gt;Inline Text Semantics&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🖼&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#image-and-multimedia&quot;&gt;Image and Multimedia&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🪟&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#embedded-content&quot;&gt;Embedded Content&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🧮&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#svg-and-mathml&quot;&gt;SVG and MathML&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🪄&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#scripting&quot;&gt;Scripting&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🛠&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#demarcating-edits&quot;&gt;Demarcating Edits&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;📦&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#table-content&quot;&gt;Table Content&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;📋&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#forms&quot;&gt;Forms&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🛝&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#interactive-elements&quot;&gt;Interactive Elements&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;🕸&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#web-components&quot;&gt;Web Components&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
                &lt;span slot=&quot;emoji&quot;&gt;📠&lt;/span&gt;
                &lt;a slot=&quot;table-of-contents-link&quot; href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/&quot;&gt;&lt;/a&gt;
            &lt;/table-of-contents-item&gt;
            &lt;table-of-contents-item&gt;
            &lt;/table-of-contents-item&gt;
        &lt;/ul&gt;
    &lt;/div&gt;
&lt;/template&gt;
&lt;script&gt;
    customElements.define(&quot;table-of-contents-item&quot;, class extends HTMLElement {
        constructor() {
            super();
            let template = document.getElementById(&quot;table-of-contents-item&quot;);
            let templateContent = template.content;
            const shadowRoot = this.attachShadow({ mode: &quot;open&quot; });
            shadowRoot.appendChild(templateContent.cloneNode(true));
        }
    });
    customElements.define(&quot;table-of-contents&quot;, class extends HTMLElement {
        constructor() {
            super();
            let template = document.getElementById(&quot;table-of-contents&quot;);
            let templateContent = template.content;
            const shadowRoot = this.attachShadow({ mode: &quot;open&quot; });
            shadowRoot.appendChild(templateContent.cloneNode(true));
        }
    });
&lt;/script&gt;
&lt;p&gt;&lt;table-of-contents&gt;&lt;/table-of-contents&gt;&lt;/p&gt;
&lt;p&gt;&lt;noscript&gt;Without JavaScript enabled components above will not render.&lt;/noscript&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;obsolete-and-deprecated-elements&quot;&gt;
&lt;h3&gt;Obsolete and Deprecated Elements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/acronym&quot;&gt;&lt;code&gt;&amp;lt;acronym&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/big&quot;&gt;&lt;code&gt;&amp;lt;big&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/center&quot;&gt;&lt;code&gt;&amp;lt;center&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;content&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dir&quot;&gt;&lt;code&gt;&amp;lt;dir&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/font&quot;&gt;&lt;code&gt;&amp;lt;font&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/frame&quot;&gt;&lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/frameset&quot;&gt;&lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/image&quot;&gt;&lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/marquee&quot;&gt;&lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menuitem&quot;&gt;&lt;code&gt;&amp;lt;menuitem&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nobr&quot;&gt;&lt;code&gt;&amp;lt;nobr&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noembed&quot;&gt;&lt;code&gt;&amp;lt;noembed&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noframes&quot;&gt;&lt;code&gt;&amp;lt;noframes&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param&quot;&gt;&lt;code&gt;&amp;lt;param&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/plaintext&quot;&gt;&lt;code&gt;&amp;lt;plaintext&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rb&quot;&gt;&lt;code&gt;&amp;lt;rb&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rtc&quot;&gt;&lt;code&gt;&amp;lt;rtc&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;shadow&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strike&quot;&gt;&lt;code&gt;&amp;lt;strike&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tt&quot;&gt;&lt;code&gt;&amp;lt;tt&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/xmp&quot;&gt;&lt;code&gt;&amp;lt;xmp&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are of course a few &lt;big&gt;deprecated&lt;/big&gt; &lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt; elements, so why not &lt;font color=&quot;#2FCF6F&quot; face=&quot;times&quot; size=&quot;5&quot;&gt;include&lt;/font&gt; them here at the end.&lt;/p&gt;
&lt;p&gt;Everyone’s favorite is of course,&lt;/p&gt;
&lt;center&gt;&lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt;&lt;/center&gt;
&lt;p&gt;&lt;marquee&gt;Hello!&lt;/marquee&gt;&lt;/p&gt;
&lt;p&gt;Many of the elements, like &lt;code&gt;&amp;lt;big&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;acronym&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;font&amp;gt;&lt;/code&gt; (used above), &lt;code&gt;&amp;lt;nobr&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;strike&amp;gt;&lt;/code&gt; are examples of layout related functionality that has been moved to CSS rather than &lt;strike&gt;have HTML handle it&lt;/strike&gt;. The narrow box below has an example of &lt;code&gt;&amp;lt;nobr&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div id=&quot;nobr-box&quot;&gt;
    &lt;nobr&gt;This sentence is wrapped in a &lt;code&gt;&amp;lt;nobr&amp;gt;&lt;/code&gt;.&lt;/nobr&gt;
    &lt;p&gt;This sentence is wrapped in a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;.&lt;/p&gt;
    &lt;p style=&quot;white-space: nowrap;&quot;&gt;This sentence is wrapped in a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; with &lt;code&gt;style=&quot;white-space: nowrap;&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;There are some other strange ones as well though. I couldn’t implement &lt;code&gt;&amp;lt;content&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;shadow&amp;gt;&lt;/code&gt; here because they were never fully implemented part of the web components spec. I’ll add some below anyway though.&lt;/p&gt;
&lt;figure&gt;
&lt;shadow&gt;&lt;/shadow&gt;
&lt;content&gt;&lt;/content&gt;
&lt;figcaption&gt;&lt;code&gt;&amp;lt;shadow&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;content&amp;gt;&lt;/code&gt; elements (that won’t render).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;And &lt;code&gt;&amp;lt;dir&amp;gt;&lt;/code&gt; is a very &lt;i&gt;webserver&lt;/i&gt; version of &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; for listing directories. Since my Eleventy static site generator does use directories here’s a list:&lt;/p&gt;
&lt;dir&gt;
    &lt;li&gt;&lt;a href=&quot;https://patrickweaver.net/blog&quot;&gt;/blog&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://patrickweaver.net/css&quot;&gt;/css&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://patrickweaver.net/images&quot;&gt;/images&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://patrickweaver.net/notes&quot;&gt;/notes&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://patrickweaver.net/pages&quot;&gt;/pages&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://patrickweaver.net/portfolio&quot;&gt;/portfolio&lt;/a&gt;&lt;/li&gt;
&lt;/dir&gt;
&lt;p&gt;Some of deprecated elements won’t render without some extra work, for example &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; are designed to be used instead of a &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, for I guess some kind of collage web page made up of other pages. I’ve ironically only been able to use them inside of an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; below.&lt;/p&gt;
&lt;figure&gt;
&lt;iframe width=&quot;300&quot; height=&quot;300&quot; src=&quot;https://patrickweaver.net/pages/frameset&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;An embed of a &lt;a href=&quot;https://patrickweaver.net/pages/frameset&quot; target=&quot;_blank&quot;&gt;page&lt;/a&gt; with &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; elements that itself embeds the Links page of this site.
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I expected that &lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt; wouldn’t work since the MDN documentation says:&lt;/p&gt;
&lt;blockquote cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/image&quot;&gt;
    This does not appear to have been part of any formal specification. It was mentioned in HTML+ Discussion Document - Dave Raggett, November 8, 1993 (Section 5.9 - Images), but was not part of the first revision of HyperText Markup Language Specification - 2.0 in 1994.
&lt;/blockquote&gt;
&lt;p&gt;But it does seem to work in some browsers in 2023, though this might be just a fallback for potential confusion with &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;image src=&quot;/images/blog/html/fax.png&quot; width=&quot;100&quot;&gt;An icon of a fax machine&lt;/image&gt;&lt;/p&gt;
&lt;p&gt;One of the more interesting browser compatibility stories of the deprecated elements is &lt;code&gt;&amp;lt;menuitem&amp;gt;&lt;/code&gt;, which supposedly was &lt;a href=&quot;https://caniuse.com/?search=menuitem&quot;&gt;partially supported in Firefox versions 8 - 84&lt;/a&gt;, but I couldn’t get &lt;a href=&quot;https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_menuitem&quot;&gt;an example&lt;/a&gt; to work in any of the older versions I installed locally (even with editing &lt;code&gt;about:config&lt;/code&gt;). The examples imply that the &lt;code&gt;&amp;lt;menu&amp;gt;&lt;/code&gt; (of &lt;code&gt;type=context&lt;/code&gt;) and child &lt;code&gt;&amp;lt;menuitem&amp;gt;&lt;/code&gt; elements shouldn’t render until the element whose &lt;code&gt;contextmenu&lt;/code&gt; property is right clicked, but as you can see below, the items always render:&lt;/p&gt;
&lt;div id=&quot;menu-button&quot; contextmenu=&quot;menuitem-example&quot;&gt;Open Menu&lt;/div&gt;
&lt;menu type=&quot;context&quot; id=&quot;menuitem-example&quot;&gt;
    &lt;menuitem type=&quot;radio&quot; radiogroup=&quot;group1&quot;&gt;Radio Button 1
    &lt;menuitem type=&quot;radio&quot; radiogroup=&quot;group1&quot;&gt;Radio Button 2
&lt;/menu&gt;
&lt;p&gt;It’s surprising that &lt;code&gt;&amp;lt;noembed&amp;gt;&lt;/code&gt;, essentially a &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; style fallback, was deprecated while &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; was kept in the spec. Here’s my &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; from above, but this time with a &lt;code&gt;&amp;lt;noembed&amp;gt;&lt;/code&gt; child. There is also &lt;code&gt;&amp;lt;noframes&amp;gt;&lt;/code&gt; which I included above with the &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;figure&gt;
&lt;embed type=&quot;video/mp4&quot; src=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags.mp4&quot; width=&quot;165&quot; height=&quot;100&quot; title=&quot;A screen recording of my dev setup while writing the paragraph above.&quot; id=&quot;audio-and-video-tags-embed-dep&quot;&gt;&lt;noembed&gt;An embed of a screen recording of my dev setup while writing the paragraph above.&lt;/noembed&gt;
&lt;figcaption&gt;An &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; with a &lt;code&gt;&amp;lt;noembed&amp;gt;&lt;/code&gt; tag child.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It’s unclear why &lt;code&gt;&amp;lt;param&amp;gt;&lt;/code&gt; ever existed instead of just using attributes on the &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; element. When I started this blog post &lt;a href=&quot;https://web.archive.org/web/20220330125752/https://developer.mozilla.org/en-US/docs/Web/HTML/Element&quot;&gt;it was listed in the “Embedding Content” section&lt;/a&gt;, but by the time I finished it was deprecated. Here is one below:&lt;/p&gt;
&lt;figure&gt;
&lt;object type=&quot;video/mp4&quot; data=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags.mp4&quot; width=&quot;165&quot; height=&quot;100&quot; id=&quot;audio-and-video-tags-object-dep&quot;&gt;
&lt;img src=&quot;https://patrickweaver.net/images/blog/html/audio-and-video-tags-poster.jpg&quot; alt=&quot;a still of a screen recording&quot;&gt;
&lt;param name=&quot;cool-param&quot;&gt;
&lt;/object&gt;
&lt;figcaption&gt;An &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; with a &lt;code&gt;&amp;lt;param&amp;gt;&lt;/code&gt; tag child.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;plaintext&amp;gt;&lt;/code&gt; is another weird one, and notable among the deprecated elements in that it works. It is another one I am going to have to wrap in an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; because otherwise the rest of the page would be rendered as the source code:&lt;/p&gt;
&lt;figure&gt;
&lt;iframe width=&quot;350&quot; height=&quot;400&quot; src=&quot;https://patrickweaver.net/pages/plaintext-element&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;An embed of a &lt;a href=&quot;https://patrickweaver.net/pages/plaintext-element/&quot; target=&quot;_blank&quot;&gt;page&lt;/a&gt; with a &lt;code&gt;&amp;lt;plaintext&amp;gt;&lt;/code&gt; element.
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;However, &lt;code&gt;&amp;lt;xmp&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;tt&amp;gt;&lt;/code&gt;, used in a similar way, have closing tags &lt;xmp style=&quot;max-width: 100%; overflow: scroll; padding: 0.5rem 0; border: 1px solid #aaa; background-color: #eee;&quot;&gt;
so they can be used here for displaying source code
&lt;/xmp&gt; or text in a monospace font&lt;tt&gt;
&lt;span id=&quot;tt-element&quot;&gt;as it would be shown on a teletype&lt;/span&gt;
&lt;/tt&gt; without an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A few of the deprecated elements have just been replaced by newer additions to the spec. &lt;code&gt;&amp;lt;rb&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;rtc&amp;gt;&lt;/code&gt; were used for displaying pronunciation of East Asian characters, but it seems like the spec was simplified. Here is the example from MDN:&lt;/p&gt;
&lt;p&gt;&lt;ruby xml:lang=&quot;zh-Hant&quot; style=&quot;ruby-position: under;&quot;&gt;&lt;rbc&gt;&lt;rb&gt;馬&lt;/rb&gt;&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;mǎ&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;rb&gt;來&lt;/rb&gt;&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;lái&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;rb&gt;西&lt;/rb&gt;&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;xī&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;rb&gt;亞&lt;/rb&gt;&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;yà&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/rbc&gt;&lt;rtc xml:lang=&quot;en&quot; style=&quot;ruby-position: over;&quot;&gt;&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;Malaysia&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/rtc&gt;&lt;/ruby&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;other-elements&quot;&gt;
&lt;h3&gt;Other Elements&lt;/h3&gt;
&lt;p&gt;I consulted a few other lists of HTML tags after realizing that the &lt;code&gt;&amp;lt;!-- comment --&amp;gt;&lt;/code&gt; tag wasn’t in my list from MDN (there’s one below this paragraph)&lt;/p&gt;
&lt;!-- The only reference to HTML comments on MDN seems to be in the web APIs section: https://developer.mozilla.org/en-US/docs/Web/API/Comment --&gt;
&lt;p&gt;An &lt;a href=&quot;https://web.archive.org/web/20130520111045/https://developer.mozilla.org/en-US/docs/Web/HTML/Element&quot; target=&quot;_blank&quot;&gt;older list from MDN in 2013&lt;/a&gt; has elements like &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt;, and for accessibility reasons, &lt;q cite=&quot;https://web.archive.org/web/20130520111045/https://developer.mozilla.org/en-US/docs/Web/HTML/Element&quot;&gt;almost all browsers currently ignore this element.&lt;/q&gt; I’ll add one hidden below if you can find a very old browser that supports it:&lt;/p&gt;
&lt;div style=&quot;display: none&quot; id=&quot;hidden-blink&quot;&gt;
    &lt;blink&gt;I am in a &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; tag (but probably not blinking).&lt;/blink&gt;
&lt;/div&gt;
&lt;div id=&quot;show-blink-button-container&quot;&gt;
    &lt;button onclick=&quot;(() =&gt; document.getElementById(&#39;hidden-blink&#39;).style.display = &#39;block&#39;)();&quot;&gt;Show &lt;code class=&quot;code-regular&quot;&gt;&amp;lt;blink&amp;gt;&lt;/code&gt;&lt;/button&gt;
&lt;/div&gt;
&lt;p&gt;Also on the list is &lt;code&gt;&amp;lt;spacer&amp;gt;&lt;/code&gt;, which doesn’t work in modern browsers and,&lt;/p&gt;
&lt;p&gt;&lt;spacer type=&quot;horizontal&quot; size=&quot;30&quot;&gt;&lt;/spacer&gt;&lt;/p&gt;
&lt;p&gt;would now be done&lt;/p&gt;
&lt;div style=&quot;height: 30px;&quot;&gt;&lt;/div&gt;
&lt;p&gt;with CSS.&lt;/p&gt;
&lt;p&gt;Then there’s others like &lt;code&gt;&amp;lt;bgsound&amp;gt;&lt;/code&gt; (&lt;a href=&quot;https://web.archive.org/web/20131004002320/https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bgsound&quot; target=&quot;_blank&quot;&gt;2013 docs&lt;/a&gt;) that seems to have never been implemented (except maybe in Netscape?), but alludes to a vision for a much more &lt;em&gt;multimedia&lt;/em&gt; enabled web documents, and &lt;code&gt;&amp;lt;isindex&amp;gt;&lt;/code&gt; (&lt;a href=&quot;https://web.archive.org/web/20130916195848/https://developer.mozilla.org/en-US/docs/Web/HTML/Element/isindex&quot; target=&quot;_blank&quot;&gt;2013 docs&lt;/a&gt;) which seems like it is from a world where JavaScript doesn’t exist.&lt;/p&gt;
&lt;p&gt;Another attempt at &lt;code&gt;&amp;lt;xmp&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;tt&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; was&lt;/p&gt;
&lt;p&gt;&lt;listing&gt;&lt;span id=&quot;listing-child&quot;&gt;listing&lt;/span&gt;&lt;/listing&gt;&lt;/p&gt;
&lt;p&gt;I’m surprised it seems to still work!&lt;/p&gt;
&lt;p&gt;And of course &lt;code&gt;&amp;lt;applet&amp;gt;&lt;/code&gt;, but I’m not even going to try with that one.&lt;/p&gt;
&lt;/section&gt;
&lt;h4&gt;Footnotes&lt;/h4&gt;
&lt;ol class=&quot;footnotes&quot;&gt;
    &lt;li id=&quot;footnote-1&quot;&gt;&lt;aside&gt;Which I am just now discovering there is no native HTML implementation for!&lt;/aside&gt; &lt;a href=&quot;https://patrickweaver.net/blog/a-blog-post-with-every-html-element/#footnote-1-link&quot;&gt;↑ Return to post&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;footer role=&quot;contentinfo&quot;&gt;
    Written between &lt;time datetime=&quot;2022-02&quot;&gt;February 2022&lt;/time&gt; and &lt;time datetime=&quot;2023-08&quot;&gt;August 2023&lt;/time&gt;.
&lt;/footer&gt;
</description><pubDate>Thu, 03 Aug 2023 00:00:00 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/a-blog-post-with-every-html-element/</guid>
    </item>
    <item>
      <title>Alphabetic Internet Time: A Time Zone for the Internet</title>
      <link>https://patrickweaver.net/blog/alphabetic-internet-time/</link><description>&lt;p&gt;In the Fall of 2020 I &lt;a href=&quot;https://www.patrickweaver.net/blog/a-recurse-center-remote-batch/&quot;&gt;participated in a 12 week batch&lt;/a&gt; at the &lt;a href=&quot;https://recurse.com&quot;&gt;Recurse Center&lt;/a&gt;. While I had a lot of previous experience &lt;em&gt;working&lt;/em&gt; remotely, both before and during the pandemic, RC was the first time where I was communicating with people in more than 1 or 2 other time zones. I now work at a fully remote company, mostly spread over the 4(ish) U.S. time zones, but with a few people permanently or temporarily in other places.&lt;/p&gt;
&lt;h4&gt;Communicating across time zones&lt;/h4&gt;
&lt;p&gt;RC when I was there (mostly I would guess because their software was designed and configured for in person retreats in NYC) operated for the most part in Eastern Time, although I see from my limited participation in the community as an alum that this may have shifted, especially thanks to &lt;a href=&quot;https://zulip.com/help/format-your-message-using-markdown#global-times&quot;&gt;Zulip’s time-zone-relative timestamp support&lt;/a&gt;. RC had transitioned to hosting Recursers remotely a few months before my batch, which is how they continue to operate now in Summer 2022. At my current job we often default to either Eastern Time or Pacific Time, but some people will also just use their local time and let others figure it out.&lt;/p&gt;
&lt;p&gt;Out of politeness and miscalculation-anxiety reducing redundancy I often will use multiple time zones, for example when proposing a meeting I might say, &lt;em&gt;“Let’s meet at 🗽 12:30 PM ET / 🏔 10:30 AM MT / 🌁 9:30 AM PT,”&lt;/em&gt; (I’m still searching for a good Central Time emoji, 🛣 🌽 🌪 are the best I’ve got), but time zone based confusion often takes more thinking than it should.&lt;/p&gt;
&lt;p&gt;(Side note, it bewilders me that Slack still doesn’t have something similar to Zulip’s &lt;em&gt;“you write in your time zone, they read in their time zone”&lt;/em&gt; timestamp support in 2022. 🙃)&lt;/p&gt;
&lt;h4&gt;Time zones are pretty confusing, DST makes them worse&lt;/h4&gt;
&lt;p&gt;Something that probably doesn’t cause very much confusion for most other people, but I can’t help but consistently notice is that most people exclusively use ”EST/CST/MST/PST” throughout the year, even during the Spring, Summer, and Fall, when most of the U.S. is observing Daylight Saving Time. This linguistic pattern (which I assume is somewhat caused by “EST” seeming like an abbreviation for “Eastern”) makes me especially curious what the commonly used abbreviations will be if the U.S. &lt;a href=&quot;https://web.archive.org/web/20220315222718/https://www.nytimes.com/2022/03/15/us/politics/daylight-saving-time-senate.html&quot;&gt;switches to permanent DST&lt;/a&gt;, and how people in Arizona, &lt;a href=&quot;https://en.wikipedia.org/wiki/Time_in_Arizona&quot;&gt;which mostly does not observe DST&lt;/a&gt; understand someone in Denver saying, “Let’s meet at 10:30 MST on August 14.” I would imagine them thinking something like, ”😫.”&lt;/p&gt;
&lt;p&gt;Even Google search seems to get this wrong as searching &lt;a href=&quot;https://google.com/search?q=current%20time%20MST&quot;&gt;“current time MST”&lt;/a&gt; at 8:39 PM EDT in NYC in August shows ”6:39 Mountain Time (MT)”, though I also wonder what the same search would show in Arizona.&lt;/p&gt;
&lt;h4&gt;A time zone for the internet&lt;/h4&gt;
&lt;p&gt;All of this leads to my Alphabetic Internet Time (AIT), which I consider &lt;em&gt;“A Great Idea That Will Never Work.”&lt;/em&gt; AIT is essentially UTC, but with the letters A through X of the (English) alphabet replacing the hour digits. Midnight UTC is A:00, Noon UTC is M:00, 1:34 PM UTC is N:34. Just like UTC, these times are not local, S:00 is the same moment everywhere (18:00 UTC). For someone in New York in August 2022 observing EDT, it is 2:00 PM, for the same person in January 2023 S:00 is 1:00 PM. For someone in San Francisco in August, S:00 is 11:00 AM.&lt;/p&gt;
&lt;p&gt;This may all sound confusing, but it’s mostly because AIT is not really useful at all for knowing what time it is somewhere else. The main goal of AIT is reducing the amount of time you spend thinking about what time it is somewhere else (though it is likely still polite to make sure you’re not trying to schedule something in the middle of someone else’s night). AIT starts to make sense when you think about people in different time zones communicating about something that will happen online. As someone who works normal business hours in NYC, my work day goes from N:00 (9 AM EDT) to V:00 (5 PM EDT), with lunch usually at Q:30 (though during the Winter it would be O:00 to W:00). If I’m planning with someone in San Francisco who also works normal business hours (Q:00 - A:00) and we want at least an hour, we could easily see that any time between R:00 and V:00 would work, and we would both know what time that was for us.&lt;/p&gt;
&lt;p&gt;If AIT were to work it wouldn’t rely on people manually calculating the current time, they would need clocks that display AIT like this reference implementation I made: &lt;a href=&quot;https://doodles.patrickweaver.net/ait/&quot; target=&quot;_blank&quot;&gt;doodles.patrickweaver.net/ait/&lt;/a&gt;&lt;/p&gt;
&lt;figure style=&quot;text-align: center; width: 100%;&quot;&gt;
  &lt;iframe id=&quot;ait-clock&quot; style=&quot;border: 1px solid black; margin: 1rem auto; height: 400px;&quot; title=&quot;Alphabetic Internet Time Clock&quot; width=&quot;400&quot; height=&quot;400&quot; src=&quot;https://doodles.patrickweaver.net/ait/&quot;&gt;
  &lt;/iframe&gt;
  &lt;figcaption&gt;A reference implementation of an AIT clock at: &lt;a href=&quot;https://doodles.patrickweaver.net/ait/&quot; target=&quot;_blank&quot;&gt;doodles.patrickweaver.net/ait/&lt;/a&gt; &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4&gt;Why not just use UTC?&lt;/h4&gt;
&lt;p&gt;People who are already used to using UTC might look at AIT and think that it’s just a confusing extra layer on top of a system that works pretty well, but in my opinion, moving whole communities of people to UTC would likely be very difficult because doing so asks them to do the same mental math as coordinating between time zones, but often with larger numbers.&lt;/p&gt;
&lt;p&gt;Even after changing my phone to use a 24 hour clock for weeks while spending time in countries where that is common I still found myself translating 16:00 to &lt;em&gt;“16 - 2 - 10 = 4 PM”&lt;/em&gt; in my head. My guess is that for most people it would be easier to convert between two separate concepts, their local time with numbers, and AIT with letters, but I may be wrong. The other advantage is clarity, even if the known best practice is to use UTC, a message like “Let’s meet at 15:30” can still be unclear, potentially UTC, the sender’s local time, or your own!&lt;/p&gt;
&lt;h4&gt;Does anything like this exist?&lt;/h4&gt;
&lt;p&gt;The closest thing to AIT that currently exists is the &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_military_time_zones&quot;&gt;Military Time Zones&lt;/a&gt; which also use the alphabet to refer to time zones, but in a way that doesn’t make much sense. Like AIT the time zones move East from UTC with UTC+1 being “Alfa”, UTC+2 “Bravo”, and continuing through the NATO phonetic alphabet (though skipping “Juliett”) to UTC+12, “Mike”. However, rather than continue through the alphabet at UTC-11, “November” is back near where we started as UTC-1 (and continues moving West from there).&lt;/p&gt;
&lt;p&gt;The military time zone names do seem useful, and are possibly too similar to AIT for my idea to catch on, but out of sequence order make the concept seem too confusing for anyone using it for much beyond just “time zone names.”&lt;/p&gt;
&lt;p&gt;One interesting (and further confusing thing) about the military time zone names is that “Mike”, UTC+12 and “Yankee”, UTC-12 are the same time, but on different dates because the border straddles the international date line. It’s quirks like this that make me think that any improvement on time zone related communication is likely to fail.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickweaver.net/images/blog/ait/military-time-zones.jpg&quot; alt=&quot;A map of military time zones from the ACP 121(I) standard&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Reasons why AIT will never work&lt;/h4&gt;
&lt;p&gt;As good an idea as I think this is, I have no hopes of AIT catching on mostly because I think that most people wouldn’t be able to get over the ridiculousness of saying “M:45” (and not being worried people would think they’re talking about a bus). But beyond the silliness, it’s still Daylight Saving Time that makes communicating across time zones unlikely to be improved on.&lt;/p&gt;
&lt;p&gt;If your own time zone stayed the same relative to AIT year round I think there would be a chance that, with tools like an AIT display next to your local time, or calendar integrations, that it would work for a lot of people who frequently communicate with people in other time zones, but having to switch recurring times back and forth by one letter twice a year seems like friction that would dampen most people’s enthusiasm.&lt;/p&gt;
&lt;p&gt;Bringing dates into the picture also seems like it would complicate things. Even though A:15 is 5:15 PM PDT in San Francisco, if we put a date on it, it would have to be “tomorrow” for the person in SF to match UTC. I don’t think that this would add any additional confusion to communicating with people whose time zone is in the next day relative to your own, but a new system like AIT would likely get blamed for the inherent awkwardness of time zones.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickweaver.net/images/blog/ait/sf-ny-sunset.jpg&quot; alt=&quot;Photographs of the sunset in San Francisco, CA, and New York, NY&quot;&gt;&lt;/p&gt;
&lt;p&gt;But who knows! Weirder ideas have caught on in the past, like Daylight Saving Time! Or, maybe we can all just join the UTC+8 “Hotel” time zone since almost 25% of the world population already lives in it!&lt;/p&gt;
&lt;p&gt;Aug 18, 2022 B:14, Brooklyn, NY&lt;/p&gt;
</description><pubDate>Thu, 18 Aug 2022 00:00:00 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/alphabetic-internet-time/</guid>
    </item>
    <item>
      <title>How to Set Up a Raspberry Pi as a Home Web Server</title>
      <link>https://patrickweaver.net/blog/how-to-raspberry-pi-server/</link><description>&lt;p&gt;Like most of the world, I’ve been spending a lot of time at home lately. Over the winter I was experimenting with building my own web-based digital tools, like an app to track articles and books I was reading. I initially thought I would need to buy hosting space to have the app accessible, but realized I mostly needed to use it when I was at home anyway, so a URL that only existed on my local network work also work.&lt;/p&gt;
&lt;p&gt;I have done a few projects with Raspberry Pis before, most notably a &lt;a href=&quot;https://www.patrickweaver.net/blog/building-a-futuristic-record-player-with-glitch-and-raspberry-pi/&quot;&gt;computer vision “record player”&lt;/a&gt;, but always found myself looking up the same things like how to set up Wi-Fi and SSH access. I seemed to need to do the same handful of things every time, but they were spread across various documentation and how-tos. This time while setting up the Pi web server I took notes so I would have it all in one place.&lt;/p&gt;
&lt;p&gt;To follow along with these steps you will need a second computer to set up the SD card (though you could probably start with a &lt;a href=&quot;https://www.adafruit.com/product/4266&quot;&gt;preformatted SD card also&lt;/a&gt;), and any kind of Raspberry Pi. I have run web servers on Pi Zeros before, though it can be a challenge to install newer versions of Node.js on ARMv6 based Pis (Zero and the original Raspberry Pi), and sometimes when building a large client app I’ve had to move to a faster Pi from a Pi Zero.&lt;/p&gt;
&lt;h4&gt;SD Card Setup&lt;/h4&gt;
&lt;h5&gt;1. Install the OS&lt;/h5&gt;
&lt;p&gt;Raspberry Pi recently released their own tool for formatting SD cards, &lt;a href=&quot;https://www.raspberrypi.org/software/&quot;&gt;Raspberry Pi Imager&lt;/a&gt;. It makes the process a lot easier than it had been previously. The first step is picking an OS to format the card with. Any of the Debian-based OSes should work, though the smallest download is Raspberry Pi OS Lite, which is all you need if you won’t be connecting a display. After that, select the SD card you want to install onto (there will probably only be one choice), and click “Write”.&lt;/p&gt;
&lt;h5&gt;Step 1 Summary&lt;/h5&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1. Install the OS:&lt;/strong&gt; Use Raspberry Pi Imager tool to write the OS to the SD card &lt;a href=&quot;https://www.raspberrypi.org/software/&quot;&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickweaver.net/images/blog/how-to-raspberry-pi-server/raspberry-pi-imager.png&quot; alt=&quot;A screenshot of the Raspberry Pi Imager Tool&quot;&gt;&lt;/p&gt;
&lt;h4&gt;2. Set up Wi-Fi&lt;/h4&gt;
&lt;p&gt;The RPi Imager tool will likely eject the SD card for you when it’s finished writing, but you want to do two more things before booting up the Pi. You may need to remove the SD card and re-insert it into your computer to see the “Boot” filesystem. First, to enable Wi-Fi you will need to create a file with your network credentials. If you will be plugging the Pi into an ethernet cable you can skip this step.&lt;/p&gt;
&lt;p&gt;Create a file called &lt;code&gt;wpa_supplicant.conf&lt;/code&gt; in the root directory of the SD card. Open the file in a text editor and paste in the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-conf&quot;&gt;ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=&amp;lt;2 LETTER ISO 3166-1 COUNTRY CODE&amp;gt;

network={
    ssid=&amp;quot;&amp;lt;WI-FI SSID&amp;gt;&amp;quot;
    psk=&amp;quot;&amp;lt;WI-FI PASSWORD&amp;gt;&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;country&lt;/code&gt; line with your &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes&quot;&gt;ISO 3166 country code&lt;/a&gt; (for the United States this will be &lt;code&gt;US&lt;/code&gt; without quotes). Update the &lt;code&gt;ssid&lt;/code&gt; and &lt;code&gt;psk&lt;/code&gt; lines of the &lt;code&gt;network&lt;/code&gt; section to your Wi-Fi network SSID and password, with quotes. Read more about setting up Wi-Fi &lt;a href=&quot;https://www.raspberrypi.org/documentation/configuration/wireless/headless.md&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My &lt;code&gt;wpa_supplicant.conf&lt;/code&gt; might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-conf&quot;&gt;ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US

network={
    ssid=&amp;quot;patricknet&amp;quot;
    psk=&amp;quot;myverysecurepassword&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Note on Text Encoding&lt;/h5&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.raspberrypi.org/documentation/configuration/wireless/headless.md&quot;&gt;Setting up a Raspberry Pi headless&lt;/a&gt;: “Depending on the OS and editor you are creating this on, the file could have incorrect newlines or the wrong file extension so make sure you use an editor that accounts for this. Linux expects the line feed (LF) newline character.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;Learn More&lt;/h5&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=TtiBhktB4Qg&quot;&gt;Scott Hanselman on CR/LF&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Step 2 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;2. Set up Wi-Fi:&lt;/strong&gt; Create a &lt;code&gt;wpa_supplicant.conf&lt;/code&gt; file in the root directory of the SD card, and populate it with your Wi-Fi credentials to enable Wi-Fi on boot. &lt;a href=&quot;https://www.raspberrypi.org/documentation/configuration/wireless/headless.md&quot;&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;3. Set up SSH&lt;/h4&gt;
&lt;p&gt;By default SSH access is disabled on a Raspberry Pi, the usual way of enabling it is either through the GUI or &lt;code&gt;raspi-config&lt;/code&gt;, but there is also a simple way to pre-setup on the SD card. Create an empty file called &lt;code&gt;ssh&lt;/code&gt; (without a file extension) in the root directory of the SD card, this will enable SSH on boot.&lt;/p&gt;
&lt;h4&gt;Step 3 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;3. Set up SSH:&lt;/strong&gt; Create an empty &lt;code&gt;ssh&lt;/code&gt; file in the root directory of the SD card to enable SSH access on boot. &lt;a href=&quot;https://www.raspberrypi.org/documentation/remote-access/ssh/README.md&quot;&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Raspberry Pi Setup&lt;/h4&gt;
&lt;p&gt;You are now ready to boot the Raspberry Pi, insert the SD card and power up the Pi. After a minute or two of boot time it should automatically connect to your network.&lt;/p&gt;
&lt;h4&gt;4. Connect via SSH&lt;/h4&gt;
&lt;p&gt;In a terminal on a second computer and connect to the Raspberry Pi via SSH. The default username and password will be &lt;code&gt;pi&lt;/code&gt; and &lt;code&gt;raspberry&lt;/code&gt;, and the default hostname will be &lt;code&gt;raspberrypi&lt;/code&gt;. If your local network supports the &lt;code&gt;.local&lt;/code&gt; TLD you may be able to connect using:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh pi@raspberrypi.local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Otherwise you will need to look up the IP address of the Pi in your network admin tools and connect using the IP address:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh pi@192.168.XXX.XXX
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also skip this step and open the Terminal app on the Pi if using a display and keyboard connected to the Pi.&lt;/p&gt;
&lt;p&gt;If you have previously connected via SSH to a Raspberry Pi with the default hostname on the computer you are using you may see a message about the remote host identification changing. To successfully connect via SSH you will need to update the &lt;code&gt;known_hosts&lt;/code&gt; file to remove the key stored for the other Raspberry Pi.&lt;/p&gt;
&lt;p&gt;On Linux or macOS the &lt;code&gt;known_hosts&lt;/code&gt; file will be at &lt;code&gt;/Users/[YOUR USERNAME]/.ssh/known_hosts&lt;/code&gt;. Find the line in this file that starts with either &lt;code&gt;raspberrypi.local&lt;/code&gt; or the Raspberry Pi’s IP address and remove it.&lt;/p&gt;
&lt;h4&gt;Step 4 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;4. Connect via SSH:&lt;/strong&gt; Connect to the Raspberry Pi via ssh: &lt;code&gt;ssh pi@raspberrypi.local&lt;/code&gt; or &lt;code&gt;ssh pi@192.168.XXX.XXX&lt;/code&gt; (and enter the default password &lt;code&gt;raspberry&lt;/code&gt;). You may need to remove a previous &lt;code&gt;raspberrypi.local&lt;/code&gt; from &lt;code&gt;/Users/USERNAME/.ssh/known_hosts&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;5. Change the Password&lt;/h4&gt;
&lt;p&gt;After connecting to the Raspberry Pi via SSH the login message will suggest that you change the default password:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;SSH is enabled and the default password for the &#39;pi&#39; user has not been changed.&lt;/code&gt; &amp;gt; &lt;code&gt;This is a security risk - please login as the &#39;pi&#39; user and type &#39;passwd&#39; to set a new password.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Following the instructions you can type &lt;code&gt;passwd&lt;/code&gt; which will prompt you first for your current password (&lt;code&gt;raspberry&lt;/code&gt;), then for a new password twice.&lt;/p&gt;
&lt;h4&gt;Step 5 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;5. Change the password:&lt;/strong&gt; Run &lt;code&gt;passwd&lt;/code&gt; to have the system prompt you for a new, more secure password.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;6. Change the Hostname&lt;/h4&gt;
&lt;p&gt;The default hostname of your Raspberry Pi is &lt;code&gt;raspberrypi&lt;/code&gt;. You may want to change this for a few reasons. If you have more than one Raspberry Pi on your network this will let you tell them apart, and if your network supports the .local TLD you can use the hostname as the URL to your app.&lt;/p&gt;
&lt;p&gt;You will need to update two files to change your hostname. First open &lt;code&gt;/etc/hostname&lt;/code&gt; and change the first and only line in the file (currently &lt;code&gt;raspberrypi&lt;/code&gt;) to your new hostname. Then, open &lt;code&gt;/etc/hosts&lt;/code&gt; and update the the line with &lt;code&gt;raspberrypi&lt;/code&gt; to your new hostname. Finally, reboot your Pi for the changes to take effect. Remember, if you SSHed in as &lt;code&gt;pi@raspberrypi.local&lt;/code&gt; you will need to use the new hostname instead.&lt;/p&gt;
&lt;h4&gt;Step 6 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;6. Change the Hostname:&lt;/strong&gt; Open &lt;code&gt;sudo nano /etc/hostname&lt;/code&gt; and change &lt;code&gt;raspberrypi&lt;/code&gt; to your new hostname, then &lt;code&gt;sudo nano /etc/hosts&lt;/code&gt; and change &lt;code&gt;raspberrypi&lt;/code&gt; to your new hostname. Then reboot, &lt;code&gt;sudo reboot now&lt;/code&gt;. &lt;a href=&quot;https://pimylifeup.com/raspberry-pi-hostname/&quot;&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;7. Change the Prompt&lt;/h4&gt;
&lt;p&gt;I also like the change the default bash prompt whenever I am going to be SSHing into a remote computer so that I can tell the difference at a glance between a local and remote terminal window.&lt;/p&gt;
&lt;p&gt;A simple change is just to prepend an emoji to the default prompt, which you can do by adding a line to &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo &#39;PS1=&amp;quot;🥧 ${PS1}&amp;quot;&#39; &amp;gt;&amp;gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I like the following prompt which adds colors that are different than the ones that I use locally, and a pie emoji:&lt;/p&gt;
&lt;!-- markdownlint-disable no-inline-html --&gt;
&lt;div class=&quot;fake-code&quot;&gt;
&lt;span style=&quot;color: white; background-color: silver;&quot;&gt;&amp;nbsp;Mon May &amp;nbsp;03 23:41:09&amp;nbsp;&lt;/span&gt;&lt;br&gt;
&lt;span style=&quot;color: black; background-color: cyan;&quot;&gt;&amp;nbsp;pi@b:&amp;nbsp;&lt;/span&gt; &lt;span style=&quot;color: white; background-color: lightGrey;&quot;&gt;&amp;nbsp;~&amp;nbsp;&lt;/span&gt;&lt;br&gt;
🥧 $&lt;br&gt;
&lt;/div&gt;
&lt;!-- markdownlint-enable no-inline-html --&gt;
&lt;p&gt;Update your prompt by editing your &lt;code&gt;~/.bashrc&lt;/code&gt; file with &lt;code&gt;sudo nano ~/.bashrc&lt;/code&gt;. The bash prompt is set with the &lt;code&gt;PS1&lt;/code&gt; variable, which has two versions in the default Raspberry Pi &lt;code&gt;~/.bashrc&lt;/code&gt;, one for when &lt;code&gt;color_prompt&lt;/code&gt; is set to &lt;code&gt;yes&lt;/code&gt;, another for otherwise. The default in Raspberry Pi OS is that &lt;code&gt;color_prompt&lt;/code&gt; is set to yes, so feel free to overwrite only the first &lt;code&gt;PS1&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;The value to set the prompt I showed above is (the sections with &lt;code&gt;[&#92;033 ...&lt;/code&gt; and &lt;code&gt;&#92;[$(tput...&lt;/code&gt; control turning on and off the colors):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;PS1=&amp;quot;&#92;n&#92;[&#92;033[48;5;7m&#92;] &#92;d &#92;t &#92;[$(tput sgr0)&#92;]&#92;n&#92;[$(tput bold)&#92;]&#92;[&#92;033[38;5;0m&#92;]&#92;[&#92;033[48;5;14m&#92;] &#92;u@&#92;H: &#92;[$(tput sgr0)&#92;] &#92;[&#92;033[38;5;0m&#92;]&#92;[&#92;033[48;5;15m&#92;] &#92;w &#92;[$(tput sgr0)&#92;]&#92;n 🥧 &#92;&#92;$ &amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;source ~/.bashrc&lt;/code&gt; (or &lt;code&gt;. ~/.bashrc&lt;/code&gt;) to reload your terminal and see your new prompt.&lt;/p&gt;
&lt;h4&gt;Step 7 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;7. Change the Prompt:&lt;/strong&gt; Update the &lt;code&gt;PS1&lt;/code&gt; variable in &lt;code&gt;~/.bashrc&lt;/code&gt; to make it look different. Add a pie emoji with &lt;code&gt;echo &#39;PS1=&amp;quot;🥧 ${PS1}&amp;quot;&#39; &amp;gt;&amp;gt; ~/.bashrc&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;8. Install Git&lt;/h4&gt;
&lt;p&gt;You will probably want to use Git to manage the source code for your app. It is not installed by default on Raspberry Pi OS so you will need to install it now with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: You may have to update by running &lt;code&gt;sudo apt-get update&lt;/code&gt; in order for Git to install.&lt;/p&gt;
&lt;p&gt;You may also want to install another text editor if you want something more customizable than nano.&lt;/p&gt;
&lt;h4&gt;Step 8 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;8. Install Git:&lt;/strong&gt; Install git to manage your source code with &lt;code&gt;sudo apt install git&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;9. Install Nginx&lt;/h4&gt;
&lt;p&gt;You can now run a web server (with &lt;code&gt;sudo&lt;/code&gt;) on port 80 and have it be available at your IP address or &lt;code&gt;[YOUR_HOSTNAME].local&lt;/code&gt;, but that might require more configuration. It will be easier to manage your app if you install a web server/reverse proxy like Nginx. You can install Nginx with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once Nginx is installed you should be able to access the default Nginx webpage at your Raspberry Pi’s IP Address or &lt;code&gt;[YOUR_HOSTNAME].local&lt;/code&gt;. Make sure to type in the &lt;code&gt;http://&lt;/code&gt; the first time if you are using the &lt;code&gt;.local&lt;/code&gt; TLD to avoid triggering search in your browser.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickweaver.net/images/blog/how-to-raspberry-pi-server/nginx-default.png&quot; alt=&quot;A screenshot of the default Nginx page&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can now serve any static website from &lt;code&gt;/var/www/html&lt;/code&gt;, if you look there you will see an &lt;code&gt;index.html&lt;/code&gt; or &lt;code&gt;index.nginx-debian.html&lt;/code&gt; file that is generating the current page.&lt;/p&gt;
&lt;h4&gt;Step 9 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;9. Install Nginx:&lt;/strong&gt; Install with &lt;code&gt;apt install nginx&lt;/code&gt;, then put a static webpage in &lt;code&gt;/var/www/html&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;10. Install nvm and npm&lt;/h4&gt;
&lt;p&gt;I make most of my web apps these days in JavaScript, there are lots of ways to install node and npm, but one easy way to manage versions is with nvm, which can be installed with the script below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then reload your terminal with &lt;code&gt;. ~/.bashrc&lt;/code&gt; and install the current LTS version of node with &lt;code&gt;nvm install lts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On an original Raspberry Pi, or a Raspberry Pi Zero, the ARMv6 chip is no longer supported by current versions of node. “Unofficial” ARMv6 builds of node are available at: &lt;a href=&quot;https://unofficial-builds.nodejs.org/&quot;&gt;unofficial-builds.nodejs.org&lt;/a&gt;. To install a version using an unofficial build use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;NVM_NODEJS_ORG_MIRROR=https://unofficial-builds.nodejs.org/download/release nvm install lts
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 10 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;10. Install nvm and npm:&lt;/strong&gt; &lt;code&gt;curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash&lt;/code&gt;, &lt;code&gt;. ~/.bashrc&lt;/code&gt;, &lt;code&gt;nvm install lts&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;11. Set Up Reverse Proxy&lt;/h4&gt;
&lt;p&gt;In order to access your app on your local network without a port number in the URL it will need to be accessible on port 80, but it will be easier to run your app on another port locally. This can be done with an Nginx reverse proxy.&lt;/p&gt;
&lt;p&gt;Nginx configuration is done via server block files, you can delete the default enabled server block by running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo rm /etc/nginx/sites-enabled/default
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, make your own empty server block in the &lt;code&gt;sites-available&lt;/code&gt; directory: . The &lt;code&gt;[YOUR SITE]&lt;/code&gt; part of the path can be anything, but it makes sense to give it the name of your app.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo touch /etc/nginx/sites-available/[YOUR SITE].conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now edit your server block file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/nginx/sites-available/[YOUR SITE].conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server block below will proxy processes running on port 8080 to port 80 (but you can substitute any port on the &lt;code&gt;proxy_pass&lt;/code&gt; line). Make sure to replace the &lt;code&gt;[HOSTNAME]&lt;/code&gt; sections with your Raspberry Pi’s hostname (or omit this line if accessing via IP Address):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-conf&quot;&gt;server {
    listen 80;
    server_name www.[HOSTNAME].local [HOSTNAME].local;

    location / {
       proxy_pass http://127.0.0.1:8080;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, link your Nginx config file to the &lt;code&gt;sites-enabled&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ln -s /etc/nginx/sites-available/[YOUR-SITE].conf /etc/nginx/sites-enabled/[YOUR-SITE].conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test your Nginx config with &lt;code&gt;sudo nginx -t&lt;/code&gt;. You should see a success confirmation message:&lt;/p&gt;
&lt;!-- markdownlint-disable no-inline-html --&gt;
&lt;samp&gt;
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
&lt;/samp&gt;
&lt;!-- markdownlint-enable no-inline-html --&gt;
&lt;p&gt;To enable the configuration reload the Nginx config:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last step is cloning your project to the Raspberry Pi and running it on port 8080, your app should now be available at &lt;code&gt;[HOSTNAME].local&lt;/code&gt;!&lt;/p&gt;
&lt;h4&gt;Step 11 Summary&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;11. Set Up Reverse Proxy:&lt;/strong&gt; Remove default: &lt;code&gt;sudo rm /etc/nginx/sites-enabled/default&lt;/code&gt;, Create config file: &lt;code&gt;sudo touch /etc/nginx/sites-available/[YOUR SITE].conf&lt;/code&gt;, Edit config file (see above): &lt;code&gt;sudo nano /etc/nginx/sites-available/[YOUR SITE].conf&lt;/code&gt;, Link to sites-enabled: &lt;code&gt;sudo ln -s /etc/nginx/sites-available/[YOUR-SITE].conf /etc/nginx/sites-enabled/[YOUR-SITE].conf&lt;/code&gt;, Reload Nginx: &lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;. &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-10&quot;&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickweaver.net/images/blog/how-to-raspberry-pi-server/hello-world.png&quot; alt=&quot;A screenshot of a “Hello, World” page hosted on a Raspberry Pi&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4&gt;All Steps:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Install the OS:&lt;/strong&gt; Use Raspberry Pi Imager tool to write the OS to the SD card &lt;a href=&quot;https://www.raspberrypi.org/software/&quot;&gt;source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set up Wi-Fi:&lt;/strong&gt; Create a &lt;code&gt;wpa_supplicant.conf&lt;/code&gt; file in the root directory of the SD card, and populate it with your Wi-Fi credentials to enable Wi-Fi on boot. &lt;a href=&quot;https://www.raspberrypi.org/documentation/configuration/wireless/headless.md&quot;&gt;source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set up SSH:&lt;/strong&gt; Create an empty &lt;code&gt;ssh&lt;/code&gt; file in the root directory of the SD card to enable SSH access on boot. &lt;a href=&quot;https://www.raspberrypi.org/documentation/remote-access/ssh/README.md&quot;&gt;source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect via SSH:&lt;/strong&gt; Connect to the Raspberry Pi via ssh: &lt;code&gt;ssh pi@raspberrypi.local&lt;/code&gt; or &lt;code&gt;ssh pi@192.168.XXX.XXX&lt;/code&gt; (and enter the default password &lt;code&gt;raspberry&lt;/code&gt;). You may need to remove a previous &lt;code&gt;raspberrypi.local&lt;/code&gt; from &lt;code&gt;/Users/USERNAME/.ssh/known_hosts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change the password:&lt;/strong&gt; Run &lt;code&gt;passwd&lt;/code&gt; to have the system prompt you for a new, more secure password.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change the Hostname:&lt;/strong&gt; Open &lt;code&gt;sudo nano /etc/hostname&lt;/code&gt; and change &lt;code&gt;raspberrypi&lt;/code&gt; to your new hostname, then &lt;code&gt;sudo nano /etc/hosts&lt;/code&gt; and change &lt;code&gt;raspberrypi&lt;/code&gt; to your new hostname. Then reboot, &lt;code&gt;sudo reboot now&lt;/code&gt;. &lt;a href=&quot;https://pimylifeup.com/raspberry-pi-hostname/&quot;&gt;source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change the Prompt:&lt;/strong&gt; Update the &lt;code&gt;PS1&lt;/code&gt; variable in &lt;code&gt;~/.bashrc&lt;/code&gt; to make it look different. Add a pie emoji with &lt;code&gt;echo &#39;PS1=&amp;quot;🥧 ${PS1}&amp;quot;&#39; &amp;gt;&amp;gt; ~/.bashrc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install Git:&lt;/strong&gt; Install git to manage your source code with &lt;code&gt;sudo apt install git&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install Nginx:&lt;/strong&gt; Install with &lt;code&gt;apt install nginx&lt;/code&gt;, then put a static webpage in &lt;code&gt;/var/www/html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install nvm and npm:&lt;/strong&gt; &lt;code&gt;curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash&lt;/code&gt;, &lt;code&gt;. ~/.bashrc&lt;/code&gt;, &lt;code&gt;nvm install lts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set Up Reverse Proxy:&lt;/strong&gt; Remove default: &lt;code&gt;sudo rm /etc/nginx/sites-enabled/default&lt;/code&gt;, Create config file: &lt;code&gt;sudo touch /etc/nginx/sites-available/[YOUR SITE].conf&lt;/code&gt;, Edit config file (see above): &lt;code&gt;sudo nano /etc/nginx/sites-available/[YOUR SITE].conf&lt;/code&gt;, Link to sites-enabled: &lt;code&gt;sudo ln -s /etc/nginx/sites-available/[YOUR-SITE].conf /etc/nginx/sites-enabled/[YOUR-SITE].conf&lt;/code&gt;, Reload Nginx: &lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;. &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-10&quot;&gt;source&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description><pubDate>Mon, 03 May 2021 00:00:00 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/how-to-raspberry-pi-server/</guid>
    </item>
    <item>
      <title>Participating in a Remote Batch at the Recurse Center</title>
      <link>https://patrickweaver.net/blog/a-recurse-center-remote-batch/</link><description>&lt;p&gt;Participating in a batch at the &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt; is something that I’ve thought on-and-off about doing since I found out about it (then called Hacker School) sometime between 2013 and 2015, but it just never seemed like the right time to leave my job and its subsidized health insurance until I was laid off (what turned out to be) early on in the pandemic. I had as recently as January 2020 been thinking, “maybe now is the right time to finally quit and do it,” and though I’m relieved I was able to get a few more months of savings and health insurance into 2020, participating in RC was the perfect change of pace and a motivating bridge between employment and the job search.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/rc2/rc-website.jpg&quot; alt=&quot;A screenshot from the Recurse Center website with a notice about operating remotely&quot; style=&quot;max-width: 500px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;Screenshot from recurse.com&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I had been working from home at my old job from mid March to when I left in mid July, but I didn’t realize until my first day at RC, that even though I had frequently seen friends and coworkers on video calls and in outdoor settings for the past 6 months, it was rare that I had the chance to speak to anyone I hadn’t previously met since March 2020.&lt;/p&gt;
&lt;p&gt;There are a few different pieces to the experience of a remote batch at RC. &lt;a href=&quot;https://www.recurse.com/virtual-rc&quot;&gt;Virtual RC&lt;/a&gt; is a fun game-like experience where each participant (and visiting alumni) has an avatar on the map. One corner of the map is more organized, and designed with a layout intended to mimic the physical RC space in Brooklyn, but the rest of the map is open for creative world building or experiments (there is even a Virual RC API now!). Each of the “rooms” in the structured corner of the map has a persistant video call link in it, and you can see which other participants are in the room on a call. Since I’ve started my new job I’ve missed being able to see who is already in a zoom call before deciding to join an optional meeting! Participants are encouaged to create a desk, where you can set an emoji status and leave a short note describing what you are up to that day. There are also audio rooms, which are great for more informal, or group conversations, and I’m only realizing now how much easier it is to start a call when you don’t have to open a new link and wait for a video call service to connect. A lot of the day-to-day conversation at remote RC happens via text on &lt;a href=&quot;https://zulip.com/&quot;&gt;Zulip&lt;/a&gt; (though this was the case for in-person RC also). It’s easy to reach out for help or to look for a pairing buddy on Zuip, and one of the most active streams (like channels in other chat apps) is Checkins where in-batch Recursers regularly post about what they’re working on.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/rc2/virtual-rc.png&quot; alt=&quot;A screenshot from Virtual RC with my avatar on top of the RC logo drawn with wall blocks&quot; style=&quot;max-width: 500px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;Hanging out on top of the RC logo in Virutal RC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I came into RC with &lt;a href=&quot;https://patrickweaver.net/blog/recurse-center-week-1/#rc-goals&quot;&gt;a few goals&lt;/a&gt; ranging form ambitious (real-time NYC subway map or self-hosted social network prototype), to the kinds of things I often made when I had a few hours to spare (SwiftUI proof of concept checklist app). I had spent the weeks leading up to my batch doing what I wanted to think of as a mini RC-like experience re-learning Swift and using SwiftUI for the first time. I had hoped to finish the &lt;a href=&quot;https://www.hackingwithswift.com/100/swiftui&quot;&gt;100 Days of SwiftUI&lt;/a&gt; tutorials in time for day 1 at RC but ended up being about a week too slow.&lt;/p&gt;
&lt;p&gt;I was surprised at the 9-5ness of the events and chatter in remote RC, though one of the most exciting things about doing a remote batch was being able to meet and collaborate with people from around the world (I counted at least 8 timezones in my batch), most events occurred bewteen 9am and 5pm Eastern Time. I would guess that this means that the average RC participant is spending less time in RC, than when in an in-person batch, which depending on your goals and ability to focus when something potentially more fun is going on, could be a benefit or a drawback to the remote RC experience.&lt;/p&gt;
&lt;p&gt;After speeding through the SwiftUI tutorials in my first week (and getting kind of tired of working in an unfamiliar language), I decided to spend most of my time working on my &lt;a href=&quot;https://patrickweaver.net/blog/making-a-real-time-nyc-subway-map-with-real-weird-nyc-subway-data/&quot;&gt;real-time NYC subway map project&lt;/a&gt; that I had first prototyped in early 2020. I eventually was able to get a mostly working prototype of the full system, coincidentally on almost the exact day that the MTA &lt;a href=&quot;https://www.curbed.com/2020/10/first-look-new-yorks-digital-subway-map-comes-alive-today.html&quot;&gt;released&lt;/a&gt; their own version. I thought that the subway map would be one of a few medium sized projects that I would be able to spend 2 to 3 weeks on during my RC time, but I had already spent more than half of my 12 week batch on it. I had done a few other one-off projects like an &lt;a href=&quot;https://doodles.patrickweaver.net/10-print-video-game/&quot;&gt;abstract “video game”&lt;/a&gt; based on the 10 Print pattern and a few explorations into techincal topics like &lt;a href=&quot;https://doodles.patrickweaver.net/canvas-lines/&quot;&gt;the browser JavaScript canvas API&lt;/a&gt;, but I decided that I should try to find a real medium-sized project for my last few weeks at RC.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/rc2/canvas-lines.png&quot; alt=&quot;A screenshot from my slides on drawing pixel perfect lines on the JS canvas&quot; style=&quot;max-width: 500px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;It’s hard to draw crisp lines on the JS canvas!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I felt like I had mostly been working though a big challenge with technologies that were pretty familiar so far (JavaScript and the &lt;a href=&quot;https://leafletjs.com/&quot;&gt;Leaflet.js mapping library&lt;/a&gt;), so I wanted to find a project that I could use to learn something new, but not spend all my time on implementation details like I had been doing learning SwiftUI. I had created a static website on the &lt;a href=&quot;https://doodles.patrickweaver.net/&quot;&gt;doodles subdomain&lt;/a&gt; of my website that I wanted to keep filling out, so a useful constraint was to make something on the web that wouldn’t need a backend (the doodles website is static and built with &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; , as are this website, and most of the doodles)). In the last year at my job, and through collaborating with other RC participants during my batch I had gotten pretty quick with prototyping things with React, but I hadn’t used either &lt;a href=&quot;https://reactjs.org/docs/hooks-intro.html&quot;&gt;React Hooks&lt;/a&gt;, or &lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt; for anything. I decided to try learning both and created a &lt;a href=&quot;https://doodles.patrickweaver.net/crossword/editor&quot;&gt;web-based Crossword Puzzle composer&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/rc2/crossword.png&quot; alt=&quot;A screenshot of the crossword puzzle app with symmetrical squares blacked out&quot; style=&quot;max-width: 500px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;A crossword puzzle with multiple kids of symmetries&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The crossword puzzle app was a fun mix of small challenges and interesting design decisions. One of the first things that stood out to me was that I had never really thought about the algorithm that is used to number crossword puzzle answers on the board, the kind of thing that is immediately understandable, but more complex that you would expect it to be when represented with a programming language. React Hooks were immediately very useful since most of the components for the app needed a sprinkling of state, but not too much, and TypeScript was helpful, especially in the context of RC because I paired with a few people on certain self-contained parts of the app like the symmetry for square toggling, and the defined types help people quickly pick up what the code we were writing would do. My favorite part of the crossword puzzle app is that to store a composed puzzle without a backend, I encode the whole puzzle state into the URL (here is a &lt;a href=&quot;https://doodles.patrickweaver.net/crossword/play/#eyJzdGF0ZSI6W1tbWyJmIiwiIiwiZmYiLG51bGwsbnVsbCwwXSxbImYiLCIiLCJ-mZiIsbnVsbCxudWxsLDFdLFsidCIsIlIiLCJ0dCIsMSwxLDJdLFsidCIsIkMiLC-JmdCIsMSwyLDNdXSxbWyJ0IiwiRiIsInR0IiwzLDMsNF0sWyJ0IiwiSSIsImZ0I-iwzLDQsNV0sWyJ0IiwiRiIsImZmIiwzLDEsNl0sWyJ0IiwiTyIsImZmIiwzLDIs-N11dLFtbInQiLCJBIiwidGYiLDUsMyw4XSxbInQiLCJNIiwiZmYiLDUsNCw5XSx-bInQiLCJJIiwiZmYiLDUsMSwxMF0sWyJ0IiwiRCIsImZmIiw1LDIsMTFdXSxbWy-J0IiwiTiIsInRmIiw2LDMsMTJdLFsidCIsIk8iLCJmZiIsNiw0LDEzXSxbInQiL-CJEIiwiZmYiLDYsMSwxNF0sWyJ0IiwiRSIsImZmIiw2LDIsMTVdXV0sW1tbImEi-LDEsIkZLQSBIYWNrZXIgU2NoLiIsIlJDIiwyXSxbImEiLDMsIk9yZGVyIGluIGE-gcXVldWUiLCJGSUZPIiw0XSxbImEiLDUsIkluIHRoZSBtaWRkbGUgb2YiLCJBTU-lEIiw4XSxbImEiLDYsIkphdmFTY3JpcHQgcnVudGltZSIsIk5PREUiLDEyXV0sW-1siZCIsMSwiV2lyZWxlc3MgdGVjaCB3aXRoIHRhZ3MiLCJSRklEIiwyXSxbImQi-LDIsIldoYXQgYSBwcm9ncmFtbWVyIHdyaXRlcyIsIkNPREUiLDNdLFsiZCIsMyw-iQ29tcHV0ZXIgYWlyIG1vdmVyIiwiRkFOIiw0XSxbImQiLDQsIlNob3J0aGFuZC-Bmb3Igb25lJ3MgcHJlZmVyZW5jZSIsIklNTyIsNV1dXV19&quot;&gt;small RC themed puzzle&lt;/a&gt;). I even discovered seemingly documented nowhere else bug in iMessage when prototyping this feature. When I first sent a friend a link to a puzzle as a test, iMessage wouldn’t parse it as a link, so they just got a wall of what looked like random characters. I looked around at other long tracking links I’d received in my emails and noticed they frequently had hyphen characters every so often. I added hyphens to my urls and all of a sudden iMessage could parse them as links!&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/rc2/imessage.jpg&quot; alt=&quot;A screenshot of a message in iMessage with a long stiring of seemingly random characters&quot; style=&quot;max-width: 500px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;A failed attempt to send a crossword&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;During the second half of my RC batch I also interviewed with a lot of companies. To help with this I attended a daily group for pairing on software job interview style questions. Coming back to these types of problems a few times a week was really helpful in being able to think through them quickly and I noticed a lot of progress in my ability to complete these kinds of challenges the more I practiced and interviewed.&lt;/p&gt;
&lt;p&gt;When I first applied to RC I had been feeling nervous about committing such a long period of time to not working before (hopefully!) starting a new job, I considered just doing a 1 week mini batch, or a 6 week half-batch, but the 12 weeks went by way more quikly than I had anticipated, and I was fortunately able to hit my idealistic goal of starting a new job in the new calendar year! The 12 weeks helped me feel free to dive into weird corners of the problems I wanted to work on like &lt;a href=&quot;https://doodles.patrickweaver.net/drawing-parallel-lines-on-a-map/&quot;&gt;drawing parallel lines on a map&lt;/a&gt; (lines were a big theme in my projects), without worrying that it would keep me from “finishing” my projects (author’s note: I still didn’t “finish” most of them). In the spirit of “never graduate” I hope to someday participate in another batch at the physical RC space, but in the meantime, anyone who like me has been sitting on the fence on applying to RC for years should take the opportunity to participate remotely and start the programming project you’ve always been meaning to get to!&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/rc2/parallel-lines-curve.jpg&quot; alt=&quot;A screenshot of a prototype of my real-time subway map&quot; style=&quot;max-width: 500px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;An early attempt to draw MTA track geography data as parallel tracks on a map&lt;/figcaption&gt;
&lt;/figure&gt;
</description><pubDate>Mon, 25 Jan 2021 16:33:26 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/a-recurse-center-remote-batch/</guid>
    </item>
    <item>
      <title>Making a Real-Time NYC Subway Map with Real Weird NYC Subway Data</title>
      <link>https://patrickweaver.net/blog/making-a-real-time-nyc-subway-map-with-real-weird-nyc-subway-data/</link><description>&lt;p&gt;Earlier this week the NYC MTA released a new &lt;a href=&quot;https://map.mta.info/&quot;&gt;digital-first map&lt;/a&gt;. The &lt;a href=&quot;https://www.curbed.com/2020/10/first-look-new-yorks-digital-subway-map-comes-alive-today.html&quot;&gt;Curbed exclusive&lt;/a&gt; that announced its release accurately portrays it as a strange child of both the 1972 map design by Massimo Vignelli and the current &lt;a href=&quot;https://new.mta.info/map/5256&quot;&gt;“paper” map&lt;/a&gt;. One feature of the new map (though it’s harder than it should be to notice at first) is real-time visualizations of each train in the system.&lt;/p&gt;
&lt;p&gt;I’ve been working on a similar concept, starting in February 2020, on which progress stalled once I stopped riding the subway regularly in March. But, when I started my batch at &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt; I decided to pick up the project again. My inspiration for the map was the large TV screens that the MTA has installed in stations over the last few years, which frustratingly display the “paper” version of the map.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/nyc-subway/tv-screen-map.jpg&quot; alt=&quot;A photograph of a TV in a subway station with the “paper” map displayed.&quot; style=&quot;width: auto; max-height: 400px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;Subway station TV (This is not a good photo, but it’s hard to take a picture of a screen underground)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Over the past few weeks at RC the subway map has been my main focus, which is longer than I expected the project to take (and though I have a prototype, I wouldn’t say I’m close to “done”). A big factor in the time the project has taken is some of the quirks in working with MTA data, which based on some of the bugs I’ve seen in the &amp;quot;official&amp;quot; version I’d say the team working on that version had to grapple with as well. I’m not sure I will ever finish this project to a state that would look great on a subway station tv, or be useful, but I do want to point out &lt;a href=&quot;https://www.theweekendest.com/trains&quot;&gt;The Weekendest&lt;/a&gt; by &lt;a href=&quot;https://sunny.ng/&quot;&gt;Sunny Ng&lt;/a&gt; (who also made &lt;a href=&quot;https://www.goodservice.io/&quot;&gt;goodservice.io&lt;/a&gt;), which is a great take on the concept, and handles some of the challenges of this kind of project much better than the MTA map does.&lt;/p&gt;
&lt;p&gt;For a long time the NYC Subway was almost &lt;a href=&quot;https://www.theatlantic.com/technology/archive/2015/11/why-dont-we-know-where-all-the-trains-are/415152/&quot;&gt;completely lacking in real-time data&lt;/a&gt;. For many years the only line that had even countdown clocks in stations was the L, which seems to be the line the MTA tries out new technology on, likely because it never shares tracks with any other line. Over the last 5 years the MTA has slowly installed countdown clocks in every station, and made the data that powers the countdown clocks available on their website, in apps, and as data online.&lt;/p&gt;
&lt;p&gt;Inspired and frustrated by the “paper” maps on the tv screens, I first became interested in working with MTA data in 2018, but I initially started working with bus data, I think for two reasons: the first was because at the time the API key for working with bus data was easier to obtain than for subway data, the second because my morning commute at the time usually started with the bus (The MTA, earlier this year, has fortunately updated the system for obtaining an API key for subway data). I made a small prototype of an iPhone app that would show real-time bus data, but got distracted by learning the Swift programming language and abandoned the project without building functionality beyond what the MTA already provided &lt;a href=&quot;https://bustime.mta.info/&quot;&gt;on their bustime website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That MTA Bustime website was the other inspiration for what became my map idea. Though you could only view one route at a time (and the functionality is not available on phone-size devices), the Bustime website showed, in addition to countdowns for each stop, the physical location of each bus on a map. This leads us to the first weird thing about working with NYC Subway data, unlike real-time bus data, the subway data does not contain the latitude and longitude data for each train that would make it easy to show them on a map.&lt;/p&gt;
&lt;h4&gt;Real-Time Transit Data&lt;/h4&gt;
&lt;p&gt;Transit data for most transit systems is available in formats called &lt;a href=&quot;https://developers.google.com/transit/gtfs&quot;&gt;GTFS&lt;/a&gt; (General Transit Feed Specification) and &lt;a href=&quot;https://developers.google.com/transit/gtfs-realtime&quot;&gt;GTFS Realtime&lt;/a&gt;, which were developed by Google (makes you wonder what the “G” originally stood for), but are now widely used. A GTFS file is, “a collection of at least six, and up to 13 CSV files (with extension .txt) contained within a .zip file.” and “The GTFS Realtime data exchange format is based on Protocol Buffers” (which are &lt;a href=&quot;https://developers.google.com/protocol-buffers&quot;&gt;“Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data”&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The GTFS Realtime feeds are available through 9 different API endpoints from the MTA. These 9 endpoints roughly correspond to the line colors, with Shuttles combined with trains they share tracks or stations with, and the 1/2/3 and 4/5/6 sharing one endpoint. This list of separate endpoints is another challenge with working with the entirety of the MTA data.&lt;/p&gt;
&lt;p&gt;I have exclusively worked with the NYC MTA’s GTFS Realtime feeds through the &lt;a href=&quot;https://www.npmjs.com/package/gtfs-realtime-bindings&quot;&gt;npm module&lt;/a&gt; maintained by Google. It is very possible that some of the challenges I’ve encountered are due to trying to squeeze the “extensible mechanism for serializing structured data” into JSON. Each API response is mostly composed of an array of &amp;quot;Feed Entity&amp;quot; objects like &lt;a href=&quot;https://patrickweaver.net/notes/nyc-subway-feed-entity/&quot;&gt;these&lt;/a&gt;, but there are a few quirks to working with this data (some maybe because of the JSON conversion).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each item in the array has an &lt;code&gt;id&lt;/code&gt; property, but unfortunately these ids do not consistently refer to the same train between each update, it’s best to ignore it.&lt;/li&gt;
&lt;li&gt;The array consists of pairs of objects that either have a &lt;code&gt;tripUpdate&lt;/code&gt; property or a &lt;code&gt;vehicle&lt;/code&gt; property. Each of these have a sub-property called &lt;code&gt;tripId&lt;/code&gt; that allows you to unite the pairs, but there are also some that don’t have a corresponding item (usually these represent trips that recently ended or haven’t yet begun).&lt;/li&gt;
&lt;li&gt;The data mixes together HH:MM:SS timestamps for data about when a train’s trip started, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Unix_time&quot;&gt;Unix timestamps&lt;/a&gt; for data about the current time (according to the API) and when a train will arrive at a station (the API provides both arrival and departure times but as far as I have seen they are always identical).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tripUpdate&lt;/code&gt; items show information about the stops a train will make in the future (stopTimeUpdates) and vehicle items show information about the current status of the train, but the first &lt;code&gt;stopTimeUpdate&lt;/code&gt; is usually in the past.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had never heard of Protocol Buffers before starting this project, so I was excited to learn more about them while reading through &lt;a href=&quot;https://dataintensive.net&quot;&gt;Designing Data Intensive Applications&lt;/a&gt; with fellow Recursers. In the book Martin Kleppmann notes that a, &amp;quot;curious detail of Protocol Buffers is that it does not have a list or array datatype, but instead has a repeated marker for fields (which is a third option alongside required and optional).&amp;quot; This could be the reason for the strange organization of the &lt;code&gt;tripUpdate&lt;/code&gt; and &lt;code&gt;vehicle&lt;/code&gt; properties.&lt;/p&gt;
&lt;h4&gt;Calculating Train Locations&lt;/h4&gt;
&lt;p&gt;The subway real-time API doesn’t have latitude and longitude data because it is designed to feed data to countdown clock style applications that show when the train will be at a specific station. One of the earliest features that I built into the real-time map was a way to translate these station-by-station countdown clocks into an approximation of the location of each train. My first attempt at this was to just show a list of stations and display an icon for a train between the names of the station it had been at previously and the station it was approaching.&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/nyc-subway/g-line.png&quot; alt=&quot;An early prototype diagram of G train positions.&quot; style=&quot;width: auto; max-height: 400px; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;A first prototype&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The next step was plotting the stations on a map. To start off, as with the diagram version, I just placed each train at the midpoint between the station it was traveling from and the station it was traveling towards.&lt;/p&gt;
&lt;p&gt;A goal I had for the project was not just to show real-time train locations, but to animate them as they moved around the map. To determine how long I should expect a train to take to travel between each station I logged updates from the MTA API for a few hours and noted both the average time for a pair of stations, and the longest time I had seen for the pair. I’m still experimenting a little bit with what values to use as the baseline, but from looking at the logged numbers there does seem to be an expected amount of time for most stations.&lt;/p&gt;
&lt;figure&gt;
&lt;div class=&quot;data&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;G: {
   G22: { N: { avg: 60, max: 71 }, S: null },
   G24: { N: { avg: 123, max: 180 }, S: { avg: 60, max: 106 } },
   G26: { N: { avg: 75, max: 90 }, S: { avg: 108, max: 180 } },
   G28: { N: { avg: 128, max: 180 }, S: { avg: 78, max: 90 } },
   G29: { N: { avg: 60, max: 76 }, S: { avg: 139, max: 180 } },
   G30: { N: { avg: 51, max: 74 }, S: { avg: 70, max: 90 } },
   G31: { N: { avg: 60, max: 90 }, S: { avg: 58, max: 69 } },
   G32: { N: { avg: 66, max: 87 }, S: { avg: 53, max: 84 } },
   G33: { N: { avg: 50, max: 66 }, S: { avg: 67, max: 84 } },
   G34: { N: { avg: 58, max: 90 }, S: { avg: 54, max: 66 } },
   G35: { N: { avg: 68, max: 86 }, S: { avg: 50, max: 81 } },
   G36: { N: { avg: 81, max: 161 }, S: { avg: 59, max: 71 } },
   A42: { N: { avg: 70, max: 177 }, S: { avg: 87, max: 157 } },
   F20: { N: { avg: 68, max: 90 }, S: { avg: 86, max: 165 } },
   F21: { N: { avg: 72, max: 120 }, S: { avg: 76, max: 120 } },
   F22: { N: { avg: 62, max: 90 }, S: { avg: 84, max: 120 } },
   F23: { N: { avg: 90, max: 120 }, S: { avg: 88, max: 150 } },
   F24: { N: { avg: 101, max: 120 }, S: { avg: 67, max: 84 } },
   F25: { N: { avg: 139, max: 180 }, S: { avg: 71, max: 90 } },
   F26: { N: { avg: 120, max: 120 }, S: { avg: 109, max: 150 } },
   F27: { N: null, S: { avg: 81, max: 120 } },
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;figcaption&gt;Average and max wait times in seconds for stops on the G line.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure style=&quot;overflow: scroll;&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trip Id&lt;/th&gt;
&lt;th&gt;Trip Start Time&lt;/th&gt;
&lt;th&gt;Trip Date&lt;/th&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;th&gt;Stop1 Arrival&lt;/th&gt;
&lt;th&gt;Stop1 Id&lt;/th&gt;
&lt;th&gt;Stop2 Arrival&lt;/th&gt;
&lt;th&gt;Stop2 Id&lt;/th&gt;
&lt;th&gt;Seconds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;073476_G..N&lt;/td&gt;
&lt;td&gt;12:14:46&lt;/td&gt;
&lt;td&gt;20200818&lt;/td&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;1597768631&lt;/td&gt;
&lt;td&gt;G33N&lt;/td&gt;
&lt;td&gt;1597768692&lt;/td&gt;
&lt;td&gt;G32N&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;074600_G..N&lt;/td&gt;
&lt;td&gt;12:26:00&lt;/td&gt;
&lt;td&gt;20200818&lt;/td&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;1597769103&lt;/td&gt;
&lt;td&gt;G33N&lt;/td&gt;
&lt;td&gt;1597769172&lt;/td&gt;
&lt;td&gt;G32N&lt;/td&gt;
&lt;td&gt;69&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;075000_G..N&lt;/td&gt;
&lt;td&gt;12:30:00&lt;/td&gt;
&lt;td&gt;20200818&lt;/td&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;1597769531&lt;/td&gt;
&lt;td&gt;G33N&lt;/td&gt;
&lt;td&gt;1597769596&lt;/td&gt;
&lt;td&gt;G32N&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;076501_G..N&lt;/td&gt;
&lt;td&gt;12:45:01&lt;/td&gt;
&lt;td&gt;20200818&lt;/td&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;1597770333&lt;/td&gt;
&lt;td&gt;G33N&lt;/td&gt;
&lt;td&gt;1597770396&lt;/td&gt;
&lt;td&gt;G32N&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;077700_G..N&lt;/td&gt;
&lt;td&gt;12:57:00&lt;/td&gt;
&lt;td&gt;20200818&lt;/td&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;1597771043&lt;/td&gt;
&lt;td&gt;G33N&lt;/td&gt;
&lt;td&gt;1597771104&lt;/td&gt;
&lt;td&gt;G32N&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;078403_G..N&lt;/td&gt;
&lt;td&gt;13:04:02&lt;/td&gt;
&lt;td&gt;20200818&lt;/td&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;1597771443&lt;/td&gt;
&lt;td&gt;G33N&lt;/td&gt;
&lt;td&gt;1597771524&lt;/td&gt;
&lt;td&gt;G32N&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;079600_G..N&lt;/td&gt;
&lt;td&gt;13:16:00&lt;/td&gt;
&lt;td&gt;20200818&lt;/td&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;1597772051&lt;/td&gt;
&lt;td&gt;G33N&lt;/td&gt;
&lt;td&gt;1597772112&lt;/td&gt;
&lt;td&gt;G32N&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;080550_G..N&lt;/td&gt;
&lt;td&gt;13:25:30&lt;/td&gt;
&lt;td&gt;20200818&lt;/td&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;1597772711&lt;/td&gt;
&lt;td&gt;G33N&lt;/td&gt;
&lt;td&gt;1597772776&lt;/td&gt;
&lt;td&gt;G32N&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption&gt;Logged travel Times between the Bedford - Nostrand stop and the Myrtle - Willoughby stop on the G train&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4&gt;Secret Stations&lt;/h4&gt;
&lt;p&gt;One thing I discovered while logging updates from the MTA API was that it contained secret stations! The MTA provides a &lt;a href=&quot;http://web.mta.info/developers/data/nyct/subway/Stations.csv&quot;&gt;list of all of the stations in the system&lt;/a&gt; with data like latitude and longitude. Each station has an ID (see Stop1 and Stop2 id in the diagram above and called &amp;quot;GTFS Stop ID&amp;quot; in the list). The stop IDs are a letter and 2 numbers, with the letter often corresponding to the line it serves (or used to historically), and the numbers mostly occurring in sequence. but some trains would have planned &amp;quot;stops&amp;quot; at stations that weren’t in the list! My best guess is that these stations are something station-like in the MTA’s infrastructure, which usually appear near the end of a line.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;H17&amp;quot; is between Howard Beach/JFK Airport and Broad Channel on the A and Rockaway Shuttle and is likely where the Shuttle trains go to turn around.&lt;/li&gt;
&lt;li&gt;&amp;quot;H19&amp;quot; is before the Broad Channel stop on the A and Rockaway Shuttle and may also be related to Shuttle turnaround?&lt;/li&gt;
&lt;li&gt;&amp;quot;H18&amp;quot; and &amp;quot;H05&amp;quot; are between Broad Channel and Beach 67 St on the A and Rockaway Shuttle, which may have to do with tracks that run between Beach 67 and Beach 90 Sts.&lt;/li&gt;
&lt;li&gt;&amp;quot;A29&amp;quot; is between Penn Station and 14th St on the A, C, and E, which is strange because the C and E stop at 23rd St., which is also between those stations, but has the ID &amp;quot;A30&amp;quot;.&lt;/li&gt;
&lt;li&gt;&amp;quot;A39&amp;quot; is between Fulton St. and High St. on the A and C, which might have something to do with the track stubs on the Brooklyn side (one of which is the NYC Transit museum).&lt;/li&gt;
&lt;li&gt;&amp;quot;A58&amp;quot; is between Grant Av. and 80th St. on the A, which, is where the A train emerges from a tunnel to run on elevated tracks.&lt;/li&gt;
&lt;li&gt;&amp;quot;A62&amp;quot; is between Rockaway Blvd. and 104th St. on the A and probably has something to do with the merging between the 3 versions of the A at Rockaway Blvd.&lt;/li&gt;
&lt;li&gt;&amp;quot;R60&amp;quot; is between Queensboro Plaza and Lexington Ave/59th St. on the N, R, and W. My guess is that this has something to do with the N/W and R tracks merging before going into a tunnel.&lt;/li&gt;
&lt;li&gt;&amp;quot;R65&amp;quot; is between the Whitehall St. and Court St. stops on the R, and could also be related to the same track stubs as &amp;quot;A39&amp;quot;.&lt;/li&gt;
&lt;li&gt;&amp;quot;B24&amp;quot; is between Bay 50th St and Coney Island on the D, and is probably the MTA Coney Island Yard.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Drawing the Static Map&lt;/h4&gt;
&lt;p&gt;I wasn’t quite satisfied with the angular paths that drawing lines directly between stations created, and I was fortunately able to find &lt;a href=&quot;https://github.com/blahblahblah-/theweekendest&quot;&gt;Sunny Ng’s advice&lt;/a&gt; on extracting shape arrays from the non real-time GTFS MTA data. Using these shape arrays I could draw route maps with smooth curves, and even animate trains along those curves. But one of the things that makes a subway map a subway map is seeing lines that run on the same tracks as parallel lines. I also wanted to double these lines and show Northbound and Southbound trains on separate tracks (something that the new MTA map fails to do).&lt;/p&gt;
&lt;p&gt;After trying to approach the parallel lines problem geometrically I was pointed in the right direction by a fellow RC participant and was able to draw great looking lines by treating the Latitude/Longitude points in the shape arrays as vectors (More on this in my &lt;a href=&quot;https://doodles.patrickweaver.net/drawing-parallel-lines-on-a-map/&quot;&gt;interactive slides on this problem&lt;/a&gt; and more on the &lt;a href=&quot;https://medium.com/transit-app/how-we-built-the-worlds-prettiest-auto-generated-transit-maps-12d0c6fa502f&quot;&gt;challenge of drawing nice train lines from the Transit app&lt;/a&gt;).&lt;/p&gt;
&lt;figure&gt;
&lt;div style=&quot;display: flex; max-width: 100%;&quot;&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/nyc-subway/nyc-subway-f-g.jpg&quot; alt=&quot;A screenshot of my map.&quot; style=&quot;width: auto; max-height: 500px; max-width: 49%; margin: 0 auto;&quot;&gt;
  &lt;span style=&quot;width: 5px;&quot;&gt;&lt;/span&gt;
  &lt;img src=&quot;https://patrickweaver.net/images/blog/nyc-subway/mta-f-g.jpg&quot; alt=&quot;A screenshot of the MTA map.&quot; style=&quot;width: auto; max-height: 500px; max-width: 49%; margin: 0 auto;&quot;&gt;
&lt;/div&gt;
&lt;figcaption&gt;Similar sections of my map and the MTA map&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4&gt;A Prototype&lt;/h4&gt;
&lt;p&gt;My map project is available on &lt;a href=&quot;https://github.com/patrickweaver/nyc-subway/tree/971c538eb5de9ac524fa1db8656ddd9febd6f0e5&quot;&gt;GitHub&lt;/a&gt;, bugs and all. I did most of the work on the map using only the G train API endpoint. This was a helpful limitation when I was first experimenting with what was possible using the data, but may have led to more bugs because of the slight differences in the data available for each set of lines.&lt;/p&gt;
&lt;p&gt;A common complaint about the new official real-time map is that it seems to use as much computer power as it can. My map isn’t much better because it is doing all of the geographic calculations in the user’s browser, my guess is that the MTA’s map is also. One update I might take on over the next week and a half as my time at RC winds down is moving these calculations to a server, and sending only train position changes to the map visualization. This may also help with the bug my current prototype exhibits where leaving and coming back to the tab a few minutes later will cause trains to fly around the map without regard for the lines or stations.&lt;/p&gt;
&lt;p&gt;The MTA data is weird because it’s created by a system that could never have anticipated the kind of systems that now try to contain it. Overall, working with and working around the weirdness in the data has been challenging, but a great reminder that the most interesting real-world problems are often hard to jam into our brittle computer systems, and that’s probably a good thing.&lt;/p&gt;
</description><pubDate>Wed, 21 Oct 2020 00:00:00 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/making-a-real-time-nyc-subway-map-with-real-weird-nyc-subway-data/</guid>
    </item>
    <item>
      <title>Week 1 at Recurse Center: Two Approaches to Learning</title>
      <link>https://patrickweaver.net/blog/recurse-center-week-1/</link><description>&lt;p&gt;I just finished my first week as part of the Fall 1 &#39;20 batch at &lt;a href=&quot;https://www.recurse.com&quot;&gt;Recurse Center&lt;/a&gt;. I tried to split my time between being social and building skills that I want to use for future projects. The social aspect of RC is interesting because this batch is being conducted remotely.&lt;/p&gt;
&lt;p&gt;My batch at RC started exactly 5 months after my last day in an office, and something I had been thinking about over the last few months, is that I haven’t met anyone new since early March when NYC shut down because of the pandemic. It’s been very refreshing to meet people again, even if it’s via video calls and chat. RC has created an online representation of their physical space we call &amp;quot;Virtual RC&amp;quot;. Each of us have avatars we can move around the space, and there are permanent links to video call rooms that we can pop into for events or impromptu conversations. The Virtual RC experience pairs well with group chat organized into streams on different topics.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickweaver.net/images/blog/rc/virtual-rc.png&quot; alt=&quot;A screenshot of Virtual RC&quot;&gt;&lt;/p&gt;
&lt;figcaption&gt;My avatar hanging out in the &quot;Shannon&quot; room at Virtual RC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Other than social events I spent my first week at RC focused on two projects. The first was going through the &lt;a href=&quot;https://www.hackingwithswift.com/100/swiftui&quot;&gt;100 Days of SwiftUI tutorials&lt;/a&gt; (at many more than one a day), which I had started before the batch. The second was reviewing the &lt;a href=&quot;https://github.com/patrickweaver/nyc-subway&quot;&gt;real time NYC Subway map&lt;/a&gt; project I first started in January.&lt;/p&gt;
&lt;p&gt;I wish I had been able to finish the SwiftUI tutorials before the batch (my original goal), because it didn’t feel as useful to share what I was working on with other RC participants as I was re-building apps that someone else had designed, but I finished the last app on Friday and I’m excited to be able to use SwiftUI to quickly prototype projects for my phone.&lt;/p&gt;
&lt;p&gt;When I first started the subway map project in early 2020 I was excited about finishing it quickly, but in March when I also stopped taking the subway I lost enthusiasm for the project. Starting at RC has helped rekindle the excitement I had for it, I think partly because even though the batch is remote and there are participants from around the world, there is also a strong NYC contingent.&lt;/p&gt;
&lt;p&gt;An interesting aspect of building a map from MTA data is that the data structures that the MTA publishes are designed for building countdown clock style apps. I would imagine that the MTA calculates this per-station data from the location of each train, but in order to build an app that is more focused on location than time, I need to reverse engineer the location data from the distance in time each train from the next station. There are also other weird quirks, like train trip start times being provided in &amp;quot;HH:MM:SS&amp;quot; format, but station arrival times being provided in Unix time format.&lt;/p&gt;
&lt;figure&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;id&amp;quot;: &amp;quot;000001G&amp;quot;,
  &amp;quot;tripUpdate&amp;quot;: {
    &amp;quot;trip&amp;quot;: {
      &amp;quot;tripId&amp;quot;: &amp;quot;126481_G..N&amp;quot;,
      &amp;quot;startTime&amp;quot;: &amp;quot;21:04:49&amp;quot;,
      &amp;quot;startDate&amp;quot;: &amp;quot;20200815&amp;quot;,
      &amp;quot;routeId&amp;quot;: &amp;quot;G&amp;quot;
    },
    &amp;quot;stopTimeUpdate&amp;quot;: [
      {
        &amp;quot;arrival&amp;quot;: { &amp;quot;time&amp;quot;: &amp;quot;1597541836&amp;quot; },
        &amp;quot;departure&amp;quot;: { &amp;quot;time&amp;quot;: &amp;quot;1597541836&amp;quot; },
        &amp;quot;stopId&amp;quot;: &amp;quot;G28N&amp;quot;
      },
      {
        &amp;quot;arrival&amp;quot;: { &amp;quot;time&amp;quot;: &amp;quot;1597541904&amp;quot; },
        &amp;quot;departure&amp;quot;: { &amp;quot;time&amp;quot;: &amp;quot;1597541904&amp;quot; },
        &amp;quot;stopId&amp;quot;: &amp;quot;G26N&amp;quot;
      }

      /* More stations below in real data */
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;Example MTA Data&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Splitting my programming time between learning SwiftUI and trying to wrap my head around the subway map again has felt like two different approaches to learning. In the past I have often jumped into building my own projects with a new tool or technology as soon as I feel like I had learned enough to do so, but I’ve often realized later that I spent a lot of time figuring out answers that I might have gotten to more quickly by completing a more thorough overview before starting.&lt;/p&gt;
&lt;p&gt;The subway map is one example of that type of project, but for good reason. It’s a project that when I started, I wasn’t sure was possible. It was inspired by a similar (but much simpler) map in the underground MUNI stations in San Francisco. The MUNI maps I remember from 15 years ago only showed trains in &amp;quot;the tunnel,&amp;quot; which although many lines run through, has a single trunk, that lines branch out from after going above ground (newer maps as seen below seem to show the whole system). Since these MUNI maps had existed since at least the early 00s I figured if one hadn’t been made (I’ve since found &lt;a href=&quot;https://tracker.geops.ch/?z=13&amp;amp;s=1&amp;amp;x=-8232001.0970&amp;amp;y=4969606.7622&amp;amp;l=transport&quot;&gt;NYC maps that have been made&lt;/a&gt;) for NY there must be a technical reason (it may just be that because until relatively recently &lt;a href=&quot;https://www.theatlantic.com/technology/archive/2015/11/why-dont-we-know-where-all-the-trains-are/415152/&quot;&gt;per station data wasn’t available&lt;/a&gt; in NYC).&lt;/p&gt;
&lt;figure&gt;
&lt;!-- markdownlint-disable no-inline-html --&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-conversation=&quot;none&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://twitter.com/MBTA?ref_src=twsrc%5Etfw&quot;&gt;@MBTA&lt;/a&gt; &lt;a href=&quot;https://twitter.com/sfmta_muni?ref_src=twsrc%5Etfw&quot;&gt;@sfmta_muni&lt;/a&gt; &lt;a href=&quot;https://twitter.com/d_tribe?ref_src=twsrc%5Etfw&quot;&gt;@d_tribe&lt;/a&gt; &lt;a href=&quot;https://twitter.com/universalhub?ref_src=twsrc%5Etfw&quot;&gt;@universalhub&lt;/a&gt; closer zoom. Incredibly useful. &lt;a href=&quot;https://t.co/jqHuGyZrJR&quot;&gt;pic.twitter.com/jqHuGyZrJR&lt;/a&gt;&lt;/p&gt;&amp;mdash; Ari Ofsevit (@ofsevit) &lt;a href=&quot;https://twitter.com/ofsevit/status/720301082899918850?ref_src=twsrc%5Etfw&quot;&gt;April 13, 2016&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;!-- markdownlint-enable no-inline-html --&gt;
&lt;figcaption&gt;
&lt;p&gt;A photo of the real time MUNI map in a tweet by &lt;a href=&quot;https://twitter.com/ofsevit/status/720301082899918850&quot;&gt;@ofsevit&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In order to gauge the feasibility of the project I wanted to get started quickly, throwing a map together and adding some markers for stations and individual trains at fixed times. Because I had limited experience working with transit data (which is provided in a &lt;a href=&quot;https://developers.google.com/transit/gtfs&quot;&gt;very complex format&lt;/a&gt;), or maps, this left me with both the sense that the project is possible, and a big spaghetti code mess.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickweaver.net/images/blog/rc/nyc-subway-v1.jpg&quot; alt=&quot;A screenshot of the first prototype of my subway map app&quot;&gt;&lt;/p&gt;
&lt;figcaption&gt;The first prototype of the subway map app&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I started looking into SwiftUI because I’m interested in making my own home screen widgets once I update my phone to iOS 14, and these widgets are built using the framework. Knowing I would have time to experiment during my batch at RC, and wanting to avoid my usual new tool mess phenomenon, I prioritized giving myself a good understanding of how SwiftUI works, what it can and can’t do, and a general review of Swift. Once my batch started I found it more and more difficult to keep going through the tutorials instead of jumping into an original idea, but throughout the week, spending time figuring out what I was even thinking reviewing the code for the subway map project re-motivated me to get through it.&lt;/p&gt;
&lt;p&gt;In the first week at RC the return to a daily routine has also put me back in the frame of mind I spent a lot of my last job in, where I would come up with ideas for about 3 apps a day that would solve tiny problems. One example is, after a particular session of an RC event for pair programming on software job interview style questions where we selected a problem that ended up being very difficult in the language we chose to work in, I thought that maybe I should create a mini app for the group where we could rate problems for each other. A lot of these ideas that are generated through trying to solve small problems in my routine end up being apps that organize data into text boxes, which are often not very interesting, so I’m conflicted on whether or not to spend time following these threads that will likely continue to appear.&lt;/p&gt;
&lt;p&gt;&lt;span id=&quot;rc-goals&quot;&gt;A few other things that are slightly more interesting, or at least broadly useful that I want to get done during my time at RC are:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The real time subway map&lt;/li&gt;
&lt;li&gt;A simple checklist app with a home screen widget made with SwiftUI&lt;/li&gt;
&lt;li&gt;A tool to convert an export from a Blogger blog to a static site generator&lt;/li&gt;
&lt;li&gt;A prototype of a self-hosted social network profile that can interface with other people’s self-hosted profiles&lt;/li&gt;
&lt;li&gt;A second iteration of my &lt;a href=&quot;https://github.com/patrickweaver/ocr-email&quot;&gt;handwritten email sending project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Something that takes ideas similar to Dynamicland (or the &lt;a href=&quot;https://www.recurse.com/blog/132-living-room-making-rc-programmable&quot;&gt;Living Room&lt;/a&gt; project at RC) into the remote world we’re living in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall I’m very excited to be spending time at RC, and I hope that it gives me time to explore ideas with weird corners and not settle for solving small simple problems quickly.&lt;/p&gt;
</description><pubDate>Sun, 16 Aug 2020 22:22:13 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/recurse-center-week-1/</guid>
    </item>
    <item>
      <title>How to Download an Image from a Google Doc</title>
      <link>https://patrickweaver.net/blog/how-to-download-an-image-from-a-google-doc/</link><description>&lt;p&gt;For some reason Google hasn’t built in a way for you to download images in Google docs! There are workarounds to get those image files like &lt;a href=&quot;https://twitter.com/corduroy/status/1184758335934849025&quot;&gt;using Google Keep&lt;/a&gt;, or &lt;a href=&quot;https://twitter.com/tonyvincent/status/1021726699178708993&quot;&gt;downloading your whole doc as a .zip file&lt;/a&gt;, but these have always felt like too many steps.&lt;/p&gt;
&lt;p&gt;And this is something that people really want!&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/user/status/1190182191520788480&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/user/status/1277776054380265478&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/user/status/710516705303384068&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/user/status/1227582581350240257&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/user/status/1225516004375179265&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/user/status/1249761603559378945&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/user/status/990395429383622656&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;As Steve Krouse points out here, it is possible to get the real URL of the image in your doc (but confusingly, as soon as you click on the image to select it the URL becomes obfuscated!).&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/user/status/1190358282877186050&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;I also noticed the URLs in the source, and decided to make an easy way to access it. The one trick ended up being, because clicking on the image made it disappear, finding a way to tell the code which image you wanted!&lt;/p&gt;
&lt;p&gt;I looked through some JavaScript documentation and realized I could use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseover_event&quot;&gt;mouseover&lt;/a&gt; event to detect when someone was hovering over the image. Unfortunately this means that it won’t work on a touchscreen device, but I’m guessing that most people who want to download an image are on a traditional computer.&lt;/p&gt;
&lt;h4&gt;How does it work?&lt;/h4&gt;
&lt;p&gt;I needed a way to run my code on any Google Doc, there’s probably a way to make Google Doc or Chrome extension to do this, but since I was asking people to run code in their potentially private docs I wanted to make the code as short and open source as possible.&lt;/p&gt;
&lt;p&gt;I decided that the best way to do this was a &lt;a href=&quot;https://support.mozilla.org/en-US/kb/bookmarklets-perform-common-web-page-tasks&quot;&gt;bookmarklet&lt;/a&gt;. If you’re unfamiliar with bookmarklets, they’re bookmarks (usually placed in your bookmarks toolbar (Cmd-Shift-B to toggle this on and off on a Mac), that instead of navigating to a webpage, run JavaScript when you click them.&lt;/p&gt;
&lt;h4&gt;Great! Tell me how to do it!&lt;/h4&gt;
&lt;p&gt;To get started you’ll have to &amp;quot;install&amp;quot; the bookmarklet. This is easy to do, and just means dragging a button into your bookmarks toolbar. &lt;a href=&quot;https://doodles.patrickweaver.net/gdoc-image-dl&quot;&gt;I’ve hosted it here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The instructions are simple!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Drag one of the bookmarklets (see the embed above) to your bookmarks toolbar. The text displayed is what will be show on the toolbar:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, when you’re on a Google Doc, click the bookmarklet, then hover over an image embedded in the doc. Depending on your browser settings it will either download immediately, or open the actual image in a new tab.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Great my problems are solved forever!&lt;/h4&gt;
&lt;p&gt;No guarantees that this will work long term, a quick look at the source code for any Google Doc will show that they’re very complex! I wouldn’t be surprised if Google changes the way these URLs work in the future, but this tool has worked for 6 months so maybe not!&lt;/p&gt;
&lt;p&gt;Long term I hope that they build in a way for people to download their images, but for now I hope this is helpful!&lt;/p&gt;
</description><pubDate>Thu, 16 Jul 2020 00:00:00 +0000</pubDate>
      <dc:creator>Patrick Weaver</dc:creator>
      <guid>https://patrickweaver.net/blog/how-to-download-an-image-from-a-google-doc/</guid>
    </item>
  </channel>
</rss>