<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Puckett</title>
    <description>The latest articles on DEV Community by Michael Puckett (@mpuckett).</description>
    <link>https://dev.to/mpuckett</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F295284%2Fca660750-4f19-4a31-b58a-33cf29a5e5a0.jpeg</url>
      <title>DEV Community: Michael Puckett</title>
      <link>https://dev.to/mpuckett</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mpuckett"/>
    <language>en</language>
    <item>
      <title>Show DEV: Open your favorite website in a standalone window on iPhone</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Mon, 27 Jan 2020 07:37:43 +0000</pubDate>
      <link>https://dev.to/mpuckett/show-dev-open-your-favorite-website-in-a-standalone-window-on-iphone-3i66</link>
      <guid>https://dev.to/mpuckett/show-dev-open-your-favorite-website-in-a-standalone-window-on-iphone-3i66</guid>
      <description>&lt;p&gt;Check it out here: &lt;a href="https://standalone-web-wrapper.firebaseapp.com"&gt;https://standalone-web-wrapper.firebaseapp.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Did you know can use the share sheet in Safari and select "Add to Home Screen" to save an app-like icon on the home screen? This functionality has existed pretty much since the iPhone launched.&lt;/p&gt;

&lt;p&gt;Personally, I've not used this feature much over the years because it always just launched a fresh instance of the site in a new tab in Safari.&lt;/p&gt;

&lt;p&gt;In the last few months, however, I noticed that some websites will open in their own window, not in Safari. Opening the site again will return you to that window, at the same place you left off.&lt;/p&gt;

&lt;p&gt;Wow! A much better experience.&lt;/p&gt;

&lt;p&gt;But not every website has opted in to this standalone experience. For good reason -- you lose your native browser frame with all the navigation buttons. This isn't so bad most of the time, because you can still swipe from the edge of the screen to go back/forward, just like in Safari.&lt;/p&gt;

&lt;p&gt;The trick is to add a manifest.json file with the "display" property set to "standalone".&lt;/p&gt;

&lt;p&gt;For me, I wanted the New York Times to work this way. I suspect that news sites might not be opting in because ad blockers still apply to the standalone experience, but anyway I'm not a fan of the NYT native app.&lt;/p&gt;

&lt;p&gt;iFrames won't work due to cross-site restrictions, so I set up a meta redirect on the page that will instantly show the desired site. This will show the site name at the top of the page, which wastes a little bit of space, but allows access to Reader View.&lt;/p&gt;

&lt;p&gt;I also made the app icon and app icon name customizable.&lt;/p&gt;

&lt;p&gt;Let me know what sites you use it for!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://standalone-web-wrapper.firebaseapp.com"&gt;https://standalone-web-wrapper.firebaseapp.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/michaelcpuckett/standalone-web-wrapper"&gt;https://github.com/michaelcpuckett/standalone-web-wrapper&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webapp</category>
      <category>webdev</category>
      <category>pwa</category>
    </item>
    <item>
      <title>How and Why I Avoid Magic Numbers in CSS</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Wed, 22 Jan 2020 18:18:27 +0000</pubDate>
      <link>https://dev.to/mpuckett/how-and-why-i-avoid-magic-numbers-in-css-dod</link>
      <guid>https://dev.to/mpuckett/how-and-why-i-avoid-magic-numbers-in-css-dod</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;There's a great article called &lt;a href="https://csswizardry.com/2012/11/code-smells-in-css/#magic-numbers"&gt;Code smells in CSS&lt;/a&gt; by Harry Roberts that first introduced me to the concept of magic numbers in CSS.&lt;/p&gt;

&lt;p&gt;When Harry wrote the article in 2012, there weren't too many great solutions for dealing with these problems. But since then, we've gotten Grid and Flex layout, along with CSS custom properties (variables!) so we can now avoid many of the pitfalls.&lt;/p&gt;

&lt;p&gt;The basic idea of magic numbers is that writing styles with arbitrary numbers like the following is brittle and unmaintainable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;250px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.box1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;11px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.box2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For one thing, what's so special about these numbers?  Even if there were comments, you might not know all that they're doing unless you look at the markup, or how they relate to the other numbers.&lt;/p&gt;

&lt;p&gt;Let's say &lt;code&gt;.box1&lt;/code&gt; and &lt;code&gt;.box2&lt;/code&gt; are next to each other.&lt;/p&gt;

&lt;p&gt;Focusing on &lt;code&gt;.box2&lt;/code&gt;, let's first look at its &lt;code&gt;padding&lt;/code&gt; -- 2 on the left and 4 on the right. The left padding is defining the space between the components, and the right side in defining the outer right margin of the page. That's not very conducive to composability. What if we wanted to reverse the order? We would have to sift out the 2 and 4, reverse them, and apply them to the other component. It would be better if the container could manage these details.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;11px&lt;/code&gt; and &lt;code&gt;7px&lt;/code&gt; &lt;code&gt;top&lt;/code&gt; values here are vertically centering the boxes inside their container. That's precarious, in case the content changes. There are better ways to achieve this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grid Layout
&lt;/h2&gt;

&lt;p&gt;In the example above, with one padding defining the space between components, and the other defining the page margin, we can leave all those details to the grid container. The container gets the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-auto-flow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;250px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now the interior components don't need padding.&lt;/p&gt;

&lt;p&gt;Notice also how Grid supports &lt;code&gt;align-&lt;/code&gt; and &lt;code&gt;justify-&lt;/code&gt; properties that can do the work of centering and aligning, so we can lose the &lt;code&gt;11px&lt;/code&gt; and &lt;code&gt;7px&lt;/code&gt; &lt;code&gt;top&lt;/code&gt; value altogether.&lt;/p&gt;

&lt;p&gt;(Flexbox also has these alignment properties, but the support for &lt;code&gt;gap&lt;/code&gt; is still limited, unfortunately.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Properties
&lt;/h2&gt;

&lt;p&gt;Any time that we still need to use specific numbers, we can replace them with CSS custom properties: &lt;code&gt;--page-padding: 4px&lt;/code&gt; and &lt;code&gt;--gap-width: 2px&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note: I think it's fair to be cautious with a statement like this, and to be conservative when applying this rule in practice. There are potential performance considerations when using CSS custom properties all over the place. But in my experience, the bottleneck in rendering a page hasn't been the CSS.&lt;/p&gt;

&lt;p&gt;In keeping with this, we can keep the &lt;code&gt;250px&lt;/code&gt; &lt;code&gt;height&lt;/code&gt; as-is, because it's unique.&lt;/p&gt;

&lt;p&gt;Similarly, &lt;code&gt;--page-padding&lt;/code&gt; might be a better candidate for being a custom property than &lt;code&gt;--gap-width&lt;/code&gt;, for example. If the gap between the components is unique, keeping it &lt;code&gt;2px&lt;/code&gt; might be fine.&lt;/p&gt;

&lt;p&gt;But what if, when it was originally conceived by the designer, the space between the components was designed to be half of the page padding? If so, you might want to preserve the relationship in case the page padding changes in the future.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;calc&lt;/code&gt; for this purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--page-padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;grid-gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--page-padding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-auto-flow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;250px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we're down to just one magic number, and one  appropriately-named custom property. In my opinion, that's much easier to reason about and the intent of the code is clearer.&lt;/p&gt;

&lt;p&gt;I found a similar article on Dev that goes even more in depth if you want to check it out: &lt;a href="https://dev.to/adrianbdesigns/mastering-css-magic-numbers-2297"&gt;https://dev.to/adrianbdesigns/mastering-css-magic-numbers-2297&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✌️&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>JS DOM API for Beginners: Removing all elements with a certain class</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Sun, 19 Jan 2020 00:14:52 +0000</pubDate>
      <link>https://dev.to/mpuckett/js-dom-api-for-beginners-removing-all-elements-with-a-certain-class-4h6p</link>
      <guid>https://dev.to/mpuckett/js-dom-api-for-beginners-removing-all-elements-with-a-certain-class-4h6p</guid>
      <description>&lt;p&gt;If you're familiar with CSS 🎨, then you can use the same selector syntax to target all matching elements in JavaScript 💻.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice the selector is the same in CSS, representing elements with a &lt;code&gt;box&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;This returns a special kind of array-like object for DOM elements called a NodeList.&lt;/p&gt;

&lt;p&gt;What if we wanted to remove all the elements with a &lt;code&gt;box&lt;/code&gt; class?&lt;/p&gt;

&lt;p&gt;One way is to call &lt;code&gt;.remove()&lt;/code&gt; on each of the elements. To do this, we need to loop over each element.&lt;/p&gt;

&lt;p&gt;NodeLists aren't technically arrays, so they don't have a &lt;code&gt;.forEach()&lt;/code&gt; method 😫 but they are iterable, meaning they can be converted to regular arrays 🤓&lt;/p&gt;

&lt;p&gt;Here's the syntax for the conversion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can use &lt;code&gt;.forEach()&lt;/code&gt; to remove each item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It can be any valid CSS selector, such as &lt;code&gt;.boxes li.box.circle[aria-selected="true"]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You could also use &lt;code&gt;.map()&lt;/code&gt; or &lt;code&gt;.reduce()&lt;/code&gt; on a list of DOM elements this way.&lt;/p&gt;

&lt;p&gt;🤟&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Handling Overscroll in a Web App with Header and Footer on iOS</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Tue, 14 Jan 2020 13:53:25 +0000</pubDate>
      <link>https://dev.to/mpuckett/the-holy-grail-web-app-shell-with-header-and-footer-for-iphone-549j</link>
      <guid>https://dev.to/mpuckett/the-holy-grail-web-app-shell-with-header-and-footer-for-iphone-549j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Web pages, originally designed as documents, want to scroll. And since at least the iPhone, web pages can "overscroll" beyond their bounds, based on touch gesture inertia. In fact, every scrollable area on a web page viewed on an iPhone gets this extra "bouncy" scrolling behavior.&lt;/p&gt;

&lt;p&gt;Nested scrollable areas "chain" with their parents (including the page itself), so that if you reach the top of a nested scrollable area, the closest scrollable parent will take over scrolling.&lt;/p&gt;

&lt;p&gt;However, some app-like web experiences may wish to opt-out of this document-flow/scroll-chaining paradigm. They need to present a fixed scrollable main content area with a fixed header and footer, which goes against the browser's nature.&lt;/p&gt;

&lt;p&gt;The main content area, if scrolled, could trigger the entire page to start scrolling unexpectedly. Or, if the page is fixed positioned, nothing will happen and the user will get "trapped" scrolling an unscrollable web page for a few seconds.&lt;/p&gt;

&lt;p&gt;To opt out of this paradigm in any browser except Safari, you can use a new CSS property called &lt;code&gt;overscroll-behavior&lt;/code&gt;. Setting &lt;code&gt;overscroll-behavior: contain&lt;/code&gt; will prevent scroll chaining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling iOS Safari
&lt;/h2&gt;

&lt;p&gt;Polyfilling this CSS property in Safari is pretty tricky.&lt;/p&gt;

&lt;p&gt;For non-scrollable elements, you can prevent scroll chaining by simply turning off touch gestures. You can do that with a CSS property that is supported by Safari: &lt;code&gt;touch-action: none&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But for scrollable elements, JavaScript will be required.&lt;/p&gt;

&lt;p&gt;Remember that scroll chaining occurs when you reach the bounds of the element. So we need to ensure that the user is never able to fully scroll to the top or bottom. Doing this the wrong way can cause UX problems, because the user will clearly be fighting against the default inertia scroll.&lt;/p&gt;

&lt;p&gt;So here's the trick:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an inner element that is at least 3px taller than the size of its scrolling parent, to force the area to get the overscroll behavior.&lt;/li&gt;
&lt;li&gt;Immediately set the scroll position to 1px to prevent scroll chaining when scrolling up&lt;/li&gt;
&lt;li&gt;With JavaScript, catch when the scroll position is exactly &lt;code&gt;0&lt;/code&gt; or exactly at the bottom. After a &lt;code&gt;requestAnimationFrame&lt;/code&gt;, set the scroll position to 1px from either the top or bottom.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The container will still get the inertia scroll (the user won't have to fight it) but it won't trigger scroll chaining.&lt;/p&gt;

&lt;p&gt;Here's the JavaScript function I have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;handleScroll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scrollTop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;scrollLeft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;clientHeight&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;atTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;beforeTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;atBottom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;clientHeight&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;beforeBottom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;clientHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;atTop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollLeft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;beforeTop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;atBottom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollLeft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;beforeBottom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



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

&lt;p&gt;Integrating this into an already-existing app probably won't be easy, since this may necessitate restructuring a lot of the page.&lt;/p&gt;

&lt;p&gt;Hopefully Safari will implement the &lt;code&gt;overscroll-behavior&lt;/code&gt; CSS property soon, so we can avoid this mess!&lt;/p&gt;

&lt;p&gt;Here's the WebKit issue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=176454"&gt;https://bugs.webkit.org/show_bug.cgi?id=176454&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've ever faced this challenge, add yourself to the CC list  on that issue to indicate that this is important to you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Is there code review site I can scroll through on my phone? </title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Mon, 13 Jan 2020 09:18:15 +0000</pubDate>
      <link>https://dev.to/mpuckett/is-there-code-review-site-i-can-scroll-through-on-my-phone-4h3k</link>
      <guid>https://dev.to/mpuckett/is-there-code-review-site-i-can-scroll-through-on-my-phone-4h3k</guid>
      <description>&lt;p&gt;I love scrolling Dev.to. It has replaced some other social apps on my phone because of the substantive feedback and discussion.&lt;/p&gt;

&lt;p&gt;The blogging aspect is great but wouldn’t it be nice if there were an interface just for code reviews, where you can comment on lines of code?&lt;/p&gt;

&lt;p&gt;This could be a good addition to Dev.to eventually.&lt;/p&gt;

&lt;p&gt;But I’m wondering if there is a site like that already?&lt;/p&gt;

&lt;p&gt;If not, should I build it? 🤓&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>idea</category>
      <category>codereview</category>
    </item>
    <item>
      <title>How can I get rendered innerHTML that includes Shadow DOM?</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Sun, 05 Jan 2020 05:48:51 +0000</pubDate>
      <link>https://dev.to/mpuckett/how-can-i-get-rendered-innerhtml-that-includes-shadow-dom-11nn</link>
      <guid>https://dev.to/mpuckett/how-can-i-get-rendered-innerhtml-that-includes-shadow-dom-11nn</guid>
      <description>&lt;p&gt;I'm using web components with shadow DOM on a project and I would like to copy the innerHTML of the page as rendered.&lt;/p&gt;

&lt;p&gt;I think I need to write a function loops over each DOM node and gets either the shadow DOM children if available or else the light DOM children.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I've been having trouble getting it right, and I'm surprised I can't find anything on Google.&lt;/p&gt;

</description>
      <category>help</category>
      <category>html</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How I Built a Dark Mode PWA without JS Libraries in 24 Hours</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Sat, 04 Jan 2020 15:57:40 +0000</pubDate>
      <link>https://dev.to/mpuckett/how-i-built-a-dark-mode-pwa-without-js-libraries-in-24-hours-2hf2</link>
      <guid>https://dev.to/mpuckett/how-i-built-a-dark-mode-pwa-without-js-libraries-in-24-hours-2hf2</guid>
      <description>&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;I decided to give my Hacker News reading experience a facelift.&lt;/p&gt;

&lt;p&gt;First and foremost, I wanted Dark Mode!&lt;/p&gt;

&lt;p&gt;Second, I wanted to be able to "install" it on my iPhone's homescreen, so that it runs in its own process, and not in Safari. (Dev.to does this natively, kudos!)&lt;/p&gt;

&lt;p&gt;I also wanted to build a project over break that would let me explore new web standards. I wanted to commit to using the latest tools of the native web platform, so I wouldn't use any JS libraries or create a build process. I also wouldn't worry about any browsers other than the ones I use every day -- latest Safari and Chromium.&lt;/p&gt;

&lt;p&gt;Before I started, I also got the idea to make it a little more functional for myself, so that it loads to the top comment along with the headline.&lt;/p&gt;

&lt;p&gt;Finally, I wanted to timebox it to 24 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #1: Loading Data
&lt;/h2&gt;

&lt;p&gt;This was the easy part. The Hacker News API has an endpoint that provides JSON data of the stories. No authorization, no setup, just load the data.&lt;/p&gt;

&lt;p&gt;Since I wasn't limited by browser support, I could safely use &lt;code&gt;fetch&lt;/code&gt;, Promises, and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storyIDs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://hacker-news.firebaseio.com/v0/topstories.json`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storyIDs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://hacker-news.firebaseio.com/v0/item/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Step #2: Templating and Dynamic Data
&lt;/h2&gt;

&lt;p&gt;Each of the loaded stories would be rendered as an instance of a web component.&lt;/p&gt;

&lt;p&gt;There are basically 3 types of data to consider when you use a web component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Named slots&lt;/li&gt;
&lt;li&gt;Custom properties&lt;/li&gt;
&lt;li&gt;Custom attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ended up not having a need for custom attributes.&lt;/p&gt;

&lt;p&gt;Let's start by looking at the template for a &lt;code&gt;top-story&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"top-story"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"top-story-submitter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"by"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"top-story-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"top-story-main"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"top-story-headline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"top-comment"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I'm using named slots where I want the dynamic content to go. This will be on the Shadow DOM side.&lt;/p&gt;

&lt;p&gt;Anything in the Light DOM side with a matching &lt;code&gt;slot&lt;/code&gt; attribute will be injected into the rendered template.&lt;/p&gt;

&lt;p&gt;So for the dynamic data, I needed to convert each JSON data property received from the API into an HTML element with a &lt;code&gt;slot&lt;/code&gt; attribute. I'm adding the JSON data to the web component as custom properties, then letting setting those properties trigger the creation of the elements with a &lt;code&gt;slot&lt;/code&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// can be null&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Object.assign&lt;/code&gt; here is setting these directly on the element, so we can set those up to be custom properties that react to changes.&lt;/p&gt;

&lt;p&gt;In the web component, I have a helper function to do the property conversion to slots, and I have a setter for each of the properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;setSlot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[slot="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[slot="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setSlot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, if I change the data on the component, the slot will also update on the Light DOM side, which will update in place in the rendered Shadow DOM.&lt;/p&gt;

&lt;p&gt;I can also use the setters to do other kinds of work. I want to embed another web component for the Top Comment inside this one, so I won't use my &lt;code&gt;setSlot&lt;/code&gt; helper function. Instead, in the setter, I set up that component the same way I set up this one. This is also where I updated the &lt;code&gt;href&lt;/code&gt; attributes on the links.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #3: Code Splitting / Imports
&lt;/h2&gt;

&lt;p&gt;Typically I use webpack for converting my projects to ES5 and concatenating into a single JS file.&lt;/p&gt;

&lt;p&gt;Here I'm using native JS imports to add the split-up files. Add that to the fact that the base markup is in its own web component, and my HTML file ends up being pretty light:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;app-screen&amp;gt;&amp;lt;/app-screen&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"./styles.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./imports/fetcher.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./imports/AppScreenTemplate.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./imports/AppScreen.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./imports/TopCommentTemplate.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./imports/TopComment.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./imports/TopStoryTemplate.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./imports/TopStory.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Step #4: Dark Mode
&lt;/h2&gt;

&lt;p&gt;Although I always use Dark Mode, I wanted to use the native CSS media query that detects Dark Mode in the system settings, in case someone else was used to Light Mode instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Step #5: PWA Installation
&lt;/h2&gt;

&lt;p&gt;One of the most important aspects of all this was to make Hacker News run like a native app, in its own window and not in Safari. That way my scroll state would be preserved.&lt;/p&gt;

&lt;p&gt;This is actually pretty simple for iOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"apple-mobile-web-app-capable"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"yes"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To make this more compliant with other browsers, including Chromium Edge, which I have been using, I also added a manifest.json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hacker News PWA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#CD00D8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standalone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orientation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"portrait"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icons/icon-512x512.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512x512"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Challenge #1: Dates!
&lt;/h2&gt;

&lt;p&gt;I ended up removing all dates from the project for now. I'm used to using a library such as moment.js or date-fns, and the native functions would sometimes show undefined or have other problems! I think for the final product, if I continue with it, I will pull in one of those libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge #2: Time Constraints
&lt;/h2&gt;

&lt;p&gt;I had planned on having the comments (and possibly even the story if iframe embed is supported) show up in a modal drawer that overlays the rest of the content. This might still happen, but it's outside the 24-hour timebox.&lt;/p&gt;

&lt;p&gt;It also isn't quite a full-fledged PWA with service workers. I need to do some work on automatically refreshing content.&lt;/p&gt;

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

&lt;p&gt;I had a great time working on this, and I have started using it whenever I want to check Hacker News. You might enjoy it too.&lt;/p&gt;

&lt;p&gt;Install it as an "Add to Homescreen" app from Safari:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://hn-pwa-1.firebaseapp.com/"&gt;http://hn-pwa-1.firebaseapp.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contribute:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/michaelcpuckett/hn-pwa-1"&gt;https://github.com/michaelcpuckett/hn-pwa-1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Final Result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fR0Qo4jv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/817h5by6ls99wppwkzlk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fR0Qo4jv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/817h5by6ls99wppwkzlk.png" alt="Final Result"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>css</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Show DEV: Hacker News “Add to Homescreen” Web App</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Fri, 03 Jan 2020 05:07:00 +0000</pubDate>
      <link>https://dev.to/mpuckett/show-dev-hacker-news-add-to-homescreen-web-app-4j11</link>
      <guid>https://dev.to/mpuckett/show-dev-hacker-news-add-to-homescreen-web-app-4j11</guid>
      <description>&lt;p&gt;&lt;a href="https://hn-pwa-1.firebaseapp.com/"&gt;https://hn-pwa-1.firebaseapp.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Killer features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It shows the top comment&lt;/li&gt;
&lt;li&gt;Dark mode! Or light mode! Depending on your system settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re on an iPhone, add it to your home screen. It will open in its own window, not in Safari.&lt;/p&gt;

&lt;p&gt;I was able to pull this off easily by adding the &lt;code&gt;apple-mobile-web-app-capable&lt;/code&gt; meta tag.&lt;/p&gt;

&lt;p&gt;This is what I was able to do in 24 hours. I’ll be working to make it an installable app soon.&lt;/p&gt;

</description>
      <category>showdev</category>
    </item>
    <item>
      <title>An Inclusive Cross-Device Testing Checklist</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Thu, 02 Jan 2020 19:48:56 +0000</pubDate>
      <link>https://dev.to/mpuckett/an-inclusive-cross-device-testing-checklist-2h6n</link>
      <guid>https://dev.to/mpuckett/an-inclusive-cross-device-testing-checklist-2h6n</guid>
      <description>&lt;p&gt;Here’s my web compatibility checklist that includes comprehensive accessibility testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Physical Devices
&lt;/h2&gt;

&lt;p&gt;I’m testing with the following real devices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MacBook&lt;/li&gt;
&lt;li&gt;iPhone with a notch&lt;/li&gt;
&lt;li&gt;Pixel with a notch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it! I’m able to use macOS Bootcamp to dual boot to native Windows 10.&lt;/p&gt;

&lt;p&gt;You can, of course, emulate both iOS and Android. This is useful for inspecting layouts without the notch. But an emulator sometimes doesn’t behave quite the same way.&lt;/p&gt;

&lt;p&gt;If you are on a Windows or Linux PC, there won’t be a way to test any proprietary Apple software. I’ll look at how to emulate Safari later — WebKit is open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browsers
&lt;/h2&gt;

&lt;p&gt;You should look at your analytics to see what kind of coverage you want to support for older browsers.&lt;/p&gt;

&lt;p&gt;In most cases, unless I want to charge more for the engineering time, the following list is my baseline.&lt;/p&gt;

&lt;p&gt;I get to use ES6, CSS Grid, CSS variables, Web Components, and other modern features.&lt;/p&gt;

&lt;p&gt;When testing across browsers, I usually like to have a common set of workflows using various amounts of data.&lt;/p&gt;

&lt;p&gt;The list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latest Chrome (79)

&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;N-10 Latest Chrome (69)

&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Latest Firefox (71)

&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Latest Firefox Enterprise (ESR 68)

&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Latest Desktop Safari (13)

&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;N-1 Latest Desktop Safari (12)

&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Latest Mobile Safari (13)

&lt;ul&gt;
&lt;li&gt;iOS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;N-1 Latest Mobile Safari (12)

&lt;ul&gt;
&lt;li&gt;iOS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What about Internet Explorer and Edge? IE is present but buried in Windows 10, and Edge Beta with Chromium is available for download. It will soon replace the existing Edge. If you have a high number of Edge users, add it to the browser support list, or consider providing a download link to Edge Beta.&lt;/p&gt;

&lt;p&gt;If you want to emulate Safari on Windows, you’ll need to install Ubuntu from the Windows Store. You’ll need to install an xServer emulator and have it running. Then run &lt;code&gt;apt-get update &amp;amp;&amp;amp; apt-get install epiphany-browser&lt;/code&gt; in the Ubuntu shell. Add &lt;code&gt;export DISPLAY =:0&lt;/code&gt; to your bash profile. Then you can run &lt;code&gt;epiphany-browser&lt;/code&gt; to test WebKit on a PC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assistive Technologies
&lt;/h2&gt;

&lt;p&gt;Are you testing for accessibility? You should be! People with disabilities, such as blind people, need to be able to interact with every app and website. That’s done with semantic HTML and ARIA.&lt;/p&gt;

&lt;p&gt;All operating systems now come with a screen reader that interacts with the underlying Accessibility API. You can use ARIA to modify the Accessibility API.&lt;/p&gt;

&lt;p&gt;When using a screen reader, you use a keyboard to navigate between items using a virtual cursor (essentially the same as the focus cursor) and interact with the web page. There is usually a developer setting to show the text on screen as it would be spoken.&lt;/p&gt;

&lt;p&gt;Here are the assistive technologies I test with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows Narrator (Windows 10)&lt;/li&gt;
&lt;li&gt;VoiceOver (macOS and iOS)&lt;/li&gt;
&lt;li&gt;TalkBack (Android)&lt;/li&gt;
&lt;li&gt;NVDA (Windows 10) is a free download&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Manual and Automated Accessibility Testing
&lt;/h2&gt;

&lt;p&gt;I test the markup at various stages (adding items to cart, deleting items from cart, etc) using three helpful developer tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://validator.w3.org/nu/"&gt;validator.nu&lt;/a&gt; HTML/ARIA Validator&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd"&gt;axe Chromium Extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chrome.google.com/webstore/detail/accessibility-insights-fo/pbjjkligggfmakdaogkfomddhfmpjeni"&gt;Accessibility Insights Chromium Extension&lt;/a&gt; - This will walk you through many different accessibility scenarios&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Of course, none of the time spent testing comes for free. Make sure you work with your client or project manager to integrate your inclusive testing plan into the overall project plan.&lt;/p&gt;

&lt;p&gt;So, did I miss any? Or go overboard? Tell me about your testing plan!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>a11y</category>
      <category>qa</category>
      <category>html</category>
    </item>
    <item>
      <title>When to use CSS Grid and when to use Flexbox for Multiline Layout</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Wed, 01 Jan 2020 17:22:07 +0000</pubDate>
      <link>https://dev.to/mpuckett/when-to-use-css-grid-and-when-to-use-flexbox-for-multiline-layout-no3</link>
      <guid>https://dev.to/mpuckett/when-to-use-css-grid-and-when-to-use-flexbox-for-multiline-layout-no3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Flexbox and CSS Grid are both powerful layout technologies built into CSS. They have many overlapping abilities. For most tasks, such as vertical centering, I could reach for either one. In some cases, one or the other is the only way to get the job done.&lt;/p&gt;

&lt;p&gt;Is one better than the other, given the current state of web standards?&lt;/p&gt;

&lt;p&gt;In the event that either Grid or Flexbox would work, I’m now reaching for Grid. I’ll explain why after discussing some various multiline scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gap
&lt;/h2&gt;

&lt;p&gt;Soon there will be a uniform way to apply consistent spacing between child items, as defined by the parent: &lt;code&gt;gap&lt;/code&gt;. So, &lt;code&gt;gap: 5px&lt;/code&gt; will not affect the outside margin, only the spacing between interior items.&lt;/p&gt;

&lt;p&gt;This was inspired by and will eventually supersede Grid’s &lt;code&gt;grid-gap&lt;/code&gt; property. Unfortunately, the only way achieve the same result on multiline (wrapping) items in Flexbox using any browser other than Firefox is the Negative Margin Hack. This is where all the child items get a margin equal to half of the gap. Then to account for the extra space on the outside, the container gets a margin of the same value multiplied by -1. You can only use &lt;code&gt;:first-child&lt;/code&gt; and &lt;code&gt;:last-child&lt;/code&gt; on the items to account for the extra space when you have a single row (non-wrapping flex container).&lt;/p&gt;

&lt;p&gt;For multiline grid containers, just apply a &lt;code&gt;grid-gap&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: Grid, but hopefully &lt;code&gt;gap&lt;/code&gt; for flex layout will soon be added to Chrome and Safari.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consistent Sizing vs Automatic Sizing
&lt;/h2&gt;

&lt;p&gt;If you have tracks in both directions (a multi-column and multi-row layout) then your choice depends on the widths of the child items relative to others.&lt;/p&gt;

&lt;p&gt;If you want all the items to line up evenly and stack vertically, use Grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0jaj91ogjjenm4i5j7m4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0jaj91ogjjenm4i5j7m4.png" alt="Grid will line up evenly"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want the items to retain their natural width and not line up vertically, use Flexbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fekk8n95oyqfp916rlkx2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fekk8n95oyqfp916rlkx2.png" alt="Flex will size automatically according to the content"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code for Grid requires a &lt;code&gt;repeat()&lt;/code&gt; function to be defined on the container’s &lt;code&gt;grid-template-columns&lt;/code&gt; property. The &lt;code&gt;repeat&lt;/code&gt; function accepts two values. The first for our purpose is either &lt;code&gt;auto-fill&lt;/code&gt; or &lt;code&gt;auto-fit&lt;/code&gt;. And the second is a &lt;code&gt;minmax&lt;/code&gt; function, which accepts a value with a fixed unit, and another fixed or relative value.&lt;/p&gt;

&lt;p&gt;All together it might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give each item a base width of 200px that will scale up as needed.&lt;/p&gt;

&lt;p&gt;For Flexbox, just add &lt;code&gt;flex-wrap: wrap&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: Depends on the use case, but Flexbox is a lot simpler. 🙃&lt;/p&gt;

&lt;h2&gt;
  
  
  Container Control vs Item Control
&lt;/h2&gt;

&lt;p&gt;There may be some cases — such as the boundary between two components — where you only want to modify the parent container’s CSS or the children’s CSS.&lt;/p&gt;

&lt;p&gt;If you have control over the parent element, use Grid, which defines the children with &lt;code&gt;grid-template-*&lt;/code&gt; and &lt;code&gt;grid-auto-*&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have control over the child elements, use Flexbox, where the children define their own &lt;code&gt;flex-basis&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you must use Flexbox for some other reason, you can still define a CSS variable on the parent that will be inherited by the children.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: Depends on the use case, but Flexbox gets a point for versatility. Personally I like having the ability to control all the items on the parent in one place whenever possible, so Grid gets a point too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested Layout
&lt;/h2&gt;

&lt;p&gt;One killer CSS Grid feature that’s only landed in Firefox is &lt;code&gt;subgrid&lt;/code&gt;. This allows a child grid item to define its grid children according to the same track definition as the top level parent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ft4ax5rwen9tto0j00mfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ft4ax5rwen9tto0j00mfn.png" alt="Subgrid and without Subgrid example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you had a set of items that internally are split into a top and bottom section with variable amounts of content, you could define two repeating &lt;code&gt;auto&lt;/code&gt; grid tracks at the top level. Then the middle-level child items take up two each of the repeating rows. Then they pass down the track definition so that the top part receives one &lt;code&gt;auto&lt;/code&gt; track and the bottom part receives the other. Now, across the items, the split will be at the same place, so internally they all line up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: Grid, but you’ll still have uneven layouts in any browser other than Firefox.&lt;/p&gt;

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

&lt;p&gt;For most other use cases, especially when working with one track in one direction with no spacing between items, I would be comfortable using either. That goes for situations where you need vertical centering and relative sizing.&lt;/p&gt;

&lt;p&gt;But I find it easier to reason about how the children should be laid out when I’m defining them in one place on the parent element — so Grid wins out most of the time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overall Winner&lt;/strong&gt;: Grid, but know its limitations!&lt;/p&gt;

&lt;p&gt;As I mentioned, Firefox has taken the lead in this area. Please ⭐️ / CC yourself on these issues on Chromium and WebKit to make both Flexbox and Grid more powerful in the future!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gap&lt;/code&gt; for Flexbox (Chromium): &lt;a href="https://bugs.chromium.org/p/chromium/issues/detail?id=762679" rel="noopener noreferrer"&gt;https://bugs.chromium.org/p/chromium/issues/detail?id=762679&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gap&lt;/code&gt; for Flexbox (WebKit): &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=206767" rel="noopener noreferrer"&gt;https://bugs.webkit.org/show_bug.cgi?id=206767&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;subgrid&lt;/code&gt; (Chromium): &lt;a href="https://bugs.chromium.org/p/chromium/issues/detail?id=618969" rel="noopener noreferrer"&gt;https://bugs.chromium.org/p/chromium/issues/detail?id=618969&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;subgrid&lt;/code&gt; (WebKit): &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=202115" rel="noopener noreferrer"&gt;https://bugs.webkit.org/show_bug.cgi?id=202115&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Web We Share</title>
      <dc:creator>Michael Puckett</dc:creator>
      <pubDate>Tue, 31 Dec 2019 23:17:52 +0000</pubDate>
      <link>https://dev.to/mpuckett/the-web-we-share-55bp</link>
      <guid>https://dev.to/mpuckett/the-web-we-share-55bp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;It’s a tale from another dimension: HTML markup representing not just a graphical experience but also virtual objects and their state.&lt;/p&gt;

&lt;p&gt;HTML allows for a single element to have multiple meanings for different use cases.&lt;/p&gt;

&lt;p&gt;You could imagine your markup as a topology with multiple layers of semantics. Or, as 3 different dimensions: visual, accessible, and machine-readable.&lt;/p&gt;

&lt;p&gt;(And with WebXR and other emerging technologies being grafted onto the web platform, there will soon be more dimensions to consider! Spooky!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Visual
&lt;/h2&gt;

&lt;p&gt;An element’s &lt;code&gt;class&lt;/code&gt; and &lt;code&gt;style&lt;/code&gt; attributes are most relevant to the rendering engine that displays the visual interface. For a lot of web developers, this is just the air we breathe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility
&lt;/h2&gt;

&lt;p&gt;An element's &lt;code&gt;role&lt;/code&gt; and &lt;code&gt;aria‑*&lt;/code&gt; attributes are most relevant to screen readers.&lt;/p&gt;

&lt;p&gt;Never heard of a screen reader? Most blind people don't use a mouse. Instead, they use a keyboard to operate an application that speaks the content and metadata of the currently focused element. People who are deaf-blind might use a screen reader with a refreshable Braille display.&lt;/p&gt;

&lt;p&gt;When using a screen reader, properly marked up elements can be acted upon in unique ways. For instance, marking up all headings using heading elements (h1-h6) will compile them and make them available as a list for easier page navigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interoperability
&lt;/h2&gt;

&lt;p&gt;Metadata attributes such as &lt;code&gt;itemprop&lt;/code&gt; are most relevant to digital assistants and search engines, for things like indexing and repurposing.&lt;/p&gt;

&lt;p&gt;Audio-enabled digital assistants often parse web pages' structured data and document outlines to find information. The relevant data can be packaged into an experience that is native to that service's voice UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Engineering Strategies
&lt;/h2&gt;

&lt;p&gt;To account for the wide range of ways HTML code can be utilized, always adhere to the HTML, ARIA, and Schema.org specifications.&lt;/p&gt;

&lt;p&gt;In my experience, building a library of reusable, spec-compliant web components is the best way to deliver beautiful, accessible, interoperable websites.&lt;/p&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>semanticweb</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
