<?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: Karl Stolley</title>
    <description>The latest articles on DEV Community by Karl Stolley (@stolley).</description>
    <link>https://dev.to/stolley</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%2F21094%2Fa014c8e5-991b-421d-840a-c47381439924.jpg</url>
      <title>DEV Community: Karl Stolley</title>
      <link>https://dev.to/stolley</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stolley"/>
    <language>en</language>
    <item>
      <title>Properly Configuring Nightwatch to Run Geckodriver</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Sat, 26 Mar 2022 22:05:55 +0000</pubDate>
      <link>https://dev.to/stolley/properly-configuring-nightwatch-to-run-geckodriver-1134</link>
      <guid>https://dev.to/stolley/properly-configuring-nightwatch-to-run-geckodriver-1134</guid>
      <description>&lt;p&gt;I’ll get right to it: both the stock Nightwatch configuration file (as of at least &lt;a href="https://nightwatchjs.org"&gt;Nightwatch&lt;/a&gt; v. 2.0.9) and the Nightwatch docs are inaccurate for using &lt;a href="https://github.com/mozilla/geckodriver"&gt;the geckodriver web driver&lt;/a&gt; (specifically, v. 0.30.0) to run tests. Here is what you need to do, isolated to the &lt;code&gt;firefox&lt;/code&gt; environment portion of the &lt;code&gt;test_settings&lt;/code&gt; object in a &lt;code&gt;nightwatch.conf.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// snip, snip, snippety-snip&lt;/span&gt;

  &lt;span class="na"&gt;test_settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// more snipping...&lt;/span&gt;

    &lt;span class="na"&gt;firefox&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;capabilities&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;browserName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firefox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;acceptInsecureCerts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moz:firefoxOptions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="c1"&gt;// '-headless',&lt;/span&gt;
            &lt;span class="c1"&gt;// '-verbose'&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 'media.navigator.permission.disable': true,&lt;/span&gt;
            &lt;span class="c1"&gt;// 'media.navigator.streams.fake': true&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="na"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;start_process&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;server_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4444&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;cli_args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="c1"&gt;// very verbose geckodriver logs&lt;/span&gt;
          &lt;span class="c1"&gt;// '-vv'&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Curious about what’s going on? I’ll elaborate, property by significant property:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The property you want is &lt;code&gt;capabilities&lt;/code&gt;, not &lt;code&gt;desiredCapabilities&lt;/code&gt;. The &lt;code&gt;capabilities&lt;/code&gt; property is what ultimately shipped in the &lt;a href="https://www.w3.org/TR/webdriver/#capabilities"&gt;WebDriver specification&lt;/a&gt;. While it seems that both geckodriver and &lt;a href="https://chromedriver.chromium.org/capabilities"&gt;chromedriver&lt;/a&gt; continue to support the older &lt;code&gt;desiredCapabilities&lt;/code&gt;, &lt;code&gt;capabilities&lt;/code&gt; is the property you want now and into the future.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;acceptInsecureCerts&lt;/code&gt; property should be a member directly on &lt;code&gt;capabilities&lt;/code&gt;; this too is in the &lt;a href="https://www.w3.org/TR/webdriver/#dfn-insecure-tls-certificates"&gt;WebDriver spec&lt;/a&gt;. And in testing today, it seems that some combo of Nightwatch and geckodriver ignore &lt;code&gt;acceptInsecureCerts&lt;/code&gt; in any other location.&lt;/li&gt;
&lt;li&gt;For Firefox-specific options (like command-line &lt;code&gt;args&lt;/code&gt; and browser about:config &lt;code&gt;prefs&lt;/code&gt;), the property to use is &lt;code&gt;moz:firefoxOptions&lt;/code&gt;. That is shipping in &lt;a href="https://github.com/nightwatchjs/nightwatch/blob/01c3f12270218eac7345767c14de5f073e6ae500/lib/runner/cli/nightwatch.conf.ejs"&gt;the latest default &lt;code&gt;nightwatch.conf.js&lt;/code&gt; file&lt;/a&gt;, as is the parallel &lt;code&gt;goog:chromeOptions&lt;/code&gt; for chromedriver. But if you've got a legacy configuration file—like I did—you'll want to update the old &lt;code&gt;chromeOptions&lt;/code&gt; property, too.&lt;/li&gt;
&lt;li&gt;For local testing, you’ll want to set an IPv4 address to your local loopback for the &lt;code&gt;host&lt;/code&gt; property on &lt;code&gt;webdriver&lt;/code&gt;. On the Macs I was working on (one Big Sur, one Monterey), &lt;code&gt;localhost&lt;/code&gt; was resolving to the IPv6 &lt;code&gt;::1&lt;/code&gt; adddress. Geckodriver was refusing the connection on that, as I discovered by running &lt;code&gt;wget&lt;/code&gt; out of desperation. &lt;code&gt;wget&lt;/code&gt; then retried on &lt;code&gt;127.0.0.1&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nv"&gt;$ &lt;/span&gt;geckodriver
  1648330755779 geckodriver INFO    Listening on 127.0.0.1:4444
  &lt;span class="c"&gt;# separate terminal process, with geckodriver still running:&lt;/span&gt;
  &lt;span class="nv"&gt;$ &lt;/span&gt;wget http://localhost:4444/
  &lt;span class="nt"&gt;--2022-03-26&lt;/span&gt; 16:49:18--  http://localhost:4444/
  Resolving localhost &lt;span class="o"&gt;(&lt;/span&gt;localhost&lt;span class="o"&gt;)&lt;/span&gt;... ::1, 127.0.0.1
  Connecting to localhost &lt;span class="o"&gt;(&lt;/span&gt;localhost&lt;span class="o"&gt;)&lt;/span&gt;|::1|:4444... failed: Connection refused.
  Connecting to localhost &lt;span class="o"&gt;(&lt;/span&gt;localhost&lt;span class="o"&gt;)&lt;/span&gt;|127.0.0.1|:4444... connected.
  HTTP request sent, awaiting response... 405 Method Not Allowed
  2022-03-26 16:49:18 ERROR 405: Method Not Allowed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 405 error is expected: there's no meaningful resource on the geckodriver for &lt;code&gt;GET /&lt;/code&gt;. But the Nightwatch test-runner (I assume) did not attempt such a retry. Instead, it left behind a seemingly impossible error message of &lt;code&gt;Failed to connect to GeckoDriver on 127.0.0.1 with port 4444&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus information:&lt;/strong&gt; geckodriver now supports parallel testing, where two browsers can be open once! This is important for me, as I’m using Nightwatch to test out the code I’ve been writing for my &lt;a href="https://pragprog.com/titles/ksrtc/programming-webrtc/"&gt;book on WebRTC&lt;/a&gt;. I’ll save the full instructions for how to pull off parallel tests in full for another post, but essentially, you just need two different environments (I have &lt;code&gt;firefoxActive&lt;/code&gt; and &lt;code&gt;firefoxPassive&lt;/code&gt;, rather than just &lt;code&gt;firefox&lt;/code&gt;). And each environment must have its own webdriver process on its own port. So &lt;code&gt;4444&lt;/code&gt; (the default) on one, and say &lt;code&gt;4445&lt;/code&gt; on another. When you go to run nightwatch, pass in the &lt;code&gt;-e&lt;/code&gt; argument with the two environments: &lt;code&gt;npx nightwatch -e firefoxActive,firefoxPassive&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>nightwatch</category>
      <category>nightwatchjs</category>
      <category>geckodriver</category>
    </item>
    <item>
      <title>WebRTC Book Update: Revised TOC and Some More Legroom</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Mon, 31 May 2021 16:08:19 +0000</pubDate>
      <link>https://dev.to/stolley/webrtc-book-update-revised-toc-and-some-more-legroom-5cn6</link>
      <guid>https://dev.to/stolley/webrtc-book-update-revised-toc-and-some-more-legroom-5cn6</guid>
      <description>&lt;p&gt;It’s the end of May, and I’m now about ten weeks into the contracted part of this project for &lt;a href="https://pragprog.com"&gt;Pragmatic Programmers&lt;/a&gt;. And in the last week, the book has grown from its original proposed 6 chapters at 150 pages to 8 chapters that will weigh in at around 250 pages. That gives me a lot more room to work and should prevent anything vital from having to be cut.&lt;/p&gt;

&lt;p&gt;As I put the finishing touches this week on the work for my first major milestone—the three-chapter publisher’s review, which I’d originally planned for mid-June—the table of contents its looking like this, minus an introductory setup chapter that’ll be part of the frontmatter and an appendix showing a signaling channel written in pure WebSockets (the book itself uses &lt;a href="https://socket.io"&gt;Socket.IO&lt;/a&gt; in its recently released &lt;a href="https://socket.io/blog/socket-io-4-release/"&gt;version 4&lt;/a&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Working with a Signaling Channel.&lt;/strong&gt; Readers set up an interface in HTML, CSS, and JavaScript to manage a basic video-chat app and wire up a pre-built signaling channel and its callbacks. A high-level description of WebRTC contextualizes the signaling channel’s purpose and limited features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Establishing a Peer-to-Peer Connection.&lt;/strong&gt; Picking up right where the last chapter left off, readers learn to request permissions for camera/mic access and display the resulting stream. They then go on to create a peer connection using the “perfect negotiation” pattern &lt;a href="https://www.w3.org/TR/webrtc/#perfect-negotiation-example"&gt;described in the WebRTC specification&lt;/a&gt; and build out the rest of the basic video-chat app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handling Data Channels.&lt;/strong&gt; Media streaming is WebRTC’s most famous feature, but it’s data channels that really open up the possibilities for all kinds of real-time, peer-to-peer apps. Readers add a video-filter function to the app built over the first two chapters and then build a complete text-chat feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming Complex Data.&lt;/strong&gt; Data channels can handle more than simple strings. This chapter opens with building fallbacks into the perfect-negotiation code so they can more confidently test their work, including especially with data channels, on browsers that have poorer implementations of WebRTC (ahem, Safari). Readers work with sending and receiving JSON strings to build out the chat feature further, and then turn to streaming binary data—as either Blobs or ArrayBuffers, using some clever feature-detection—to share images in the chat. Readers build a standalone peer-to-peer file-sharing app to close out the chapter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building Peer-to-Peer Interfaces.&lt;/strong&gt; The first four chapters cover all the fundamentals of WebRTC. This chapter looks more closely at real-time, peer-to-peer interface design—all framed by accessibility. Readers learn to build interfaces that take advantage of semantic HTML and ARIA attributes, while also giving users greater control over real-time interfaces, such as arranging interface elements in relation to their cameras.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managing Multi-Peer Connections.&lt;/strong&gt; This chapter moves from one-to-one to many-to-many WebRTC apps. Sticking to the browser-based focus of the book, it introduces a mesh-network architecture for connecting three or more peers in a single call. Readers also learn the practical and theoretical upper limits to the number of simultaneous peer connections, and begin to work with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats"&gt;the &lt;code&gt;RTCPeerConnection.getStats()&lt;/code&gt; interface&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimizing Media Streaming.&lt;/strong&gt; Readers take a deep dive on different media APIs in this chapter, and learn to optimize streaming media based on local and remote statistics and even the simple appearance of media streams in the browser (how big does that video stream need to be? how small can it get?). The chapter also looks at current and forthcoming methods for optimizing images sent over data channels, including &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap"&gt;the exciting but currently not universally supported&lt;/a&gt; &lt;code&gt;self.createImageBitmap()&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploying WebRTC Apps to Production.&lt;/strong&gt; This final chapter of the book looks at what it takes to deploy a WebRTC app into production. Readers learn about using public STUN servers as well as setting up a private STUN/TURN server, with the TURN component as a fallback for when a direct peer-to-peer connection cannot be established.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are a lot of topics packed into chapters six, seven, and eight especially. So I wouldn’t be surprised to discover a topic that deserves its own chapter—either on my own as I write or from feedback that’ll come in from the technical and beta reviews later in the summer.&lt;/p&gt;

&lt;p&gt;The first three chapters are draft complete, apart from some intro and outro work that I need to do today. And I’ve got good chunks of the fourth and sixth chapters coming along, in addition to a growing number of topics for the fifth chapter, on interfaces and accessibility. &lt;/p&gt;

&lt;p&gt;Put another way, this is starting to feel more like a book to me—and not just a collection of stuff I’ve written. Considering that the spring semester just ended for me a little over two weeks ago, I’m pretty happy with where I am. But I’m hoping to pick up the pace as I recover from this weird academic year and settle into the summer.&lt;/p&gt;

</description>
      <category>devto</category>
      <category>webrtc</category>
      <category>pragprog</category>
      <category>writing</category>
    </item>
    <item>
      <title>Working to Get Off the Sidelines</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Tue, 18 May 2021 20:48:00 +0000</pubDate>
      <link>https://dev.to/stolley/working-to-get-off-the-sidelines-262g</link>
      <guid>https://dev.to/stolley/working-to-get-off-the-sidelines-262g</guid>
      <description>&lt;p&gt;For the last few weeks, the brisk progress I’d been making on &lt;a href="https://stolley.dev/im-writing-a-book-about-webrtc-for-pragmatic-programmers/"&gt;my WebRTC book&lt;/a&gt; basically ground to a halt thanks to my “day job” (as my development editor whimsically puts it). My day job is being a professor, and there’s really no worse time of year than mid-April until final grades get turned in. That period isn’t just about helping students over the finish line, but also accommodating a bunch of panicky colleagues and administrators scheduling last-minute meetings before people start disappearing for the summer.&lt;/p&gt;

&lt;p&gt;Anyway. While I’ve not written much at all on the manuscript, I decided to take what time I could manage to work on my own organization, particularly all of the thousands of lines of source code I’m writing that’ll accompany the book.&lt;/p&gt;

&lt;p&gt;Writing code for yourself or for your job is usually a bit different from writing code to teachothers. Even the code that I write in order to teach in the classroom is different from code I write to accompany books and articles: my usual way of prepping for code-intensive classes is to do a dry run and then have skeletal notes or sometimes more complete examples to refer from, if I’m teaching something extra complex and am likely to forget some small but essential detail in the middle of class. I don’t like just walking through canned, finished examples in class: live coding is how I roll.&lt;/p&gt;

&lt;p&gt;In an instructional setting, even if it’s online because of the pandemic, there’s always the chance to go back and fix something later or, in the best cases, improve something on the spot in response to a student question or point of confusion. Writing code live makes for a better class than a shitty slide deck with completed examples, I think, but it also helps students sort of wrap their heads around the timescales and especially the &lt;em&gt;process&lt;/em&gt; of writing code. As every developer knows, the process is never linear.&lt;/p&gt;

&lt;p&gt;But when you’re writing code for a book or an article, you don’t have the luxury of refining things once the piece is published. Throughout the writing process, you’re building a foundation and guiding someone down a path that you, as the author, will have finished before the reader even starts out. And that means that you’ve got to do a lot more planning, as an author and a developer,to figure out what the reader’s journey is going to look like.&lt;/p&gt;

&lt;p&gt;To smooth the way for themselves, the authors of some books and articles—&lt;em&gt;too many&lt;/em&gt; books and articles, I’ll add—basically set up the code equivalent of a straw person: early examples so amateurishly written and poorly formed that they’re basically laughable. Of course, just like the straw-person argument, it’s easy to swoop in as the hero-author and make yourself look amazing by fixing crappy, amateurish code.&lt;/p&gt;

&lt;p&gt;I don’t like that approach, though, and I avoid it in my own work. And this is why: from years of both reading a lot of these books as well as teaching them, I can say that the first examples readers see tend to make the deepest impressions. I notice this in my own work, when I make a mistake that a book deliberately showed me and only &lt;em&gt;then&lt;/em&gt; admonished me not to make. I see the same thing happen in student work too, either when they repeat a mistake that they were shown in a book or a mistake that I showed off in class before clarifying it as a mistake. That pattern is annoying for readers, too: &lt;em&gt;Why did we just spend all that time talking about this thing that you should never do?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So rather than starting from crappy code, I like to start with reasonably good code—which, just like the running copy of anything I write, is never the code that I draft.&lt;/p&gt;

&lt;p&gt;The trick I’ve found, and the thing I’ve been working on for the last few weeks with my book, is to write the absolute best code you can on your own. And then you dial it back a few notches from there. Maybe that fancy ternary operator has to go, or that little shortcut method or piece of syntactic sugar: anything that would take time and space too much to explain to readers, especially when they’re first starting out. It doesn’t mean that you won’t ultimately get the code whipped into that shape, but that you don’t have to start there—and leave readers feeling lost by all the ancillary, look-how-smart-the-author-is stuff that can wait.&lt;/p&gt;

&lt;p&gt;I do think it’s useful, however, to point out common mistakes after walking through a piece of code—especially when readers are expected to write it out themselves and run it. Beginners trying to pass a callback function in by reference in JavaScript, for a simple example, will very often include parentheses, so that the correct code &lt;code&gt;on('event', callbackFunction);&lt;/code&gt; gets miswritten as&lt;code&gt;on('event', callbackFunction());&lt;/code&gt;. It’s an easy mistake for beginners to make, because they’re used to seeing functions with parentheses attached, either the function definition— &lt;code&gt;function someFunction() { }&lt;/code&gt; —or the place in the code where the function is called: &lt;code&gt;someFunction()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The catch, of course, is when you as an author make a mistake in your own code. Alongside all of this, I’ve also been upping my automated testing game. (That’s included me bringing &lt;a href="https://nightwatchjs.org"&gt;Nightwatch.js&lt;/a&gt; to the party. Testing WebRTC apps means having multiple, simultaneous browsers open for the duration of the test. Chrome can do that, but Gecko’s web driver implementation seems to be lagging somewhat. But this is all material for a whole other post.) Once I’ve got good-enough draft examples, I can’t help but continue to improve them. Sometimes the improvement is just about the code itself, but other times it’s about making the text read more gracefully. But even for books or articles, there’s no confidence in refactoring code without good test coverage. You’ve really not experienced BDD in full until you’re writing tests on code that you know will evolve a particular way over the length of a book.&lt;/p&gt;

&lt;p&gt;One of the things I’m finding challenging about writing WebRTC code over a book-length work is that ideally the signaling and connection logic works completely separately and independently from any application logic. The application logic is what carries the examples throughout my book. At the same time, it’s too much to ask readers to write, say, fallbacks for older browsers in the signaling and connection code when they’re just getting started. So it’s necessary for me to keep track of gradual improvements to be made to the signaling and connection logic, even as I’m working through different examples with readers as they progress deeper into the book.&lt;/p&gt;

&lt;p&gt;The latest strategy I’ve employed is to write the best, most thoroughly tested examples I can before I even begin to write a chapter. Then, as I write the chapter, I’m re-writing the completed example just as I expect a reader will. That frequently gives me insight as to the fancier or more complicated parts of my completed examples that I can save to guide readers through later.&lt;/p&gt;

&lt;p&gt;So, that’s where I am. I’m ready to get back to churning out the running content of the book. And while I wish I had a few thousand more words to show for the last few weeks, I’m feeling pretty good that this code work is going to pave the way for me to better focus on how the book is put together—and how readers will work through it.&lt;/p&gt;

</description>
      <category>webrtc</category>
    </item>
    <item>
      <title>I'm Writing a Book about WebRTC for Pragmatic Programmers</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Sun, 21 Mar 2021 23:25:30 +0000</pubDate>
      <link>https://dev.to/stolley/i-m-writing-a-book-about-webrtc-for-pragmatic-programmers-1ij5</link>
      <guid>https://dev.to/stolley/i-m-writing-a-book-about-webrtc-for-pragmatic-programmers-1ij5</guid>
      <description>&lt;p&gt;I went from not even realizing I was going to write a book to having a signed contact with PragProg in 82 days: December 17, 2020 was the first day I worked in earnest on the proposal, and March 9, 2021 was the day I received and signed the contract.&lt;/p&gt;

&lt;p&gt;I’d only just submitted the proposal itself eight days earlier, on March 1. At 9:18am. I note the time, because it was just 28 minutes later that I heard from an Author Relations contact at PragProg with some of the best words a writer can hear:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You’ve done a great job with the proposal and sample chapter, so I think I can present your proposal without any revisions. It’s clear that you get the PragProg style and I’m grateful for that fact and happy to hear you are using PragProg books in your classroom!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Yes, I use PragProg books in the classroom all the time, and no, I wasn’t ashamed to slip that little detail into my proposal itself.)&lt;/p&gt;

&lt;p&gt;Since signing the contract, I’ve just been getting up to speed with Prag’s publication system—which I’m contractually bound not to discuss in detail, and I can see why. It’s pretty great. I’ve also begun working with my development editor, and I can say that this is book project is fixing to be whatever the opposite of lonely is. It’s an incredibly collaborative effort already, and I’ve only just gotten started.&lt;/p&gt;

&lt;p&gt;Okay, but what about the book? What’s it actually about? Let me try out my elevator pitch on you:&lt;/p&gt;

&lt;p&gt;You’re a web developer. You’ve spent the pandemic racking up untold hours on Google Meet, Zoom, FaceTime, or whatever real-time video-chat service. But what you might not know is that all modern browsers ship with an implementation of &lt;a href="https://www.w3.org/TR/webrtc/"&gt;a web API called WebRTC: Web Real-Time Communication Between Browsers&lt;/a&gt;. WebRTC allows you to build real-time applications that run directly in the browser. And sort of like with FaceTime or whatever, you can build your own video-chat app. Only unlike all of those popular services, WebRTC works peer to peer: the app you build directly connects one web browser to another. There’s no server involved. And video chatting is only just the most obvious example. With WebRTC, you can open up channels to exchange any arbitrary data that might power all kinds of real-time activities: text-based chats, secure peer-to-peer file transfers, collaborative brainstorming sessions, and even multiplayer gaming. This book will have you doing all of that. And not just with two connected peers: an entire chapter of the book is devoted to engineering multi-peer WebRTC apps, using a mesh-network architecture.&lt;/p&gt;

&lt;p&gt;Okay. So that needs some work. But hopefully the gist of it comes across.&lt;/p&gt;

&lt;p&gt;So where did the book come from? Last fall, I taught &lt;a href="https://courses.stolley.co/rtc/"&gt;a course on WebRTC&lt;/a&gt; for the first time. As I wrote in the proposal, putting that class together didn’t go very smoothly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The biggest challenge I faced in putting together my WebRTC class over the last year was the lack of a suitable, up-to-date book for teaching or engaging in self-study of WebRTC—especially in the context of web development. As I detail in the competing books section below, there are a few rapidly aging titles on the market that are now little more than works of history. There is little, in book form or otherwise, that offers a substantial, pragmatic application of the WebRTC specification as it has matured since achieving Candidate Recommendation status in late 2017. (The spec became a full W3C Recommendation on January 26, 2021, while I was preparing this proposal).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Having scoured the web for as many articles and resources as I could to try to structure my class, I still had to write a lot of material for students—especially source-code examples. It was only after the fog had lifted at the end of fall semester that I realized I might have the material necessary to actually write a book about the topic.&lt;/p&gt;

&lt;p&gt;It was really important to me to have the class structured around the native WebRTC APIs found in the browser, rather than rely on a third-party library. The book will be the same, native-API deal: although I’ll be using Socket.IO as a signaling server, all of the client-side code uses nothing but the available browser APIs (occasionally with an assist from the &lt;a href="https://github.com/webrtcHacks/adapter"&gt;adapter.js&lt;/a&gt; to shim certain browsers as needed).&lt;/p&gt;

&lt;p&gt;So that’s my news. I’ll be posting about the book here as I write it. And you’re welcome to message me about it on Twitter &lt;a href="https://twitter.com/stolleydotdev"&gt;@stolleydotdev&lt;/a&gt;, of course.&lt;/p&gt;

</description>
      <category>webrtc</category>
    </item>
    <item>
      <title>Dynamic Namespaces in Socket.io</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Fri, 30 Oct 2020 23:42:55 +0000</pubDate>
      <link>https://dev.to/stolley/dynamic-namespaces-in-socket-io-35o6</link>
      <guid>https://dev.to/stolley/dynamic-namespaces-in-socket-io-35o6</guid>
      <description>&lt;p&gt;I’m teaching &lt;a href="https://socket.io/"&gt;socket.io&lt;/a&gt; as a convenient WebRTC signaling channel in &lt;a href="https://courses.stolley.co/rtc/"&gt;my WebRTC class&lt;/a&gt; this semester. As part of prepping that, I finally had to sit down and figure out dynamic namespaces in socket.io. There is some really tricky business to namespaces, dynamic or otherwise, particularly when it comes to listening for messages and events server-side sent from connected clients on the namespace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short, namespaced sockets work differently on the client from how they do on the server.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the client, we simply create a namespaced socket connection (&lt;code&gt;ns&lt;/code&gt;, here with a Google Meet–like namespace of &lt;code&gt;/jkl-mnop-qrs&lt;/code&gt;) and both listen and emit events on it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Client-side code (site.js)&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/jkl-mnop-qrs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ordinarily set dynamically in JavaScript, somehow&lt;/span&gt;

&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Message received: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nb"&gt;document&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&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;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Body was clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// the `send()` method is essentially shorthand for `emit('message', data)`;&lt;/span&gt;
  &lt;span class="c1"&gt;// that is, the `send()` method emits the `message` event for us:&lt;/span&gt;
  &lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Someone clicked the body element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that example, the listener &lt;code&gt;ns.on(...)&lt;/code&gt; handles incoming messages and sends a pre-determined message to the socket server when someone clicks anywhere in the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element. Both use the &lt;code&gt;ns&lt;/code&gt; object created from calling the &lt;code&gt;io()&lt;/code&gt; constructor.&lt;/p&gt;

&lt;p&gt;On the server, though, it’s a completely different, more complicated story.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server-side code (app.js)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;namespaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\/[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]{3}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]{4}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]{3}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;namespaces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socket&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;namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// You can emit messages directly on the `namespace` object...&lt;/span&gt;
  &lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Successfully connected on namespace: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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="c1"&gt;// ...BUT--BUT, BUT, BUT--you must listen for messages coming from the clients&lt;/span&gt;
  &lt;span class="c1"&gt;// on the socket (`socket`) object, NOT the namespace:&lt;/span&gt;
  &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A message was received from a client: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// AND if you want to do a broadcast emit -- which sends the message to&lt;/span&gt;
    &lt;span class="c1"&gt;// all the connected clients *except* for the sender -- you MUST use the&lt;/span&gt;
    &lt;span class="c1"&gt;// socket object (`socket`), as the `namespace` does not understand the&lt;/span&gt;
    &lt;span class="c1"&gt;// `broadcast` method:&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To summarize the content of the comments: you listen for connections on the namespaces that match the pattern in &lt;code&gt;socket.of(...)&lt;/code&gt;. You can emit messages on the &lt;code&gt;namespace&lt;/code&gt; returned by &lt;code&gt;namespaces.on(...)&lt;/code&gt;, but you &lt;strong&gt;cannot&lt;/strong&gt; listen for incoming messages or any other events on &lt;code&gt;namespace&lt;/code&gt;. Instead, you listen on the socket object (&lt;code&gt;socket&lt;/code&gt;) created on the connection event.&lt;/p&gt;

&lt;p&gt;Additionally, if you want to broadcast a message (which sends the message to all connected clients except the sending client), you must use &lt;code&gt;socket.broadcast.emit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So a simplified version of the code above looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server-side code (app.js)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;namespaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\/[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]{3}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]{4}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]{3}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;namespaces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socket&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;namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Successfully connected on namespace: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A message was received from a client: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the &lt;code&gt;namespace&lt;/code&gt; variable is only being used for diagnostic purposes (&lt;code&gt;Successfully connected on namespace: ${namespace.name}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Everything else is listening or emitting on the socket object (&lt;code&gt;socket&lt;/code&gt;) returned to the initial &lt;code&gt;namespaces.on(...)&lt;/code&gt; callback on each client connection.&lt;/p&gt;

&lt;p&gt;The thing that is tricky to grasp (and that cost me about 3 hours of my life) is that that &lt;code&gt;socket&lt;/code&gt; object is unique to the connection on each namespace. This is not properly reflected ANYWHERE in &lt;a href="https://socket.io/docs/namespaces/"&gt;socket.io’s documentation&lt;/a&gt;, which makes me crazy. And yes, I should write something and submit a pull request. I know.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>WCAG 2.1's Seventeen New Success Criteria</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Wed, 15 Jul 2020 17:38:02 +0000</pubDate>
      <link>https://dev.to/stolley/wcag-2-1-s-seventeen-new-success-criteria-3189</link>
      <guid>https://dev.to/stolley/wcag-2-1-s-seventeen-new-success-criteria-3189</guid>
      <description>&lt;p&gt;I didn’t try my very hardest to find a list of all of the new Success Criteria in &lt;a href="https://www.w3.org/TR/WCAG21/"&gt;Web Content Accessibility Guidelines 2.1 (WCAG 2.1)&lt;/a&gt; that were not a part of &lt;a href="https://www.w3.org/TR/WCAG20/"&gt;WCAG 2.0&lt;/a&gt;, but if there is such a list, it’s not easy to find.&lt;/p&gt;

&lt;p&gt;And given the formatting for WCAG 2.0, it’s not all that easy to make such a list, either. But in case this is helpful to anyone, here are the seventeen new Success Criteria in WCAG 2.1, which became a W3C Recommendation in June 2018, with links to each criterion in the specification:&lt;/p&gt;

&lt;h3&gt;
  
  
  1: &lt;a href="https://www.w3.org/TR/WCAG21/#perceivable"&gt;Perceivable&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;h4&gt;
  
  
  1.3: Adaptable
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;1.3.4: &lt;a href="https://www.w3.org/TR/WCAG21/#orientation"&gt;Orientation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;1.3.5: &lt;a href="https://www.w3.org/TR/WCAG21/#identify-input-purpose"&gt;Identify Input Purpose&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;1.3.6: &lt;a href="https://www.w3.org/TR/WCAG21/#identify-purpose"&gt;Identify Purpose&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h4&gt;
  
  
  1.4: Distinguishable
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;1.4.10: &lt;a href="https://www.w3.org/TR/WCAG21/#reflow"&gt;Reflow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;1.4.11: &lt;a href="https://www.w3.org/TR/WCAG21/#non-text-contrast"&gt;Non-text Contrast&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;1.4.12: &lt;a href="https://www.w3.org/TR/WCAG21/#text-spacing"&gt;Text Spacing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;1.4.13: &lt;a href="https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus"&gt;Content on Hover or Focus&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  2: &lt;a href="https://www.w3.org/TR/WCAG21/#operable"&gt;Operable&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;h4&gt;
  
  
  2.1: Keyboard Accessible
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;2.1.4: &lt;a href="https://www.w3.org/TR/WCAG21/#character-key-shortcuts"&gt;Character Key Shortcuts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h4&gt;
  
  
  2.2: Enough Time
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;2.2.6: &lt;a href="https://www.w3.org/TR/WCAG21/#timeouts"&gt;Timeouts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h4&gt;
  
  
  2.3: Seizures and Physical Reactions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;2.3.3: &lt;a href="https://www.w3.org/TR/WCAG21/#animation-from-interactions"&gt;Animation from Interactions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h4&gt;
  
  
  2.5: &lt;a href="https://www.w3.org/TR/WCAG21/#input-modalities"&gt;Input Modalities&lt;/a&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;2.5.1: &lt;a href="https://www.w3.org/TR/WCAG21/#pointer-gestures"&gt;Pointer Gestures&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2.5.2: &lt;a href="https://www.w3.org/TR/WCAG21/#pointer-cancellation"&gt;Pointer Cancellation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2.5.3: &lt;a href="https://www.w3.org/TR/WCAG21/#label-in-name"&gt;Label in Name&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2.5.4: &lt;a href="https://www.w3.org/TR/WCAG21/#motion-actuation"&gt;Motion Actuation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2.5.5: &lt;a href="https://www.w3.org/TR/WCAG21/#target-size"&gt;Target Size&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2.5.6: &lt;a href="https://www.w3.org/TR/WCAG21/#concurrent-input-mechanisms"&gt;Concurrent Input Mechanisms&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  4: &lt;a href="https://www.w3.org/TR/WCAG21/#robust"&gt;Robust&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;h4&gt;
  
  
  4.1: Compatible
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;4.1.3: &lt;a href="https://www.w3.org/TR/WCAG21/#status-messages"&gt;Status Messages&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Note that Section 2.5: &lt;a href="https://www.w3.org/TR/WCAG21/#input-modalities"&gt;Input Modalities&lt;/a&gt; is the only entirely new section, and that there were no additional Success Criteria added under Section 3:&lt;a href="https://www.w3.org/TR/WCAG21/#understandable"&gt;Understandable&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>wcag</category>
    </item>
    <item>
      <title>Hey, Wait: Git Has a New Complaint</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Sun, 28 Jun 2020 05:00:00 +0000</pubDate>
      <link>https://dev.to/stolley/hey-wait-git-has-a-new-complaint-5goi</link>
      <guid>https://dev.to/stolley/hey-wait-git-has-a-new-complaint-5goi</guid>
      <description>&lt;p&gt;When you upgrade to &lt;a href="https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/2.27.0.txt"&gt;Git 2.27&lt;/a&gt;, one of the first changes you’ll notice is Git issuing this dire warning every time you go to pull from a remote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git pull origin main
warning: Pulling without specifying how to reconcile divergent branches is
discouraged. You can squelch this message by running one of the following
commands sometime before your next pull:

  git config pull.rebase false # merge (the default strategy)
  git config pull.rebase true # rebase
  git config pull.ff only # fast-forward only

You can replace "git config" with "git config --global" to set a default
preference for all repositories. You can also pass --rebase, --no-rebase,
or --ff-only on the command line to override the configured default per
invocation.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Almost certainly the best option here is to (probably globally) specify fast-forward only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git config --global pull.ff only

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A fast-forward is what happens under the happiest of circumstances: your local branch has no new changes that aren’t already on the remote, so you’re updating (fast-forwarding) the pointer on the HEAD commit to match the remote. Setting &lt;code&gt;pull.ff only&lt;/code&gt; ensures that that happy behavior continues.&lt;/p&gt;

&lt;p&gt;But there are plenty of unhappy circumstances. The one that most commonly ruins my day is when I’ve been working on the same branch of the same repo on two different computers. I push the commits from Computer A to the remote, but forget to run &lt;code&gt;git pull&lt;/code&gt; from Computer B before writing new commits.&lt;/p&gt;

&lt;p&gt;The default strategy, a merge commit, means you can end up with a repository full of merge commits from your own remote branch. It’s ugly and almost always leads to &lt;a href="https://blog.developer.atlassian.com/stop-foxtrots-now/"&gt;foxtrot commits&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By setting &lt;code&gt;pull.ff only&lt;/code&gt;, Git will no longer race ahead and create a merge commit. You can opt to do so manually (&lt;code&gt;git merge origin/main&lt;/code&gt;), but if you’re not working on a public branch, or if your divergent commits are still local, you can rebase your local branch onto the missing commits from your remote (&lt;code&gt;git rebase origin/main&lt;/code&gt;). They’ll then appear as though they’ve been there the whole time.&lt;/p&gt;

&lt;p&gt;Of course, when it comes to working with branches shared with others, it’s better by far to create your own, independent feature-branches and keep your work isolated, and rebase from the parent branch before pushing the commits on your own branch.&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Cache-busting Assets with sed</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Sat, 11 Apr 2020 05:00:00 +0000</pubDate>
      <link>https://dev.to/stolley/cache-busting-assets-with-sed-3g8m</link>
      <guid>https://dev.to/stolley/cache-busting-assets-with-sed-3g8m</guid>
      <description>&lt;p&gt;Thanks to the ongoing quarantine, I’ve found the time to take on some of the long-standing items on my web dev to-do list. One of these items was to implement cache-busting on CSS and JavaScript assets in coordination with service workers.&lt;/p&gt;

&lt;p&gt;Being a developer with an already strong but still growing aversion to frameworks and taskrunners, especially when all I really need is a simple feature, I prefer mining old-school Unix tools and using them to building out a custom deploy script, usually using a &lt;code&gt;post-receive&lt;/code&gt; hook with Git. It’s hard to find an old-school tool as old as sed, which first appeared in 1974.&lt;/p&gt;

&lt;p&gt;For those who don’t know sed (short for “streaming editor”), &lt;a href="https://www.gnu.org/software/sed/"&gt;its friendly overviewpage&lt;/a&gt; opens with a fine summary:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;sed is commonly used to filter text, i.e., it takes text input, performs some operation (or set of operations) on it, and outputs the modified text. sed is typically used for extracting part of a file using pattern matching or substituting multiple occurrences of a string within a file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that’s what I need: to comb through a static set of HTML files for references like &lt;code&gt;&amp;lt;script&lt;br&gt;
src="assets/js/site.​js"&amp;gt;&lt;/code&gt; and insert a hash so the file referenced becomes something like&lt;code&gt;assets/js/site.0a1cb23.​js&lt;/code&gt;. The file itself remains &lt;code&gt;site.​js&lt;/code&gt; on the server, so I don’t have to worry about moving files around and changing their names. A quick little location-scoped regex rewrite block in Nginx will ignore the hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This assumes assets off of an /assets directory, obviously
location ~ /assets {
  rewrite (.+)\.([a-f0-9]+)\.(js|css)$ $1.$3 break;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To grab a meaningful hash, I need to capture the HEAD commit hash from my site’s repository, using&lt;a href="https://git-scm.com/docs/git-rev-parse"&gt;&lt;code&gt;git rev-parse&lt;/code&gt;&lt;/a&gt;. As a side note, it’s essential to include the argument &lt;code&gt;--git-dir=&amp;lt;bare repo directory&amp;gt;&lt;/code&gt; when trying to run commands on a specific repo—especially bare repos, which I use for all my basic deploy scripts.&lt;/p&gt;

&lt;p&gt;So the relevant part of my deploy script (which is written for zsh and &lt;a href="http://zsh.sourceforge.net/Doc/Release/Expansion.html#Filename-Expansion"&gt;its awesome recursive globbing syntax&lt;/a&gt; looks something 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;cd $TMP_GIT_CLONE
SHORTHASH=`git --git-dir=$TMP_GIT_CLONE/.git rev-parse --short HEAD`;
for file in **/*.html;
sed -E -i "s/=([^:]+)\.(css|js)/=\1.$SHORTHASH.\2/g" $file;
done;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that run, the files have all of their paths updated. I then need to give a similar treatment to my service worker script, which actually needs a variable declaration rewritten.&lt;/p&gt;

&lt;p&gt;For the sake of local development and not hanging on too long to an existing service worker cache, I open my service worker script with these lines JavaScript, which falls back to a &lt;code&gt;mockTenMinuteVersion()&lt;/code&gt; function that generates a new time-based hash every ten minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var VERSION;
const version = VERSION ? VERSION : mockTenMinuteVersion();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefer using the ES 2015 &lt;code&gt;const&lt;/code&gt; declaration, but because &lt;code&gt;const&lt;/code&gt; &lt;a href="https://tc39.es/ecma262/#sec-let-and-const-declarations"&gt;by definition&lt;/a&gt; cannot have its value reassigned, simply declaring &lt;code&gt;const VERSION;&lt;/code&gt; is enough kick out a nasty little syntax error and prevent the rest of the script from running.&lt;/p&gt;

&lt;p&gt;To allow for that, I write the regex on this call to sed to also replace &lt;code&gt;var&lt;/code&gt; with &lt;code&gt;const&lt;/code&gt; and assign the SHORTHASH value to &lt;code&gt;const VERSION&lt;/code&gt; as well. These lines get added to the&lt;code&gt;post-receive&lt;/code&gt; lines listed above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sed -E -i "s/var VERSION/const VERSION = \"$SHORTHASH\"/" _site/sw.​js;
sed -E -i "s/'([^:]+)\.(css|js)/'\1.$SHORTHASH.\2/g" _site/sw.​js;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that changes the opening lines of my service worker script to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const VERSION = "0a1cb23";
const version = VERSION ? VERSION : mockTenMinuteVersion();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, sed needs to do its thing and update the files listed in the manifest of preloaded files togo from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const site_preloaded_assets = {
  essential: [
    '/assets/css/screen.​css',
    '/assets/js/site.​js',
    '/',
    site_offline_path
  ],
  supporting: []
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const site_preloaded_assets = {
  essential: [
    '/assets/css/screen.0a1cb23.css',
    '/assets/js/site.0a1cb23.​js',
    '/',
    site_offline_path
  ],
  supporting: []
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Quoting in HTML: Quotations, Citations, and Blockquotes</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Wed, 11 Dec 2019 03:37:00 +0000</pubDate>
      <link>https://dev.to/stolley/quoting-in-html-quotations-citations-and-blockquotes-k09</link>
      <guid>https://dev.to/stolley/quoting-in-html-quotations-citations-and-blockquotes-k09</guid>
      <description>&lt;p&gt;John Rhea writes a beautifully detailed article about HTML semantics:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s all too common to see the incorrect HTML used for quotes in markup. In this article, let’s dig into all this, looking at different situations and different HTML tags to handle those situations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bonus points for including a creative use for the &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; element for presenting a &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; element with an attendant &lt;code&gt;&amp;lt;cite&amp;gt;&lt;/code&gt;, and likewise the mention of the CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/hanging-punctuation"&gt;&lt;code&gt;hanging-punctuation&lt;/code&gt;&lt;/a&gt; property toward the end of the article.&lt;/p&gt;

</description>
      <category>html</category>
    </item>
    <item>
      <title>`@supports` Shines Brightest on Dependent Styles</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Wed, 13 Nov 2019 06:00:00 +0000</pubDate>
      <link>https://dev.to/stolleydotdev/supports-shines-brightest-on-dependent-styles-ho</link>
      <guid>https://dev.to/stolleydotdev/supports-shines-brightest-on-dependent-styles-ho</guid>
      <description>&lt;p&gt;Feature queries via &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports"&gt;the &lt;code&gt;@supports&lt;/code&gt; CSS at-rule&lt;/a&gt; provide syntax to conditionally apply a set of style declarations when a given feature is supported. It’s common to see web developers test and immediately apply a newer CSS property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@supports(display: grid) {
  main { display: grid; }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But considering that &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Supporting_Older_Browsers#Creating_fallbacks_in_CSS"&gt;browsers ignore CSS that they don’t understand&lt;/a&gt;, such applications of feature queries can be redundant and unncessary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aRw-1ckZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://stolley.dev/assets/img/stolley-co-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aRw-1ckZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://stolley.dev/assets/img/stolley-co-screenshot.png" alt="Screenshot of CSS shapes example"&gt;&lt;/a&gt;&lt;br&gt;
    CSS shapes enable developers to draw outlines around floated content. Text will flow to conform&lt;br&gt;
    to the shape, as shown here. The hyphenation and justification, however, is only desirable when&lt;br&gt;
    CSS shapes are supported.&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;Where feature queries really shine, however, is in preventing dependent styles from being applied. Feature queries are exceptionally suited to applying properties browsers &lt;em&gt;do&lt;/em&gt; understand, but that are only appropriate when a more advanced property is available.&lt;/p&gt;

&lt;p&gt;For a recent example, I started experimenting with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Shapes"&gt;CSS Shapes&lt;/a&gt; on &lt;a href="https://stolley.co/"&gt;a floated image of a Raspberry Pi on my professional website&lt;/a&gt; (Save a visit to the website itself and see the figure).&lt;/p&gt;

&lt;p&gt;I did not use a feature query on &lt;a href="https://github.com/karlstolley/stolley.co/commit/8595b8b4dfa1fb0417648ed2943d9cd269025a06#diff-ce5e030f2e2e2a50f18199464f07ea70"&gt;the &lt;code&gt;shape-outside&lt;/code&gt;property&lt;/a&gt; itself.&lt;/p&gt;

&lt;p&gt;Instead, I used a feature query to prevent applying two other, more well supported features: justified text and hyphenation. &lt;a href="https://github.com/karlstolley/stolley.co/commit/e3fa2f5f8c30e20689d4649045e5b6c5b9fe4071"&gt;The code&lt;/a&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@supports (shape-outside: polygon(0px 0px, 0% 0%)) {
  #about p {
    hyphens: auto;
    text-align: justify;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I don’t want to use hyphens or text justification in the absence of the custom shape I drew around the Pi image. Their purpose is only to help the text better conform to the CSS shape and the image content. In the absence of CSS shapes support, a plain old rectangular float appears. And that’s fine. But there’s no need to bring hyphens or justification to that party, which will only further intensify the boxiness of that older-school design.&lt;/p&gt;

&lt;p&gt;A couple of notes here to close out the post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@supports&lt;/code&gt; rules require not just the property but also a valid value. The value &lt;code&gt;polygon()&lt;/code&gt; is_not_ valid by itself, so I passed in some coordinates as zero values (with units, which is poor form). Consult &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/shape-outside"&gt;the MDN documentation on&lt;code&gt;shape-outside&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I highly recommend &lt;a href="https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Edit_CSS_shapes"&gt;the Firefox CSS Shapes Editor&lt;/a&gt;, even though it feels to me like bizarro world using a WYSIWYG interface on newer CSS properties. The coordinates it generates are responsive, too, which is awesome.&lt;/li&gt;
&lt;li&gt;I’m forever admonishing my students not to use justification. That is still the case for long passages of text. Run a ragged right-edge. It occurs to me that it might be wise to also check for&lt;code&gt;hyphens&lt;/code&gt; support before applying justified text, although &lt;a href="https://justmarkup.com/articles/2019-01-28-a-look-at-css-hyphenation-in-2019/"&gt;browser hyphenation itself is still a big work in progress&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>featurequeries</category>
    </item>
    <item>
      <title>Two-Value Display Properties in CSS</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Mon, 28 Oct 2019 05:00:00 +0000</pubDate>
      <link>https://dev.to/stolley/two-value-display-properties-in-css-obi</link>
      <guid>https://dev.to/stolley/two-value-display-properties-in-css-obi</guid>
      <description>&lt;p&gt;The incomparable Rachel Andrew introduces the brave new world of two-value CSS &lt;code&gt;display&lt;/code&gt; properties:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the outer &lt;code&gt;display&lt;/code&gt; type of an element is always block or inline, and dictates how the box behaves in the normal flow of the document. The inner &lt;code&gt;display&lt;/code&gt; type then changes the formatting context of the children. To better describe this behavior, the CSS Display specification has been refactored to allow for &lt;code&gt;display&lt;/code&gt; to accept two values. The first describes whether the outer &lt;code&gt;display&lt;/code&gt; type is block or inline, whereas the second value describes the formatting of the children.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m of two minds for this change. On the one hand, this introduces some complexity (e.g., the venerable &lt;code&gt;display: block&lt;/code&gt; is, in its two-value declaration, &lt;code&gt;display: block flow&lt;/code&gt;). But on the other hand, with that complexity comes an invitation to web developers to better understand document flow and formatting context—to which Rachel Andrew’s book &lt;em&gt;&lt;a href="https://abookapart.com/products/the-new-css-layout"&gt;The New CSS Layout&lt;/a&gt;&lt;/em&gt; is an indispensable guide.&lt;/p&gt;

&lt;p&gt;The two-value syntax for &lt;code&gt;display&lt;/code&gt; is currently &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/display#Browser_compatibility"&gt;only available in Firefox 70&lt;/a&gt;, listed in the compatibility table at MDN under “multi-keyword values.”&lt;/p&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>Back to a Single Spec for HTML and the DOM</title>
      <dc:creator>Karl Stolley</dc:creator>
      <pubDate>Sun, 20 Oct 2019 05:00:00 +0000</pubDate>
      <link>https://dev.to/stolley/back-to-a-single-spec-for-html-and-the-dom-1egg</link>
      <guid>https://dev.to/stolley/back-to-a-single-spec-for-html-and-the-dom-1egg</guid>
      <description>&lt;p&gt;Coralie Mercer at the World Wide Web Consortium blog, back in May 2019:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Today W3C and the WHATWG signed an agreement to collaborate on the development of a single version of the HTML and DOM specifications. The &lt;a href="https://www.w3.org/2019/04/WHATWG-W3C-MOU.html"&gt;Memorandum of Understanding jointly published as the WHATWG/W3C Joint Working Mode&lt;/a&gt; gives the specifics of this collaboration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That announcement is now reflected &lt;a href="https://github.com/whatwg/html/pull/4993/commits/2b193e1f5e70c312449bd2fb6a3c5859f78321e0"&gt;in this pull request&lt;/a&gt; at the WHATWG. The return to a single, shared specification for HTML and another for the DOM is good news for developers, browser makers, and those of us who pitch in to make dev-friendly documentation over on &lt;a href="https://developer.mozilla.org/en-US/"&gt;MDN&lt;/a&gt;. It’s also important, I think, that the WHATWG “living standard” is what will live on, rather than the post-hoc versioned HTML 5.x specifications the W3C had been publishing.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
