<?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: Paul Roub</title>
    <description>The latest articles on DEV Community by Paul Roub (@paulroub).</description>
    <link>https://dev.to/paulroub</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%2F325962%2Ffb0d4bb0-4800-46c0-911e-f67ec8dc367e.png</url>
      <title>DEV Community: Paul Roub</title>
      <link>https://dev.to/paulroub</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paulroub"/>
    <language>en</language>
    <item>
      <title>How to Over-Complicate Your First Conference Talk</title>
      <dc:creator>Paul Roub</dc:creator>
      <pubDate>Tue, 03 Mar 2020 14:48:04 +0000</pubDate>
      <link>https://dev.to/paulroub/how-to-over-complicate-your-first-conference-talk-4g6</link>
      <guid>https://dev.to/paulroub/how-to-over-complicate-your-first-conference-talk-4g6</guid>
      <description>&lt;p&gt;In January 2020, I was lucky enough to be selected as a speaker at &lt;a href="https://codemash.org/"&gt;CodeMash&lt;/a&gt;, an annual software development conference in Sandusky, OH. It is a &lt;em&gt;fantastic&lt;/em&gt; event which I would highly recommend to anyone.&lt;/p&gt;

&lt;p&gt;This was my first time speaking at a conference, so I made things up as I went along. Some things were easier than I expected, some were harder, and some were simply unexpected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Easy Part: Deciding to Speak
&lt;/h2&gt;

&lt;p&gt;Why did I want to do this now? Honestly, I’d always had it in the back of my mind as something that might be fun — and, just maybe, something I’d be good at. I’m passionate about code, tools, strategies – systems, really. How we do what we do, why we do it, how we can do better. I’m comfortable enough in front of an audience, and (to my co-workers’ occasional dismay) perfectly capable of going on (and on) about a topic. And I love teaching; wrapping something interesting and useful up in a story, helping someone get it the way that &lt;em&gt;I&lt;/em&gt; get it, hopefully passing along my excitement.&lt;/p&gt;

&lt;p&gt;There were two catalysts for the talk I gave.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; During our &lt;a href="https://dev.to/blog/iterating-on-our-hiring-process/"&gt;hiring round&lt;/a&gt; last year, I had some great conversations with people who were less-familiar with &lt;a href="https://www.agilealliance.org/glossary/tdd/"&gt;Test-Driven Development (TDD)&lt;/a&gt;. I found that I loved walking through the core concepts, and that I had an easy time explaining why I  loved to work this way.&lt;/li&gt;
&lt;li&gt; While listening to two developers I admire on &lt;a href="https://www.relay.fm/radar/172"&gt;one of my favorite podcasts&lt;/a&gt;, talking about all the reasons they were certain TDD &lt;em&gt;just wasn’t for them&lt;/em&gt;, I had quite a &lt;a href="https://imgs.xkcd.com/comics/duty_calls.png"&gt;“someone is wrong on the internet”&lt;/a&gt; moment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I thought a “TDD for Skeptics”-type talk might be useful. And very likely fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Second Easy Part: Submitting
&lt;/h2&gt;

&lt;p&gt;I’d been aware of CodeMash for a while, but hadn’t yet attended. It seemed like this talk might fit CodeMash’s relaxed-yet-incredibly-professional vibe.&lt;/p&gt;

&lt;p&gt;I submitted my abstract:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For the skeptical: Both new and more-seasoned devs -- especially solo practitioners -- can have a lot of misconceptions about TDD. Mostly of the "it's extra work, it's extra code, I can't ship tests, why would I test something simple and obvious" variety. But magic happens when the light bulb switches to "on".&lt;/p&gt;

&lt;p&gt;Walk through the design of a simple-enough class, showing along the way how initial assumptions are often wrong; how to avoid making those assumptions too early; throw away less code; and feel comfortable and safe when you &lt;em&gt;do&lt;/em&gt; throw away code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was a bit surprised that those two paragraphs were all that was required. I’ve since learned that this is typical of most conference submissions, which makes sense in retrospect: the organizers have to wade through a &lt;em&gt;lot&lt;/em&gt; of submissions, and they need to see an idea that comes across as interesting enough to attract an audience. The submission abstract becomes the conference session abstract.&lt;/p&gt;

&lt;p&gt;I waited a couple of months (as expected), trying not to worry about the outcome too much. The email came...&lt;/p&gt;

&lt;p&gt;They said “yes”!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Appropriately-Difficult Part: Writing the Talk
&lt;/h2&gt;

&lt;p&gt;I had a few rules in mind before I started, gained from watching many other technical presentations (live and in YouTube form):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A complete program, even a toy one, is going to make more sense to an audience than snippets of some larger, unseen “real” application&lt;/li&gt;
&lt;li&gt;  Don’t. Read. The. Slides.&lt;/li&gt;
&lt;li&gt;  I wanted to show the progress of the test/code/refactor cycle in something like real time, but didn’t want to actually code live in the room. Typos are not interesting.&lt;/li&gt;
&lt;li&gt;  I wanted to address &lt;em&gt;real&lt;/em&gt; concerns/objections to TDD, not straw men. This talk would be aimed at intelligent people who didn’t necessarily see things the way I did. My job was to demonstrate my point of view.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had a list of objections I’d heard and felt I could counter. I needed to be sure I hit all of those points.&lt;/p&gt;

&lt;p&gt;I wanted the talk to have a natural flow -- natural to &lt;em&gt;me&lt;/em&gt;, that is, so I could speak comfortably. On a long drive, I “wrote” a first draft by dictating to my phone, explaining to an imaginary friend why they should reconsider their objections to TDD. I used almost none of this in the actual materials, but it helped me get the “shape” or flow of the talk straight. Another benefit of an on-the-fly draft like that is that I had no attachment to it; I knew it would mostly be thrown away, so I could just talk without worrying if it was good enough.&lt;/p&gt;

&lt;p&gt;I also did a “draft” of the demo project itself. My plan was to capture the final project step-by-step in Git commits; but I didn’t want it to be too aimless. So I worked through the same problem in another language (Python) ahead of time, to convince myself it was the right size for the talk, while keeping the final demo project “real” -- mistakes and all.&lt;/p&gt;

&lt;p&gt;Then, I really did sit down and solve the problem in JavaScript, refactoring and changing course along the way. I made sure to use meaningful, narrative comments as I went.&lt;/p&gt;

&lt;p&gt;I then used those comments, and the code changes they accompanied, to build the flow of the demo slides. I literally went through the changes, adding slides when something interesting or novel had been done. “Here’s where I refactored for the first time”, “here’s where I knock off a bunch of corner cases in a row”, and so on. I left notes that just mentioned the key points, the reason, for each slide.&lt;/p&gt;

&lt;p&gt;And now I had a first draft.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-Imposed Roadblock #1: Writing My Own Presentation Software
&lt;/h2&gt;


&lt;blockquote class="twitter-tweet"&gt;
&lt;p&gt;Writing your own slide-presentation app is a bad idea, even if you really want that one extra feature. Anyway, here’s the thing I cobbled together to deliver my &lt;a href="https://twitter.com/hashtag/CodeMash?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#CodeMash&lt;/a&gt; presentation. Do as I say, not as I do (or at least do it better). &lt;a href="https://t.co/mXAptK7uqL"&gt;&lt;/a&gt;&lt;a href="https://t.co/mXAptK7uqL"&gt;https://t.co/mXAptK7uqL&lt;/a&gt;&lt;/p&gt;— Paul Roub (&lt;a class="comment-mentioned-user" href="https://dev.to/paulroub"&gt;@paulroub&lt;/a&gt;
) &lt;a href="https://twitter.com/paulroub/status/1225480448496807938?ref_src=twsrc%5Etfw"&gt;February 6, 2020&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;To be clear, this wasn’t &lt;em&gt;just&lt;/em&gt; for fun. As part of my presentation I wanted to show test failures — and subsequent successes — live, as I went through the slides. So of course, I wrote a whole web app that served up the presentation; showed my notes in another window; and performed Git checkouts behind-the-scenes.&lt;/p&gt;

&lt;p&gt;As detailed above, I recorded each step of the process in Git. This allowed me to &lt;em&gt;replay&lt;/em&gt; those steps in as fine detail as I liked. I expected that I would be flipping from the slides to a terminal window running tests &lt;em&gt;constantly&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In actuality, I think I ran the live tests three times during the actual presentation. Static content would probably have sufficed.&lt;/p&gt;

&lt;p&gt;In hindsight, there was probably an element of procrasti-working at play. Writing a talk: unfamiliar and uncertain. Writing code: comfortable and immediately rewarding. And still, technically, making progress towards the task at hand! One could argue that time might have been spent more effectively working on the slides themselves.&lt;/p&gt;

&lt;p&gt;It was still fun to write, though. And I’ll use it again the next time I give this talk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-Imposed Roadblock #2: Failing to Ask for Help
&lt;/h2&gt;

&lt;p&gt;You know that terrible interview cliché, “What are your greatest weaknesses”? In my case, the one that leaps to mind is my tendency to want to handle things myself and not bother anyone. I don’t think this is unusual in our profession.&lt;/p&gt;

&lt;p&gt;CodeMash has a wonderful speaker-mentorship program, wherein experienced speakers offer advice/feedback/etc. to first-time speakers. I’m no fool, I signed right up for that! And then… never used it. Bob and Russell, thank you for your kind offer to help. I really wish I’d taken advantage of it.&lt;/p&gt;

&lt;p&gt;I also work with a great team who were more than happy to look things over ahead of time. Again, I didn’t give them the chance. They’ve given great feedback after seeing the video of my talk, and that will be great in March when I give an updated version of the talk at &lt;a href="https://orlandocodecamp.com/"&gt;Orlando Code Camp&lt;/a&gt;; I regret not having those improvements baked in the first time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complications I’ll Try to Avoid Repeating
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Re-(re-)writing
&lt;/h3&gt;

&lt;p&gt;I rewrote too many slides, too many times. That time would certainly have been better spent practicing the actual delivery a few more times, or attending another session, or maybe playing in the water park. (CodeMash is at the Kalahari Resort, which has a water park. Like I said, I recommend the daylights out of this conference)&lt;/p&gt;

&lt;p&gt;I also tried to write in the hotel room for the first day, which I’ve found just doesn’t work well for me. There was a perfectly nice speaker’s lounge, and once I migrated there, everything was much more focused and easy. Your mileage may vary.&lt;/p&gt;

&lt;p&gt;My rewrite-obsession also led me to miss a speaker get-together early in the week. I really regret this; there’s a lot to learn from talking to fellow presenters (seasoned and new).&lt;/p&gt;

&lt;h3&gt;
  
  
  Cutting Travel Time Too Close
&lt;/h3&gt;

&lt;p&gt;I’d booked a return flight for Friday night, the last day of the conference. The flight was at 8pm, the conference ended at 5, it seemed like just enough time. I’d miss the closing ceremonies, but that seemed like a small price to get home sooner.&lt;/p&gt;

&lt;p&gt;Then, a month before the conference, the flight schedule was changed. 7:15pm. No later options that didn’t involve overnight stays in some far-flung airport. “Well,” I thought, “as long as my talk isn’t scheduled for the last slot on Friday, I can leave early and make the flight.”&lt;/p&gt;

&lt;p&gt;A week later, the conference schedule was published. Guess what?&lt;/p&gt;

&lt;p&gt;I was able to add a night and re-book for the following morning’s flight, but all-in-all really wish I’d just done it that way in the first place. Cutting travel close, in the winter, in northeast Ohio, is a crap shoot anyway. I should know - I learned to drive in the northeast Ohio winter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complications Which I Do Not Regret At All
&lt;/h2&gt;

&lt;h3&gt;
  
  
  MacGyver-ing a Test Setup
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OflP0PuJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3wzk72stgo55up8b086v.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OflP0PuJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3wzk72stgo55up8b086v.jpg" alt="Precarious presentation test setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get a feel for the slides on a big-ish screen, separate from the laptop, I HDMI-ed my way into the hotel TV and rigged a podium.&lt;/p&gt;

&lt;p&gt;As one does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Venturing Out and Making Music
&lt;/h3&gt;

&lt;p&gt;I grew up in the Cleveland area, about an hour from the conference. I couldn’t resist finding a way to meet up with some old friends; and whenever possible, I’m inclined to find a way to be on stage with a guitar.&lt;/p&gt;

&lt;p&gt;So I got in touch with the gentleman who runs a songwriters’ night at a Cleveland lounge, and booked myself a slot well in advance. This &lt;em&gt;did&lt;/em&gt; mean I missed the CodeMash Lightning Talks, which was regrettable. But in the midst of four solid days of sessions and rewrites, getting out was worth it (even if it meant getting to sleep well after 1am).&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporary Guitar Ownership
&lt;/h3&gt;

&lt;p&gt;Oh, about that guitar. While I borrowed the host’s for the actual performance, I wanted to have one in the hotel room. 5 days is a long stretch to be without, after a lifetime of habitual noodling. Traveling with a guitar is expensive and risky, so I rolled the dice and visited a pawn shop halfway between the airport and the hotel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wvTurfXU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p2ejjm00exdiof4mhd8r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wvTurfXU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p2ejjm00exdiof4mhd8r.jpg" alt="Epi Classical Guitar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The little old Epi stayed in tune, played easily, and sounded fine. Basically an $8-a-day rental.&lt;/p&gt;

&lt;p&gt;At breakfast on the last day, I mentioned the guitar  (and my desire to give it away) to a couple in attendance, who mentioned that their son was learning to play. We decided to meet up after the closing ceremonies.&lt;/p&gt;

&lt;p&gt;When the evening came, it turned out that one of these nice folks was having dinner with friends in the hotel restaurant, and I was welcome to join. The entire crew turned out to be speakers and/or conference organizers; I had a great dinner, and made up for the missed connections earlier in the week.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Best Part
&lt;/h2&gt;

&lt;p&gt;The talk itself was a blur and a blast.&lt;/p&gt;

&lt;p&gt;I hoped there would be, I don’t know, 20 attendees? There were over 100. Almost all of them stayed, and I got heartwarming feedback immediately after, and later on Twitter and via the conference ratings.&lt;/p&gt;

&lt;p&gt;I hoped I wouldn’t be stressed and miserable. Turns out? I loved it.&lt;/p&gt;

&lt;p&gt;Going in, I was worried that I’d run short; run long; read the slides; stumble over topics; be bored; or be nervous. I didn’t know what to expect from &lt;em&gt;myself&lt;/em&gt; in this situation, no matter what I’d watched anyone else do.&lt;/p&gt;

&lt;p&gt;These concerns ended up fading away due, I think, to a number of factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  I was genuinely excited to talk about the material. This is a &lt;em&gt;huge&lt;/em&gt; advantage you have when you’re giving a talk of your own choosing.&lt;/li&gt;
&lt;li&gt;  I purposely kept the slides, and even my notes, terse. “Reading the slides” just wouldn’t work even if I’d been so inclined.&lt;/li&gt;
&lt;li&gt;  I’m naturally self-deprecating and dad-joke-y, and included that in the presentation. “Let’s all laugh &lt;em&gt;with&lt;/em&gt; me” has anti-nervous effects.&lt;/li&gt;
&lt;li&gt;  CodeMash had a helper/prompter in the front row, who’d let me know when I was close to running out of time.&lt;/li&gt;
&lt;li&gt;  I hadn’t practiced as much as I’d like, but I &lt;em&gt;had&lt;/em&gt; practiced. And all that rewriting had me very familiar with the flow of the slides.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m already keeping a list of possible topics to submit &lt;em&gt;next&lt;/em&gt; year, and I’m watching for other Calls For Proposals along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaways
&lt;/h2&gt;

&lt;p&gt;I went about this somewhat instinctively. Some of those instincts served me well. Others, hopefully, will be tempered next time. If I had to do this again, I would apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;More&lt;/strong&gt; people. Feedback from friends, advice from mentors, discussion with fellow presenters -- the talk &lt;em&gt;and&lt;/em&gt; the week would have benefited.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;More&lt;/strong&gt; rehearsal. Once I really, actually, no kidding, presented the talk out loud to an empty room, two things happened: I made a number of helpful changes to the talk, and I realized I didn’t feel nearly as silly as I’d feared.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;More&lt;/strong&gt; downtime during the conference. Which would have been possible with…&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Less&lt;/strong&gt; last-minute re-writing, which would have been helped by…&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Earlier&lt;/strong&gt; rehearsal. Don’t wait until conference week, kids.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Same&lt;/strong&gt; level of excitement. Bored speakers == bored audiences. Talk about something you have a hard time &lt;em&gt;not&lt;/em&gt; talking about.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Same&lt;/strong&gt; first-draft process. A throwaway draft is the enemy of writer’s block (at least in my case).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think you have a talk to give? Write it up. Submit it. Rejection is survivable, and you’ll have learned something for the next time.  And if it went this well despite all of my twists and turns, imagine the relative ease and satisfaction a sensible approach might bring.&lt;/p&gt;

&lt;p&gt;Oh, and if you want to watch the actual talk, &lt;a href="https://pluralsight.com/"&gt;Pluralsight&lt;/a&gt; and CodeMash have made all this year’s talks available for free, &lt;a href="https://www.pluralsight.com/authors/codemash-conference"&gt;here&lt;/a&gt;, including this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pluralsight.com/courses/codemash-session-87"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---syUTCBU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/piut13z76e5jrr2arxwa.jpg" alt="CodeMash: Paul Roub : Test-driven Development: Save Your Time, Save Your Sanity, Write Great Code Fast"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>speaking</category>
      <category>conference</category>
    </item>
    <item>
      <title>Matching form posts in the Pretenders mock HTTP server</title>
      <dc:creator>Paul Roub</dc:creator>
      <pubDate>Tue, 28 Jan 2020 20:47:29 +0000</pubDate>
      <link>https://dev.to/paulroub/matching-form-posts-in-the-pretenders-mock-http-server-2ga9</link>
      <guid>https://dev.to/paulroub/matching-form-posts-in-the-pretenders-mock-http-server-2ga9</guid>
      <description>&lt;p&gt;I've used the  &lt;a href="https://github.com/pretenders/pretenders"&gt;Pretenders&lt;/a&gt;  "Fake servers for testing" on a number of work and personal projects, with generally good results.  But I've wished that it was easier to write tests against different HTML form post contents.&lt;/p&gt;

&lt;p&gt;Pretenders can return results based on specific URLs (of course), HTTP headers, query string parameters, body contents, and so on. But if that body is the result of a form? You'd better know:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If it was sent url-encoded or multipart&lt;/li&gt;
&lt;li&gt;In what order the fields will be arriving&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And be willing to deal with those specifically via RegEx matches.&lt;/p&gt;

&lt;p&gt;But I don't &lt;em&gt;want&lt;/em&gt; to care about those things. I want to say "here are the form fields and values I'm expecting, respond if you see them."&lt;/p&gt;

&lt;p&gt;I'd submit a feature request if the project wasn't borderline abandonware&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. But hey, that's why we use open source tools, no?&lt;/p&gt;

&lt;p&gt;In my fork at &lt;a href="https://github.com/paulroub/pretenders/tree/match-form-data"&gt;github.com/paulroub/pretenders/tree/match-form-data&lt;/a&gt;, I've added an optional &lt;code&gt;data&lt;/code&gt; parameter to the &lt;a href="https://pretenders.readthedocs.io/en/stable/http.html#pretenders.client.http.HTTPMock.when"&gt;&lt;code&gt;when()&lt;/code&gt;&lt;/a&gt; method. It takes a dictionary, &lt;em&gt;e.g.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;http_mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'POST /hello'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'first'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'second'&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b'First and Second passed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;times&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FOREVER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will match when a form is posted to &lt;code&gt;/hello&lt;/code&gt;, and fields "one" and "two" have the expected values. It does not care in what order those fields were passed, nor which encoding method was used to submit the form.&lt;/p&gt;

&lt;p&gt;I've submitted a &lt;a href="https://github.com/pretenders/pretenders/pull/138"&gt;pull request&lt;/a&gt;, but since the project was last touched in 2017 I'm not overly optimistic. In the meantime, you can use the forked version directly, either by saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'git+https://github.com/paulroub/pretenders.git@match-form-data#egg=pretenders'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;or by including&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git+https://github.com/paulroub/pretenders.git@match-form-data#egg=pretenders
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;in your &lt;a href="https://pip.readthedocs.io/en/stable/user_guide/#requirements-files"&gt;requirements.txt&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;The fork currently has a userbase of "me", so comments, suggestions, and bug fixes are even more welcome than usual.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;If you're starting a &lt;em&gt;new&lt;/em&gt; project, I'd look at alternatives; &lt;a href="https://github.com/httptoolkit/mockttp"&gt;mockttp&lt;/a&gt; is nice, for example. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>python</category>
      <category>testing</category>
    </item>
    <item>
      <title>Responsive calendar layout with flexbox</title>
      <dc:creator>Paul Roub</dc:creator>
      <pubDate>Tue, 28 Jan 2020 20:45:27 +0000</pubDate>
      <link>https://dev.to/paulroub/responsive-calendar-layout-with-flexbox-l18</link>
      <guid>https://dev.to/paulroub/responsive-calendar-layout-with-flexbox-l18</guid>
      <description>&lt;p&gt;For the better part of two decades, I've been running &lt;a href="https://openmikes.org/"&gt;openmikes.org&lt;/a&gt;, a directory of open mikes&lt;sup id="fnref1"&gt;1&lt;/sup&gt;, jam nights, etc. across the US and Canada. As with many hobby sites, especially of this vintage, it's showing its age.&lt;/p&gt;

&lt;p&gt;I was particularly annoyed with the &lt;a href="https://openmikes.org/calendar/"&gt;calendar&lt;/a&gt; page. Although it's probably the &lt;em&gt;worst&lt;/em&gt; way to display this data in terms of scannability and useful-information density, it's also the most popular. I've tried killing it, I've tried hiding it... either way, I get complaints.&lt;/p&gt;

&lt;p&gt;But there's no longer any excuse to display a squished desktop calendar on mobile devices. I wanted to restructure the layout so that it would collapse to a list on narrower screens. But the old code was a mess - all sorts of "off day" padding logic to place the first day of the month on the right day of the week (in the &lt;em&gt;table&lt;/em&gt; I was using for layout), empty days that really shouldn't show up in a list view, etc.&lt;/p&gt;

&lt;p&gt;I took a first cut at a new layout using CSS Grid, but a small percentage of my users are still on IE 10 or 11. No real grid support, and this layout includes corner cases that the polyfills can't handle. So, &lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/"&gt;Flexbox&lt;/a&gt; it is.&lt;/p&gt;

&lt;p&gt;My goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I wanted to keep the HTML simple, and let CSS do the heavy lifting (with some math help from &lt;a href="https://sass-lang.com/"&gt;SASS&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;JavaScript shouldn't be necessary&lt;/li&gt;
&lt;li&gt;I wanted to start the calendar HTML off on the 1st of the month, with no "padding" cells&lt;/li&gt;
&lt;li&gt;No fill-out-the-week padding at the end of the month, either.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I &lt;em&gt;was&lt;/em&gt; willing to include in the HTML:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each day's "cell" has a "calday" class.&lt;/li&gt;
&lt;li&gt;Each cell is also tagged with its day of the week, so we have &lt;code&gt;class="calday sunday"&lt;/code&gt;, &lt;code&gt;class="calday monday"&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Days with no events also get an "emptyday" class.&lt;/li&gt;
&lt;li&gt;Days that have already passed get a "calpast" class&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first few days of February 2019, therefore, might look like this:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"calheader"&lt;/span&gt;&lt;span class="nt"&gt;&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;"calday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sunday&lt;span class="nt"&gt;&amp;lt;/div&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;"calday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Monday&lt;span class="nt"&gt;&amp;lt;/div&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;"calday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Tuesday&lt;span class="nt"&gt;&amp;lt;/div&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;"calday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Wednesday&lt;span class="nt"&gt;&amp;lt;/div&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;"calday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Thursday&lt;span class="nt"&gt;&amp;lt;/div&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;"calday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Friday&lt;span class="nt"&gt;&amp;lt;/div&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;"calday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Saturday&lt;span class="nt"&gt;&amp;lt;/div&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"calendar"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"calendar"&lt;/span&gt;&lt;span class="nt"&gt;&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;"calday friday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"caldate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"calday saturday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"caldate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;2&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"calday sunday emptyday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"caldate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;3&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&amp;lt;/ul&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"calday monday"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"caldate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;4&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;words&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- and so on --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Going mobile-first, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let each day take 100%  width&lt;/li&gt;
&lt;li&gt;Hide "emptyday" and "calpast" cells&lt;/li&gt;
&lt;li&gt;Hide the "calheader" section which labels the day-of-week columns in full-sized views.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.calheader&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.calpast&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.emptyday&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="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.calday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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;That's the default view in this CodePen:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/paulroub/embed/RvjgNw?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;On wider screens (at least 55em, adjustable via the &lt;code&gt;$calbreak&lt;/code&gt; SASS variable), we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bring the "calheader" section back&lt;/li&gt;
&lt;li&gt;Restore empty days and past days&lt;/li&gt;
&lt;li&gt;Set each calendar day to 1/7th of the available width&lt;/li&gt;
&lt;li&gt;Use Flexbox layout in the calendar and its header
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$cellwidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$calbreak&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;55em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$cellwidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="nf"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$calbreak&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.calendar&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;.calheader&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;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;flex-flow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-around&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.calpast&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;.emptyday&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="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.calday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$cellwidth&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;(SASS is not strictly necessary here, but I'd rather let the tool do the calculations for me, for correctness and readability's sake)&lt;/p&gt;

&lt;p&gt;If that's all we did, every month would start in the first column -- i.e. every month would start on Sunday. Historically, I'd either add empty cells before the first of the month (1 for Monday, 2 for Tuesday, etc.), and perhaps something similar at the end to maintain the last week's cell spacing. That's annoying, and adds more junk that we need to hide in mobile views.&lt;/p&gt;

&lt;p&gt;So each cell also gets a "sunday", "monday", etc. class. And we let CSS (again, via SASS) do something special with that information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dayOfWeek&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="nf"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$calbreak&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:first-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cellwidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dayOfWeek&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cellwidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$dayOfWeek&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="nc"&gt;.sunday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.monday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;day&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="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.tuesday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.wednesday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.thursday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.friday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.saturday&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&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;Assume the first day of the month is on a Tuesday. That "tuesday" class compiles to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="nf"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;55em&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.tuesday&lt;/span&gt;&lt;span class="nd"&gt;:first-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;28&lt;/span&gt;&lt;span class="mi"&gt;.5714285714%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.tuesday&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="mi"&gt;.1428571429%&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;Meaning: on wide-enough displays, if this Tuesday is the first day in the list (&lt;em&gt;i.e.&lt;/em&gt; the first day of the month), add a left margin of 2/7th of the screen — the same as two calendar cells.&lt;/p&gt;

&lt;p&gt;Similarly, if the last day of the month is a Tuesday, right-pad it with 4-cells' width of space.&lt;/p&gt;

&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each week will span exactly one row (because each cell is 1/7th of the width)&lt;/li&gt;
&lt;li&gt;Flexbox will keep the heights of the cells in a row the same, without any extra CSS needed&lt;/li&gt;
&lt;li&gt;Our list will start in the correct column.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can reduce the size of the preview in the above CodePen, or &lt;a href="https://codepen.io/paulroub/full/RvjgNw"&gt;view it full size&lt;/a&gt; to see this more clearly. Resize the window to watch the break happen.&lt;/p&gt;

&lt;p&gt;The JavaScript in the CodePen is there to fill out the calendar for a given month, with semi-random content, and add the appropriate classes so you can see — for example — the lack of empty days in mobile view.&lt;/p&gt;

&lt;p&gt;Feedback, suggestions, improvements are very much welcome.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;"Um, actually, it's 'mic'". Oh, thanks. You may be surprised to find &lt;a href="https://openmikes.org/faq/#mikedammit"&gt;this has come up before&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>css</category>
    </item>
  </channel>
</rss>
