Journal

2687 sparkline

Thursday, June 11th, 2020

CSS custom properties and the cascade

When I wrote about programming CSS to perform Sass colour functions I said this about the brilliant Lea Verou:

As so often happens when I’m reading something written by Lea—or seeing her give a talk—light bulbs started popping over my head (my usual response to Lea’s knowledge bombs is either “I didn’t know you could do that!” or “I never thought of doing that!”).

Well, it happened again. This time I was reading her post about hybrid positioning with CSS variables and max() . But the main topic of the post wasn’t the part that made go “Huh! I never knew that!”. Towards the end of her article she explained something about the way that browsers evaluate CSS custom properties:

The browser doesn’t know if your property value is valid until the variable is resolved, and by then it has already processed the cascade and has thrown away any potential fallbacks.

I’m used to being able to rely on the cascade. Let’s say I’m going to set a background colour on paragraphs:

p {
  background-color: red;
  background-color: color(display-p3 1 0 0);
}

First I’ve set a background colour using a good ol’ fashioned keyword, supported in browsers since day one. Then I declare the background colour using the new-fangled color() function which is supported in very few browsers. That’s okay though. I can confidently rely on the cascade to fall back to the earlier declaration. Paragraphs will still have a red background colour.

But if I store the background colour in a custom property, I can no longer rely on the cascade.

:root {
  --myvariable: color(display-p3 1 0 0);
}
p {
  background-color: red;
  background-color: var(--myvariable);
}

All I’ve done is swapped out the hard-coded color() value for a custom property but now the browser behaves differently. Instead of getting a red background colour, I get the browser default value. As Lea explains:

…it will make the property invalid at computed value time.

The spec says:

When this happens, the computed value of the property is either the property’s inherited value or its initial value depending on whether the property is inherited or not, respectively, as if the property’s value had been specified as the unset keyword.

So if a browser doesn’t understand the color() function, it’s as if I’ve said:

background-color: unset;

This took me by surprise. I’m so used to being able to rely on the cascade in CSS—it’s one of the most powerful and most useful features in this programming language. Could it be, I wondered, that the powers-that-be have violated the principle of least surprise in specifying this behaviour?

But a note in the spec explains further:

Note: The invalid at computed-value time concept exists because variables can’t “fail early” like other syntax errors can, so by the time the user agent realizes a property value is invalid, it’s already thrown away the other cascaded values.

Ah, right! So first of all browsers figure out the cascade and then they evaluate custom properties. If a custom property evaluates to gobbledygook, it’s too late to figure out what the cascade would’ve fallen back to.

Thinking about it, this makes total sense. Remember that CSS custom properties aren’t like Sass variables. They aren’t evaluated once and then set in stone. They’re more like let than const. They can be updated in real time. You can update them from JavaScript too. It’s entirely possible to update CSS custom properties rapidly in response to events like, say, the user scrolling or moving their mouse. If the browser had to recalculate the cascade every time a custom property didn’t evaluate correctly, I imagine it would be an enormous performance bottleneck.

So even though this behaviour surprised me at first, it makes sense on reflection.

I’ve probably done a terrible job explaining the behaviour here, so I’ve made a Codepen. Although that may also do an equally terrible job.

(Thanks to Amber for talking through this with me and encouraging me to blog about it. And thanks to Lea for expanding my mind. Again.)

Tuesday, June 9th, 2020

Intent

There are intentions and there are outcomes. Sometimes bad outcomes are the result of good intentions. Less often, good outcomes can be the result of bad intentions. But generally we associate the two: we expect good outcomes to come from good intentions and we expect bad outcomes to come from bad intentions.

Perhaps it’s because of this conflation that we place too much emphasis on intentions. If, for example, someone is called out for causing a bad outcome, their first response is often to defend their intentions. That’s understandable. When someone says “you have created a bad outcome”, I understand why the person on the receiving end would receive that feedback as “you intended to create this bad outcome.” Cue a non-apology that clarifies the (good) intention without acknowledging the reality of the outcome (“It was never my intention to…”).

I get it. Intentions do matter …just not as much as we give them credit for. I mean, in general, I’d prefer bad outcomes to be the inadvertent result of good intentions. But in some ways, it really doesn’t matter: a bad outcome is a bad outcome.

Anyway, all of this is just to preface something I’m going to say about myself:

I am almost certainly racist.

I don’t intend to be racist, but like I said, intentions aren’t really what matter. Outcomes are.

Note, for example, the cliché of the gormless close-minded goon who begins a sentence with “I’m not racist, but…” before going on to say something clearly racist. It’s as though the racism could be defanged by disavowing bad intent.

The same defence mechanism is used to defend racist traditions. “Oh, it’s not racist—that’s just something we’ve always done.” Again, the defence is for the intention, not the outcome. And again, outcomes matter far, far more than intentions.

I really don’t intend to be racist. But how could I not be? I grew up in a small town in Ireland where literally everyone else looked like me. By the same token, I’m also almost certainly sexist. Growing up as a cisgender male in a patriarchal society guarantees that my mind has been shaped in ways I now wish it weren’t.

Acknowledging my racism—and sexism—doesn’t mean I’m okay with it. On the contrary. It’s a source of shame. But acknowledging my racism is a necessary step to changing it.

In any case, it doesn’t really matter how I feel about any of this. This isn’t meant to be a confessional. What matters are outcomes. Outcomes aren’t really the direct result of intentions—outcomes are the direct result of actions.

Most of my actions lately have been very passive. Listening. Watching. Because my actions are passive, they are indistinguishable from silence. That’s not good. Silence can be interpreted as acquiescence, acceptance. That’s not what I intend …but my intentions don’t matter.

So, even though this isn’t about me or my voice or my intentions, and even though this is something that is so self-evident that it shouldn’t need to be said, I want to say:

Black lives matter.

Saturday, May 30th, 2020

Programming CSS to perform Sass colour functions

I wrote recently about moving away from Sass to using native CSS features. I had this to say on the topic of mixins in Sass:

These can be very useful, but now there’s a lot that you can do just in CSS with calc(). The built-in darken() and lighten() mixins are handy though when it comes to colours.

I know we will be getting these in the future but we’re not there yet with CSS.

Anyway, I had all this in the back of my mind when I was reading Lea’s excellent feature in this month’s Increment: A user’s guide to CSS variables. She’s written about a really clever technique of combining custom properites with hsl() colour values for creating colour palettes. (See also: Una’s post on dynamic colour theming with pure CSS.)

As so often happens when I’m reading something written by Lea—or seeing her give a talk—light bulbs started popping over my head (my usual response to Lea’s knowledge bombs is either “I didn’t know you could do that!” or “I never thought of doing that!”).

I immediately set about implementing this technique over on The Session. The trick here is to use separate custom properties for the hue, saturation, and lightness parts of hsl() colour values. Then, when you want to lighten or darken the colour—say, on hover—you can update the lightness part.

I’ve made a Codepen to show what I’m doing.

Let’s say I’m styling a button element. I make custom propertes for hsl() values:

button {
  --button-colour-hue: 19;
  --button-colour-saturation: 82%;
  --button-colour-lightness: 38%;
  background-color: hsl(
    var(--button-colour-hue),
    var(--button-colour-saturation),
    var(--button-colour-lightness)
  );
}

For my buttons, I want the borders to be slightly darker than the background colour. When I was using Sass, I used the darken() function to this. Now I use calc(). Here’s how I make the borders 10% darker:

border-color: hsl(
  var(--button-colour-hue),
  var(--button-colour-saturation),
  calc(var(--button-colour-lightness) - 10%)
);

That calc() function is substracting a percentage from a percentage: 38% minus 10% in this case. The borders will have a lightness of 28%.

I make the bottom border even darker and the top border lighter to give a feeling of depth.

On The Session there’s a “cancel” button style that’s deep red.

Here’s how I set its colour:

.cancel {
  --button-colour-hue: 0;
  --button-colour-saturation: 100%;
  --button-colour-lightness: 40%;
}

That’s it. The existing button declarations take care of assigning the right shades for the border colours.

Here’s another example. Site admins see buttons for some actions only available to them. I want those buttons to have their own colour:

.admin {
  --button-colour-hue: 45;
  --button-colour-saturation: 100%;
  --button-colour-lightness: 40%;
}

You get the idea. It doesn’t matter how many differently-coloured buttons I create, the effect of darkening or lightening their borders is all taken care of.

So it turns out that the lighten() and darken() functions from Sass are available to us in CSS by using a combination of custom properties, hsl(), and calc().

I’m also using this combination to lighten or darken background and border colours on :hover. You can poke around the Codepen if you want to see that in action.

I love seeing the combinatorial power of these different bits of CSS coming together. It really is a remarkably powerful programming language.

Saturday, May 23rd, 2020

Integration

Back when I started staying at home under lockdown, Robin Sloan wrote a blog post called An integration loop. In it, he tells the story of The Disintegration Loops by William Basinski:

Monitoring a radio station in New York City, the composer William Basinski hears the melody, records it. He intends to use a fragment as a loop in an avant-garde music project. The tape goes into a box. It is the 1980s.

It is decades later: the summer of 2001. Digitizing a room full of forgotten material, Basinski finds this loop again. But the tape is old; as it moves through the player, it starts to come apart, the magnetic medium peeling off its plastic backing, more and more with each repetition. Enthralled, Basinski keeps recording as the melody disintegrates before his eyes, his ears.

Standing on his rooftop in Brooklyn, Basinski watches the World Trade Center collapse. It is September 11, 2001. Suddenly, the summer’s recordings have a meaning, a purpose. He titles them The Disintegration Loops and offers them as an elegy for the dead.

I hadn’t heard this before. Listening to it, I found it incredibly moving, haunting, and strangely cathartic.

Robin included a clip in his blog post of an ensemble of computer-generated sequences made from Basinki’s source material. He called it an integration loop, pt. 1.

He also published the sheet music for the looping musical phrase:

Sheet Music3

Anyone reading the blog post was encouraged to record that phrase on any instrument (or voice) and send it to Robin. I recorded the phrase on mandolin. Jessica recorded it on a muted fiddle.

Robin then assembled all the submissions into a seven minute piece called an integration loop, pt. 2. You can hear Jessica at 3:33. I’m at 4:33.

All the submissions and the final piece are released under a public domain license.

I very much relate to Robin’s interpration of this creation:

In my imagination, each contribution is a rung in a ladder out of the pit of confusion and loss, all of us both (a) carrying the melody forward and (b) being carried by it, up towards something new, something whole.

Monday, May 18th, 2020

Hard to break

I keep thinking about some feedback that Cassie received recently.

She had delivered the front-end code for a project at Clearleft, and—this being Cassie we’re talking about—the code was rock solid. The client’s Quality Assurance team came back with the verdict that it was “hard to break.”

Hard to break. I love that. That might be the best summation I’ve heard for describing resilience on the web.

If there’s a corollary to resilient web design, it would be brittle web design. In a piece completely unrelated to web development, Jamais Cascio describes brittle systems:

When something is brittle, it’s susceptible to sudden and catastrophic failure.

That sounds like an inarguably bad thing. So why would anyone end up building something in a brittle way? Jamais Cascio continues:

Things that are brittle look strong, may even be strong, until they hit a breaking point, then everything falls apart.

Ah, there’s the rub! It’s not that brittle sites don’t work. They work just fine …until they don’t.

Brittle systems are solid until they’re not. Brittleness is illusory strength. Things that are brittle are non-resilient, sometimes even anti-resilient — they can make resilience more difficult.

Kilian Valkhof makes the same point when it comes to front-end development. For many, accessibility is an unknown unknown:

When you start out it’s you, notepad and a browser against the world. You open up that notepad, and you type

<div onclick="alert('hello world');">Click me!</div>

You fire up your browser, you click your div and …it works! It just works! Awesome. You open up the devtools. No errors. Well done! Clearly you did a good job. On to the next thing.

At the surface level, there’s no discernable difference between a resilient solution and a brittle one:

For all sorts of reasons, both legitimate and, as always, weird browser legacy reasons, a clickable div will mostly work. Well enough to fool someone starting out anyway.

If everything works, how would they know it kinda doesn’t?

Killian goes on to suggest ways to try to make this kind of hidden brittleness more visible.

Furthermore we could envision a browser that is much stricter when developing.

This something I touched on when I was talking about web performance with Gerry on his podcast:

There’s a disconnect in the process we go through when we’re making something, and then how that thing is experienced when it’s actually on the web, which is dependent on network speeds and processing speeds and stuff.

I spend a lot of time wondering why so many websites are badly built. Sure, there’s a lot can be explained by misaligned priorities. And it could just be an expression of Sturgeon’s Law—90% of websites are crap because 90% of everything is crap. But I’ve also come to realise that even though resilience is the antithesis to brittleness, they both share something in common: they’re invisible.

We have a natural bias towards what’s visible. Being committed to making sure something is beautiful to behold is, in some ways, the easy path to travel. But being committed to making sure something is also hard to break? That takes real dedication.

Sunday, May 17th, 2020

Photograph

Do you have a favourite non-personal photograph?

By non-personal, I mean one that isn’t directly related to your life; photographs of family members, friends, travel (remember travel?).

Even discounting those photographs, there’s still a vast pool of candidates. There are all the amazing pictures taken by photojournalists like Lee Miller. There’s all the awe-inspiring wildlife photography out there. Then there are the kind of posters that end up on bedroom walls, like Robert Doisneau’s The Kiss.

One of my favourite photographs of all time has music as its subject matter. No, not Johnny Cash flipping the bird, although I believe this picture to be just as rock’n’roll.

In the foreground, Séamus Ennis sits with his pipes. In the background, Jean Ritchie is leaning intently over her recording equipment.

This is a photograph of Séamus Ennis and Jean Ritchie. It was probably taken around 1952 or 1953 by Ritchie’s husband, George Pickow, when Jean Ritchie and Alan Lomax were in Ireland to do field recordings.

I love everything about it.

Séamus Ennis looks genuinely larger than life (which, by all accounts, he was). And just look at the length of those fingers! Meanwhile Jean Ritchie is equally indominatable, just as much as part of the story as the musician she’s there to record.

Both of them have expressions that convey how intent they are on their machines—Ennis’s uilleann pipes and Ritchie’s tape recorder. It’s positively steampunk!

What a perfect snapshot of tradition and technology meeting slap bang in the middle of the twentieth century.

Maybe that’s why I love it so much. One single photograph is filled with so much that’s dear to me—traditional Irish music meets long-term archival preservation.

Wednesday, May 13th, 2020

Sass and clamp

CSS got some pretty nifty features recently. There’s the min() and max() functions. If you use them for, say, width you can use one rule where previously you would’ve needed to use two (a width declaration followed by either min-width or max-width). But they can also be applied to font-size! That’s very nifty—we’ve never had min-font-size or max-font-size properties.

There’s also the clamp() function. That allows you to set a minimum size, a default size, and a maximum size. Again, it can be used for lengths, like width, or for font-size.

Over on thesession.org, I’ve had some media queries in place for a while now that would increase the font-size for larger screens. It’s nothing crucial, just a nice-to-have so that on wide screens, the font is bumped up accordingly. I realised I could replace all those media queries with one clamp() statement, thanks to the vw (viewport width) unit:

font-size: clamp(1rem, 1.333vw, 1.5rem);

By default, the font-size is 1.333vw (1.333% of the viewport width), but it will never get smaller than 1rem and it will never get larger than 1.5rem.

That works, but there’s a bit of an issue with using raw vw units like that. If someone is on a wide screen and they try to adjust the font size, nothing will happen. The viewport width doesn’t change when you bump the font size up or down.

The solution is to mix in some kind of unit that does respond to the font size being bumped up or down (like, say, the rem unit). Handily, clamp() allows you to combine units, just like calc(). So I can do this:

font-size: clamp(1rem, 0.5rem + 0.666vw, 1.5rem);

The result is much the same as my previous rule, but now—thanks to the presence of that 0.5rem value—the font size responds to being adjusted by the user.

You could use a full 1rem in that default value:

font-size: clamp(1rem, 1rem + 0.333vw, 1.5rem);

…but if you do that, the minimum size (1rem) will never be reached—the default value will always be larger. So in effect it’s no different than saying:

font-size: min(1.rem + 0.333vw, 1.5rem);

I mentioned this to Chris just the other day.

Anyway, I got the result I wanted. I wanted the font size to stay at the browser default size (usually 16 pixels) until the screen was larger than around 1200 pixels. From there, the font size gets gradually bigger, until it hits one and a half times the browser default (which would be 24 pixels if the default size started at 16). I decided to apply it to the :root element (which is html) using percentages:

:root {
  font-size: clamp(100%, 50% + 0.666vw, 150%);
}

(My thinking goes like this: if we take a screen width of 1200 pixels, then 1vw would be 12 pixels: 1200 divided by 100. So for a font size of 16 pixels, that would be 1.333vw. But because I’m combining it with half of the default font size—50% of 16 pixels = 8 pixels—I need to cut the vw value in half as well: 50% of 1.333vw = 0.666vw.)

So I’ve got the CSS rule I want. I dropped it in to the top of my file and…

I got an error.

There was nothing wrong with my CSS. The problem was that I was dropping it into a Sass file (.scss).

Perhaps I am showing my age. Do people even use Sass any more? I hear that post-processors usurped Sass’s dominance (although no-one’s ever been able to explain to me why they’re different to pre-processers like Sass; they both process something you’ve written into something else). Or maybe everyone’s just writing their CSS in JS now. I hear that’s a thing.

The Session is a looooong-term project so I’m very hesitant to use any technology that won’t stand the test of time. When I added Sass into the mix, back in—I think—2012 or so, I wasn’t sure whether it was the right thing to do, from a long-term perspective. But it did offer some useful functionality so I went ahead and used it.

Now, eight years later, it was having a hard time dealing with the new clamp() function. Specifically, it didn’t like the values being calculated through the addition of multiple units. I think it was clashing with Sass’s in-built ability to add units together.

I started to ask myself whether I should still be using Sass. I looked at which features I was using…

Variables. Well, now we’ve got CSS custom properties, which are even more powerful than Sass variables because they can be updated in real time. Sass variables are like const. CSS custom properties are like let.

Mixins. These can be very useful, but now there’s a lot that you can do just in CSS with calc(). The built-in darken() and lighten() mixins are handy though when it comes to colours.

Nesting. I’ve never been a fan. I know it can make the source files look tidier but I find it can sometimes obfuscate what you’re final selectors are going to look like. So this wasn’t something I was using much any way.

Multiple files. Ah! This is the thing I would miss most. Having separate .scss files for separate interface elements is very handy!

But globbing a bunch of separate .scss files into one .css file isn’t really a Sass task. That’s what build tools are for. In fact, that’s what I was already doing with my JavaScript files; I write them as individual .js files that then get concatenated into one .js file using Grunt.

(Yes, this project uses Grunt. I told you I was showing my age. But, you know what? It works. Though seeing as I’m mostly using it for concatenation, I could probably replace it with a makefile. If I’m going to use old technology, I might as well go all the way.)

I swapped out Sass variables for CSS custom properties, mixins for calc(), and removed what little nesting I was doing. Then I stripped the Sass parts out of my Grunt file and replaced them with some concatenation and minification tasks. All of this makes no difference to the actual website, but it means I’ve got one less dependency …and I can use clamp()!

Remember a little while back when I was making a dark mode for my site? I made this observation:

Let’s just take a moment here to pause and reflect on the fact that we can now use CSS to create all sorts of effects that previously required a graphic design tool like Photoshop.

It feels like something similar has happened with tools like Sass. Sass was the hare. CSS is the tortoise. Sass blazed the trail, but now native CSS can achieve much the same result.

It’s like when we used to need something like jQuery to do DOM Scripting succinctly using CSS selectors. Then we got things like querySelector() in JavaScript so we no longer needed the trailblazer.

I’ve said it before and I’ll say it again, the goal of any good library should be to get so successful as to make itself redundant. That is, the ideas and functionality provided by the tool are so useful and widely adopted that the native technologies—HTML, CSS, and JavaScript—take their cue from those tools.

You could argue that this is what happened with Flash. It certainly happened with jQuery and Sass. I’m pretty sure we’ll see the same cycle play out with frameworks like React.

Monday, May 4th, 2020

A decade apart

Today marks ten years since the publication of HTML5 For Web Designers, the very first book from A Book Apart.

I’m so proud of that book, and so honoured that I was the first author published by the web’s finest purveyors of brief books. I mean, just look at the calibre of their output since my stumbling start!

Here’s what I wrote ten years ago.

Here’s what Jason wrote ten years ago.

Here’s what Mandy wrote ten years ago.

Here’s what Jeffrey wrote ten years ago.

They started something magnificent. Ten years on, with Katel at the helm, it’s going from strength to strength.

Happy birthday, little book! And happy birthday, A Book Apart! Here’s to another decade!

A Book Apart authors, 1-6

Sunday, May 3rd, 2020

Television

What a time, as they say, to be alive. The Situation is awful in so many ways, and yet…

In this crisis, there is also opportunity—the opportunity to sit on the sofa, binge-watch television and feel good about it! I mean just think about it: when in the history of our culture has there been a time when the choice between running a marathon or going to the gym or staying at home watching TV can be resolved with such certitude? Stay at home and watch TV, of course! It’s the only morally correct choice. Protect the NHS! Save lives! Gorge on box sets!

What you end up watching doesn’t really matter. If you want to binge on Love Island or Tiger King, go for it. At this moment in time, it’s all good.

I had an ancient Apple TV device that served me well for years. At the beginning of The Situation, I decided to finally upgrade to a more modern model so I could get to more streaming services. Once I figured out how to turn off the unbelievably annoying sounds and animations, I got it set up with some subscription services. Should it be of any interest, here’s what I’ve been watching in order to save lives and protect the NHS…

Watchmen, Now TV

Superb! I suspect you’ll want to have read Alan Moore’s classic book to fully enjoy this series set in the parallel present extrapolated from that book’s ‘80s setting. Like that book, what appears to be a story about masked vigilantes is packing much, much deeper themes. I have a hunch that if Moore himself were forced to watch it, he might even offer some grudging approval.

Devs, BBC iPlayer

Ex Machina meets The Social Network in Alex Garland’s first TV show. I was reading David Deutsch while I was watching this, which felt like getting an extra bit of world-building. I think this might have worked better in the snappier context of a film, but it makes for an enjoyable saunter as a series. Style outweighs substance, but the style is strong enough to carry it.

Breeders, Now TV

Genuinely hilarious. Watch the first episode and see how many times you laugh guiltily. It gets a bit more sentimental later on, but there’s a wonderfully mean streak throughout that keeps the laughter flowing. If you are a parent of small children though, this may feel like being in a rock band watching Spinal Tap—all too real.

The Mandalorian, Disney Plus

I cannot objectively evaluate this. I absolutely love it, but that’s no surprise. It’s like it was made for me. The execution of each episode is, in my biased opinion, terrific. Read what Nat wrote about it. I agree with everything they said.

Westworld, Now TV

The third series is wrapping up soon. I’m enjoying this series immensely. It’s got a real cyberpunk sensibility; not in a stupid Altered Carbon kind of way, but in a real Gibsonian bit of noirish fun. Like Devs, it’s not as clever as it thinks it is, but it’s throroughly entertaining all the same.

Tales From The Loop, Amazon Prime

The languid pacing means this isn’t exactly a series of cliffhangers, but it will reward you for staying with it. It avoids the negativity of Black Mirror and instead maintains a more neutral viewpoint on the unexpected effects of technology. At its best, it feels like an updated take on Ray Bradbury’s stories of smalltown America (like the episode directed by Jodie Foster featuring a cameo by Shane Carruth—the time traveller’s time traveller).

Years and Years, BBC iPlayer

A near-future family and political drama by Russell T Davies. Subtlety has never been his strong point and the polemic aspects of this are far too on-the-nose to take seriously. Characters will monologue for minutes while practically waving a finger at you out of the television set. But it’s worth watching for Emma Thompson’s performance as an all-too believable populist politician. Apart from a feelgood final episode, it’s not light viewing so maybe not the best quarantine fodder.

For All Mankind, Apple TV+

An ahistorical space race that’s a lot like Mary Robinette Kowal’s Lady Astronaut books. The initial premise—that Alexei Leonov beats Neil Armstrong to a moon landing—is interesting enough, but it really picks up from episode three. Alas, the baton isn’t really kept up for the whole series; it reverts to a more standard kind of drama from about halfway through. Still worth seeing though. It’s probably the best show on Apple TV+, but that says more about the paucity of the selection on there than it does about the quality of this series.

Avenue Five, Now TV

When it’s good, this space-based comedy is chucklesome but it kind of feels like Armando Iannucci lite.

Picard, Amazon Prime

It’s fine. Michael Chabon takes the world of Star Trek in some interesting directions, but it never feels like it’s allowed to veer too far away from the established order.

The Outsider, Now TV

A tense and creepy Stephen King adaption. I enjoyed the mystery of the first few episodes more than the later ones. Once the supernatural rules are established, it’s not quite as interesting. There are some good performances here, but the series gives off a vibe of believing it’s more important than it really is.

Better Call Saul, Netflix

The latest series (four? I’ve lost count) just wrapped up. It’s all good stuff, even knowing how some of the pieces need to slot into place for Breaking Bad.

Normal People, BBC iPlayer

I heard this was good so I went to the BBC iPlayer app and hit play. “Pretty good stuff”, I thought after watching that episode. Then I noticed that it said Episode Twelve. I had watched the final episode first. Doh! But, y’know, watching from the start, the foreknowledge of how things turn out isn’t detracting from the pleasure at all. In fact, I think you could probably watch the whole series completely out of order. It’s more of a tone poem than a plot-driven series. The characters themselves matter more than what happens to them.

Hunters, Amazon Prime

A silly 70s-set jewsploitation series with Al Pacino. The enjoyment comes from the wish fulfillment of killing nazis, which would be fine except for the way that the holocaust is used for character development. The comic-book tone of the show clashes very uncomfortably with that subject matter. The Shoah is not a plot device. This series feels like what we would get if Tarentino made television (and not in a good way).

Thursday, April 30th, 2020

User agents

I was on the podcast A Question Of Code recently. It was fun! The podcast is aimed at people who are making a career change into web development, so it’s right up my alley.

I sometimes get asked about what a new starter should learn. On the podcast, I mentioned a post I wrote a while back with links to some great resources and tutorials. As I said then:

For web development, start with HTML, then CSS, then JavaScript (and don’t move on to JavaScript too quickly—really get to grips with HTML and CSS first).

That’s assuming you want to be a good well-rounded web developer. But it might be that you need to get a job as quickly as possible. In that case, my advice would be very different. I would advise you to learn React.

Believe me, I take no pleasure in giving that advice. But given the reality of what recruiters are looking for, knowing React is going to increase your chances of getting a job (something that’s reflected in the curricula of coding schools). And it’s always possible to work backwards from React to the more fundamental web technologies of HTML, CSS, and JavaScript. I hope.

Regardless of your initial route, what’s the next step? How do you go from starting out in web development to being a top-notch web developer?

I don’t consider myself to be a top-notch web developer (far from it), but I am very fortunate in that I’ve had the opportunity to work alongside some tippety-top-notch developers at ClearleftTrys, Cassie, Danielle, Mark, Graham, Charlotte, Andy, and Natalie.

They—and other top-notch developers I’m fortunate to know—have something in common. They prioritise users. Sure, they’ll all have their favourite technologies and specialised areas, but they don’t lose sight of who they’re building for.

When you think about it, there’s quite a power imbalance between users and developers on the web. Users can—ideally—choose which web browser to use, and maybe make some preference changes if they know where to look, but that’s about it. Developers dictate everything else—the technology that a website will use, the sheer amount of code shipped over the network to the user, whether the site will be built in a fragile or a resilient way. Users are dependent on developers, but developers don’t always act in the best interests of users. It’s a classic example of the principal-agent problem:

The principal–agent problem, in political science and economics (also known as agency dilemma or the agency problem) occurs when one person or entity (the “agent”), is able to make decisions and/or take actions on behalf of, or that impact, another person or entity: the “principal”. This dilemma exists in circumstances where agents are motivated to act in their own best interests, which are contrary to those of their principals, and is an example of moral hazard.

A top-notch developer never forgets that they are an agent, and that the user is the principal.

But is it realistic to expect web developers to be so focused on user needs? After all, there’s a whole separate field of user experience design that specialises in this focus. It hardly seems practical to suggest that a top-notch developer needs to first become a good UX designer. There’s already plenty to focus on when it comes to just the technology side of front-end development.

So maybe this is too simplistic a way of defining the principle-agent relationship between users and developers:

user :: developer

There’s something that sits in between, mediating that relationship. It’s a piece of software that in the world of web standards is even referred to as a “user agent”: the web browser.

user :: web browser :: developer

So if making the leap to understanding users seems too much of a stretch, there’s an intermediate step. Get to know how web browsers work. As a web developer, if you know what web browsers “like” and “dislike”, you’re well on the way to making great user experiences. If you understand the pain points for browser when they’re parsing and rendering your code, you’ve got a pretty good proxy for understanding the pain points that your users are experiencing.