<?xml version="1.0" encoding="utf-8"?>






<feed xmlns="http://www.w3.org/2005/Atom">
  <title>localghost</title>
  <subtitle>Sophie builds fun things out of HTML, CSS &amp; JavaScript, and writes blog posts about tech and mental health.</subtitle>
  <link href="https://localghost.dev/feed.xml" rel="self"/>
  <link href="https://localghost.dev/"/>
  <updated>2024-02-18T00:00:00Z</updated>
  <id>https://localghost.dev/</id>
  <author>
    <name>Sophie Koonin</name>
    <email>sophie@localghost.dev</email>
  </author>
    
    <entry>
      <title>Good links: 18 February 2024</title>
      <link href="https://localghost.dev/blog/good-links-2024-02-18/"/>
      <updated>2024-02-18T00:00:00Z</updated>
      <id>https://localghost.dev/blog/good-links-2024-02-18/</id>
      <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://midnight.pub/&quot;&gt;The Midnight Pub&lt;/a&gt; - A tiny forum in the form of a virtual pub. It even has a speakeasy.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://frills.dev/blog/070224-this-website-is-personal-girls/&quot;&gt;This website is personal - Frills&lt;/a&gt; - I really identify with this post, and the feeling that my posts aren’t worth anything if they’re not useful in some way. In reality there are no rules at all about what you should post on a personal site!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://adrianroselli.com/2024/02/dont-disable-form-controls.html&quot;&gt;Don’t Disable Form Controls&lt;/a&gt; - Stop disabling submit buttons.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://computer.rip/2024-02-11-the-top-of-the-DNS-hierarchy.html&quot;&gt;the top of the DNS hierarchy&lt;/a&gt; - TLD nameservers are well-known, but what about the very top level - the root? An in-depth look at the thirteen root DNS servers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tonsky.me/blog/checkbox/&quot;&gt;In Loving Memory of Square Checkbox&lt;/a&gt; - How are we supposed to tell the difference now?&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
      <title>Good links: 4 February 2024</title>
      <link href="https://localghost.dev/blog/good-links-2024-02-04/"/>
      <updated>2024-02-04T00:00:00Z</updated>
      <id>https://localghost.dev/blog/good-links-2024-02-04/</id>
      <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hamatti.org/posts/please-dont-force-me-to-log-in/&quot;&gt;Please, don’t force me to log in&lt;/a&gt; - It feels like every website or connected device wants you to sign up before you can use it these days…&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://piccalil.li/blog/react-is-getting-a-bit-of-a-kicking-recently/&quot;&gt;It feels like React is getting a bit of a kicking recently - Piccalilli&lt;/a&gt; - There&#39;s a lot of React criticism floating around at the mo, much of it justified. I think people are quick to use it when they don&#39;t need to. But as someone working on a very big, very complex project that does benefit from a framework like React, I&#39;m glad Andy is taking a nuanced look at the debate.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://piccalil.li/blog/a-highly-configurable-switch-component-using-modern-css/&quot;&gt;A highly configurable switch component using modern CSS techniques - Piccalilli&lt;/a&gt; - We’ve all built plenty of switches out of checkboxes, I’m sure, but Andy has updated his technique to use some newer CSS features and produced a rather lovely result.&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
      <title>Listen to this: The Stand-In by Caitlin Rose</title>
      <link href="https://localghost.dev/blog/listen-to-this-the-stand-in-by-caitlin-rose/"/>
      <updated>2024-01-30T00:00:00Z</updated>
      <id>https://localghost.dev/blog/listen-to-this-the-stand-in-by-caitlin-rose/</id>
      <content type="html">&lt;p&gt;At university I worked at Manchester Academy, and would often end up behind the bar at all sorts of gigs from pop, to metal, to folk. Metal gigs were always my favourite because the crowd were very nice to bar staff, whereas sometimes you&#39;d have &amp;quot;POUR OUT CANS&amp;quot; written on the sign in sheet and you knew it was going to be rowdy.&lt;/p&gt;
&lt;p&gt;Anyway, one of the gigs I ended up working at was Caitlin Rose, a country singer hailing from Tenessee. A tiny gig in our smallest venue, Academy 3, where I could lean back against the bar and watch quite happily. I&#39;d never considered myself a fan of country (though a few years later I&#39;d get very, very emotionally invested in the TV series Nashville, and in fact a song of hers did feature) but I was really taken by her witty and honest lyrics, and catchy refrains.&lt;/p&gt;
&lt;p&gt;A few years later in 2013 she released this absolute dynamite record, &lt;em&gt;The Stand-In&lt;/em&gt;, a vintage-flavoured record full of excellent writing, velvety vocals and heartbreak. If you don&#39;t like country, don&#39;t be put off: it&#39;s more kind of alternative country.&lt;/p&gt;
&lt;p&gt;She then promptly disappeared for about ten years only to surface with a new album last year (&lt;em&gt;Cazimi&lt;/em&gt;). But The Stand-In is still my favourite.&lt;/p&gt;
&lt;p&gt;Top track: &lt;em&gt;No One To Call&lt;/em&gt; is a short one, but a bittersweet plea from a lonely life on the road.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Good links: 28 January 2024</title>
      <link href="https://localghost.dev/blog/good-links-2024-01-28/"/>
      <updated>2024-01-28T00:00:00Z</updated>
      <id>https://localghost.dev/blog/good-links-2024-01-28/</id>
      <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.oldavista.com/&quot;&gt;Old&#39;aVista&lt;/a&gt; - Altavista was my first search engine (or should I say altavista+was+my+first+search+engine) and this is a delightful trip down memory lane.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jvns.ca/blog/2024/01/26/inside-git/&quot;&gt;Inside .git&lt;/a&gt; - Legendary explainer of things Julia Evans has written a fascinating and understandable guide to what lives inside the .git folder.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cari.institute/aesthetics&quot;&gt;CARI | Aesthetics&lt;/a&gt; - I shared this on Mastodon a while back but this is just so compelling, and some of these aesthetics come with a heady dose of nostalgia.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.fromjason.xyz/p/notebook/where-have-all-the-websites-gone/&quot;&gt;Where have all the websites gone?&lt;/a&gt; - A heartfelt lamentation for the human web.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ohhelloana.blog/another-round-of-bookmarks/&quot;&gt;Oh Hello Ana&lt;/a&gt; - Ana Rodrigues has posted a very comprehensive list of good tech and web-related links.&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
      <title>So you&#39;ve decided to get into mechanical keyboards</title>
      <link href="https://localghost.dev/blog/so-you-ve-decided-to-get-into-mechanical-keyboards/"/>
      <updated>2024-01-27T00:00:00Z</updated>
      <id>https://localghost.dev/blog/so-you-ve-decided-to-get-into-mechanical-keyboards/</id>
      <content type="html">&lt;p&gt;This is a dangerous path you&#39;re about to walk down. Is your wallet ready?&lt;/p&gt;
&lt;p&gt;Whether you prefer the smooth glide of linear switches, the gentle bump of a tactile switch or the full-on audio assault of a clicky switch, there’s something extremely gratifying about typing on a mechanical keyboard (even if it’s just a reply to someone who is wrong on the internet). And it’s even better if it looks as adorable as this.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/keyboards/dreamscape.png&quot; alt=&quot;A top-down view of a mechanical keyboard with ISO layout. Its keycaps form a sort of gradient, colours in stripes going diagonally left to right from peach to pink to purple to blue to white. There is also a small purple plate on the keyboard with stars engraved on it. SOme of the keys have cute symbols on like stars, moons, clouds and meteors; the ISO enter key has a constellation on it. The keyboard sits on top of a deskmat with a cat in space on it.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;I’ll level with you: I got into mechanical keyboards because a) they’re so PRETTY! and b) it was the pandemic, what else was I supposed to do? I had been perfectly happy tapping away on my Apple wireless keyboard for a very long time. But I really do prefer them now, and find that I’m much less prone to wrist strain than I was with the extremely flat profile of the Apple keyboard. Sure, I could have bought a Logitech keyboard that’s a bit fatter and be done with it, but it wouldn’t look like this:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/keyboards/ikki68-2.webp&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/keyboards/ikki68-2.webp&quot; alt=&quot;The ikki68 Aurora keyboard with white and pastel purple polycarbonate case. Most of the keys are off-white, the escape and pg up/pg down/home/end keys are pastel coloured with fruit legends. It is in Mac ISO layout with a split left shift.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;I’ll continue to level with you: it is not a cheap hobby. From the keyboard itself, to the switches for each key, to each keyset (the keys themselves, usually sold separately), the cost adds up quickly. The only positive I can offer is that by the time the keyset you ordered arrives, it’s usually been so long you forgot how much it cost you.&lt;/p&gt;
&lt;p&gt;You’ll also need a lot of &lt;strong&gt;patience&lt;/strong&gt;. Since it’s a pretty niche hobby, keyboards and keysets have to go through a few stages before they arrive at your doorstep: garnering interest to see if people want to buy it, presale, production and quality assurance, then delivery and distribution. This can take anywhere between a few months to over a year – in fact, one keyset I ordered back in 2021 has been so beset by production issues and delays that it still hasn’t arrived yet.&lt;/p&gt;
&lt;h3 id=&quot;how-mechanical-keyboards-work&quot; tabindex=&quot;-1&quot;&gt;How mechanical keyboards work&lt;/h3&gt;
&lt;p&gt;Your average mechanical keyboard will have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a case, made out of plastic or aluminium&lt;/li&gt;
&lt;li&gt;a PCB (printed circuit board) with a USB connector (or Bluetooth if you’re fancy)&lt;/li&gt;
&lt;li&gt;a controller that can translate those keypresses into input instructions for your computer&lt;/li&gt;
&lt;li&gt;switches to trigger keypresses&lt;/li&gt;
&lt;li&gt;keycaps for you to type on&lt;/li&gt;
&lt;li&gt;stabilisers (aka stabs) for the longer keys so they don’t wobble&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keyboard switches complete a circuit in the keyboard which causes a specific keypress to be transmitted via the controller to the computer.&lt;/p&gt;
&lt;p&gt;Switches have a plastic shell with a moveable plastic “stem”, the bit that the keycap attaches to and that moves up and down when you press it. There’s a metal spring inside to control that movement, and finally a metal plate with legs that protrude from underneath the switch which you either solder into the keyboard’s PCB, or slot into place on a hot-swap keyboard.&lt;/p&gt;
&lt;p&gt;Hot-swap means you can remove and change switches without having to do any soldering, which is great if you want to try a few different kinds; but hot-swap PCBs are a bit less common – and more expensive – than regular ones.&lt;/p&gt;
&lt;h2 id=&quot;switches&quot; tabindex=&quot;-1&quot;&gt;Switches&lt;/h2&gt;
&lt;p&gt;Mechanical keyboard switches come in three categories: linear (smooth), tactile (bump), and clicky (exactly what it sounds like).&lt;/p&gt;
&lt;p&gt;All switches have what’s called an “actuation point”, which is the point at which the keypress is registered. It’s usually when you press the switch about halfway down. While linear switches don’t give any feedback that you’ve reached the actuation point, tactile switches will have a bit of resistance or a “bump” that lets you know you’re there, and clicky switches make the click sound at that point. Some people like this because it means you don’t have to push the keys all the way down.&lt;/p&gt;
&lt;p&gt;It’s completely down to personal preference here: I like linear switches, as I actually prefer my keyboards to be as quiet as possible. If you’re intending to bring your keyboard to a place where other people are working, please don’t buy clicky switches unless you want to find your expensive new keyboard mysteriously covered in tea.&lt;/p&gt;
&lt;h3 id=&quot;mechanical-vs-optical-switches&quot; tabindex=&quot;-1&quot;&gt;Mechanical vs optical switches&lt;/h3&gt;
&lt;p&gt;Standard mechanical switches trigger when the stem is pushed down, causing the metal leaf at the bottom of the switch to connect, completing the circuit. Optical switches have springs too, but these have a light signal inside them which registers a keypress. There aren’t quite as many optical switches out there but there isn’t a lot of difference between the two types, it’s just a newer technology.&lt;/p&gt;
&lt;h3 id=&quot;cherry-mx-switches-and-their-colours&quot; tabindex=&quot;-1&quot;&gt;Cherry MX switches and their colours&lt;/h3&gt;
&lt;p&gt;You’ll hear people talking about red switches, brown switches, blue switches... this relates to the colour of the stem in Cherry MX switches. These are one of the most popular shapes of switch, and lots of manufacturers make them other than Cherry MX – I tend to buy Gateron as they’re a bit cheaper.&lt;/p&gt;
&lt;p&gt;The colours tell you what kind of switch it is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;red: linear, light resistance, relatively quiet&lt;/li&gt;
&lt;li&gt;black: linear, heavy resistance, relatively quiet&lt;/li&gt;
&lt;li&gt;brown: tactile, moderate sound, medium resistance&lt;/li&gt;
&lt;li&gt;clear: tactile, moderate sound, heavy resistance&lt;/li&gt;
&lt;li&gt;blue: loud, clicky, medium resistance&lt;/li&gt;
&lt;li&gt;green: very loud, clicky, heavy resistance&lt;/li&gt;
&lt;/ul&gt;
 &lt;figure&gt;
 &lt;img src=&quot;https://localghost.dev/img/blog/mech-boards/switches.JPG&quot; alt=&quot;Three switches on a table. Two are brown and one is red. One of the brown ones is upside down showing its metal prongs underneath.&quot; /&gt;
 &lt;figcaption&gt;Gateron red and brown switches&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;p&gt;There are lots of manufacturers and limited runs of this switch shape with different colours and actions, and it can get quite overwhelming, but you can just stick to the normal colours!&lt;/p&gt;
&lt;p&gt;If you’re not sure what colour you want a lot of places sell a switch tester with a few different colours in so you can get a feel for what you like most.&lt;/p&gt;
&lt;p&gt;Which colour you buy is entirely up to you, it’s based on personal preference. What &lt;em&gt;does&lt;/em&gt; matter is what shape of switch you buy, and this will usually be specified on the keyboard/PCB listing.&lt;/p&gt;
&lt;p&gt;There are other types of switches e.g. &lt;a href=&quot;https://switchandclick.com/low-profile-switches-explained/&quot;&gt;low profile switches&lt;/a&gt;, but the majority of keyboards and keycaps that I’ve seen are made for the Cherry MX profile.&lt;/p&gt;
&lt;h2 id=&quot;keyboards&quot; tabindex=&quot;-1&quot;&gt;Keyboards&lt;/h2&gt;
&lt;p&gt;The keyboard itself can come in lots of different shapes and sizes. You didn’t think this would be simple, did you?&lt;/p&gt;
&lt;p&gt;You can buy keyboards as kits ready to be assembled (you&#39;ll need some tiny screwdrivers and a soldering iron), or prebuilt. Some places offer assembly service for you, though I think assembly is half the fun!&lt;/p&gt;
&lt;h3 id=&quot;keyboard-size-and-shape&quot; tabindex=&quot;-1&quot;&gt;Keyboard size and shape&lt;/h3&gt;
&lt;p&gt;Your regular rectangular keyboard can have any number of keys, and some limited edition ones have a truly bizarre layout. I recommend the &lt;a href=&quot;https://switchandclick.com/keyboard-sizes/&quot;&gt;Switch &amp;amp; Click keyboard size guide&lt;/a&gt; for an overview of the most common ones.&lt;/p&gt;
&lt;p&gt;When it comes to choosing a size, think about what’s important to you. Do you use the numpad? Better get a compact or a full-sized one. Do you need the F-keys (go 75% or larger) or will you be all right with using a function key and the num row (60-65% will do)? Do you like pain (a 40% is for you)?&lt;/p&gt;
&lt;p&gt;I have a KBDFans Tofu84 (75%) at work, and an ikki68 (65%) at home. The F2 key gets a lot of use at work when I’m renaming variables!&lt;/p&gt;
 &lt;figure&gt;
 &lt;img src=&quot;https://localghost.dev/img/keyboards/tofu65-2.webp&quot; alt=&quot;The KBDFans Tofu65 keyboard with black aluminium case. Its keycaps are purple and black, and the alpha key legends have a gradient from turquoise to peach. It is in Mac ISO layout with a split left shift.&quot; /&gt;
 &lt;figcaption&gt;KBDFans Tofu65 with DSA Magic Girl keycaps&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;p&gt;Then there’s the &lt;a href=&quot;https://splitkb.com/&quot;&gt;split keyboards&lt;/a&gt;, literally split down the middle and connected by a cable. They’re meant to be really great for wrist strain, much better for you posture-wise, though I haven’t tried one myself as I don’t have the energy to re-learn to type. They’re also excellent if you want to look like you come from, or travel regularly to, space.&lt;/p&gt;
  &lt;figure&gt;
 &lt;img src=&quot;https://localghost.dev/img/blog/mech-boards/split-keyboard.jpg&quot; alt=&quot;A dark blue split keyboard, one side in front of the other. It has black keycaps with white legends.&quot; /&gt;
 &lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@peppytoad&quot;&gt;Peppy Toad&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/black-computer-keyboard-on-black-table-FR7DkhhW2oA&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;p&gt;Joking aside, they are supposed to be much better for you. A popular ergo keyboard is the Ergodox, which is actually open source: you can download the files for the PCB from &lt;a href=&quot;https://github.com/Ergodox-io/&quot;&gt;GitHub&lt;/a&gt; and get it printed yourself, sourcing whichever compatible parts you like (or 3D printing them). There are also a lot of kits or pre-made versions if you prefer.&lt;/p&gt;
&lt;p&gt;Then there&#39;s macropads, tiny and adorable mini keyboards that are a great way to get used to building keyboards if you don&#39;t have much experience soldering. I&#39;ve got a &lt;a href=&quot;https://keeb.io/collections/bdn9-collection/products/bdn9-rev-2-3x3-9-key-macropad-rotary-encoder-and-rgb&quot;&gt;keeb.io BDN9&lt;/a&gt; and a mint-coloured Owlab Voice Mini which is the cutest thing. They both have rotary encoders (knobs) on them which make them 100% more satisfying, but unfortunately I keep forgetting what I&#39;ve programmed the keys to do so I find I rarely use them.&lt;/p&gt;
 &lt;figure&gt;
 &lt;img src=&quot;https://localghost.dev/img/keyboards/bdn9.JPG&quot; alt=&quot;An angled view of the BDN9 macropad. It is a black square with two rotary knobs at the top, and 7 keys that are pastel coloured with fruit legends. There is a green cable plugged into it.&quot; /&gt;
 &lt;figcaption&gt;BDN9 with DSA Milkshake keycaps&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;h3 id=&quot;keyboard-layouts-iso-ansi-jis-ortholinear&quot; tabindex=&quot;-1&quot;&gt;Keyboard layouts: ISO, ANSI, JIS, ortholinear&lt;/h3&gt;
&lt;p&gt;The most common keyboard layout I’ve seen is the ANSI layout, which is the standard keyboard layout in the US. It’s characterised by its flat enter key, longer shift key on the left, and extra key above the enter key.&lt;br /&gt;
ISO is generally used in Europe, and has a delightfully chunky enter key that’s two rows high, with a smaller left shift key (two keys where ANSI has one).&lt;br /&gt;
JIS is the Japanese International Standard, and has an extra key next to backspace, an ISO-style enter key, and a smaller right shift key.&lt;br /&gt;
Mechkeys has an &lt;a href=&quot;https://mechkeys.com/blogs/guide/understanding-different-physical-layouts-for-keyboards-ansi-vs-iso-vs-jis&quot;&gt;article about these common layouts&lt;/a&gt;, including some diagrams.&lt;/p&gt;
&lt;p&gt;I know plenty of folks in Europe who use ANSI layout simply because the keyboards are more common, but I much prefer ISO. Generally, keyboard kits and PCBs will say which layouts they support; often they support multiple configurations so you could even mix and match, say, an ISO enter key and a US-style left shift.&lt;/p&gt;
&lt;p&gt;Finally we have the &lt;strong&gt;ortholinear&lt;/strong&gt; layout. These are quite amazing looking, a bit like a long &lt;a href=&quot;https://en.wikipedia.org/wiki/Boggle&quot;&gt;Boggle&lt;/a&gt;. Unlike regular keyboards which have staggered rows, the keys are all aligned in a grid. &lt;a href=&quot;https://www.tryorthokeys.com/ultimate-guide-to-ortholinear-keyboards&quot;&gt;Read more about ortho keyboards&lt;/a&gt; on Try Ortho Keys.&lt;/p&gt;
&lt;h3 id=&quot;typing-angle&quot; tabindex=&quot;-1&quot;&gt;Typing angle&lt;/h3&gt;
&lt;p&gt;Perhaps a minor one, but this may make a difference to you; different keyboards are set at different angles, and unlike your 2001 Compaq keyboard most of these mechanical ones don’t have those little fold-out feet that you snapped off while waiting for the dialup to connect. Most keyboard listings will tell you the typing angle.&lt;/p&gt;
&lt;h3 id=&quot;dampening-sound&quot; tabindex=&quot;-1&quot;&gt;Dampening sound&lt;/h3&gt;
&lt;p&gt;If you’re like me, you want to minimise the sound from a keyboard as much as possible, especially in the office. There are a few ways of doing that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use quieter switches (red, black)&lt;/li&gt;
&lt;li&gt;use &lt;a href=&quot;https://mechswitcher.com/most-silent-linear-switches/&quot;&gt;silent switches&lt;/a&gt; (NB these are quieter, not actually silent)&lt;/li&gt;
&lt;li&gt;put &lt;a href=&quot;https://mechkeyboardexpert.com/what-are-mechanical-keyboard-o-rings-everything-you-need-to-know/&quot;&gt;O-rings&lt;/a&gt; on your keycaps (I got some cheap off eBay)&lt;/li&gt;
&lt;li&gt;line your case with &lt;a href=&quot;https://switchandclick.com/the-best-dampening-foam-for-a-mechanical-keyboard/&quot;&gt;foam&lt;/a&gt; to absorb sound&lt;/li&gt;
&lt;li&gt;put a desk mat underneath your keyboard&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;keycaps&quot; tabindex=&quot;-1&quot;&gt;Keycaps&lt;/h2&gt;
&lt;p&gt;The most exciting bit, I reckon. The bits with the letters on that are prettier than they have any right to be. You can get retro ones, cute pastel ones, dark ones with light writing... the possibilities are endless.&lt;/p&gt;
&lt;p&gt;Some of my favourite keysets of recent years:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://novelkeys.com/products/dsa-milkshake&quot;&gt;DSA Milkshake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vala.supply/products/epbt-dreamscape&quot;&gt;ePBT Dreamscape&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.zfrontier.com/products/space-dust&quot;&gt;KAT Space Dust&lt;/a&gt; (this has been delayed forever, bah)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mintlodica.com/products/dsa-pastel-dreams-keycaps&quot;&gt;Mintlodica Pastel Dreams&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;figure&gt;
 &lt;img src=&quot;https://localghost.dev/img/blog/mech-boards/dreamscape-render.webp&quot; alt=&quot;TA top-down view of a mechanical keyboard with ANSI layout. Its keycaps form a sort of gradient, colours in stripes going diagonally left to right from peach to pink to purple to blue to white.&quot; /&gt;
 &lt;figcaption&gt;Render of Dreamscape keycaps from &lt;a href=&quot;https://vala.supply/products/epbt-dreamscape&quot;&gt;vala.supply&lt;/a&gt;&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;h3 id=&quot;kits&quot; tabindex=&quot;-1&quot;&gt;Kits&lt;/h3&gt;
&lt;p&gt;Keycaps will usually have different kits on sale, so check the listing carefully to see what each one contains. You&#39;ll definitely want to get the base kit (with all the alphanumeric keys and ANSI keys). Sometimes you have to buy the ISO kit separately for the big enter key and some Europe-specific keys like the UK 3 (# and £). Often sets will do a novelties kit: Milkshake had a set of pastel-coloured keys with fruit legends, and Dreamscape has novelties with stars, clouds and meteors. I always enjoy the novelties.&lt;/p&gt;
&lt;h3 id=&quot;materials&quot; tabindex=&quot;-1&quot;&gt;Materials&lt;/h3&gt;
&lt;p&gt;Keycaps are generally plastic, except for the occasional novelty one. They’re either made of &lt;a href=&quot;https://switchandclick.com/abs-vs-pbt-keycaps-whats-the-difference/&quot;&gt;PBT or ABS&lt;/a&gt;; PBT is nicer but ABS is cheaper. The sets you get with your off-the-shelf keyboard, or off AliExpress, are probably ABS. It’s more flexible, but the legends can wear off the surface of the keycaps.&lt;/p&gt;
&lt;p&gt;Additionally, keycaps may be &lt;a href=&quot;https://teksbit.com/what-are-double-shot-keycaps-guide/&quot;&gt;“double shot”&lt;/a&gt; (two layers of plastic, one for the lettering and one for the overlay).&lt;/p&gt;
&lt;p&gt;Keycaps can come in a variety of different profiles, and may be flat (all one height) or sculpted (different rows have different heights). Different profiles have different heights. Generally I’m not fussy about profiles, I just accept whatever profile they’ve chosen for the keyset I’m after.&lt;/p&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://thekeeblog.com/overview-of-different-keycap-profiles/&quot;&gt;The Keeblog’s article about different keycap profiles&lt;/a&gt; for a full overview.&lt;/p&gt;
&lt;h3 id=&quot;key-sizes&quot; tabindex=&quot;-1&quot;&gt;Key sizes&lt;/h3&gt;
&lt;p&gt;The keys themselves are measured in units (u): your average letter or number key is 1u wide, and everything else is relative to that. Most keysets will come with various different lengths for backspace, shift and caps lock keys which you might use depending on the layout of your keyboard. Some folks like a split spacebar; this is a must if you’ve got a split ergonomic keyboard, as you’ll need one on each side.&lt;/p&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://www.keyboard.university/100-courses/keycaps-101-ydy8j&quot;&gt;Keyboard University’s guide to keycaps&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-to-actually-buy-keyboards-and-keycaps&quot; tabindex=&quot;-1&quot;&gt;How to actually buy keyboards and keycaps&lt;/h2&gt;
&lt;p&gt;You can buy most switches in large enough quantities from wherever (even eBay), but many keyboards or keycaps require a bit more effort.&lt;/p&gt;
&lt;h3 id=&quot;the-group-buy-process&quot; tabindex=&quot;-1&quot;&gt;The group buy process&lt;/h3&gt;
&lt;p&gt;Since it’s a niche hobby, there isn’t enough demand for all of these things to just be mass-produced. Many keyboards, and most keysets, will go through a process called &lt;strong&gt;group buy&lt;/strong&gt;, where a certain number of sets need to be sold before production can go ahead.&lt;/p&gt;
&lt;p&gt;Generally things will go through &lt;strong&gt;interest checks (ICs)&lt;/strong&gt; before this stage, to gauge how much interest there is in the first place. Designers will produce very lifelike renders of the proposed keyset or keyboard, and if there’s enough interest they’ll approach a manufacturer. There will be a minimum order quantity, so they’ll have to guarantee a certain number of sales before production can go ahead. Production generally happens in China, and takes many months (especially if there are lots of sets in the queue in front of it).&lt;/p&gt;
&lt;p&gt;This is where the group buy phase begins. Various vendors around the world will agree to be the distributors for the set; this means you buy the set through the vendor, and the vendor in turn places an order for a certain number of sets from the manufacturer. The group buy phase has a limited time period, though usually there are no limits on how many units are available. A warning: generally there are &lt;strong&gt;no refunds&lt;/strong&gt; on group buys because they need to guarantee the order quantity.&lt;/p&gt;
&lt;p&gt;Once the group buy is over, production can begin. The factory will produce a sample set which will go back to the designer to see if they’re happy with it, and this can happen a few times until the designer is happy. Finally, it’ll go into full production, and eventually distribution. The sets will be shipped to the vendor and they’ll be in charge of shipping them to their customers.&lt;/p&gt;
&lt;p&gt;Vendors should provide updates with how things are progressing, but there are often delays. Group buys generally have a ship date of a particular quarter rather than a specific month (e.g. Q3 2024) but this is subject to change.&lt;/p&gt;
&lt;p&gt;Most vendors will have a list of upcoming and current group buys on their website; see &lt;a href=&quot;https://localghost.dev/blog/so-you-ve-decided-to-get-into-mechanical-keyboards/#where-to-buy&quot;&gt;“Where to buy”&lt;/a&gt; below.&lt;/p&gt;
&lt;p&gt;If you missed out on a group buy, don&#39;t despair. Sometimes creators will do re-runs or new revisions of popular keysets/keyboards.&lt;/p&gt;
&lt;h3 id=&quot;what-to-buy-if-you-want-it-now&quot; tabindex=&quot;-1&quot;&gt;What to buy if you want it &lt;em&gt;now&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;If you genuinely don’t care about group buys and want something right away (fair play), consider checking out a &lt;a href=&quot;https://www.keychron.com/&quot;&gt;Keychron&lt;/a&gt;. It’s a solid choice and readily available. It definitely doesn’t fulfil the “pretty” requirement, but it’s nice to type on and won’t set you back as much as the custom ones. You can swap out the keycaps and many of them are hot-swappable.&lt;/p&gt;
&lt;p&gt;A lot of keyboard retailers will have some keyboards and keysets in stock most of the time, including the “extras” that are additional sets they ordered from a group buy to sell on. These tend to go quite quickly, so if you missed a group buy make sure you pay attention to when the extras will be in stock and act fast.&lt;/p&gt;
&lt;h2 id=&quot;where-to-buy&quot; tabindex=&quot;-1&quot;&gt;Where to buy&lt;/h2&gt;
&lt;p&gt;There’s a very comprehensive &lt;a href=&quot;https://www.alexotos.com/keyboard-vendor-list/&quot;&gt;list of keyboard vendors&lt;/a&gt; by Alexotos. My personal (UK) faves are &lt;a href=&quot;https://prototypist.net/&quot;&gt;Prototypist&lt;/a&gt; (for group buys) and &lt;a href=&quot;https://mechboards.co.uk/&quot;&gt;Mechboards&lt;/a&gt; for in-stock kits and accessories. I buy switches from Mechboards or eBay.&lt;/p&gt;
&lt;p&gt;If you&#39;re not sure about a vendor, check out one of the mechanical keyboard discord servers and ask around for reviews. I find people are generally pretty helpful.&lt;/p&gt;
&lt;p&gt;As always, prepare to be hit by customs charges when ordering from abroad. That includes ordering from the EU to the UK (sob). Ordering from local vendors doesn&#39;t incur customs fees as these are shipped locally, though generally the customs charges will be factored into the group buy price.&lt;/p&gt;
&lt;h2 id=&quot;further-reading-and-links&quot; tabindex=&quot;-1&quot;&gt;Further reading &amp;amp; links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.keyboard.university/&quot;&gt;Keyboard University&lt;/a&gt; – a beginner’s guide to all things mechanical keyboards&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mintlodica.com/pages/cutekeyboard-club-discord-server&quot;&gt;Cute Keyboard Club&lt;/a&gt; – a discord server about adorable mechanical keyboards and all sorts of other things&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mintlodica.com/&quot;&gt;Mintlodica&lt;/a&gt; – one of my favourite creators of beautiful cute keycaps and keyboards&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kbdfans.com/&quot;&gt;KBDFans&lt;/a&gt; make some of the best keyboards out there, including my personal favourite Tofu65. They&#39;re based in China, so watch out for customs fees.&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
      <title>Automated weekly links posts with raindrop.io and Eleventy</title>
      <link href="https://localghost.dev/blog/automated-weekly-links-posts-with-raindrop-io-and-eleventy/"/>
      <updated>2024-01-22T00:00:00Z</updated>
      <id>https://localghost.dev/blog/automated-weekly-links-posts-with-raindrop-io-and-eleventy/</id>
      <content type="html">&lt;p&gt;&lt;em&gt;Edit 04/02/24 to change the comparator date from midnight on previous Saturday to midnight on previous Sunday to prevent duplicate links being published.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A post that’s been getting a lot of traction recently is &lt;a href=&quot;https://blog.cassidoo.co/post/human-curation/&quot;&gt;I miss human curation&lt;/a&gt; by Cassidy Williams, in which she laments that we’re so reliant on algorithms to show us new stuff now, instead of having it recommended to us by other humans.&lt;/p&gt;
&lt;p&gt;Inspired by that, I decided to start posting weekly collections of posts and links I liked that week. Knowing I wouldn’t keep it up if I had to manually post it every week, I set about finding a way to automate it. How could I mark a link or blog post as “good”, and have it show up in my blog on a Sunday without me having to do anything?&lt;/p&gt;
&lt;p&gt;It occurred to me that I’m already paying for &lt;a href=&quot;http://raindrop.io/&quot;&gt;raindrop.io&lt;/a&gt;, an excellent bookmark manager. It’s a great way of keeping links in sync across multiple platforms... and it has an API! This meant I could add links to a particular &lt;a href=&quot;http://raindrop.io/&quot;&gt;raindrop.io&lt;/a&gt; collection as I come across them, and then fetch them once a week and turn them into a post.&lt;/p&gt;
&lt;h2 id=&quot;collecting-the-links&quot; tabindex=&quot;-1&quot;&gt;Collecting the links&lt;/h2&gt;
&lt;p&gt;I’ve created a separate &lt;a href=&quot;http://raindrop.io/&quot;&gt;raindrop.io&lt;/a&gt; collection for these links, which I can easily share to from the iOS share sheet, or from the Firefox extension. When I save the bookmark, I also add an accompanying note with a sentence or two about the link.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/raindrop-bookmark.png&quot; alt=&quot;A screenshot of a Firefox tab navigated to hidde.blog, with the raindrop.io extension visible. I am bookmarking a blog post by Hidde de Vries about his own link sharing plans, and I&#39;ve written a note about it: &#39;Hidde&#39;s posted about sharing links on his own blog as well - great minds think alike!&#39;. I have tagged the post &#39;good links&#39;.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;I generated an API token for raindrop, and wrote a little script to pull the links from the collection using the &lt;code&gt;/raindrops/[collectionID]&lt;/code&gt; &lt;a href=&quot;https://developer.raindrop.io/v1/raindrops/multiple&quot;&gt;endpoint&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I made sure to only fetch links from the past week so I didn’t duplicate anything. You can pass specific &lt;a href=&quot;https://help.raindrop.io/using-search/#operators&quot;&gt;search parameters&lt;/a&gt; in the query, so I restricted the links to any created &lt;em&gt;after&lt;/em&gt; midnight on the previous Sunday, and &lt;em&gt;before&lt;/em&gt; midnight on the current day - so, Sunday to Saturday. That means any links I clip on the Sunday will appear in the following week’s link post.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todayDate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lastSunDate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;subDays&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todayDate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// using date-fns here&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lastSun &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastSunDate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yyyy-MM-dd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; today &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todayDate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yyyy-MM-dd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchLinks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Get content bookmarked between last Sunday and this Saturday inclusive&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; search &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;created:&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;lastSun&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; created:&amp;lt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;today&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://api.raindrop.io/rest/v1/raindrops/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;collectionId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; search&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rsp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Bearer &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;token&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; rsp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;creating-the-post&quot; tabindex=&quot;-1&quot;&gt;Creating the post&lt;/h2&gt;
&lt;p&gt;Once I’ve pulled the links, I need to turn them into an actual markdown post. I’ve created a very simple template that I can inject content into:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;

{{links}}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using my method for creating &lt;a href=&quot;https://localghost.dev/blog/building-post-types-and-category-rss-feeds-in-eleventy/&quot;&gt;post types&lt;/a&gt;, I&#39;ve added a new &lt;code&gt;link&lt;/code&gt; type which has its own shared config. I&#39;m using a couple of custom date filters to get the dates in the right format for titles and URLs.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;layout&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;single-post.njk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;hasCustomOGImage&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;eleventyComputed&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Good links: {{ date | dateFilter }}&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;excerptText&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Links to posts and websites I&#39;ve enjoyed this week, curated and automated.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;link&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;links&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;permalink&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/blog/good-links-{{date | urlDateFilter }}/index.html&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I format the links into markdown, defaulting to raindrop’s excerpt if I didn’t write a note:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formattedLinks &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; raindrops&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;raindrop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; link&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; excerpt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; note &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; raindrop&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; note &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; excerpt &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;* [&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;title&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;link&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;) - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;description&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I read the template as a string, interpolate the date and links I’ve just formatted, and write them to a file in my blog directory with the date as a filename.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; postContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./scripts/link_template.md&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  postContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; postContent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{{date}}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; formattedToday&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  postContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; postContent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{{links}}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; formattedLinks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;./src/blog/links/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;formattedToday&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.md&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; postContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;scheduling-the-script&quot; tabindex=&quot;-1&quot;&gt;Scheduling the script&lt;/h2&gt;
&lt;p&gt;Running the script is easy enough: I added it into my &lt;code&gt;package.json&lt;/code&gt; scripts as &lt;code&gt;yarn generate-links&lt;/code&gt;. But I’d like to not even have to think about running it and have something do it for me.&lt;/p&gt;
&lt;p&gt;It made sense to use GitHub Actions to run it on a schedule. I already deploy my website twice a day automatically so that new webmentions are fetched regularly.&lt;/p&gt;
&lt;p&gt;I randomly entered numbers into &lt;a href=&quot;https://crontab.guru/&quot;&gt;crontab.guru&lt;/a&gt; until it came out with “every Sunday at 6pm”, and then created a new workflow:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Generate and publish weekly link post&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 18 * * 0
&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;generate-post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Run script to generate post&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;node-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;18&quot;&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yarn&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yarn install
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yarn generate&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;links
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;RAINDROP_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.RAINDROP_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;RAINDROP_COLLECTION_ID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.RAINDROP_COLLECTION_ID &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; stefanzweifel/git&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;commit&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v5
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;commit_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Generate weekly link post&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;m storing the &lt;a href=&quot;http://raindrop.io/&quot;&gt;raindrop.io&lt;/a&gt; token and collection ID in the repository secrets.&lt;/p&gt;
&lt;p&gt;My script generates a new &lt;code&gt;.md&lt;/code&gt; file with the post, so I need it to commit and push the changes. For this I used &lt;a href=&quot;https://github.com/stefanzweifel/git-auto-commit-action&quot;&gt;git-auto-commit-action&lt;/a&gt;. It detects changed files during a workflow run and commits and pushes them back to the repo.&lt;/p&gt;
&lt;p&gt;My &lt;a href=&quot;https://github.com/sophiekoonin/localghost/blob/main/.github/workflows/deploy-neocities.yml&quot;&gt;deployment workflow&lt;/a&gt; listens for the link creation workflow to complete, so once this workflow finishes running and pushes the changes, the deployment one will kick off and deploy my new blog post. Magic!&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# deploy-neocities.yml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Deploy to Neocities&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;workflow_run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;workflows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Generate and publish weekly link post&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; completed&lt;/code&gt;&lt;/pre&gt;
</content>
    </entry>
    
    <entry>
      <title>Good links: 21 January 2024</title>
      <link href="https://localghost.dev/blog/good-links-2024-01-21/"/>
      <updated>2024-01-21T00:00:00Z</updated>
      <id>https://localghost.dev/blog/good-links-2024-01-21/</id>
      <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://aftermath.site/the-internet-is-full-of-ai-dogshit&quot;&gt;The Internet Is Full of AI Dogshit - Aftermath&lt;/a&gt; - How AI-generated content is ruining search engines for everyone.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chriscoyier.net/2024/01/13/exposed-rss/&quot;&gt;Exposed RSS&lt;/a&gt; - I get sites not having an “RSS” for “Feed” link on their website while actually having an RSS feed. I don’t like it, but I get it. Maybe they picked an off-the-shelf t…&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://keith.is/posts/you-should-blog/&quot;&gt;You should blog | keith.is&lt;/a&gt; - Keith calls out for folks to make this the year of blogging about whatever the hell you want. He’s suggested some great places to get started!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lethain.com/layers-of-context/&quot;&gt;Layers of context.&lt;/a&gt; - Will Larson on the importance of thinking outside of your immediate context: why a change that seems like a great idea may not be so well received elsewhere in the organisation, and how you can get a better radar for these things.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.cassidoo.co/post/annoyed-at-react/&quot;&gt;Kind of annoyed at React&lt;/a&gt; - Cassidy Williams shares her frustrations about React, echoing some of the things I’ve been feeling recently.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://svgfm.chriskirknielsen.com/&quot;&gt;SVG Filter Maker&lt;/a&gt; - SVGFM, a node graph builder for SVG filters&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hidde.blog/sharing-links/&quot;&gt;Sharing links&lt;/a&gt; - Hidde&#39;s posted about sharing links on his own blog as well - great minds think alike!&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
      <title>How I deploy my Eleventy site to Neocities</title>
      <link href="https://localghost.dev/blog/how-i-deploy-my-eleventy-site-to-neocities/"/>
      <updated>2024-01-20T00:00:00Z</updated>
      <id>https://localghost.dev/blog/how-i-deploy-my-eleventy-site-to-neocities/</id>
      <content type="html">&lt;p&gt;Skip to the bit you care about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/how-i-deploy-my-eleventy-site-to-neocities/#about-neocities&quot;&gt;About Neocities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/how-i-deploy-my-eleventy-site-to-neocities/#hosting-a-static-site-on-neocities&quot;&gt;Hosting a static site on Neocities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/how-i-deploy-my-eleventy-site-to-neocities/#continuous-deployment-to-neocities-with-github-actions&quot;&gt;Continuous deployment to Neocities with Github Actions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/how-i-deploy-my-eleventy-site-to-neocities/#scheduling-builds&quot;&gt;Scheduling builds&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ve hosted this website in a few different places since I started it in 2019. It started out on Netlify, but after my post about everything I googled went viral, I exceeded the bandwidth of the free plan. Then I moved it to Vercel, which (at least at the time) had more generous bandwidth on the free tier. I enjoyed very quick deployment speeds, but it gave me big Corporate Web vibes. Now I host localghost.dev on &lt;a href=&quot;https://neocities.org/&quot;&gt;Neocities&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;about-neocities&quot; tabindex=&quot;-1&quot;&gt;About Neocities&lt;/h2&gt;
&lt;p&gt;Neocities reminds me of everything I love about the old web. It’s a place for people to create &lt;a href=&quot;https://neocities.org/browse&quot;&gt;bizarre, oddly specific websites&lt;/a&gt; for things they love, or random collections of digital trinkets they’ve found on their online travels.&lt;/p&gt;
&lt;p&gt;The manifesto on their &lt;a href=&quot;https://neocities.org/about&quot;&gt;about page&lt;/a&gt; resonated with me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We are tired of living in an online world where people are isolated from each other on boring, generic social networks that don&#39;t let us truly express ourselves. &lt;strong&gt;It&#39;s time we took back our personalities from these sterilized, lifeless, monetized, data mined, monitored addiction machines and let our creativity flourish again.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;hosting-a-static-site-on-neocities&quot; tabindex=&quot;-1&quot;&gt;Hosting a static site on Neocities&lt;/h2&gt;
&lt;p&gt;Neocities offers free static site hosting, and (unlike the free hosts of yore) there are NO ADS. None. It’s been around for years, so it’s not going anywhere any time soon.&lt;/p&gt;
&lt;p&gt;You can manually build your site and upload the assets via the web editor, if you want. However, I’d recommend using version control so that you can easily roll back changes, and move your site somewhere else if you need to. There’s a &lt;a href=&quot;https://neocities.org/cli&quot;&gt;CLI tool&lt;/a&gt; and an &lt;a href=&quot;https://neocities.org/api&quot;&gt;API&lt;/a&gt; that make it much easier to upload new versions of your site.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://neocities.org/supporter&quot;&gt;supporter plan&lt;/a&gt; allows you to use custom domains, gives you loads more storage and bandwidth, and allows as many sites as you want under the same account. It costs $5 a month, which really isn’t very much, and I’m happy to pay for it knowing that it’s supporting people creating websites for free and learning web development the same way I did.&lt;/p&gt;
&lt;h2 id=&quot;continuous-deployment-to-neocities-with-github-actions&quot; tabindex=&quot;-1&quot;&gt;Continuous deployment to Neocities with Github Actions&lt;/h2&gt;
&lt;p&gt;Like with Netlify and Vercel, I wanted to be able to set up a pipeline to automatically deploy my site when I pushed to the main branch. I’ve used CircleCI in the past, but I wanted to use &lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions&quot;&gt;Github Actions&lt;/a&gt; as it’s just... nicer.&lt;/p&gt;
&lt;p&gt;I was very excited to discover that there’s a &lt;a href=&quot;https://github.com/bcomnes/deploy-to-neocities&quot;&gt;GitHub Action for deploying to Neocities&lt;/a&gt; by Bret Comnes! It uses the Neocities API under the hood, so you need to create an API key.&lt;/p&gt;
&lt;p&gt;You can do this via the API (this example is from the API docs):&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://USER:PASS@neocities.org/api/key&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;api_key&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;da77c3530c30593663bf7b797323e48c&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or you can create a new key from your site settings page at &lt;code&gt;https://neocities.org/settings/&amp;lt;your username&amp;gt;#api_key&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once you’ve got the key, store it in your repo’s actions secrets. Head to your repo’s settings page, and open “Secrets and Variables” &amp;gt; “Actions” under the “Security” heading. Create a new repository secret called &lt;code&gt;NEOCITIES_TOKEN&lt;/code&gt; and set its value as the key you just created.&lt;/p&gt;
&lt;p&gt;If you don’t have any other GH Actions workflows set up, you’ll need to create the &lt;code&gt;.github/workflows&lt;/code&gt; directory. In here, I’ve got a workflow called &lt;code&gt;deploy-neocities.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I’ve added some comments to show you what each bit does.&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Deploy to Neocities&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main &lt;span class=&quot;token comment&quot;&gt;# run this job when I push to main&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Deploy to Neocities&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest &lt;span class=&quot;token comment&quot;&gt;# the system the runner uses&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4 &lt;span class=&quot;token comment&quot;&gt;# checkout the repo&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node@v3 &lt;span class=&quot;token comment&quot;&gt;# install node.js&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;node-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;18&quot;&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yarn&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# I&#39;m using yarn rather than npm&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yarn install &lt;span class=&quot;token comment&quot;&gt;# install dependencies&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yarn build &lt;span class=&quot;token comment&quot;&gt;# build my project&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# the env vars I need to build my site&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;WEBMENTION_IO_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $ 
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bcomnes/deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;neocities@v1 &lt;span class=&quot;token comment&quot;&gt;# deploy!&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# config for the action&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;api_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $
          &lt;span class=&quot;token key atrule&quot;&gt;dist_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_site/&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# my build output directory&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;cleanup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# delete anything on neocities that&#39;s not in my dist_dir&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The workflow runs every time I push commits to the main branch.&lt;/p&gt;
&lt;p&gt;The environment variables you need for the &lt;code&gt;yarn build&lt;/code&gt; step will vary, and you might not need any at all.&lt;/p&gt;
&lt;p&gt;You &lt;em&gt;will&lt;/em&gt; need the &lt;code&gt;NEOCITIES_TOKEN&lt;/code&gt; for the &lt;code&gt;deploy-to-neocities&lt;/code&gt; action, which we pass in here as the &lt;code&gt;api_token&lt;/code&gt; param.&lt;/p&gt;
&lt;p&gt;The default value of the &lt;code&gt;cleanup&lt;/code&gt; param is &lt;code&gt;false&lt;/code&gt;, and I had it set to that for ages, but then I realised that if I accidentally published a post and unpublished it by setting it back to draft, it wouldn’t delete the built post on Neocities. So even if it wasn’t linked from the site, the page itself would still be hosted there. I’ve since set &lt;code&gt;cleanup&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; so it removes any files from Neocities that aren’t in my newly built site output.&lt;/p&gt;
&lt;p&gt;Once you’ve got that workflow file finished, commit it and push it to your repo. GitHub will pick it up, and start running it automatically depending on what you told it to do. I find it goes pretty fast – my site deploys in about 30s.&lt;/p&gt;
&lt;h3 id=&quot;scheduling-builds&quot; tabindex=&quot;-1&quot;&gt;Scheduling builds&lt;/h3&gt;
&lt;p&gt;Branch pushes aren’t the only triggers for workflows! You can have time-based triggers as well, using cron, the scheduling tool.&lt;/p&gt;
&lt;p&gt;For example, I automatically rebuild my site at 8am and 6pm every day to fetch the latest webmentions (there’s a script that runs every time the site is built).&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 8/9 * * *  &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I recommend &lt;a href=&quot;https://crontab.guru/&quot;&gt;crontab.guru&lt;/a&gt; for help with cron syntax.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Sending webmentions from a static site</title>
      <link href="https://localghost.dev/blog/sending-webmentions-from-a-static-site/"/>
      <updated>2024-01-20T00:00:00Z</updated>
      <id>https://localghost.dev/blog/sending-webmentions-from-a-static-site/</id>
      <content type="html">&lt;p&gt;It occurred to me this week that even though I’ve been using webmentions as comments for a really long time, I hadn’t actually been sending them myself. The shame! Here&#39;s how I set it up.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Edit 22/01/23:&lt;/strong&gt; I&#39;ve added in some checks to make sure I don&#39;t send webmentions for a post if I&#39;ve already done it before.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Certified good egg &lt;a href=&quot;https://remysharp.com/&quot;&gt;Remy Sharp&lt;/a&gt; has a tool called &lt;a href=&quot;https://webmention.app/&quot;&gt;webmention.app&lt;/a&gt; which will send webmentions for you – you CURL it with the URL of your post, and it’ll search through it for any links and check to see if the owners of those sites have webmentions or pingbacks set up. There’s also an npm package, &lt;a href=&quot;https://www.npmjs.com/package/@remy/webmention&quot;&gt;@remy/webmention&lt;/a&gt;, which you give an RSS feed and it does the same thing. That’s the version I’ve used.&lt;/p&gt;
&lt;p&gt;You can add it as the &lt;code&gt;postbuild&lt;/code&gt; script in &lt;code&gt;package.json&lt;/code&gt;, so it’ll run after every &lt;code&gt;yarn build&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;postbuild&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webmention _site/feed.xml --limit 1 --send&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was concerned that if my build runs every 12 hours, it’ll keep sending webmentions for the same posts. Remy assures me that duplicate webmentions aren’t an issue, as the accepting server will just respond with a 200 if I send a webmention that it’s already seen. However, if you build your site very often you might be at risk of nearly DOS-ing the websites you&#39;re sending mentions to. To get around this, you could:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;write a script that runs the webmention CLI but only after checking the post timestamp of the last post to see if it&#39;s recent enough&lt;/li&gt;
&lt;li&gt;keep a record somehow of the last post you sent webmentions for, e.g. in a text file in the repo&lt;/li&gt;
&lt;li&gt;check the message from the last commit and only send the webmentions if you mention &amp;quot;new post&amp;quot; or similar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To be super safe, I&#39;ve added it as a script in &lt;code&gt;package.json&lt;/code&gt; but I&#39;m calling it from within a bash script that checks the commit hash for the last commit, and records whether or not I&#39;ve sent webmentions for that commit already.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#! /usr/bin/env bash&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; ./last_webmentions_commit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Last webmention commit file not found, exiting&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Read the contents of the file&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;LAST_COMMIT_SENT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; ./last_webmentions_commit&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Get latest commit hash&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;LATEST_COMMIT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-parse &lt;span class=&quot;token parameter variable&quot;&gt;--short&lt;/span&gt; HEAD&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# If the last commit hash I sent webmentions for is&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# the same as the latest commit hash, exit&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$LAST_COMMIT_SENT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$LATEST_COMMIT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;No new commits since we last sent webmentions, nothing to do&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Get the last commit message&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;LAST_COMMIT_MESSAGE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log &lt;span class=&quot;token parameter variable&quot;&gt;-1&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--pretty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;%B&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Does the commit message contain &#39;post&#39;?&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$LAST_COMMIT_MESSAGE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; *&lt;span class=&quot;token string&quot;&gt;&quot;post&quot;&lt;/span&gt;* &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Last commit message does not contain &#39;post&#39;, nothing to do&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Update the file with the latest commit hash&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$LATEST_COMMIT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; ./last_webmentions_commit

&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; send-webmentions&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I write a new post, I always use the word &amp;quot;post&amp;quot; in the commit message, so this will help distinguish which commits we need to send webmentions for.&lt;/p&gt;
&lt;p&gt;Now, every time I build and deploy my site, the script will send webmentions to anyone I’ve mentioned in the last post I wrote, as long as I haven&#39;t already sent webmentions for that post and the recipients have the webmention meta tags set up!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Just because you can doesn&#39;t mean you should: the &lt;meter&gt; element</title>
      <link href="https://localghost.dev/blog/just-because-you-can-doesn-t-mean-you-should-the-meter-element/"/>
      <updated>2024-01-07T00:00:00Z</updated>
      <id>https://localghost.dev/blog/just-because-you-can-doesn-t-mean-you-should-the-meter-element/</id>
      <content type="html">&lt;p&gt;I came across Sara Joy&#39;s (very cool) demo of &lt;a href=&quot;https://codepen.io/sarajw/details/xxBGmRZ&quot;&gt;CSS theming without classes&lt;/a&gt; today, and looking through the code spotted a couple of elements I hadn&#39;t come across before: &lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;meter&amp;gt;&lt;/code&gt;. Granted, I&#39;ve probably seen the &lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt; element plenty of times, but I struggled to see what the &lt;code&gt;&amp;lt;meter&amp;gt;&lt;/code&gt; was for.&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt; is fairly self-explanatory – it shows how far along something is, such as your progress through a form – the &lt;code&gt;&amp;lt;meter&amp;gt;&lt;/code&gt; element was less obvious to me, so I had a look at the MDN page to see what they suggested.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/meter.png&quot; alt=&quot;A screenshot from MDN. HTML:  Heat the oven to 350 degrees.  Result:  &amp;quot;Heat the oven to&amp;quot; and then a half-full progress bar that doesn&#39;t show you any indication of the actual number.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;This is a prime example of &lt;strong&gt;following the letter, not the spirit, of semantic HTML&lt;/strong&gt;. Yes, technically the cooking temperature is somewhere between the lowest and highest temperature you can set the oven to, but is this actually helping people understand the recipe? Quite the opposite, it&#39;s making the recipe less accessible for anyone &lt;em&gt;not&lt;/em&gt; using a screen reader.&lt;/p&gt;
&lt;p&gt;Now, chances are the person who wrote this article simply couldn&#39;t think of a better example, and isn&#39;t necessarily proposing that everyone starts using meters instead of numbers in their recipes, but a &lt;em&gt;lot&lt;/em&gt; of developers rely on MDN to tell them what is good practice, so I don&#39;t think this is particularly useful. (I&#39;m going to try and contribute a better example.)&lt;/p&gt;
&lt;p&gt;Inspired by this, and with a burning desire to build something truly terrible, I&#39;ve created this recipe page using the &lt;code&gt;&amp;lt;meter&amp;gt;&lt;/code&gt; element for every numerical value.&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;265&quot; data-theme-id=&quot;dark&quot; data-default-tab=&quot;result&quot; data-user=&quot;sophiekoonin&quot; data-slug-hash=&quot;ZEPWxLL&quot; data-preview=&quot;true&quot; data-pen-title=&quot;CodePen Home
A recipe, but all the numbers are &lt;meter&gt; bars&quot;&gt;
  &lt;span&gt;See the Pen 
    &lt;a href=&quot;https://codepen.io/sophiekoonin/pen/ZEPWxLL&quot;&gt;
      CodePen Home
A recipe, but all the numbers are &lt;meter&gt; bars&lt;/meter&gt;&lt;/a&gt; by &lt;a href=&quot;https://codepen.io/sophiekoonin&quot;&gt;@sophiekoonin&lt;/a&gt;
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.w3.org/TR/2011/WD-html5-author-20110809/the-meter-element.html&quot;&gt;spec&lt;/a&gt; has a few more sensible examples, though I&#39;m not entirely persuaded that a meter is a good illustration of newsgroup activity.&lt;/p&gt;
&lt;p&gt;A more tangible use case for the &lt;code&gt;&amp;lt;meter&amp;gt;&lt;/code&gt; element would be to indicate something like available storage space, or percentage of remaining budget on a service where your plan only allows you a certain number of events or entities: an example of this would be &lt;a href=&quot;https://sentry.io/welcome/&quot;&gt;Sentry&lt;/a&gt;, where your plan has a limit to the number of events/errors it&#39;ll accept, depending on how much money you throw at them.&lt;/p&gt;
&lt;p&gt;The key UX thing here, though, is that if it&#39;s important information it should be accompanied by a numerical value. A meter is good for an at-a-glance sense of how much of something has been used, but you need to present it alongside the actual value for it to be at all useful.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/google-drive-meter.png&quot; alt=&quot;A screenshot from Google Drive with a cloud icon next to text that says &#39;Storage 74% full&#39;, a graphical meter that is approx 74% full, then underneath the text &#39;11.2 GB of 15 GB used&#39;.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;I checked both Dropbox and Google Drive, and both of them have a meter accompanied by a numerical description of how much space I&#39;ve used; in both cases those meters are, of course, &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s. Usually I&#39;d complain about using a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; when there&#39;s a semantic element available, but... it&#39;s not immediately clear to me what the advantage of using a &lt;code&gt;&amp;lt;meter&amp;gt;&lt;/code&gt; would be from an accessibility viewpoint, if you&#39;ve got the written description right there.&lt;/p&gt;
&lt;p&gt;When you&#39;re choosing the right element for the job, it&#39;s entirely possible to go too far the other way, and &lt;em&gt;overuse&lt;/em&gt; semantic elements when actually they hinder more than they help.&lt;/p&gt;
&lt;p&gt;As my good pal and accessibility specialist Helen put it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s a really good example of thinking about what you’re trying to communicate and to who and whether your “semantic” choices actually enable that.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
    </entry>
    
    <entry>
      <title>Remembering the early 00s teen website scene</title>
      <link href="https://localghost.dev/blog/remembering-the-early-00s-teen-website-scene/"/>
      <updated>2023-12-30T00:00:00Z</updated>
      <id>https://localghost.dev/blog/remembering-the-early-00s-teen-website-scene/</id>
      <content type="html">&lt;p&gt;localghost.dev has a new theme! In search of a little project over the merrineum that didn’t require me to learn anything and therefore use my brain, I remembered there was a stylesheet hidden in the themes directory of my website that I hadn’t finished. The theme: teenage personal websites in the early 00s. It was a lot of fun to build, and really nostalgic to recreate the websites of my youth. Think impossibly tiny fonts, blocky layouts with a sidebar full of assorted crap, and grungey photoshop brushes. (But this time with CONTAINER QUERIES.)&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/00s-sites/2003-theme.png&quot; alt=&quot;A screenshot of my new theme, with a purple background and two boxes - sidebar and content - scrunched up against the left hand side of the page. The font is very small. The header image has assorted grungy patterns on it with a distorted cursive font that says &#39;localghost&#39;.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Try out the new theme by clicking the scribbly heart in the middle of the theme switcher.&lt;/p&gt;
&lt;p&gt;I touched on this era of my online past in my talk &lt;a href=&quot;https://www.youtube.com/watch?v=vGYm9VdfJ8s&quot;&gt;“This talk is under construction: a love letter to the personal website”&lt;/a&gt; from FFConf 2022. &lt;a href=&quot;https://medium.com/@ohhoe/keep-the-internet-weird-1137eece27c4&quot;&gt;Rachel White&lt;/a&gt; also wrote an excellent post about this topic back in 2016 called &lt;em&gt;Keep The Internet Weird&lt;/em&gt;, and even gave a talk about it at &lt;a href=&quot;https://www.youtube.com/watch?v=vji_6ofE5Wg&quot;&gt;JSConf EU 2017&lt;/a&gt;. This quote from her article particularly rings true for me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I can still remember spending hours on the Internet scouring free resources created by other teenagers for really great grunge brushes to create the best layouts that represented me at that point in my life.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img alt=&quot;A screenshot of a website from 2003, with a dark red background and small pink frame in the centre of the page containing a blog. The text is very small and the content scrolls in its tiny box. The background has pink paint splatters on. The left hand sidebar has a navigation with &#39;girl&#39;, &#39;site&#39;, &#39;misc&#39;, &#39;links&#39;, &#39;gbook&#39;, and next to it the sentence &#39;girl, 17, new york, freshman in college&#39;.&quot; src=&quot;https://localghost.dev/img/blog/00s-sites/rachel-site.png&quot; /&gt;&lt;figcaption&gt;One of Rachel&#39;s sites, circa 2003&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I&#39;ve recreated some of this magic in the new theme, complete with the stupidly tiny content box that scrolls, and the font that you have to squint to read.&lt;/p&gt;
&lt;p&gt;All of this has me reminisicing about my adventures building websites as part of this early 00s scene, so I thought I&#39;d share some of those memories.&lt;/p&gt;
&lt;h3 id=&quot;tld-is-everything&quot; tabindex=&quot;-1&quot;&gt;TLD is everything&lt;/h3&gt;
&lt;p&gt;If you were going to have a domain name, you had to have the right TLD. For some reason &lt;code&gt;.org&lt;/code&gt; and &lt;code&gt;.nu&lt;/code&gt; domain names were really fashionable. I remember sites with names like &lt;code&gt;partly-cloudy.org&lt;/code&gt; and &lt;code&gt;lipsofpink.org&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Of course I never had a domain because my parents wouldn’t pay for it (very reasonably), so I had to do the next best thing: be hosted on someone else&#39;s website who was cool enough to have a domain.&lt;/p&gt;
&lt;p&gt;Since the webmasters of the early 00s let these domain names lapse a long time ago, there’s very little evidence of this scene on the internet. Google searches for “2000s teenage blog scene” come up with a lot of “Y2K” inspired tumblrs and reminiscences of the actual scene subculture, something entirely different. There’s the &lt;a href=&quot;https://www.reddit.com/r/CasualConversation/comments/vr01wv/anyone_remember_angsty_teen_web_journals_from_the/&quot;&gt;occasional Reddit post&lt;/a&gt; where someone asks “hey, does anyone remember...” and there will be one person who does, and everyone else just remembers LiveJournal (everyone remembers LJ).&lt;/p&gt;
&lt;p&gt;Thank goodness for the Internet Archive and its Wayback Machine, which has snapshots of many of these sites. Sadly a lot of the images aren&#39;t there, so you can&#39;t experience the full, over-the-top glory of the layouts. Hopefully the new theme on this site will give you a taste of what it was like.&lt;/p&gt;
&lt;p&gt;I had three different hosts over a few years, and slices of those sites are still findable on the Wayback Machine, so you can be assured I won&#39;t be telling you what those domains were in the interests of self-preservation (translation: it&#39;s too embarrassing).&lt;/p&gt;
&lt;p&gt;It&#39;s been a stark reminder of the importance of digital archiving, finding all of these sites incomplete in some way: content missing, stylesheet missing, images missing. With every site, there’s probably going to be a point where you don’t want to renew the domain name any more, or the host itself disappears. It’s really important that you have some kind of backup of the content, not only in text format but visually as well: the Wayback Machine has only captured snippets of my old sites, and often the images and stylesheets don’t work. I’d give anything to see full screenshots of what some of those sites looked like.&lt;/p&gt;
&lt;h3 id=&quot;this-is-my-space-but-here-s-something-for-you&quot; tabindex=&quot;-1&quot;&gt;This is my space, but here&#39;s something for you&lt;/h3&gt;
&lt;p&gt;Blog posts – annotated with the music you were listening to while writing, of course – would be little windows into your everyday life. I remember reading some older teenagers&#39; blogs, especially those in the States, and thinking how extremely cool they seemed. Driving! Relationships! Life that didn&#39;t revolve around exams!&lt;/p&gt;
&lt;p&gt;The homepage would always be the blog (splash screens were passé by this point), but there would always be a page about the site&#39;s owner, then a page of things for the visitor such as downloadable graphics, Photoshop brushes or HTML snippets to use on your own site.&lt;/p&gt;
&lt;p&gt;Something that was particularly great about this scene was that there was a real sense of community, both literally with the networks you formed with other bloggers, but also through helping other people be a part of it by teaching them HTML skills. We&#39;d all make friends and comment on each other&#39;s blogs and link to our friends&#39; and fellow hostees&#39; sites, most of whom were people that we&#39;d never even met.&lt;/p&gt;
&lt;p&gt;Most importantly, though: a lot of these websites were run by teenage girls and young women. It was a space on the internet for marginalised people where we could be ourselves, whether under a real name or an alias. Our families wouldn&#39;t find it, and IRL friends wouldn&#39;t either unless you gave them the URL of your site, so the like-minded audience that you had was very small and controlled as well. It felt like a really safe space to be yourself. None of the carefully curated content feeds that you get on social media these days (or even blogs these days, a lot of the time).&lt;/p&gt;
&lt;p&gt;At the time, I was pretty unpopular at school, in part because I was such a computer geek (it was very much Not A Cool Hobby) and I used to spend breaks at school working on my website in the IT room. The group of friends I fell into – who I&#39;m still very close with today – were all like-minded geeky types with our own websites. Seeing and interacting with these older teenagers online with these fancy domains and cool layouts made me feel like I was part of something and that my interests were actually socially acceptable.&lt;/p&gt;
&lt;p&gt;Of course, joke&#39;s on all the people who thought I was uncool, because now I&#39;m so cool that I have a domain name that I need to explain to everyone who isn&#39;t a developer every time I give someone my email address.&lt;/p&gt;
&lt;h3 id=&quot;version-17&quot; tabindex=&quot;-1&quot;&gt;Version 17&lt;/h3&gt;
&lt;p&gt;I’d happily fire up FileZilla and FTP into my server, set up Greymatter or later Wordpress, and spend hours after school building layouts for my website, and my friends’ websites. Later I learned some very basic PHP so that I could include layout headers and footers on separate pages of my website, and even make the footer copyright date dynamic instead of just hard-coded.&lt;/p&gt;
&lt;p&gt;We’d change the layouts on our sites very frequently – once a month or even more often – and keep track of which version we were on in our site sidebar. Layouts were usually a main bit and a sidebar, first with tables and later with absolutely positioned divs; every layout featured a large header image composed of a transparent cutout PSD of some band or celebrity with the obligatory grunge photoshop brushes on top. I have a handful of my later header images saved, but most are lost to time.&lt;/p&gt;
&lt;p&gt;There were websites offering cutouts of celebrities from magazine scans and photoshoots; I’d make header images in my cracked version of Paint Shop Pro with &lt;a href=&quot;https://web.archive.org/web/20050130021007/http://www.lime-light.org/psds/&quot;&gt;whatever PSDs I could get my hands on&lt;/a&gt;, regardless of how much I actually liked that celebrity. That’s how I ended up with layouts featuring such gems as, er, Paris Hilton or Gwyneth Paltrow.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/00s-sites/gwyneth.jpg&quot; alt=&quot;The header image from an old layout of mine featuring three cutouts of Gwyneth Paltrow, all greyscale with grungy textures on top, and a light green flowery pixel art background.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Over time, tiny scrunched-up scrolling box layouts were replaced by full-page ones, still clamped to the left hand side of the browser, but with even more room in the sidebar for fun things.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/00s-sites/screenshot.png&quot; alt=&quot;A screenshot of a full page site with a grungy graphic of Tegan and Sara in the top left with lyrics from &#39;I Bet It Stung&#39;, and lots of very small text in both the sidebar on the right and the content underneath the header image.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;h3 id=&quot;website-tsatskes&quot; tabindex=&quot;-1&quot;&gt;Website tsatskes&lt;/h3&gt;
&lt;p&gt;Another thing I miss from early websites is the random tsatskes you&#39;d accumulate from around the web and display proudly on your homepage, and these websites were no exception. You might have &lt;a href=&quot;https://localghost.dev/blog/remembering-the-early-00s-teen-website-scene/www.thefanlistings.org&quot;&gt;fanlistings&lt;/a&gt; for things you love, links to &lt;a href=&quot;https://www.wired.com/2003/07/web-cliques-too-cool-for-school/&quot;&gt;cliques&lt;/a&gt; you’d joined, or 88 x 31px buttons linking to other websites.&lt;/p&gt;
&lt;p&gt;Those of us on PHP-enabled servers might have some &lt;a href=&quot;https://web.archive.org/web/20040805055326/http://www.codegrrl.com/archives/cat_scripts.php&quot;&gt;codegrrl&lt;/a&gt; scripts like &lt;a href=&quot;https://web.archive.org/web/20040805055326/http://www.codegrrl.com/archives/000100.php&quot;&gt;PHPCurrently&lt;/a&gt;, a customisable list of what you were doing at any given point in time. I mentioned this in my post about &lt;a href=&quot;https://localghost.dev/blog/everything-should-have-an-api-adventures-in-trying-to-automate-stuff/&quot;&gt;APIs for things&lt;/a&gt; and I can only apologise for this absolutely awful example of what I was like as an Evanescence-obsessed teenager in 2004:&lt;/p&gt;
&lt;figure&gt;
&lt;img class=&quot;small&quot; alt=&quot;A screenshot from an old website. It&#39;s a list of statistics about what I&#39;m doing. It says &#39;Currently:
MSN display picture: Amy in a pink dress, with lyrics from Missing.
date: 11th June.
thinking: no more exams!!!!
wearing: bathrobe.
makeup: none.
jewellery: none.
hair: loose.
MSN screenname: Grammar Nazi.
time: 11:30.
feeling: amused.
eating: raisin wheats.
drinking: nothing.
surfing: this thread on AGF. you may need this (link) for some of it.
IMing: no-one.
hating: spelling, grammar and punctuation ignorance.
Powered by PHPCurrently.&quot; src=&quot;https://localghost.dev/img/blog/phpcurrently.png&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Or maybe PHPCalendar with mundane events from your life:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/00s-sites/phpcalendar.png&quot; alt=&quot;Upcoming events:
07/06 - Maths and Citizenship exams
08/06 - Physics and Geog 2 exams
09/06 - English Lit exam
10/06 - German and RS exams
Powered by PHPCalendar&quot; class=&quot;small&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;h3 id=&quot;the-end-of-an-era&quot; tabindex=&quot;-1&quot;&gt;The end of an era&lt;/h3&gt;
&lt;p&gt;I think it was ultimately LiveJournal that killed off this scene – well, that and Myspace, probably. I moved to LJ in about 2005, and was already a late adopter. It offered a customisable blog linked to infinite user-run communities, so you didn’t have to build your own network of friends, and ultimately the experience was very similar and didn&#39;t require paying for a domain. All your friends’ posts appeared on your Friends Page, so you didn’t need RSS and you could comment on everything right there.&lt;/p&gt;
&lt;p&gt;Of course, websites don&#39;t really carry the same weight as they did back then. The way we experience the internet is very different, not only for those of us who have &lt;em&gt;seen&lt;/em&gt; it change, but even more so for the younger generations who&#39;ve grown up with endless short form video. Perhaps the current Y2K aesthetic trend will lead teens to rediscover the joy of building blogs and learning web dev skills as they trace the digital footsteps of us crusty millennials.&lt;/p&gt;
&lt;p&gt;🎵 &lt;em&gt;avril lavigne - don&#39;t tell me&lt;/em&gt;&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>A good podcast: Twenty Thousand Hertz</title>
      <link href="https://localghost.dev/blog/a-good-podcast-twenty-thousand-hertz/"/>
      <updated>2023-12-24T00:00:00Z</updated>
      <id>https://localghost.dev/blog/a-good-podcast-twenty-thousand-hertz/</id>
      <content type="html">&lt;p&gt;Everything you&#39;ve ever wanted to know about sound, and things you didn&#39;t know you wanted to know but are glad you learned, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;why all film trailers sound the same with that &lt;a href=&quot;https://www.20k.org/episodes/boojstrikesback&quot;&gt;deep &amp;quot;booj&amp;quot; noise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how &lt;a href=&quot;https://www.20k.org/episodes/spaceaudity&quot;&gt;NASA ran live audio from the Moon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how the &lt;a href=&quot;https://www.20k.org/episodes/sulsul&quot;&gt;language in The Sims&lt;/a&gt; came about, and the people who voice it&lt;/li&gt;
&lt;li&gt;the history of &lt;a href=&quot;https://www.20k.org/episodes/tadaitswindows&quot;&gt;Windows startup sounds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;the &lt;a href=&quot;https://www.20k.org/episodes/loudnesswars&quot;&gt;Loudness Wars&lt;/a&gt;, aka the reason so much popular music from the last few decades is just badly mastered&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An additional treat is the Mystery Sound competition they run every episode, where they play a snippet of an obscure (or sometimes very familiar) sound, and listeners can submit their guesses. Everything from the &lt;a href=&quot;https://www.youtube.com/watch?v=q_slAJmZBeQ&quot;&gt;Gracie Films tag&lt;/a&gt; at the end of the Simpsons, to &lt;a href=&quot;https://www.youtube.com/watch?v=tGzxq5qopoc&quot;&gt;herring farts&lt;/a&gt;. Every year they do a Mystery Sound Game Show episode, and &lt;a href=&quot;https://www.20k.org/episodes/tournamentofchampions&quot;&gt;this year&#39;s&lt;/a&gt; is hilarious. There are some sounds in there that will no doubt trigger Pavlovian reactions for anyone listening.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>2023: The year in lists</title>
      <link href="https://localghost.dev/blog/2023-the-year-in-lists/"/>
      <updated>2023-12-21T00:00:00Z</updated>
      <id>https://localghost.dev/blog/2023-the-year-in-lists/</id>
      <content type="html">&lt;p&gt;Last year&#39;s roundup post was good fun, so I decided I&#39;d do another one this year! I feel like I blinked in April and suddenly it was December.&lt;/p&gt;
&lt;p&gt;Skip to bits you care about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#the-year-in&quot;&gt;The year in...&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#conferences&quot;&gt;...conferences&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#travel&quot;&gt;...travel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#books&quot;&gt;...books&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#podcasts&quot;&gt;...podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#music&quot;&gt;...music&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#video-games&quot;&gt;...video games&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#learning-things&quot;&gt;...learning things&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#purchases&quot;&gt;...purchases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#apps-and-programs&quot;&gt;...apps and programs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#blog-posts&quot;&gt;...blog posts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2023-the-year-in-lists/#the-web&quot;&gt;...the web&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-year-in&quot; tabindex=&quot;-1&quot;&gt;The year in...&lt;/h2&gt;
&lt;h3 id=&quot;conferences&quot; tabindex=&quot;-1&quot;&gt;...conferences&lt;/h3&gt;
&lt;p&gt;After a bumper conference year in 2022, I decided to rein it in a bit and did four conferences this year. Four is a good number and a lot more doable than seven. I also attended a few on top of that.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;April&lt;/strong&gt;, I spoke about personal websites at the incredible &lt;a href=&quot;https://beyondtellerand.com/&quot;&gt;Beyond Tellerand&lt;/a&gt; in Düsseldorf. I also wrote about the experience. In fact, I loved it so much, I&#39;m speaking at BTConf Berlin in 2024!&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;May&lt;/strong&gt;, I went back to &lt;a href=&quot;https://heypresents.com/&quot;&gt;All Day Hey&lt;/a&gt; in Leeds. I particularly enjoyed Jack Franklin&#39;s talk on &lt;a href=&quot;https://heypresents.com/talks/abstractions-complexities-and-off-ramps&quot;&gt;Abstractions, complexities and off-ramps&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;June&lt;/strong&gt; I spent my birthday at &lt;a href=&quot;https://www.youtube.com/watch?v=H2Ux0hGQcs4&quot;&gt;CSS Day&lt;/a&gt; in Amsterdam where I gave my personal website talk and got completely blown away by all the new features in CSS. Later in the month I attended &lt;a href=&quot;https://leaddev.com/staffplus-london&quot;&gt;StaffPlus&lt;/a&gt; at the Barbican, a very different experience from the usual kinds of conferences I tend to find myself at, but super useful for things I actually do day-to-day.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;July&lt;/strong&gt; I found myself in Middlesbrough, in the North East, for &lt;a href=&quot;https://www.middlesbroughfe.co.uk/&quot;&gt;Middlesbrough Frontend&lt;/a&gt; – where I gave the final form of my Virtual Piano talk (refined and perfected!) and witnessed the absolute joy of a local, supportive community. I also gained a pair of &lt;a href=&quot;https://en.wikipedia.org/wiki/Parmo&quot;&gt;parmo socks&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;At the end of &lt;strong&gt;August&lt;/strong&gt; I travelled to Zurich for the first time for &lt;a href=&quot;https://frontconference.com/&quot;&gt;Front Conference&lt;/a&gt;, which was an absolute joy; onstage I built a website like it&#39;s 1999 wearing some very bright orange trousers, and experimented with sort-of live coding using &lt;a href=&quot;https://inspirejs.org/&quot;&gt;inspire.js&lt;/a&gt;. Also a joy: travelling around the lake on a boat in the sunshine.&lt;/li&gt;
&lt;li&gt;Back at the Barbican in &lt;strong&gt;October&lt;/strong&gt;, for the glorious &lt;a href=&quot;https://2023.stateofthebrowser.com/&quot;&gt;State of the Browser 2023&lt;/a&gt;. This year, Dave made everyone&#39;s conference badges out of floppy disks (you&#39;re a legend Dave), and I got a special red one as the backup MC in case he was ill.&lt;/li&gt;
&lt;li&gt;It&#39;s not a conference season without going down to Brighton for &lt;a href=&quot;https://ffconf.org/&quot;&gt;FFConf&lt;/a&gt; in &lt;em&gt;November&lt;/em&gt;, and it was excellent as always &amp;lt;3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Attending lots of front-of-the-frontend conferences always makes me a little sad that I don&#39;t do much of that for a living – my job is a lot more around back-of-the-frontend, leading teams and building at scale. There&#39;s been a lot of talk this year about how React and other frameworks have ruined frontend development, and I don&#39;t disagree; but it&#39;s also something I use daily at work. The best thing I can do is make sure we&#39;re using it in a sensible way and defaulting to native browser capabilities where we can. (I&#39;ve got a post in me somewhere about all of this.)&lt;/p&gt;
&lt;h3 id=&quot;travel&quot; tabindex=&quot;-1&quot;&gt;...travel&lt;/h3&gt;
&lt;p&gt;My holidays this year took me to Copenhagen with my mum, then later to Norway to visit family. We flew up to Ålesund and drove to Geiranger (our suitcase only made it as far as Oslo, but thankfully joined us the next day) and then flew back down to Oslo for a few days on my cousin&#39;s farm. It was wonderful to meet some of my family for the first time!&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://localghost.dev/img/blog/2023-recap/geiranger.JPG&quot;&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/2023-recap/geiranger.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/2023-recap/geiranger.JPG&quot; alt=&quot;A view from the harbour of Geiranger in Norway, looking out over shimmering waters into the fjord.&quot; /&gt;
&lt;/picture&gt;
&lt;/a&gt;
&lt;figcaption&gt;Geiranger
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;My annual lads&#39; trip with some of my best friends was in the Cotswolds this year, where we stayed in a gorgeous farmhouse and went to WWT Slimbridge to gawp at some excellent waders. We saw a rare goose that had got blown off course and across the Atlantic during its seasonal migration across North America, but it seemed to be happy enough amongst the local geese.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://localghost.dev/img/blog/2023-recap/slimbridge.JPG&quot;&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/2023-recap/slimbridge.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/2023-recap/slimbridge.JPG&quot; alt=&quot;A landscape photo of WWT Slimbridge wetlands. It&#39;s bright and sunny with a lovely view over fields and the River Severn estuary, with Wales visible on the other side.&quot; /&gt;
&lt;/picture&gt;
&lt;/a&gt;
&lt;figcaption&gt;WWT Slimbridge, looking out over the Severn estuary towards Wales
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;a href=&quot;https://localghost.dev/img/blog/2023-recap/slimbridge2.JPG&quot;&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/2023-recap/slimbridge2.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/2023-recap/slimbridge2.JPG&quot; alt=&quot;A variety of ducks sit on sunny, green wetlands&quot; /&gt;
&lt;/picture&gt;
&lt;/a&gt;
&lt;figcaption&gt;Excellent duck action at Slimbridge
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id=&quot;books&quot; tabindex=&quot;-1&quot;&gt;...books&lt;/h3&gt;
&lt;p&gt;I continued to make extensive use of my library&#39;s ebook service. I tend to only read in bed, which means I often manage about 3 pages before conking out.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/tomorrow-and-tomorrow-and-tomorrow-discover-the-moving-powerful-sunday-times-bestseller-that-everyone-is-talking-about-gabrielle-zevin/7312831?ean=9781529115543&quot;&gt;Tomorrow, and Tomorrow, and Tomorrow&lt;/a&gt;: there probably isn&#39;t anyone left who hasn&#39;t read this book, but it&#39;s really excellent. A story of two children who grow up to build video games together.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/feet-of-clay-discworld-novel-19-terry-pratchett/455317?ean=9780552167574&quot;&gt;Feet of Clay - Terry Pratchett&lt;/a&gt;: continuing my Pratchett education with the City Watch series, this one about murders and golems.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/there-there-tommy-orange/482024?ean=9781784707972&quot;&gt;There There - Tommy Orange&lt;/a&gt;: a stark tale about a community of Native Americans in California, the legacy of oppression and colonialism.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/raven-black-ann-cleeves/849472?ean=9781529050189&quot;&gt;Raven Black - Anne Cleeves&lt;/a&gt;: lighter reading perhaps, but not too light, there&#39;s plenty of murder; the first of the &lt;em&gt;Shetland&lt;/em&gt; series that inspired one of my favourite BBC dramas.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/my-year-of-rest-and-relaxation-ottessa-moshfegh/978286?ean=9781784877477&quot;&gt;My Year of Rest and Relaxation - Ottessa Moshfegh&lt;/a&gt;: a very depressed woman tries to hide from the world and sleep for an entire year.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/mr-penumbra-s-24-hour-bookstore-robin-sloan/2649325?ean=9781782391210&quot;&gt;Mr Penumbra&#39;s 24-hour Bookstore - Robin Sloan&lt;/a&gt;: An unemployed software developer gets a job at a strange bookshop and discovers a secret society hidden within.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/a-woman-sibilla-aleramo/2981507?ean=9780241345726&quot;&gt;A Woman - Sibilla Aleramo&lt;/a&gt;: from 1906, an autobiographical novel of the author&#39;s troubled childhood growing up in Italy, her unhappy marriage to her abuser, and her decision to leave.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/soul-music-discworld-the-death-collection-terry-pratchett/4603633?ean=9781473200128&quot;&gt;Soul Music - Terry Pratchett&lt;/a&gt;: more Pratchett! It&#39;s like a hug for your brain! In this one, Ankh-Morpork discovers rock music.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/the-four-winds-the-number-one-bestselling-richard-judy-book-club-pick-kristin-hannah/6430754?ean=9781529054583&quot;&gt;The Four Winds - Kristin Hannah&lt;/a&gt;: poverty and struggles during the Great Depression and Dust Bowl in the Great Plains.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/the-anomaly-the-mind-bending-thriller-that-has-sold-1-million-copies-herve-le-tellier/6588773?ean=9781405950800&quot;&gt;The Anomaly - Hervé le Tellier&lt;/a&gt;: I love a good sci-fi mystery. A plane inexplicably duplicates itself during a storm, and every passenger now has an exact double.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/making-money-terry-pratchett/6928436?ean=9781804990476&quot;&gt;Making Money - Terry Pratchett&lt;/a&gt;: the second Moist von Lipwig/Ankh-Morpork industrialisation book. Sadly I didn&#39;t make it all the way through the third one.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/sea-of-tranquility-the-instant-sunday-times-bestseller-from-the-author-of-station-eleven-emily-st-john-mandel/6247165?ean=9781529083514&quot;&gt;Sea of Tranquility - Emily St.John Mandel&lt;/a&gt;: from the author of Station Eleven, a sci-fi story about a rift in time with an unravelling mystery. The ending was rather abrupt, but the story itself was great.&lt;/li&gt;
&lt;li&gt;I also read another book of hers called &lt;a href=&quot;https://uk.bookshop.org/p/books/the-glass-hotel-emily-st-john-mandel/4863171?ean=9781509882830&quot;&gt;The Glass Hotel&lt;/a&gt;, about the aftermath of a Ponzi scheme.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/p/books/the-bandit-queens-parini-shroff/7351504?ean=9781838957148&quot;&gt;The Bandit Queens - Parini Shroff&lt;/a&gt;: a widow in an Indian village helps another woman kill her abusive husband and Events ensue. It&#39;s dark but also really charming and quite funny.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;podcasts&quot; tabindex=&quot;-1&quot;&gt;...podcasts&lt;/h3&gt;
&lt;p&gt;New to me in 2023:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pod.link/1651876897&quot;&gt;If Books Could Kill&lt;/a&gt;: Michael Hobbes and Peter Shamshiri take us through some of the biggest &amp;quot;self-help&amp;quot;/pseudoscience books from the last few decades and deservedly tear them apart. I can&#39;t recommend this enough.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.dungeonsanddaddies.com/&quot;&gt;Dungeons and Daddies&lt;/a&gt;: a Dungeons and Dragons-but-also-not podcast. I&#39;m working my way through season one, which is about &amp;quot;four dads from our world flung into a land of high fantasy and magic in a quest to rescue their lost sons&amp;quot;. It&#39;s one of the funniest things I&#39;ve ever listened to and I&#39;m completely hooked.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;music&quot; tabindex=&quot;-1&quot;&gt;...music&lt;/h3&gt;
&lt;p&gt;According to my Apple Music Replay (aka Spotify Wrapped ripoff) I actually didn&#39;t listen to a lot of music this year. It checks out, as I&#39;ve been mainlining podcasts on my commute, and I don&#39;t listen to much while I work apart from the odd bit of lo-fi. Some good things came out this year, though.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://music.apple.com/us/album/the-loveliest-time/1697646383&quot;&gt;Carly Rae Jepsen - The Loveliest Time&lt;/a&gt;: CRJ has a tradition of releasing a companion album for every album she releases, and this is a great one. Standout tracks include the Daft Punk-esque &lt;em&gt;Psychedelic Switch&lt;/em&gt;, and &lt;em&gt;Kamikaze&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.apple.com/us/album/speak-now-taylors-version/1690839749&quot;&gt;Speak Now - Taylor&#39;s Version&lt;/a&gt;: I was honestly surprised how much I liked this album, given that Speak Now was always my second least favourite TS album (least favourite is the self-titled one). The maturity of her voice suits the songs so well, and I love the one she did with Hayley Williams (&lt;em&gt;Castles Crumbling&lt;/em&gt;). &lt;em&gt;Haunted&lt;/em&gt; is one of my favourite Taylor Swift songs, as well, so it was great to hear the re-record. I had high hopes for the 1989 re-record because that was always my favourite, but I think I actually liked this one more.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.apple.com/us/album/guts/1694386825&quot;&gt;GUTS - Olivia Rodrigo&lt;/a&gt;: This would have completely passed me by had I not listened to an episode of Switched On Pop where they talked about two tracks from this album, &lt;em&gt;vampire&lt;/em&gt; and &lt;em&gt;bad idea right?&lt;/em&gt;. Boy, are these fucking great pop songs, and I can&#39;t resist a great pop song.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://evanescence.craftrecordings.co.uk/&quot;&gt;Evanescence - Fallen 20th Anniversary Edition&lt;/a&gt; – after spending years embarrassed that I was really into Evanescence as a teenager, I&#39;ve come out the other side and you know what, this album slaps. It&#39;s just excellent. Now I&#39;m at the age where 20th anniversary editions start coming out for some of the albums that shaped my teenage years, which is utterly ridiculous, but the good news is that they sound great because a lot of the mastering was a bit trash in the early 00s.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.apple.com/us/album/the-record/1666138312&quot;&gt;the record - boygenius&lt;/a&gt;: Julien Baker, Lucy Dacus and Phoebe Bridgers come up with some really glorious harmonies which is a sure way to get me on board. I&#39;m not so much of a fan of sad girl music so I don&#39;t tend to listen to their solo stuff or anything but I really loved some of the tracks off this album. &lt;em&gt;Not Strong Enough&lt;/em&gt; is a standout.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Regretfully (especially after last year&#39;s glowing recommendation) I cleared any trace of Rammstein and associated acts out of my music library following some really dreadful accusations. I really enjoyed their music, but I couldn&#39;t really justify listening to it any more given how fucking creepy and problematic the singer is. The investigation was dropped due to lack of evidence, but there have been enough stories that came out to make me feel uncomfortable about the whole thing regardless.&lt;/p&gt;
&lt;h3 id=&quot;video-games&quot; tabindex=&quot;-1&quot;&gt;...video games&lt;/h3&gt;
&lt;p&gt;Another great year for games! I also wrote up a list of my &lt;a href=&quot;https://localghost.dev/games&quot;&gt;favourite games&lt;/a&gt;, some of which were new to me this year.&lt;/p&gt;
&lt;p&gt;The standout for me and basically everyone else was &lt;a href=&quot;https://baldursgate3.game/&quot;&gt;Baldur&#39;s Gate 3&lt;/a&gt;. I&#39;m 2/3 through my second playthrough; first run I played a Druid, second time is a Dark Urge playthrough. I thought I&#39;d be evil but it turns out I&#39;m incapable of making questionably moral decisions even in video games. I still got to romance Astarion, though – and it&#39;s extra fun as the Dark Urge. I&#39;ve sunk so many hours into this game and yet I always discover something I missed.&lt;/p&gt;
&lt;p&gt;Other highlights from 2023:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.focus-entmt.com/en/games/chants-of-sennaar&quot;&gt;Chants of Sennaar&lt;/a&gt;: It&#39;s a linguist&#39;s dream, a puzzle game where you have to decipher the languages of the civilisations around you through context and environmental clues. It&#39;s also really beautiful. One of the most unique puzzle games I&#39;ve played - I actually played this with my husband, solving puzzles together, and it was very wholesome.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.albawildlife.com/&quot;&gt;Alba: A Wildlife Adventure&lt;/a&gt;: you play a young girl visiting her grandparents on a Spanish island, who gets together with her friend to start a campaign to save the island&#39;s wildlife. It&#39;s so wholesome and the fauna is very accurate — I spent far too long running around the island looking for teals and kestrels. It&#39;s a great gateway to real life birdwatching!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://disneydreamlightvalley.com/&quot;&gt;Disney Dreamlight Valley&lt;/a&gt;. Yes, really. I don&#39;t even like Disney that much, it wasn&#39;t a huge part of my life growing up and I haven&#39;t seen half of the films (not even the Lion King, which always shocks people when I say that) but the game is basically Animal Crossing with more expensive intellectual property, and it&#39;s been a lovely way to unwind. I&#39;m currently in bed with sinusitis as I write this and I&#39;ve absolutely been rinsing the game. Though it&#39;s firmly in the category of &amp;quot;games that make you wonder why the residents of this town don&#39;t ever fucking contribute&amp;quot;. If you have Apple Arcade, it&#39;s just become available on there (though no cross save); I have it on Switch and the framerate is not great, so other platforms are probably better.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cloudheadgames.com/pistol-whip&quot;&gt;Pistol Whip&lt;/a&gt;. I got a PS VR 2 for my birthday and it&#39;s a super fun rhythm game but with shooting.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.playstation.com/en-us/games/horizon-call-of-the-mountain/&quot;&gt;Horizon: Call Of The Mountain&lt;/a&gt; (PS VR 2). I nearly didn&#39;t include this because it is bascially just a climbing simulator with a bit of combat, but it&#39;s a beautiful game, VR archery is so much fun and the environments you go through are so cool. I struggled a lot with this game at first because I&#39;m actually terrified of heights, so I got quite anxious climbing some of the rusty metal structures hanging over massive drops!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nomanssky.com/&quot;&gt;No Man&#39;s Sky&lt;/a&gt;. Very much new to me, not new game – I got it on offer originally to try out with PS VR, but found I actually preferred playing it normally. I wasn&#39;t expecting to enjoy this game as much as I did, but it&#39;s a great combination of exploration, crafting and weird little space animals.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cyberpunk.net/gb/en/&quot;&gt;Cyberpunk 2077&lt;/a&gt; gets an honourable mention because the DLC was excellent and the new patch balanced a lot of the annoying things about the game. If you&#39;ve been holding off, give it a go – this game rules.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;learning-things&quot; tabindex=&quot;-1&quot;&gt;...learning things&lt;/h3&gt;
&lt;p&gt;Some of the things I learned this year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I did some really complex backend work, complained the whole way through, and begrudgingly learned a lot from it.&lt;/li&gt;
&lt;li&gt;I was thrown into a new situation at work which required a lot of disambiguation and interviewing lots of people to get enough information to solve a problem and make a decision on how to proceed. It was exhausting, but interesting, and great experience.&lt;/li&gt;
&lt;li&gt;I did a couple of painting and drawing courses and discovered that I really liked painting. Unfortunately I don&#39;t have a lot of space for the hobby right now, but you can be sure I bought all the things for it and they&#39;re in a drawer now.&lt;/li&gt;
&lt;li&gt;My work held a Security Awareness Week and ran a Capture the Flag competition which I got completely hooked on (and won!). As a fiend for puzzles, it ticked every box for me. I absolutely loved diving through the problems and trying to reverse engineer things. I&#39;d really like to learn more about that stuff.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I came away from the various frontend conferences this year intending to learn some cool new CSS stuff, but honestly my appetite for building things outside of work was so minimal that I just didn&#39;t get around to it. I did play with &lt;code&gt;:has&lt;/code&gt; on the way home from CSS Day, though.&lt;/p&gt;
&lt;h3 id=&quot;purchases&quot; tabindex=&quot;-1&quot;&gt;...purchases&lt;/h3&gt;
&lt;p&gt;The best things I bought this year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a second-hand soda stream. £40 off ebay, and I get sparkling water whenever I want.&lt;/li&gt;
&lt;li&gt;a moth-patterned dress from &lt;a href=&quot;https://www.disturbia.co.uk/products/mortmoth-short-sleeve-midi-dress&quot;&gt;Disturbia&lt;/a&gt;. I&#39;ve enjoyed indulging my ~inner~ outer goth this year.&lt;/li&gt;
&lt;li&gt;a &lt;a href=&quot;https://www.meaco.com/collections/fans&quot;&gt;Meaco&lt;/a&gt; fan, which is wonderfully quiet and has about 954 different speeds, all of which feel like some kind of deity is blowing on your face.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shop.barbican.org.uk/products/cat-sims-conservatory-a5-risograph?_pos=1&amp;amp;_sid=cacfa0abf&amp;amp;_ss=r&amp;amp;variant=40837127045168&quot;&gt;two gorgeous prints&lt;/a&gt; of the Barbican Conservatory by Cat Sims&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.dogecore.com/collections/unisex-t-shirts/products/haha-business-1&quot;&gt;&amp;quot;haha business&amp;quot; t-shirt&lt;/a&gt; from dogecore&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://media.social.lol/media_attachments/files/111/577/710/363/832/647/original/b840d6fce0948625.jpeg&quot;&gt;Beautiful moon earrings&lt;/a&gt; from my favourite jewellery designer &lt;a href=&quot;https://etsy.com/shop/rosapietsch&quot;&gt;Rosa Pietsch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;apps-and-programs&quot; tabindex=&quot;-1&quot;&gt;...apps and programs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://apps.apple.com/gb/app/in-your-face-meeting-reminder/id1476964367?mt=12&quot;&gt;In Your Face&lt;/a&gt; means I&#39;ll never be late to a meeting again&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apps.apple.com/gb/app/bettersnaptool/id417375580?mt=12&quot;&gt;BetterSnapTool&lt;/a&gt; lets you drag windows to the side of the screen and instantly snap them to half or a quarter of the desktop. For work that requires a lot of side-by-side comparison it&#39;s a godsend.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apps.apple.com/us/app/boop/id1518425043?mt=12&quot;&gt;Boop&lt;/a&gt; is a great little tool for doing a lot of common dev tasks like decoding URI-encoded text, converting CSV to JSON, and so on.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt;: I decided it was time to get organised and put all my notes and docs in one place so I&#39;d actually know where they were. I&#39;m hoping that over time it&#39;ll become a really useful reference for my own brain, which is a swirling vortex of smaller, swirlier vortexes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;blog-posts&quot; tabindex=&quot;-1&quot;&gt;...blog posts&lt;/h3&gt;
&lt;p&gt;I did even better than last year and wrote 10 posts, shared 2 recipes and talked about one podcast. Some of my favourite posts from this year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/engineering-progression-for-humans/&quot;&gt;Engineering progression for humans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/beautiful-musical-chaos/&quot;&gt;Beautiful musical chaos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/ai-and-the-trouble-with-inaccessible-saas/&quot;&gt;AI and the trouble with inaccessible SaaS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-web&quot; tabindex=&quot;-1&quot;&gt;...the web&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Mastodon continued to grow on me. I still miss the old Twitter, and the reach I used to get on there, but I&#39;ve kind of embraced the slowness of Mastodon now. I don&#39;t even check it every day, which seems like a depressing thing to be surprised about, but I was so hooked on Twitter.&lt;/li&gt;
&lt;li&gt;I subscribed to the &lt;a href=&quot;https://www.garbageday.email/&quot;&gt;Garbage Day&lt;/a&gt; newsletter, and it&#39;s very good if you&#39;re an Internet Person.&lt;/li&gt;
&lt;li&gt;Robin Rendle and Matthias Ott started newsletters, and you should absolutely subscribe (either via RSS or email). Robin&#39;s is called &lt;a href=&quot;https://robinrendle.com/the-cascade/&quot;&gt;The Cascade&lt;/a&gt; and it&#39;s about CSS; Matthias&#39;s is &lt;a href=&quot;https://buttondown.email/ownyourweb&quot;&gt;Own Your Web&lt;/a&gt;, about designing, building and publishing on the web. They are both Very Good.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;and-so-we-re-at-the-end&quot; tabindex=&quot;-1&quot;&gt;And so we&#39;re at the end &lt;!-- omit in toc --&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s been a whirlwind year and I&#39;ve ended it pretty shattered, but I&#39;m pleased with what I achieved (and by that I mean saving the world in Baldur&#39;s Gate 3, obviously).&lt;/p&gt;
&lt;p&gt;I used a fair bit of my time off this year on conferences which, although fun, are not restful in the slightest. That on top of a stressful few months towards the end of the year mean I&#39;ve ended it a bit burnt out again. Not as bad as last year, but still not great. The plus side: I get a 3-month paid sabbatical next year (!) so I&#39;m going to make sure I get more of a rest. I&#39;m thinking of doing some volunteering/conservation work with a wildlife trust or something similar, and we&#39;re hoping to finally go to Japan later in 2024.&lt;/p&gt;
&lt;p&gt;Wishing you all a lovely Christmas, a happy new year, and a 2024 that&#39;s exactly what you are hoping for!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Engineering progression for humans</title>
      <link href="https://localghost.dev/blog/engineering-progression-for-humans/"/>
      <updated>2023-12-12T00:00:00Z</updated>
      <id>https://localghost.dev/blog/engineering-progression-for-humans/</id>
      <content type="html">&lt;p&gt;As engineers it can be unclear where we’re heading, especially when we’re heads down writing code all the time. It can be hard to think even a few months ahead. What does an engineering &lt;em&gt;career&lt;/em&gt; in a larger organisation look like? What even is a staff engineer? How long should you stay at the same company?&lt;/p&gt;
&lt;p&gt;Whether you&#39;re new to the industry or several years in, there&#39;s a good chance you&#39;ve identified with at least one of these statements at some point:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It seems like there’s a dozen different ways you could progress and you have no idea what’s right for you&lt;/li&gt;
&lt;li&gt;There isn’t anyone in your immediate organisation who has a role you see yourself going into&lt;/li&gt;
&lt;li&gt;You don’t really know what you want from your career&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is everything I&#39;ve learned about progression in larger organisations, with some thoughts and opinions on the way. If you&#39;re not working in a capital-T capital-C Tech Company, this might not be so applicable to your situation, but never say never!&lt;/p&gt;
&lt;p&gt;This &lt;em&gt;isn&#39;t&lt;/em&gt; going to be a step-by-step guide to how to reach each level of the engineering career path. For one thing, it varies wildly from company to company, so my advice might not be applicable where you work. Instead, by looking at what these levels involve, you might be able to identify where you need more experience or where you&#39;re already well on your way.&lt;/p&gt;
&lt;h3 id=&quot;know-what-motivates-you&quot; tabindex=&quot;-1&quot;&gt;Know what motivates you&lt;/h3&gt;
&lt;p&gt;Understanding how you want to progress in your engineering career depends on what motivates you.&lt;/p&gt;
&lt;p&gt;It’s completely okay to be motivated by money, for example. Money can be exchanged for goods and services. Houses are expensive. Families are expensive. Dogs are expensive. Maybe you’re saving for your future, or supporting someone back home, or maybe you like having nice things.&lt;/p&gt;
&lt;p&gt;Or maybe you want to be in a position where you can influence teams at a higher level. You have thoughts and opinions, and you like being able to help architect systems or decide on the best way forward.&lt;/p&gt;
&lt;p&gt;Perhaps you want a role that lets you help others, and grow people. Watching them develop in their own careers thanks to your support. Helping teams achieve their goals and be productive.&lt;/p&gt;
&lt;p&gt;You might need a change because your current role is negatively affecting your mental health. For me, mental health trumps everything, and there is no job that is worth doing at the expense of mental health. &lt;a href=&quot;https://x.com/dasharez0ne/status/1125839557352742913&quot;&gt;If it sucks, hit the bricks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Maybe you want to do work, clock out, and focus on the other things in life. Like extreme ironing, or beekeeping.&lt;/p&gt;
&lt;p&gt;You’ll probably fall into a few of these categories. I know I’m definitely motivated by helping people but also taking a high-level view and influencing things. And I have to say, I also like having money. But I&#39;ve also been careful to choose jobs that allow me to protect my work-life balance.&lt;/p&gt;
&lt;p&gt;Your career decisions should reflect what’s best for you. Not your company. Not anyone else. We spend 40 hours a week in our jobs, so it needs to be worth it.&lt;/p&gt;
&lt;p&gt;Speaking of which:&lt;/p&gt;
&lt;h3 id=&quot;not-everybody-needs-to-or-wants-to-progress&quot; tabindex=&quot;-1&quot;&gt;Not everybody needs to (or wants to) progress&lt;/h3&gt;
&lt;p&gt;It&#39;s so easy to fall into the trap of thinking you have to follow the same path as everyone else. You could be forgiven for thinking careers are all about progression. But ultimately it&#39;s whatever you want it to be, and if you&#39;re happy where you are, that&#39;s the most important thing. Many folks out there will tell you that you don&#39;t have to have a Big Tech job to be a successful engineer, and you should listen to them. Engineering, software development, web design, whatever you want to call it: there are so many versions of this job and only you know which one suits you best.&lt;/p&gt;
&lt;h3 id=&quot;the-way-up-or-sideways-or-backwards&quot; tabindex=&quot;-1&quot;&gt;The way up (or sideways. Or backwards.)&lt;/h3&gt;
&lt;p&gt;The first few stages of the engineering career path are relatively well-trodden. You come in at an entry-level or so-called “junior” position, you work your way up to a mid-level engineer – often just called “engineer” – and then you might go up to Senior. Here&#39;s one such example:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/blog/eng-progression/eng-path.png&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/eng-path.png&quot; alt=&quot;A graph of nodes demonstrating the first few stages of engineering progression. The first node is Engineer I, which has an arrow to Engineer II, then Engineer III, then Senior Engineer I, then Senior Engineer II.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;What each of these steps looks like depends on your organisation, but commonly Engineer I is like an intern or apprentice (what you might expect for someone with little to no background in engineering at all), Engineer II is what is often called &amp;quot;junior&amp;quot; (entry-level for someone with a qualification or a bit of training) and Engineer III is a mid-level position. Senior might just be one role, or split out into two levels.&lt;/p&gt;
&lt;p&gt;It depends on where you work, but generally speaking there are no set timelines for this progression. Some folks progress faster than others, or have their start at different times in their life: sometimes you’ll work with a 22-year-old senior engineer and feel like you are turning into dust, sometimes you’ll meet folks in their 40s or 50s who are starting out as developers. It’s all good.&lt;/p&gt;
&lt;p&gt;A note on promotions: many tech companies these days will only consider a promotion once you&#39;ve shown that you&#39;re already working at the next level up. I don&#39;t especially like this, as I think it disadvantages people who aren&#39;t extroverted or highly visible in an organisation, and it kind of means you end up doing the job for a while before you get paid for it. In these kinds of organisations, progression is as much about playing the game as it is about doing a good job and being recognised for it. I&#39;ll write up another post about that soon.&lt;/p&gt;
&lt;p&gt;I also know some bigger orgs have a kind of rule that you have to be in a role for X number of years before you can get promoted, and I particularly don’t agree with that approach because your tenure has no bearing on your ability. I&#39;d agree that it takes time to build up the required experience for higher levels of jobs, but that amount of time differs from person to person and each individual case should be considered.&lt;/p&gt;
&lt;p&gt;The first few stages of an engineer&#39;s career are a bit more well-defined; let&#39;s have a look at them.&lt;/p&gt;
&lt;h4 id=&quot;engineer-i-and-ii&quot; tabindex=&quot;-1&quot;&gt;Engineer I and II&lt;/h4&gt;
&lt;p&gt;As Engineer I, you&#39;re at the start of your career, learning and asking questions. Not that you won&#39;t be learning and asking questions for the rest of your career – you should definitely be doing that regardless of your experience. But you&#39;ll be getting to grips with the tools, with the practices, with how things work.&lt;/p&gt;
&lt;p&gt;The kind of work you do at this stage should be very well-defined tasks that are curated for you by a more senior engineer as good things to get your teeth into and learn from. You might spend a lot of time pairing on tasks.&lt;/p&gt;
&lt;p&gt;At level II, you&#39;ve got the base knowledge down, you&#39;re a solid, trusted contributor to your team and beginning to design solutions on a smaller scale for problems your team is working on. You&#39;re more independent now, though you&#39;re probably still working with senior engineers to break down the problems and scope the work that you&#39;re doing.&lt;/p&gt;
&lt;h4 id=&quot;mid-level-engineer-engineer-iii&quot; tabindex=&quot;-1&quot;&gt;Mid-level engineer / Engineer III&lt;/h4&gt;
&lt;p&gt;Now you&#39;re a lot more independent – a good individual contributor (IC). You&#39;re happy designing and implementing solutions, suggesting improvements to ways of working and engineering patterns, writing code with a good level of proficiency. You may also start to mentor less experienced engineers. You&#39;ll be leading mid-sized projects and are trusted to get the job done. Your PR reviews and pairing sessions help more junior engineers learn the ropes.&lt;/p&gt;
&lt;p&gt;It’s totally legit to just stay here and hang out for however long you want. You don’t need to go for senior.&lt;/p&gt;
&lt;h3 id=&quot;senior-engineer&quot; tabindex=&quot;-1&quot;&gt;Senior engineer&lt;/h3&gt;
&lt;p&gt;Senior engineers are experienced ICs who can lead projects or features that have a higher level of technical complexity. You&#39;ll be mentoring less experienced engineers, either through formal 1:1s or by leading by example through the code you ship and the feedback you leave on PRs. You&#39;ll get good at identifying opportunities for your team and how they relate to the wider goals of your organisation.&lt;/p&gt;
&lt;p&gt;The more senior you get, the more ambiguity there is in what you’re working on, and the more high-level you start to operate. My work often finds me looking across teams and even across the organisation.&lt;/p&gt;
&lt;h4 id=&quot;senior-engineers-come-in-all-shapes-and-sizes&quot; tabindex=&quot;-1&quot;&gt;Senior engineers come in all shapes and sizes!&lt;/h4&gt;
&lt;p&gt;The senior engineer role isn&#39;t the same for everyone. I like how &lt;a href=&quot;https://monzo.com/documents/engineering-progression-framework-v3-0.pdf&quot;&gt;Monzo&#39;s engineering progression framework&lt;/a&gt;, er, frames it, and I&#39;ve very much paraphrased here:&lt;/p&gt;
&lt;p&gt;You might be a galaxy-brained domain expert, who knows the ins and outs of a particular area or system like the back of your hand. If there&#39;s a project involving this system, you&#39;re possibly leading it, but you&#39;re &lt;em&gt;definitely&lt;/em&gt; advising on it.&lt;/p&gt;
&lt;p&gt;Or you might be more of a &amp;quot;tech lead&amp;quot;-shaped senior engineer, focussing on enabling your team to build great quality software and fostering a great engineering culture. At Monzo this doesn’t translate to literally being tech lead of a team – it’s a hat you put on, and you might not be senior yet when you&#39;re tech-leading.&lt;/p&gt;
&lt;p&gt;You can also sit somewhere in the middle, being a great individual contributor and leading projects, helping your team to do great things.&lt;/p&gt;
&lt;h4 id=&quot;senior-to&quot; tabindex=&quot;-1&quot;&gt;Senior to ???&lt;/h4&gt;
&lt;p&gt;What comes next?&lt;/p&gt;
&lt;p&gt;So, for one thing, maybe you don’t go anywhere.&lt;/p&gt;
&lt;p&gt;The long-tenured senior engineers on our teams are indispensable. They’re fountains of knowledge, they’ve seen it all, they are wise beyond their years. Every company needs some great senior engineers who are happy continuing to be great senior engineers. You may choose to reach senior, and stay there.&lt;/p&gt;
&lt;h4 id=&quot;senior-to-staff&quot; tabindex=&quot;-1&quot;&gt;Senior to staff&lt;/h4&gt;
&lt;p&gt;If you want to continue down the IC path, generally it&#39;ll be to something like Staff Engineer. This is technically a leadership position, but it shouldn’t involve managing people.&lt;/p&gt;
&lt;p&gt;What this role looks like varies massively across different organisations and even across different teams in the same organisation.&lt;/p&gt;
&lt;p&gt;If you do decide to pursue further progression to Staff and above, this is where things start to get murky. It’s surprisingly not a very well-trodden path.&lt;/p&gt;
&lt;p&gt;Thankfully, both Tanya Reilly and Will Larson have done lots of path-treading and word-writing about this very topic: Tanya’s book, &lt;a href=&quot;https://www.oreilly.com/library/view/the-staff-engineers/9781098118723/&quot;&gt;The Staff Engineer’s Path&lt;/a&gt;, provides a great overview of what you might expect to be doing and what you need to excel in this role. Will Larson is the author of &lt;a href=&quot;https://staffeng.com/book/&quot;&gt;Staff Engineer&lt;/a&gt; and also runs the &lt;a href=&quot;https://staffeng.com/&quot;&gt;StaffEng&lt;/a&gt; website with a lot of helpful stories and guides to staff engineering across organisations.&lt;/p&gt;
&lt;p&gt;Notably, he came up with &lt;a href=&quot;https://staffeng.com/guides/staff-archetypes/&quot;&gt;Staff Engineer archetypes&lt;/a&gt; that nicely outline what this role might look like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Tech Lead guides the approach and execution of a particular team. They partner closely with a single manager, but sometimes they partner with two or three managers within a focused area. Some companies also have a Tech Lead Manager role, which is similar to the Tech Lead archetype but exists on the engineering manager ladder and includes people management responsibilities.&lt;/p&gt;
&lt;p&gt;The Architect is responsible for the direction, quality, and approach within a critical area. They combine in-depth knowledge of technical constraints, user needs, and organization level leadership.&lt;/p&gt;
&lt;p&gt;The Solver digs deep into arbitrarily complex problems and finds an appropriate path forward. Some focus on a given area for long periods. Others bounce from hotspot to hotspot as guided by organizational leadership.&lt;/p&gt;
&lt;p&gt;The Right Hand extends an executive&#39;s attention, borrowing their scope and authority to operate particularly complex organizations. They provide additional leadership bandwidth to leaders of large-scale organizations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’m working on this path myself, recognising the areas I need to develop. My engineering director framed it as having muscles I need to develop and train to get to staff; for example, I know I’m very technically competent and have formed good relationships across the company, but I need to get better at managing my time and focus, and I&#39;m gaining experience of tackling highly ambiguous problems.&lt;/p&gt;
&lt;p&gt;This year we also had the inaugural &lt;a href=&quot;https://leaddev.com/staffplus-london&quot;&gt;StaffPlus&lt;/a&gt; conference from the same folks that run LeadDev. I found this a really great opportunity to absorb some really useful information for my own progression in a bigger organisation. (Shame about the eye-watering ticket price, though.)&lt;/p&gt;
&lt;h4 id=&quot;senior-to-engineering-management&quot; tabindex=&quot;-1&quot;&gt;Senior to engineering management&lt;/h4&gt;
&lt;p&gt;In tech companies these days it’s very common to have dedicated engineering managers (EMs) who look after a group or team of engineers, along with some other responsibilities. At Monzo our EMs also function as delivery leads, making sure the teams are unblocked and productive, and acting as envoys between product teams and leadership.&lt;/p&gt;
&lt;p&gt;You might take a step sideways away from the hands-on engineering role, and become an engineering manager. It’s not a promotion as such, more of a role change; generally speaking engineers wanting to make the transition into engineering management will need to get to senior first, before moving laterally.&lt;/p&gt;
&lt;p&gt;The most well-known guide in this area is &lt;a href=&quot;https://www.oreilly.com/library/view/the-managers-path/9781491973882/&quot;&gt;The Manager’s Path&lt;/a&gt; by Camille Fournier. I read a good part of it myself some years ago when I was looking into becoming a tech lead, and it has some great advice on what makes an effective technical manager (and of course, how to get there).&lt;/p&gt;
&lt;p&gt;I know plenty of engineers who have gone into engineering management and loved it; I also know a handful who have done it for a while and realised it wasn&#39;t for them and gone back to being an IC. It doesn&#39;t have to be a permanent choice.&lt;/p&gt;
&lt;h4 id=&quot;other-potential-sideways-steps&quot; tabindex=&quot;-1&quot;&gt;Other potential sideways steps&lt;/h4&gt;
&lt;p&gt;I&#39;ve outlined some very common paths for engineers, but there are other roles you might choose to explore in the world of engineering that are more of a sideways step, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@dev.n/understanding-devrel-a-beginners-guide-a71063e4249e&quot;&gt;Developer relations&lt;/a&gt; (devrel) – fostering relationships with the developers who use products, showing them the art of the possible, and building a community around that&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.indeed.com/career-advice/finding-a-job/what-does-sales-engineer-do&quot;&gt;Sales engineering&lt;/a&gt; – combining technical expertise and people skills in a role that could see you travelling all over!&lt;/li&gt;
&lt;li&gt;Specialist consultancy roles e.g. accessibility consultancy – laser-focussing your skills in a certain area and helping companies to improve, or even taking on a full-time leadership position as head of your specialism.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(If you know of any others I&#39;ve missed, do let me know, and I&#39;ll add them in.)&lt;/p&gt;
&lt;p&gt;So, that&#39;s the engineer&#39;s path. But it&#39;s rare that people spend their entire careers in one company these days. Where might your career take you?&lt;/p&gt;
&lt;h3 id=&quot;cross-company-career-paths-illustrated-using-tenuous-playground-metaphors&quot; tabindex=&quot;-1&quot;&gt;Cross-company career paths illustrated using tenuous playground metaphors&lt;/h3&gt;
&lt;p&gt;You might recognise these paths from your own career or those of the people around you. They&#39;re somewhat simplified for brevity.&lt;/p&gt;
&lt;h4 id=&quot;the-ladder&quot; tabindex=&quot;-1&quot;&gt;The ladder&lt;/h4&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/ladder-photo.jpg&quot; alt=&quot;A yellow-painted metal playground ladder, viewed from the top. The steps say &#39;MEXICO FORGE&#39;.&quot; /&gt;
&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@mglazier98&quot;&gt;Michael Glazier&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/a-yellow-sign-with-black-text-LZwp8gGP74s&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The “traditional” career ladder model tended to be applied to people within one organisation. At a company I used to work at, it looked a bit like this:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/blog/eng-progression/ladder-2.png&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/ladder-2.png&quot; alt=&quot;A diagram of several nodes, each pointing to the next from left to right. The first node is labelled Company 1 Engineer, which points to Company 1 Engineer again, then Company 1 Senior Engineer, then Company 1 Senior Engineer &amp;amp; Manager, then Company 1 Senior Manager.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;It was a very old company, and people stayed for their whole careers. The more senior you got, the bigger the likelihood was you’d end up managing people. There wasn’t really an IC track in the way there is elsewhere.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/blog/eng-progression/ladder-1.png&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/ladder-1.png&quot; alt=&quot;A diagram of several nodes, each pointing to the next from left to right. The first node is labelled Company 1 Engineer, which points to Company 1 Engineer again, then Company 1 Senior Engineer, then Company 1 Staff Engineer, then Company 1 Senior Staff Engineer.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;With a long career in one place, you get great in-depth familiarity with systems and organisational patterns, and you become a trusted face in the company. Many companies offer bonuses and benefits for long service, like sabbaticals. And if you&#39;re happy there, that&#39;s great.&lt;/p&gt;
&lt;p&gt;Do bear in mind that that tenure might come at a cost of not gaining experience of the outside world: you could end up with tunnel vision, only knowing how things are done where you work. As time goes by you might find yourself more resistant to change, because it’s not the way you’ve always done things (this was a common pattern where I worked). There are ways around this, of course – make sure you keep in touch with folks from other orgs, hire people from outside and trust their opinions, and keep up to date with what’s going on in the wider industry.&lt;/p&gt;
&lt;h4 id=&quot;the-climbing-frame&quot; tabindex=&quot;-1&quot;&gt;The climbing frame&lt;/h4&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/climbing-frame-photo.jpg&quot; alt=&quot;An artistic photo of a climbing frame, zoomed in on the frame itself&quot; /&gt;
&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@s_tsuchiya&quot;&gt;S. Tsuchiya&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/a-close-up-of-a-roller-coaster-tnI2qV2IF6I&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/blog/eng-progression/climbing-frame.png&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/climbing-frame.png&quot; alt=&quot;A diagram of career moves. We begin with Company 1 Engineer, which has an arrow pointing to Company 2 Engineer; that points to Company 2 Senior Engineer, which in turn points to Company 3 Senior Engineer.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;A more common pattern these days is to move every few years. You might take a promotion for the new role, or do a sideways step into another company at the same level. You don’t always have to be getting a promotion when you move, though 9 times out of 10 you should be getting a pay rise with that move, unless it’s a conscious decision (e.g. moving to a nonprofit or smaller company).&lt;/p&gt;
&lt;p&gt;This might also involve a role change – I’ve known engineers who climbed the ladder and then did a sideways step into a non-engineering role. That engineering background is so powerful when you go into product management, for example.&lt;/p&gt;
&lt;h4 id=&quot;monkey-bars&quot; tabindex=&quot;-1&quot;&gt;Monkey bars&lt;/h4&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/monkey-bars-photo.jpeg&quot; alt=&quot;A child&#39;s hand clutches one of several handles hanging from a horizontal wooden pole on a jungle gym&quot; /&gt;
&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@anniespratt&quot;&gt;Annie Spratt&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/girl-playing-at-monkey-bar-during-daytime-1Jg-_nekJT0&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/blog/eng-progression/monkey-bars.png&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/monkey-bars.png&quot; alt=&quot;A diagram of career moves. We begin with Company 1 Engineer, which has an arrow pointing to Company 2 Engineer; that points to Company 3 Engineer, which in turn points to Company 4 Engineer, which goes to Company 5 Engineer.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;Some folks hop from company to company every 1-2 years; there’s nothing inherently wrong with this approach. The old advice goes that lots of short stints at different places will look bad on your CV/résumé, but 1-2 years is a decent amount of time in the industry these days.&lt;/p&gt;
&lt;p&gt;One advantage is that you get insight into how different companies work and different tech stacks, and you should hopefully be negotiating pay rises with those moves, even if they’re lateral moves. Some of those roles might involve promotions, as well.&lt;/p&gt;
&lt;p&gt;Of course, people working short-term contracts will have lots of different roles in their employment history and that’s to be expected.&lt;/p&gt;
&lt;p&gt;The downside is that if you’re more into the leadership side of things, you might not be hanging around long enough to establish yourself as an authority of anything, and being really familiar with your organisation and its systems. So have a think about whether that’s something you value.&lt;/p&gt;
&lt;h4 id=&quot;merry-go-round&quot; tabindex=&quot;-1&quot;&gt;Merry-go-round&lt;/h4&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/merry-go-round-photo.jpg&quot; alt=&quot;A faded blue merry-go-round with red seats in the middle of that spongy playground flooring&quot; /&gt;
&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@loegunntunghok&quot;&gt;Loegunn Lai&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/2-black-metal-framed-red-padded-armchairs-on-brown-sand-near-body-of-water-during-daytime-SyZMtSYTPy0&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/blog/eng-progression/merry-go-round.png&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/merry-go-round.png&quot; alt=&quot;A diagram of career moves. We begin with Company 1 Graduate, which has an arrow pointing to Company 1 Engineer II; that points to Company 2 Engineer III, which in turn points to Company 2 Senior Engineer I. This node has two arrows coming out of it. The first points to Company 3 Senior Engineer, and then the arrow from that points back to Company 2 Senior Engineer I. The second arrow points to Company 2 Senior Engineer II.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;This is actually my career history. Also known as the &lt;a href=&quot;https://giphy.com/gifs/fDO2Nk0ImzvvW&quot;&gt;&amp;quot;Grampa Simpson&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I left my job at Monzo to pursue a role I was offered at a startup because I thought it was a great opportunity I shouldn’t pass up. It ended up not working out – our expectations of the role weren’t really aligned, it was nobody&#39;s fault – so I ended up coming back to Monzo in a similar role but a different team. At Monzo they call this a “boomerang”, but that doesn’t fit with my ridiculous playground analogy.&lt;/p&gt;
&lt;p&gt;I don’t regret leaving to try it out, and I don’t regret coming back. It’s completely fine to make a U-turn as long as it’s the right thing for you. As I wrote at the time: &lt;a href=&quot;https://localghost.dev/blog/when-going-back-doesn-t-mean-going-backwards/&quot;&gt;going back doesn’t always mean going backwards&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Like with any merry-go-round, you don’t want to spin too many times or you’ll get dizzy. It’s good to go on some other equipment too.&lt;/p&gt;
&lt;h4 id=&quot;the-rocker&quot; tabindex=&quot;-1&quot;&gt;The rocker&lt;/h4&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/rocker-photo.jpg&quot; alt=&quot;A double-seater rocker playground toy in the shape of a helicopter, supported by a large metal spring&quot; /&gt;
&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@crazeker&quot;&gt;Shiau Ling&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/yellow-and-black-robot-toy-wNo1WTdZ_ec&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/blog/eng-progression/rocker.png&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/eng-progression/rocker.png&quot; alt=&quot;Three nodes, each with an arrow going into the next one. Company 1 Engineer points to Company 2 Engineer which points to Company 3 Engineer.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;Not everyone wants to climb to the top, and that’s okay. There are any number of reasons why someone might want to stay in the same role.&lt;/p&gt;
&lt;p&gt;Maybe they like it. Maybe they’ve got other responsibilities outside of work that take up any extra space in their brain. Maybe they value other things in life. It’s all good.&lt;/p&gt;
&lt;p&gt;I’m definitely guilty of feeling like I always need to be moving up to the next level, as if by staying still I’m not good enough; but of course, that’s absolute rubbish.&lt;/p&gt;
&lt;h3 id=&quot;there-s-no-one-right-way&quot; tabindex=&quot;-1&quot;&gt;There&#39;s no one right way&lt;/h3&gt;
&lt;p&gt;Progression can mean so many different things. It can mean going forwards and backwards, moving around trying different things and coming back again, or staying in the same place having a great time. It really depends what suits you. There isn&#39;t one &amp;quot;right&amp;quot; path to follow!&lt;/p&gt;
&lt;p&gt;I hope this has gone some way to demystify the path your career could take, both role-wise and company-wise. And remember, don&#39;t let anyone make you feel like you &lt;em&gt;must&lt;/em&gt; pursue one particular path or fit your career into a specific box. Only you can make that decision.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Beautiful musical chaos</title>
      <link href="https://localghost.dev/blog/beautiful-musical-chaos/"/>
      <updated>2023-12-02T00:00:00Z</updated>
      <id>https://localghost.dev/blog/beautiful-musical-chaos/</id>
      <content type="html">&lt;p&gt;When I tell people I’m in a choir, some of them imagine the same thing: churches, robes and &lt;em&gt;choral music&lt;/em&gt;. Pious singing faces.&lt;/p&gt;
&lt;p&gt;(To be clear, I’ve done that kind of choir before and it’s great in its own way, but that’s very much not what goes on in this one.)&lt;/p&gt;
&lt;p&gt;Generally, though, people respond with the question, “what kind of choir is it?”. What indeed.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://www.hawksworx.com/blog/on-song/&quot;&gt;post by Phil Hawksworth&lt;/a&gt; earlier this year about his newfound love of choir really resonated because I knew exactly how it felt to be completely, unexpectedly sucked in to this hobby.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/choir/daylightmusic.jpeg&quot; alt=&quot;a photo of the choir on a stage at the Union Chapel in Islington, with the iconic round flower shaped window above them&quot; /&gt;
&lt;figcaption&gt;Daylight Music at the Union Chapel&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I’d been in various choirs before, classical ones, but only joined &lt;a href=&quot;https://shechoir.com/&quot;&gt;SHE Choir&lt;/a&gt; because I’d just moved back to London and didn’t know anyone. The choir started life as a student society in Manchester where I studied, and I’d gone to a few sessions the year before, but now the founders had moved back to London and brought the choir with them. It was chaotic, but I was hooked immediately.&lt;/p&gt;
&lt;p&gt;A few months in, I decided I wanted to try arranging something. I remember asking if it was okay for me to have a go, and I put together a three-part arrangement of Don’t Panic by Coldplay. I was too nervous to sing in front of people so I got someone else to teach it. But it sounded &lt;em&gt;good&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A weekend away with both the London and Manchester choirs to a druid camp in a remote part of the Midlands – involving sleeping next to a creepy fur-clad mannequin, lots of singing in yurts, and a bunch of city women completely failing to work a log fire – finally gave me the confidence to start singing on my own in front of people. I realised that choir was a safe space for me to do that, to teach and learn at the same time. After I got back, coasting on the high from the weekend, I arranged Fleetwood Mac’s song Everywhere and that would become the first thing I taught by myself.&lt;/p&gt;
&lt;p&gt;From there, the choir became something I could actively contribute to. Not long after that the founders left and, not wanting to see this lovely motley crew disappear, a couple of us took over as organisers. That was a lot of work for two people (you can see why people do it as a full-time job!) so we eventually transitioned to a team-based structure. It’s less rigorous and a bit more anarchic, but it means less responsibility falls on one person’s shoulders. (I’m always and forever the admin queen... and of course I built the website.)&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/choir/teaching.jpeg&quot; alt=&quot;Sophie leans against a desk, hand in the air to indicate changes in pitch, mouth in an ooh shape as she teaches a song. There are two seated people visible and more out of shot.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;We became an a cappella group solely because none of us could play the piano. I got us on Slack because that was what you did in 2014, and we got organised. We started taking attendance, and the revolving door of people coming and going and never learning the songs became a more reliable core membership of regulars who got better every week. I got fed up of taking attendance very quickly, and built a &lt;a href=&quot;https://github.com/sophiekoonin/shebot&quot;&gt;Slack integration&lt;/a&gt; that did it for us.&lt;/p&gt;
&lt;p&gt;We met up with another more established women’s choir in London and attended each other’s rehearsals to swap tips. They had been the inspiration for our choir’s existence. I was blown away by them, and imagined that maybe one day we’d be as good as them.&lt;/p&gt;
&lt;p&gt;Over the years I’ve arranged and taught songs by Alice Cooper, The Postal Service, Imogen Heap, Bruce Springsteen, Chaka Khan, Wheatus, Regina Spektor, Dolly Parton, and more. Ain’t Nobody is well-known enough to most, and my arrangement simple enough, that for a while it became the first song anyone taught when a new branch of the choir popped up. It’s quite a feeling to stand on a stage in front of a hundred or so people from all over, most of whom you’ve never met, and conduct them all singing a song that you arranged.&lt;/p&gt;
&lt;p&gt;Conducting isn’t just waving your hands about and being a human metronome. It&#39;s conveying emotion, volume, shape of the sound. For me, the most special part of it is having your arrangement sung back at you with the full weight of the choir behind it – it often makes me emotional to hear those little crunchy chords I put in reflected so beautifully in the choir’s strong voices. &lt;em&gt;I did this.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As time goes by the thing I enjoy the most is watching new conductors get that same joy from standing at the front and hearing their arrangement sung back at them. It can be a very vulnerable thing to put yourself at the front, and I’m so glad we’ve given people the opportunity to try new things out, arrange these songs nobody else knows but that mean so much to them, and learn new skills along the way.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/choir/wow.jpg&quot; alt=&quot;The choir performing in the South Bank centre, wearing all black and floral. Behind then, through the windows, you can see the Thames and buildings along the north bank of the river&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Three years after we attended the other choir’s rehearsal, we were on the same billing as them at a festival in London. I watched their set and realised that we were not only that good, but also so different. Going a cappella had actually forced us to be more creative with our arrangements, being the bass and the instruments as well.&lt;/p&gt;
&lt;p&gt;The choir that was once just in Manchester and London is now all over England, Germany and New Zealand. People love it so much that when they move away, they start up a new one wherever they end up. For a while there was one in a remote skiing town somewhere in France. Our London group is changing our name now, to better reflect the diversity of our members: we&#39;ve called ourselves Mixtape. The perfect name for a weird and wonderful collection of songs, and people.&lt;/p&gt;
&lt;p&gt;On Thursday night at our winter gig I stood in front of the choir and waved my arms around to the tune of Dancing In The Dark by Bruce Springsteen, one of the most ambitious arrangements I’ve done – and the choir nailed every note. We’re an amateur choir in every way: no auditions, collectively and chaotically self-organised, and we’re all just making it up as we go along. But we’re so &lt;em&gt;good&lt;/em&gt; at it.&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube-nocookie.com/embed/Uwudzh3UjvQ?si=hJSrS-Kp1bCDXzLS&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;I’ve invested a third of my life in this choir and there have been moments when I’ve been overcome with frustration (nobody’s doing anything! I have to do everything!) or worry (why hasn’t anyone learned the song?). But reflecting on how far we’ve come and what we’ve brought to people’s lives makes it all worth it.&lt;/p&gt;
&lt;p&gt;Those are the moments I live for: the biggest smile on a new conductor’s face as they conduct their song in public for the first time; the “I’m not sure if this is going to sound good” but it always does; the “okay, sure, I’ll have a go” from a quiet person; the drunken renditions of every song we’ve ever done, fuelled by too much wine, at every single choir retreat (and yes, it really is exactly like Pitch Perfect); and the teary reflections the morning of the last day of retreat as everyone shares what this choir means to them. And ultimately, it’s been a lifeline for me during some of the hardest times of my life.&lt;/p&gt;
&lt;p&gt;Of course, I could never convey all of that in one sentence when asked “what kind of choir is it?”. So I just say “it’s a pop choir.” But it’s a lot more than that, really, isn’t it?&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/choir/wedding.jpeg&quot; alt=&quot;Sophie in a knee length white wedding dress, surrounded by women in smart dresses, peparing to sing. they stand together in a converted barn, the exposed rafters visible above them.&quot; /&gt;
&lt;figcaption&gt;Photo: &lt;a href=&quot;https://jessicajillphoto.com/&quot;&gt;Jessica Jill Photo&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;</content>
    </entry>
    
    <entry>
      <title>A good recipe: The best chocolate brownies</title>
      <link href="https://localghost.dev/blog/a-good-recipe-the-best-chocolate-brownies/"/>
      <updated>2023-11-23T00:00:00Z</updated>
      <id>https://localghost.dev/blog/a-good-recipe-the-best-chocolate-brownies/</id>
      <content type="html">&lt;p&gt;I&#39;ve been making this &amp;quot;Bonfire Brownies&amp;quot; recipe since it was aired on UK children&#39;s TV institution &lt;a href=&quot;https://en.wikipedia.org/wiki/Blue_Peter&quot;&gt;Blue Peter&lt;/a&gt; in the early 00s, and I maintain it&#39;s still the best brownie recipe. Chewy and delicious. I recommend freezing them and eating them straight out of the freezer (I discovered this when my mum used to put them in the freezer to keep them out of the way of temptation, and I&#39;d nick them when she wasn&#39;t looking). There&#39;s nothing bonfire-specific about it, they&#39;re chocolate brownies and I suppose the episode aired around 5th November, but they&#39;re fucking great.&lt;/p&gt;
&lt;p&gt;Look, I don&#39;t have any photos of this, but you&#39;ve just got to take my word for it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Makes 12&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;ingredients&quot; tabindex=&quot;-1&quot;&gt;Ingredients&lt;/h3&gt;
&lt;p&gt;100g unsalted butter&lt;br /&gt;
225g golden caster sugar (or regular caster sugar)&lt;br /&gt;
40g cocoa powder&lt;br /&gt;
2 eggs&lt;br /&gt;
50g self-raising flour&lt;br /&gt;
50g chocolate chips&lt;br /&gt;
1/2 tsp vanilla essence (optional)&lt;/p&gt;
&lt;h3 id=&quot;method&quot; tabindex=&quot;-1&quot;&gt;Method&lt;/h3&gt;
&lt;p&gt;Preheat the oven to 180°C/350°F. Grease and line a 20cm square brownie tin.&lt;/p&gt;
&lt;p&gt;In a large bowl beat the eggs, add the sugar and mix together until smooth.&lt;/p&gt;
&lt;p&gt;Melt the butter in a small saucepan over a medium heat. Sift in the cocoa powder and mix. Add this mixture along with the vanilla essence (if using) to the egg and sugar mixture.&lt;/p&gt;
&lt;p&gt;Sift the flour into the wet ingredients and mix together until smooth. Fold in the chocolate chips.&lt;/p&gt;
&lt;p&gt;Pour into the brownie tin, and put in the oven. Cook for around 20-25 mins, until the top is a darker brown (but watch it doesn&#39;t burn!).&lt;/p&gt;
&lt;p&gt;Leave to cool in the tin for 5-10 mins, then carefully turn out onto a cooling rack. For best results, wait until they&#39;re completely cooled to slice.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>How to schedule posts in Eleventy</title>
      <link href="https://localghost.dev/blog/how-to-schedule-posts-in-eleventy/"/>
      <updated>2023-11-21T00:00:00Z</updated>
      <id>https://localghost.dev/blog/how-to-schedule-posts-in-eleventy/</id>
      <content type="html">&lt;p&gt;I scheduled this post, isn&#39;t that cute?&lt;/p&gt;
&lt;p&gt;I find I have very little energy to write blog posts during the week, but occasionally I sit down on the weekend to write something and have a few ideas that turn into multiple posts. I&#39;d like to be able to publish posts more often, but realistically I won&#39;t be writing things more than once every few weeks (which is okay!). I decided I needed to be able to schedule posts so I don&#39;t just publish a load at once.&lt;/p&gt;
&lt;p&gt;Eleventy&#39;s suggested &lt;a href=&quot;https://www.11ty.dev/docs/quicktips/draft-posts/&quot;&gt;draft post setup&lt;/a&gt; has been working nicely for me, and posts with &lt;code&gt;draft: true&lt;/code&gt; in the frontmatter don&#39;t appear in the production site. However, I miss Hugo&#39;s scheduled posts feature, where I&#39;d be able to write a post with a date in the future and it&#39;d only appear on the blog after that date. Thankfully it&#39;s pretty straightforward to set up in Eleventy, in a similar way to drafts.&lt;/p&gt;
&lt;h2 id=&quot;scheduling-website-builds&quot; tabindex=&quot;-1&quot;&gt;Scheduling website builds&lt;/h2&gt;
&lt;p&gt;My website already builds twice a day to fetch the latest webmentions. I had a GitHub Actions job to build and deploy my site to Neocities, with a cron schedule that ran every 12 hours:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Deploy to Neocities&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 &lt;span class=&quot;token important&quot;&gt;*/12&lt;/span&gt; * * *
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main
&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Deploy to Neocities&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;node-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;18&quot;&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yarn&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yarn install
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yarn build
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bcomnes/deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;neocities@v1
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;api_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.SUPER_SECRET_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;dist_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_site/&quot;&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;cleanup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I tweaked the cron schedule so that it runs daily at 8am and 5pm, so I wouldn&#39;t publish a post at midnight.&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Deploy to Neocities&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 8/9 * * *&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;exclude-scheduled-posts-from-collections&quot; tabindex=&quot;-1&quot;&gt;Exclude scheduled posts from collections&lt;/h2&gt;
&lt;p&gt;In my &lt;code&gt;drafts.js&lt;/code&gt; plugin, I added a new function to check whether a post&#39;s timestamp is in the future:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; now &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isScheduledPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; now&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally I added this function to the conditions in the &lt;code&gt;eleventyComputedPermalink&lt;/code&gt; and &lt;code&gt;eleventyComputedExcludeFromCollections&lt;/code&gt; functions which check for draft posts:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;eleventyComputedPermalink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// When using `addGlobalData` and you *want* to return a function, you must nest functions like this.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// `addGlobalData` acts like a global data file and runs the top level function it receives.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Always skip during non-watch/serve builds&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BUILD_DRAFTS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isScheduledPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;permalink&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s it! The scheduled posts will always show up when running the site locally, as &lt;code&gt;process.env.BUILD_DRAFTS&lt;/code&gt; is always true in the dev server. But they won&#39;t show up in the production site until the day comes and the site gets rebuilt. To be extra sure, you can run your site&#39;s prod build command and check the output – your scheduled page shouldn&#39;t be listed.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Building post types and category RSS feeds in Eleventy</title>
      <link href="https://localghost.dev/blog/building-post-types-and-category-rss-feeds-in-eleventy/"/>
      <updated>2023-11-19T00:00:00Z</updated>
      <id>https://localghost.dev/blog/building-post-types-and-category-rss-feeds-in-eleventy/</id>
      <content type="html">&lt;p&gt;I &lt;a href=&quot;https://localghost.dev/blog/introducing-separate-category-rss-feeds/&quot;&gt;mentioned recently&lt;/a&gt; that I&#39;d built separate RSS feeds for different kinds of posts. Here&#39;s how I did it!&lt;/p&gt;
&lt;p&gt;I had to do a bit of fiddling to get this working in Eleventy – this is one of the few things I think Hugo actually does in a simpler way, as you can create different &lt;code&gt;rss.xml&lt;/code&gt; templates inside specific layout directories. My solution is not especially sophisticated, but it works! (If you know a less complicated way... I&#39;d love to hear it.)&lt;/p&gt;
&lt;h2 id=&quot;building-post-types&quot; tabindex=&quot;-1&quot;&gt;Building post types&lt;/h2&gt;
&lt;p&gt;Alongside the traditional long-form blog posts, I wanted to be able to publish little &amp;quot;micro&amp;quot; posts of recommendations and things I liked. In order to distinguish them, I added a new frontmatter field called &lt;code&gt;type&lt;/code&gt; to all my posts. Right now, there are a few different types: article, podcast, game, book, and recipe.&lt;/p&gt;
&lt;p&gt;In my blog post list page, I add the &lt;code&gt;type&lt;/code&gt; as a data attribute on the &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; for each &amp;quot;micro&amp;quot; post, so that I can display an appropriate emoji on the list item. (I haven&#39;t quite figured out how I want to show filters for post types yet.)&lt;/p&gt;
&lt;p&gt;Each post type has a json file which specifies the default frontmatter for all files in that directory. It automatically formats the post title into a consistent format, and tags the post accordingly. Here&#39;s &lt;code&gt;podcasts.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;layout&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;good-podcast.njk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;hasCustomOGImage&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;eleventyComputed&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A good podcast: {{ podcast }}&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;podcast&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;podcast&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;permalink&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/blog/a-good-podcast-{{ podcast | slugify }}/index.html&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tags and type are identical because I wanted a quick way of accessing post type in things like the layout, but I wanted readers to be able to list all podcasts just like they can any other tag – and post tags might contain more than just &amp;quot;podcast&amp;quot;, say. Any tags I set on the post itself will be added to that array, not replace it.&lt;/p&gt;
&lt;p&gt;I use the &lt;code&gt;hasCustomOGImage&lt;/code&gt; variable to tell a layout whether to find a specific OG image based on the title of the post, or just use the default one.&lt;/p&gt;
&lt;p&gt;The post itself has its own frontmatter, with the name of the podcast (or book, or game, etc), the date, an image with alt text, and the URL of the podcast.&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;podcast: If Books Could Kill
url: https://pod.link/1651876897
date: 2023-07-16
image: if-books-could-kill.jpeg
alt: &quot;The cover image for If Books Could Kill, with an illustration of a bleeding book&quot;&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;

Michael Hobbes and Peter Shamshiri take us through some of the biggest &quot;self-help&quot;/pseudoscience books from the last few decades and deservedly tear them apart.

[...]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;separating-rss-feeds&quot; tabindex=&quot;-1&quot;&gt;Separating RSS feeds&lt;/h2&gt;
&lt;p&gt;If you don&#39;t already, you&#39;ll need the Eleventy RSS plugin set up. See the &lt;a href=&quot;https://www.11ty.dev/docs/plugins/rss/&quot;&gt;plugin docs&lt;/a&gt; for instructions.&lt;/p&gt;
&lt;p&gt;My RSS feed was previously one single feed, set up with some basic settings – it was pretty much identical to the &lt;a href=&quot;https://www.11ty.dev/docs/plugins/rss/#sample-feed-templates&quot;&gt;example in the RSS plugin docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wanted to split up my feeds into the various different types of &amp;quot;micro&amp;quot; posts (the recommendations), and then everything else as one &amp;quot;articles&amp;quot; feed; however, I also wanted to keep the &amp;quot;everything&amp;quot; feed for people who like their posts about Content-Security-Policy headers interspersed with brownie recipes from 2001.&lt;/p&gt;
&lt;h3 id=&quot;collections-collections-collections&quot; tabindex=&quot;-1&quot;&gt;Collections, collections, collections&lt;/h3&gt;
&lt;p&gt;I&#39;ve got a few custom collections for my post types. The first one pulls &lt;em&gt;all&lt;/em&gt; of the posts in the &lt;code&gt;blog&lt;/code&gt; directory, which encompasses all articles and post types:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// eleventy.config.js&lt;/span&gt;

eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;blog&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; collection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFilteredByGlob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./src/blog/**/*.md&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I&#39;ve got the &lt;code&gt;categoryFeeds&lt;/code&gt; collection which contains all of the specific feeds I want for each &amp;quot;micro&amp;quot; post type.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;categoryFeeds&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;recipe&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;game&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;podcast&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, I&#39;ve got one for &amp;quot;articles&amp;quot; - all the posts that &lt;em&gt;aren&#39;t&lt;/em&gt; micro-categories. These all live in the same directory, so I used &lt;code&gt;getFilteredByGlob&lt;/code&gt; again. (I&#39;ve actually called the directory &amp;quot;posts&amp;quot; but I couldn&#39;t be bothered to change it.)&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;articles&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; collection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFilteredByGlob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./src/blog/posts/*.md&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;paginating-the-category-feeds&quot; tabindex=&quot;-1&quot;&gt;Paginating the category feeds&lt;/h3&gt;
&lt;p&gt;I used Eleventy&#39;s excellent pagination feature to generate the RSS feeds themselves. I&#39;ve got a file called &lt;code&gt;rss-categories.11ty.js&lt;/code&gt; which contains info about the data I want to paginate, and that will produce a feed for each of the feed categories in my &lt;code&gt;categoryFeeds&lt;/code&gt; collection. Each generated file will have the same template (&lt;code&gt;rss.njk&lt;/code&gt;) but a different title, subtitle and feed URL; it&#39;ll also have a different value of &lt;code&gt;feed&lt;/code&gt; (set to whichever category the feed is for, based on the collection item).&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RSSCategories&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;pagination&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;collections.categoryFeeds&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;feed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token function-variable function&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; feed &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;feed&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;s.xml&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;eleventyExcludeFromCollections&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rss.njk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;eleventyComputed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function-variable function&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; feed &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;localghost.dev - posts about &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;feed&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function-variable function&quot;&gt;subtitle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; feed &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;A feed of the latest &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;feed&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; recommendations from localghost.dev&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function-variable function&quot;&gt;feedUrl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; feed &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://localghost.dev/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;feed&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;s.xml&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; RSSCategories&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;one-rss-template-for-all&quot; tabindex=&quot;-1&quot;&gt;One RSS template for all&lt;/h3&gt;
&lt;p&gt;My RSS feed template lives in &lt;code&gt;_includes/rss.njk&lt;/code&gt; alongside all my other layouts. It&#39;s pretty much the standard RSS template they suggest in the docs, but with a couple of tweaks to make it a bit more generic, allowing &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;subtitle&lt;/code&gt; and &lt;code&gt;feedUrl&lt;/code&gt; to be optionally passed in as variables.&lt;/p&gt;
&lt;pre class=&quot;language-njk&quot;&gt;&lt;code class=&quot;language-njk&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;?&lt;span class=&quot;token variable&quot;&gt;xml&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.0&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;?&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;subtitle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;subtitle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;feed&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/2005/Atom&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;subtitle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;subtitle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;subtitle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{{ feedUrl }}&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{{ metadata.url }}&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;getNewestCollectionItemDate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;dateToRfc3339&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;absolutePostUrl&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;absoluteUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;endset&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{{ absolutePostUrl }}&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;dateToRfc3339&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;absolutePostUrl&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;templateContent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;htmlToAbsoluteUrls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;absolutePostUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oddly, the variables seem to leave a blank space in the outputted XML, but as long as the &lt;code&gt;&amp;lt;?xml&amp;gt;&lt;/code&gt; declaration is at the top it doesn&#39;t seem to matter.&lt;/p&gt;
&lt;p&gt;The RSS template iterates over a given collection and includes all the posts it finds, so I have to tell it which collection to pull from. This is where the &lt;code&gt;feed&lt;/code&gt; variable comes in. It tells the template to iterate over posts tagged with e.g. &amp;quot;recipe&amp;quot; or &amp;quot;game&amp;quot;.&lt;/p&gt;
&lt;h3 id=&quot;a-separate-articles-feed&quot; tabindex=&quot;-1&quot;&gt;A separate articles feed&lt;/h3&gt;
&lt;p&gt;Unlike my specific category fields, &amp;quot;everything else&amp;quot; isn&#39;t a tag, so I can&#39;t use the same pagination to generate the &amp;quot;articles&amp;quot; feed. That has to have its own file. Since it hasn&#39;t got any fancy variable templating, I can just create a nunjucks file and fill out the frontmatter:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
  &lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;permalink: &#39;articles.xml&#39;
  eleventyExcludeFromCollections: true
  layout: &quot;rss.njk&quot;
  feed: articles
  title: &quot;localghost.dev - all articles&quot;
  subtitle: &quot;Sophie builds fun things out of HTML, CSS &amp;amp; JavaScript, and writes blog posts about tech and mental health. This feed excludes recommendations about books, games, podcasts, recipes, etc.&quot;
  feedUrl: &quot;https://localghost.dev/articles.xml&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--  rss-articles.njk --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because I created the &lt;code&gt;articles&lt;/code&gt; category in my Eleventy config, I can pass that in as the &lt;code&gt;feed&lt;/code&gt; variable, and then &lt;code&gt;collections[feed]&lt;/code&gt; in the RSS layout will translate to &lt;code&gt;collections.articles&lt;/code&gt;. The only posts in this feed will be anything that lives in my &lt;code&gt;/blog/posts/&lt;/code&gt; folder.&lt;/p&gt;
&lt;h2 id=&quot;the-primary-feed&quot; tabindex=&quot;-1&quot;&gt;The primary feed&lt;/h2&gt;
&lt;p&gt;And finally, the tweak to my &amp;quot;everything&amp;quot; feed to get it to work with this new layout: make sure it also has a &lt;code&gt;feed&lt;/code&gt; variable. The &lt;code&gt;blog&lt;/code&gt; collection already exists, and contains everything in the &lt;code&gt;blog&lt;/code&gt; directory, so we can pass that in and it&#39;ll add everything in there to the RSS feed (which is actually what it was doing before, anyway).&lt;br /&gt;
The metadata for my primary RSS feed is shared with the meta tags for the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of my site, so it lives elsewhere in &lt;code&gt;_data&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;permalink : &quot;feed.xml&quot;
eleventyExcludeFromCollections : true
feed: &quot;blog&quot;
layout: &quot;rss.njk&quot;
feedUrl: &quot;https://localghost.dev/feed.xml&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- rss-all.njk --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there you go! Multiple RSS feeds. I changed the &lt;a href=&quot;https://localghost.dev/rss&quot;&gt;RSS link&lt;/a&gt; in my footer to point towards a page where I added links to all the feeds.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>A good recipe: Smitten Kitchen&#39;s apple pie cookies</title>
      <link href="https://localghost.dev/blog/a-good-recipe-smitten-kitchen-s-apple-pie-cookies/"/>
      <updated>2023-11-12T00:00:00Z</updated>
      <id>https://localghost.dev/blog/a-good-recipe-smitten-kitchen-s-apple-pie-cookies/</id>
      <content type="html">&lt;p&gt;If I need to impress, or bribe people to like me, I make these cookies. A shortcrust pastry/biscuit dough hybrid case with appley-cinnamony goodness inside, packaged in an adorable pie shape.&lt;/p&gt;
&lt;p&gt;The process is a little involved, but they&#39;re flaky and delicious (and often gone in an instant). I first made them over a decade ago and it&#39;s still one of my go-to recipes.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Introducing separate category RSS feeds</title>
      <link href="https://localghost.dev/blog/introducing-separate-category-rss-feeds/"/>
      <updated>2023-11-12T00:00:00Z</updated>
      <id>https://localghost.dev/blog/introducing-separate-category-rss-feeds/</id>
      <content type="html">&lt;p&gt;Recently I&#39;ve been inspired by folks like &lt;a href=&quot;https://css-irl.info/&quot;&gt;Michelle Barker&lt;/a&gt; and &lt;a href=&quot;https://amyhupe.co.uk/&quot;&gt;Amy Hupe&lt;/a&gt; doing National Blog Posting Month (NaBloPoMo!), and seeing folks writing down little thoughts and things they&#39;ve encountered. I&#39;m absolutely not doing NaBloPoMo, I am far too tired, but I thought I&#39;d make it easier for myself to write things down a bit more often than once every three to six months.&lt;/p&gt;
&lt;p&gt;I&#39;ve introduced some new categories to my blog, where I can share things like books, games and podcasts I&#39;ve enjoyed recently, and recipes I love. I figured that having these little bites to post occasionally might spur me to write a bit more often. However, I know people won&#39;t necessarily want to &lt;em&gt;read&lt;/em&gt; all of those things if they subscribe to my RSS feed. I also have very specific categories in my own RSS reader, so I wanted to make sure people can only subscribe to the things that interest them. So I built separate RSS feeds!&lt;/p&gt;
&lt;p&gt;My site&#39;s main feed still contains &lt;em&gt;everything&lt;/em&gt;, but if you don&#39;t want the occasional brownie recipe with your accessibility rants, you can subscribe to my &amp;quot;articles&amp;quot; feed which only has my writings and rantings. If you want to get a podcast recommendation now and then, the &amp;quot;podcasts&amp;quot; feed is for you. The new feeds are a bit quiet at the moment, but will pick up as I post more!&lt;/p&gt;
&lt;p&gt;I&#39;m writing up how I built it, but for now you can pick your favourite feeds on the &lt;a href=&quot;https://localghost.dev/rss&quot;&gt;RSS page&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>&quot;AI&quot;, and the trouble with inaccessible SaaS</title>
      <link href="https://localghost.dev/blog/ai-and-the-trouble-with-inaccessible-saas/"/>
      <updated>2023-09-17T00:00:00Z</updated>
      <id>https://localghost.dev/blog/ai-and-the-trouble-with-inaccessible-saas/</id>
      <content type="html">&lt;p&gt;I wasn&#39;t particularly enthused by the alpha release of &lt;a href=&quot;https://v0.dev/&quot;&gt;Vercel&#39;s v0 UI generation tool&lt;/a&gt; this week. Provided with a prompt, it produces React code with Tailwind to style it. It&#39;s being marketed as a prototyping tool, but was described by Vercel&#39;s CEO on Twitter as &lt;a href=&quot;https://twitter.com/rauchg/status/1702355455362912595&quot;&gt;&amp;quot;production-grade&amp;quot;&lt;/a&gt;. Well, which one is it?&lt;/p&gt;
&lt;p&gt;To be clear, my issue isn&#39;t with the concept of large language models (LLMs) in general: I use Copilot in my everyday work, and it&#39;s saved me a lot of time when writing tests! &lt;strong&gt;My concern is with the source material used to train it, and what people do with the output.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hidde de Vries wrote a &lt;a href=&quot;https://hidde.blog/interactions-about-accessibility/&quot;&gt;great post on the incredibly problematic dev community response&lt;/a&gt; after the accessibility of the UI was called out, so I won&#39;t go into that here.&lt;/p&gt;
&lt;h2 id=&quot;stop-putting-inaccessible-prototypes-into-production&quot; tabindex=&quot;-1&quot;&gt;Stop putting inaccessible prototypes into production&lt;/h2&gt;
&lt;p&gt;Prototyping is one thing. Noodling around with UI ideas? Fantastic. Please don&#39;t put it into production.&lt;/p&gt;
&lt;p&gt;A pattern I&#39;ve noticed with a huge number of tech/SaaS startups is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;have idea&lt;/li&gt;
&lt;li&gt;build very quick, inaccessible UI &amp;quot;prototype&amp;quot; to test idea&lt;/li&gt;
&lt;li&gt;ship prototype to production&lt;/li&gt;
&lt;li&gt;prototype becomes the actual product&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Inevitably, people will copy existing patterns in the codebase, adding more stuff on top, and time passes. At this point, you have a completely inaccessible SaaS product, and the amount of work needed to go back and fix all the problems is so much, and so costly, that it&#39;s never going to be a &amp;quot;business priority&amp;quot;. &lt;em&gt;So many&lt;/em&gt; of the B2B SaaS tools I&#39;ve used are terribly inaccessible; companies seem to forget that staff may have access needs, as well as customers, and that those access needs may extend beyond being blind or partially sighted.&lt;/p&gt;
&lt;p&gt;In fact, a 2020 study by RNIB (&lt;a href=&quot;https://media.rnib.org.uk/documents/Employment_facts_and_stats_2020_-_External_version.docx#:~:text=There%20is%20a%20significant%20employment,and%20partially%20sighted%20%5B7%5D&quot;&gt;docx&lt;/a&gt;) found that &amp;quot;50% of employers thought that there may be additional health and safety risks in the workplace [employing someone blind or partially sighted]; 33% of employers thought that they may not be able to operate a computer/laptop; 33% of employers thought that they may not be able to operate the necessary equipment, excluding computers/laptops&amp;quot;. Isn&#39;t that just terribly fucking depressing?&lt;/p&gt;
&lt;p&gt;I truly wish the founders of SaaS companies worldwide would learn some proper semantic HTML before they build these UIs that are going to form the basis of their entire product for years to come. I&#39;d love to see SaaS accessibility viewed as a competitive advantage rather than an expensive afterthought.&lt;/p&gt;
&lt;p&gt;As &lt;a href=&quot;https://twitter.com/AshleeMBoyer/status/1702379264836882623&quot;&gt;Ashlee Boyer&lt;/a&gt; puts it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The right time to work on accessibility is before you launch a product into alpha.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before v0 was announced I was planning on writing a blog post about the inaccessibility of SaaS software, and how it starts at prototypes making it to production, so I guess this is that post. The problem has existed for years, long before generative LLMs came along. LLMs are just making it worse, and faster.&lt;/p&gt;
&lt;h2 id=&quot;people-write-inaccessible-code-and-ll-ms-copy-people&quot; tabindex=&quot;-1&quot;&gt;People write inaccessible code, and LLMs copy people&lt;/h2&gt;
&lt;p&gt;Tools like v0 are destined to become the new &amp;quot;copying and pasting from open-source UI libraries&amp;quot;. In the wider community, ChatGPT has already overtaken StackOverflow as the first port of call for coding questions. Developers shouldn&#39;t unreservedly trust the output from code-generating LLMs, though. I double-triple-check everything that Copilot produces for me, and generally end up tweaking it about half of the time – and I rarely let it write blocks of code unless I&#39;m writing loads of repetitive tests. It&#39;s great for autocompleting variables, repeating things I&#39;ve already written and finishing lines, but I have found it has a tendency to make up functions that don&#39;t exist.&lt;/p&gt;
&lt;p&gt;Likewise, ChatGPT and v0 may produce decent enough code, or they might give you &lt;a href=&quot;https://v0.dev/t/LnxRCcq&quot;&gt;&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s that should be links&lt;/a&gt;. At least v0 seems to be adding labels to form inputs, which is more than I can say for a lot of developers on the web. But that&#39;s kind of the issue: people don&#39;t write accessible code, so why should we be trusting algorithms trained on other people&#39;s inaccessible code to write UI code for us? Vercel say it was trained on code written by their team, but considering the UI for v0 &lt;a href=&quot;https://twitter.com/AshleeMBoyer/status/1702367107130720534&quot;&gt;wasn&#39;t accessible itself&lt;/a&gt;, I don&#39;t have a huge amount of confidence that the input source material for the LLM was.&lt;/p&gt;
&lt;p&gt;This isn&#39;t purely a Vercel problem, of course, it&#39;s an everyone problem. But we as developers can make it better by &lt;em&gt;learning how to write semantic HTML and proper CSS&lt;/em&gt;, and then training the models on &lt;em&gt;that&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I do believe that LLM-backed tools can speed up the development process and make us more productive, but not through developers indiscriminately pasting whatever they generate into our codebases and trusting that it&#39;s all fine. Our users deserve a little more diligence than that.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>A good podcast: If Books Could Kill</title>
      <link href="https://localghost.dev/blog/a-good-podcast-if-books-could-kill/"/>
      <updated>2023-07-16T00:00:00Z</updated>
      <id>https://localghost.dev/blog/a-good-podcast-if-books-could-kill/</id>
      <content type="html">&lt;p&gt;Michael Hobbes and Peter Shamshiri take us through some of the biggest &amp;quot;self-help&amp;quot;/pseudoscience books from the last few decades and deservedly tear them apart.&lt;/p&gt;
&lt;p&gt;Some of the worst relationship books are here – &lt;em&gt;Men Are From Mars, Women Are From Venus&lt;/em&gt;, &lt;em&gt;The 5 Love Languages&lt;/em&gt;, &lt;em&gt;The Rules&lt;/em&gt; and &lt;em&gt;The Game&lt;/em&gt; (yes, that dreadful pickup artist one) – alongside pop-economics and pop-psychology rubbish such as &lt;em&gt;Freakonomics&lt;/em&gt; and &lt;em&gt;Atomic Habits&lt;/em&gt;. One of the funniest podcasts I&#39;ve listened to in a while, and honestly gobsmacking that these books were so well-received in the first place. I feel like this should be called &amp;quot;Citation Needed: The Podcast&amp;quot;.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Beyond Tellerrand: beyond amazing</title>
      <link href="https://localghost.dev/blog/beyond-tellerrand-beyond-amazing/"/>
      <updated>2023-05-01T00:00:00Z</updated>
      <id>https://localghost.dev/blog/beyond-tellerrand-beyond-amazing/</id>
      <content type="html">&lt;p&gt;I had the absolute privilege of opening Beyond Tellerrand Düsseldorf recently. Truth be told, I had no idea what to expect, other than a great conference (from what everyone had told me), but it was incredible! (Apologies to anyone who tried to chat to me on the Monday after my talk – if I seemed a bit distant, it was because I was literally struggling to string words together because of adrenaline and tiredness.) It took me a week to recover from the travel, socialising and speaking, and I went straight back to work so I&#39;m only getting around to writing this now!&lt;/p&gt;
&lt;p&gt;From the first conversation with &lt;a href=&quot;http://www.marcthiele.com/&quot;&gt;Marc&lt;/a&gt; I knew this was something special: he and the Beyond Tellerrand team put so much love into this event, and it really shows. The venue is awesome, the production quality is amazing, and the speakers are treated really well. I arrived at the hotel exhausted after a day of travel to find a handwritten letter from Marc, a &lt;a href=&quot;https://www.shauntan.net/arrival-book&quot;&gt;beautiful book&lt;/a&gt;, and a very useful backpack which will be accompanying me to all my future conferences! The team and the speakers had a wonderful meal the night before the conference, and the first talk wasn&#39;t until 11am which meant I didn&#39;t have to panic-inhale my breakfast and leg it to the venue for an early start. I never sleep well before giving talks, so the extra time was very welcome.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/btconf-me-stage.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/btconf-me-stage.jpg&quot; alt=&quot;Me on the stage&quot; /&gt;&lt;/a&gt;
&lt;figcaption&gt;Photo credit: &lt;a href=&quot;https://florian.photo/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;Florian Ziegler&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Speaking of the incredible production quality: Marc got dina Amin to do these &lt;a href=&quot;https://youtu.be/SfIzk_9fdYs&quot;&gt;unbelievably cool stop-motion videos of our names&lt;/a&gt; (by the way, she&#39;s the loveliest person and I&#39;m so excited to see her speak at All Day Hey next week). Even more impressive: there were posters for each of our talks, and they were AR-enabled! I managed to get my poster home on the plane, and I&#39;ve framed it by my desk.&lt;/p&gt;
&lt;div class=&quot;two-col&quot;&gt; 
&lt;a href=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/bt-poster.JPG&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/bt-poster.JPG&quot; alt=&quot;My Beyond Tellerrand poster hanging up in the venue. It says &#39;A Beyond Tellerrand Film&#39; and underneath has the title of my talk - &#39;This website is under construction - a love letter to the personal website&#39;. Then it has a series of squares with household objects in, that when you scan it with a special app move to spell my name. Underneath are credits for the conference.&quot; /&gt;&lt;/a&gt;
&lt;video controls=&quot;&quot;&gt;
&lt;source src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/bt-poster.webm&quot; type=&quot;video/webm&quot; /&gt;
&lt;source src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/bt-poster.mp4&quot; type=&quot;video/mp4&quot; /&gt;
A video showing the stop motion animation of my name with household objects.
&lt;/video&gt;
&lt;/div&gt;
&lt;p&gt;Between the talks we had DJ sets from the electric &lt;a href=&quot;https://baldower.com/&quot;&gt;Tobi&lt;/a&gt;, incorporating snippets of our talks. It was quite surreal hearing my own voice played back at me as I sat down after I&#39;d spoken!&lt;/p&gt;
&lt;p&gt;So, that&#39;s my speaker review (100/10 would speak again), but I attended the conference too, so here&#39;s my attendee review: incredible vibes all the way through. I think it&#39;s actually the first multi-day conference where I&#39;ve watched &lt;em&gt;every single talk&lt;/em&gt; - it was that good. Speaking is incredibly exhausting and usually I have to tap out at some point to either go for a walk or chill out somewhere quiet, but the talks were such high quality that I just couldn&#39;t miss them. It&#39;s quite the honour to have spoken on that roster!&lt;/p&gt;
&lt;p&gt;There were art talks: meditation and the art of Japanese calligraphy with Aoi Yamaguchi, psychedelic slogans and incredible murals from Gemma O&#39;Brien, algorithmic art and machine learning with Mario Klingemann, stories of toasters and living as a goat from Thomas Thwaites, and light painting with the charming Hugh Elliott. The last talk by Eike König was more of a career retrospective which perhaps went over my head a little, but it was still something different.&lt;/p&gt;
&lt;div class=&quot;two-col&quot;&gt;
&lt;a href=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/gemma-obrien.JPG&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/gemma-obrien.JPG&quot; alt=&quot;Gemma O&#39;Brien shows the illustrated sick bags she&#39;s done on various flights, all with puns relating to movie titles.&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/thomas-thwaites.JPG&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/thomas-thwaites.JPG&quot; alt=&quot;Thomas Thwaites shows his handmade toaster, which he snuck onto the shelf of John Lewis&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Opening the second day was Dr Emily Anhalt, a clinical psychologist, sharing her tips for being an &amp;quot;emotionally fit&amp;quot; leader - at times what she was talking about resonated so much with me that I got a bit emotional. I&#39;m definitely taking some of her tips away with me to use with my teams.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/emily.JPG&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/emily.JPG&quot; alt=&quot;Emily Anhalt stands in front of a slide titled &#39;Emotional Fitness Survey Questions&#39;. &#39;Do you like to be praised in public or in private? How do you like to receive feedback? How do you like to be cared for or cheered up during a tough time? How would I know if you were feeling overwhelmed? How do you like your birthday to be celebrated?&#39;&quot; /&gt;&lt;/a&gt;
&lt;figcaption&gt;Emily Anhalt&#39;s list of questions that all teams should answer to work together better.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Then we had some excellent typography-related talks from Scott Kellum and Tobias Kunisch, showcasing the importance of dynamic typography on different size screens, and the power of variable fonts. I actually use variable fonts a bit on this site, but I&#39;ve never really explored their full potential!&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/tobias.JPG&quot; alt=&quot;Tobias Kunisch talking about variable weight fonts, and the slides just say &#39;aaaaaaaaaa&#39;&quot; /&gt;
&lt;figcaption&gt;Same.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Joining me in the web dev contingent were my pals Cassie Evans and Michelle Barker, with some brilliant talks on UI animation and modern CSS layout respectively. I get so excited when I see talks like theirs because they remind me what the web is capable of (and that it&#39;s really fun!). Michelle is speaking about Modern CSS Layout at All Day Hey this week, I&#39;ll be there!&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/cassie.JPG&quot; alt=&quot;Cassie Evans stands in front of a slide that says &#39;The web is an infinite and unknowable canvas&#39; - Miriam Suzanne&quot; /&gt;
&lt;figcaption&gt;Cassie shares a lovely quote from Miriam Suzanne&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;All in all, I came away feeling so inspired (and so very, very tired). I can&#39;t wait to go back next year.&lt;/p&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/beyond-tellerrand-2023/coffee.JPG&quot; /&gt;</content>
    </entry>
    
    <entry>
      <title>Painting the whole beetle: an adventure in learning to learn</title>
      <link href="https://localghost.dev/blog/painting-the-whole-beetle-an-adventure-in-learning-to-learn/"/>
      <updated>2023-02-18T00:00:00Z</updated>
      <id>https://localghost.dev/blog/painting-the-whole-beetle-an-adventure-in-learning-to-learn/</id>
      <content type="html">&lt;p&gt;I&#39;m not very good at being bad at things. In fact, I have a track record of giving up on things if I&#39;m not immediately good at it. (So I guess I&#39;m good at giving up on things?)&lt;/p&gt;
&lt;p&gt;Case in point: the piano. There&#39;s an electric piano gathering dust on a shelf in my house which was until recently gathering dust on a stand in the living room. A few years ago I started having lessons with a view to being able to accompany myself/my choir, but it was such a challenge to work through being bad at it. I&#39;m a very musical person but my tiny hands made it very difficult to reach some of the intervals, and, well, I just didn&#39;t put in the practise because it was so frustrating not being automatically good at it. Of course if I&#39;d persevered, I would have been really good by now, but that&#39;s not always how it works in reality.&lt;/p&gt;
&lt;p&gt;A lot of this comes from my teenage years: I went to a very competitive school where getting a B was tantamount to a failure and I grew up thinking I was average at everything. It wasn&#39;t until I left that school and went to a local college for an extra year that I realised that I was actually &lt;em&gt;good at stuff&lt;/em&gt;, and my attitude towards education changed considerably (and I worked hard and did well at both college and university).&lt;/p&gt;
&lt;p&gt;(&amp;quot;Fun&amp;quot; fact: I only went to that college in the first place to study English Language A-Level, because my school didn&#39;t offer it – I was told &amp;quot;the kind of universities our girls like to apply to don&#39;t see it as a proper subject&amp;quot;. ROTATING YIKES EMOJI.)&lt;/p&gt;
&lt;p&gt;As a result, I&#39;ve tended to stick to things I know I&#39;m good at. Singing. Web dev. Cooking and baking. But when something goes wrong – when the cake I&#39;m making for my gran&#39;s birthday doesn&#39;t rise properly and it&#39;s all dense and underbaked true story – I&#39;ll have a meltdown and feel like a complete failure.&lt;/p&gt;
&lt;p&gt;So instead of doing something I might be bad at and feeling frustrated, I&#39;m much more likely to camp out on the sofa and play video games, which I can control the difficulty of if it gets too hard.&lt;/p&gt;
&lt;p&gt;It&#39;s been an uphill struggle to try and unlearn this mindset; to allow myself to be shit at something for a while until I&#39;m good at it. I&#39;m always reminded of the quote from Jake the Dog in Adventure Time:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Dude, suckin’ at something is the first step to being sorta good at something.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He&#39;s not wrong, but also I struggle to internalise that because of the years of schooling that told me if I&#39;m even &lt;em&gt;decent&lt;/em&gt; at something, there&#39;s 20 other people in my class who are a LOT better than I am, and who win the awards at the end of the year.&lt;/p&gt;
&lt;p&gt;In a fit of uncharacteristic optimism I booked myself onto a one-day &amp;quot;&lt;a href=&quot;https://www.city-academy.com/how-to-paint-beginners&quot;&gt;How to paint&lt;/a&gt;&amp;quot; class. It always struck me as something I&#39;d enjoy doing: the physical act of painting is really relaxing. I turned up to this class with a determination to lower my expectations of myself, and had the best time. We spent the morning on watercolours, which I had basically no experience with, and then in the afternoon moved on to acrylics. The extent of my experience with acrylics had been a cheap paint set I was sent as part of a team social where I painted a creepy looking Monzo card mascot in the same pose as the HA HA! BUSINESS meme, but I do remember it being very satisfying.&lt;/p&gt;
&lt;p&gt;I had a good time with the watercolours but when we moved onto the acrylics I was struggling a bit. At the teacher&#39;s suggestion I&#39;d picked quite a difficult reference image: a purpley-green iridescent beetle.&lt;/p&gt;
&lt;p&gt;I got about 30 minutes in, and was really struggling to get the colours right, and the highlights were looking really muddy. The leg was pointing out at the wrong angle, and the places where I hadn&#39;t applied enough paint looked really rough. I said to the teacher that I thought I might have to give up on this and start a new one but she urged me to keep going with it, and see where it took me.&lt;/p&gt;
&lt;p&gt;Two hours later, I had finished my beetle, and it was better than anything I&#39;d ever painted before.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/beetle/beetle.JPG&quot; alt=&quot;A photograph of a paint-covered board with a printed reference picture of two iridescent beetles clipped to it. The top beetle is a bright gold-green mix, and the bottom beetle is a purple and blue mix. Underneath the reference photo, a piece of paper is taped to the board with a painting of the bottom beetle. It is slightly messy, but the colours are accurate and the blending is quite good.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;For me, painting the whole beetle was the first time in a long time I hadn&#39;t given up when things started to get difficult and out of my comfort zone. I persevered. And amazingly, I discovered I was Quite Good. Especially at blending colours together. The finer detail was difficult and I didn&#39;t use enough paint, but the technique was there.&lt;/p&gt;
&lt;p&gt;And it occurred to me: I&#39;ve been painting my own face for 20 years, of course I&#39;m quite good at it.&lt;/p&gt;
&lt;p&gt;I don&#39;t do it nearly as much these days, but in my late teens to my late twenties I&#39;d do the most incredible colourful eyeshadow looks. I even had a makeup blog briefly, though I didn&#39;t persevere with that either! But this is it, this is me putting the time in to learn some base skills that have led to me being able to paint a bit.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/beetle/makeup-2012.jpg&quot; alt=&quot;A photo of me aged 23 with very colourful eyeshadow in shades of purple, blue and green, looking into the camera at an angle.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Me, circa 2012, doing my best Makeup Blogger face. Serious eyeshadow skills though.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I wasn&#39;t always good at it. I can think of numerous occasions where I left the house as a teenager looking like someone had put shoe polish on my binoculars. I&#39;ve made some questionable makeup choices over the years and the execution has not always been &lt;em&gt;on point&lt;/em&gt;, shall we say. But all of this is practise and the blending skills I gained from years of doing my makeup in my bedroom when I was bored have clearly stuck with me.&lt;/p&gt;
&lt;p&gt;At the time it didn&#39;t &lt;em&gt;feel&lt;/em&gt; like an active process of learning, much like when I was first building websites around the age of 10/11. It was a fun thing to do and I didn&#39;t really care about the outcome. I remember struggling to understand why my pictures weren&#39;t showing up on the page I&#39;d made when I put it on GeoCities, even though I&#39;d added a nice &lt;code&gt;&amp;lt;IMG SRC=&amp;quot;C:/Sophie/image.jpeg&amp;quot;&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;And then 14 years later when I started learning Java I marvelled about how naturally coding came to me. When I&#39;d been building websites for 14 years. I&#39;d put in the time! I just couldn&#39;t see it!&lt;/p&gt;
&lt;p&gt;Realistically I know there are very few things one can be naturally good at. Singing is one of those things, but also people who &amp;quot;can&#39;t sing&amp;quot; can learn, and even if you&#39;ve got a naturally good voice it&#39;s important to learn how to use it properly so as not to strain it, learn breathing techniques, learn how to sing from your diaphragm etc. People talk a lot about how talented some folks are, but is it really talent or is it &lt;em&gt;skill&lt;/em&gt;, honed through years and years of hard work?&lt;/p&gt;
&lt;p&gt;It&#39;s also very possible to have an &lt;em&gt;aptitude&lt;/em&gt; for something, but you&#39;ve still got to put in the time to learn it. I have an aptitude for languages and always did well in German and Spanish at school/uni, but I&#39;m not anywhere near fluent in any languages apart from English, because I haven&#39;t put in the time.&lt;/p&gt;
&lt;p&gt;I&#39;m certainly not naturally good at makeup, or baking, or web development. All of these took a lot of time and practise to learn. And I still screw up a lot because you never really stop learning. My eyeliner may look great but you should see the pile of cotton buds I needed to tidy it up.&lt;/p&gt;
&lt;p&gt;I spent a bit of time this morning trying out the watercolour paints I&#39;d bought and it took a lot of convincing myself to keep going and not quit as soon as a line wasn&#39;t neat enough or the paint was blooming (a bit like a tide mark). But I kept going, and I painted an apple, and it&#39;s good for a beginner, and I&#39;m happy that I did it.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/beetle/apple.JPG&quot; alt=&quot;A rough watercolour painting of a red apple which has green bits at the top.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;This post is not a metaphor; I&#39;m not using this painting experience to dish out some life-changing career advice. For me it&#39;s literally about painting. But I know some of you out there will have similar experiences to me, so I guess I wanted to dish out a bit of solidarity and tell you that sometimes with enough determination you can paint the whole beetle too.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Everything should have an API: adventures in trying to automate stuff</title>
      <link href="https://localghost.dev/blog/everything-should-have-an-api-adventures-in-trying-to-automate-stuff/"/>
      <updated>2023-01-24T00:00:00Z</updated>
      <id>https://localghost.dev/blog/everything-should-have-an-api-adventures-in-trying-to-automate-stuff/</id>
      <content type="html">&lt;p&gt;Inspired by &lt;a href=&quot;https://rknight.me/automating-my-now-page/&quot;&gt;Robb Knight&lt;/a&gt; I want to build my own &lt;a href=&quot;https://nownownow.com/about&quot;&gt;/now page&lt;/a&gt;. As a teen I used to use &lt;a href=&quot;https://web.archive.org/web/20040603160236/http://www.codegrrl.com/scripts/phpcurrently/index.php&quot;&gt;PHPCurrently&lt;/a&gt; on my personal website to list what I was listening to, thinking, feeling, even what my MSN display picture was. Here&#39;s an objectively terrible screenshot from peak Evanescence phase, circa 2004.&lt;/p&gt;
&lt;figure&gt;
  &lt;img class=&quot;small&quot; alt=&quot;A screenshot from an old website. It&#39;s a list of statistics about what I&#39;m doing. It says &#39;Currently:
MSN display picture: Amy in a pink dress, with lyrics from Missing.
date: 11th June.
thinking: no more exams!!!!
wearing: bathrobe.
makeup: none.
jewellery: none.
hair: loose.
MSN screenname: Grammar Nazi.
time: 11:30.
feeling: amused.
eating: raisin wheats.
drinking: nothing.
surfing: this thread on AGF. you may need this (link) for some of it.
IMing: no-one.
hating: spelling, grammar and punctuation ignorance.
Powered by PHPCurrently.&quot; src=&quot;https://localghost.dev/img/blog/phpcurrently.png&quot; /&gt;
    &lt;figcaption&gt;
  I have grown up a bit since then, thankfully.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;And just as Robb did, I want to automate as much of it as I possibly could. No matter how many apps I try for tracking books, games, TV etc., I always forget to actually update them. Everything I do requires a sign-in these days, and it&#39;s all internet based, so why shouldn&#39;t I be able to automatically generate a page based on the data these companies have on me?&lt;br /&gt;
In short: they don&#39;t let you access it. Netflix, Nintendo, Kobo, PSN to an extent: there&#39;s no simple way of getting your history out of these apps. And most of the third-party apps that let you track your history, like GameTrack, have no way out.&lt;/p&gt;
&lt;p&gt;At the time of writing this I haven&#39;t cracked it, but I&#39;ll share my findings so far because I&#39;m sure I won&#39;t be the only one who wants to do this.&lt;/p&gt;
&lt;p&gt;If anyone has any suggestions, let me know via Mastodon or email!&lt;/p&gt;
&lt;h2 id=&quot;games&quot; tabindex=&quot;-1&quot;&gt;Games&lt;/h2&gt;
&lt;p&gt;Steam has an API because it was historically for nerds (source: am nerd), but I also play a lot of PlayStation and Nintendo Switch.&lt;/p&gt;
&lt;p&gt;I tried using &lt;a href=&quot;https://rawg.io/&quot;&gt;rawg.io&lt;/a&gt; which connects to your PSN account, but it doesn&#39;t have a personalised API (just an IMDB-style one for game info) and scraping didn&#39;t work because the whole site is a single-page app so the content isn&#39;t there without JavaScript. It also only logs what games you &lt;em&gt;have&lt;/em&gt;, not what you&#39;ve played recently - you have to do that bit manually.&lt;/p&gt;
&lt;p&gt;Nintendo has no API at all, and no public profile pages, so there&#39;s no way of logging what you&#39;ve been playing that I can find.&lt;/p&gt;
&lt;p&gt;Robb scrapes his latest trophies from &lt;a href=&quot;https://psnprofiles.com/&quot;&gt;psnprofiles.com&lt;/a&gt; as a workaround, but I&#39;d really like date-based data.&lt;/p&gt;
&lt;h2 id=&quot;music&quot; tabindex=&quot;-1&quot;&gt;Music&lt;/h2&gt;
&lt;p&gt;This bit is actually pretty easy, even being an Apple Music user. I dusted off my old &lt;a href=&quot;http://last.fm/&quot;&gt;Last.fm&lt;/a&gt; account and downloaded &lt;a href=&quot;https://apps.apple.com/gb/app/marvis-pro/id1447768809&quot;&gt;Marvis Pro&lt;/a&gt;. This is like a layer on top of Apple Music with a better interface and automatic &lt;a href=&quot;http://last.fm/&quot;&gt;Last.fm&lt;/a&gt; scrobbling. (There is an official &lt;a href=&quot;http://last.fm/&quot;&gt;Last.fm&lt;/a&gt; app for iOS which picks up Apple Music plays, but it&#39;s got limitations and you have to scrobble semi-manually.)&lt;/p&gt;
&lt;p&gt;I can then render that data pretty nicely using &lt;a href=&quot;https://github.com/rknightuk/api/blob/main/services/lastfm.js&quot;&gt;Robb&#39;s script&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;books&quot; tabindex=&quot;-1&quot;&gt;Books&lt;/h2&gt;
&lt;p&gt;I stopped using Goodreads in 2020 when I de-Amazonified my life as much as I could, so I haven&#39;t been tracking what I&#39;m reading. I have a Kobo eReader, sometimes I buy books from their shop, and I borrow a lot of books via Libby/OverDrive. OverDrive &lt;a href=&quot;https://developer.overdrive.com/apis&quot;&gt;does apparently have an API&lt;/a&gt;, but I&#39;m not sure how easy it&#39;ll be for an individual to get access. So manually tracking it is.&lt;/p&gt;
&lt;h3 id=&quot;manually-tracking-books&quot; tabindex=&quot;-1&quot;&gt;Manually tracking books&lt;/h3&gt;
&lt;p&gt;Two apps were recommended to me recently: &lt;a href=&quot;https://oku.club/&quot;&gt;Oku&lt;/a&gt; and &lt;a href=&quot;https://librarything.com/&quot;&gt;Library Thing&lt;/a&gt;. (Side note: I love the two completely different aesthetics of these sites: Oku looks like someone copied the CSS from Notion, and Library Thing transports me straight back to 2003.)&lt;/p&gt;
&lt;p&gt;Oku has an RSS feed for every user collection, which means I can easily grab the feed for &amp;quot;currently reading&amp;quot;! The feed also contains metadata like cover image, so I&#39;ll be able to render the books nicely (though quite a few books appear to be missing covers in their database). Its search is pretty bad, but if you feed it an ISBN it gets the book straight away.&lt;/p&gt;
&lt;p&gt;LibraryThing has better book data and it does have APIs, but they&#39;re disabled right now and it&#39;s unclear if they&#39;re going to turn them back on again, so that&#39;s a dead end (at least for now). So Oku it is. Let&#39;s see if I remember to track what I&#39;m reading this time.&lt;/p&gt;
&lt;h2 id=&quot;podcasts&quot; tabindex=&quot;-1&quot;&gt;Podcasts&lt;/h2&gt;
&lt;p&gt;My podcast app of choice is &lt;a href=&quot;https://pocketcasts.com/&quot;&gt;Pocket Casts&lt;/a&gt;, and there appears to be no official way of getting listening data out of that. But there does seem to be an API &lt;em&gt;somewhere&lt;/em&gt;, as some folks have &lt;a href=&quot;https://willschenk.com/articles/2019/reverse_engineering_apis_using_chrome/&quot;&gt;reverse engineered it&lt;/a&gt;. Authentication looks a bit dodgy (not about to store username and password anywhere) so I&#39;ll have to figure out how long that authorization token lasts.&lt;/p&gt;
&lt;p&gt;If anyone knows a better podcast app for gathering this kind of data, let me know!&lt;/p&gt;
&lt;h2 id=&quot;recipes&quot; tabindex=&quot;-1&quot;&gt;Recipes&lt;/h2&gt;
&lt;p&gt;I&#39;d love to log what I&#39;ve been cooking this week! On the weeks when I have my shit together I plan what we&#39;re going to cook using &lt;a href=&quot;https://www.paprikaapp.com/&quot;&gt;Paprika&lt;/a&gt; (where I store all my recipes - about 3000 of them!) - this is ripe for automation. Sadly, the app is pretty much &amp;quot;done&amp;quot; in as much as they don&#39;t add new stuff to it any more, and there&#39;s no API or anything. I&#39;ve considered switching to &lt;a href=&quot;https://pestlechef.app/&quot;&gt;Pestle&lt;/a&gt;, but there&#39;s no MacOS app yet which is unfortunately a dealbreaker for me. So it might have to be a manual process for the time being.&lt;/p&gt;
&lt;p&gt;Paprika &lt;em&gt;does&lt;/em&gt; have a calendar export option, where it provides a calendar feed of planned meals. I wonder if there&#39;s any way of intercepting that.&lt;/p&gt;
&lt;h2 id=&quot;tv-and-films&quot; tabindex=&quot;-1&quot;&gt;TV &amp;amp; films&lt;/h2&gt;
&lt;p&gt;Again, plenty of apps here for manually tracking what I&#39;m watching, but no way of getting the data out that I could see. However, in my search today I found &lt;a href=&quot;https://trakt.tv/&quot;&gt;Trakt&lt;/a&gt; which &lt;em&gt;does&lt;/em&gt; have a public API!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Part of the fun with such data is making it available for anyone to mash up and use on their own site or app. The Trakt API was made just for this purpose. It is very easy to use, you basically call a URL and get some JSON back.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&#39;s what I&#39;m talking about! Why bother tracking what you&#39;re playing/reading/watching if you can&#39;t then do fun stuff with that data? Recommendations are useful enough, but this is the good stuff.&lt;/p&gt;
&lt;p&gt;The interface is confusing at best, and it&#39;s surprisingly fiddly to mark things as watched. It thinks I watched all 9 seasons of Breaking Bad &lt;em&gt;today&lt;/em&gt;, as I marked them as watched in one tap.&lt;/p&gt;
&lt;p&gt;Again, the biggest downside is I have to track manually, but at least that way I don&#39;t accidentally reveal to everyone that I&#39;m three seasons in to Emily in Paris. But ultimately I&#39;m not sure if I&#39;ll be able to stick it out (the app, not Emily in Paris).&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;At this point I can only conclude that I&#39;ve spent longer investigating how I can automate this than I would have done manually updating the /now page once a week. But it&#39;s also a matter of remembering/having the energy to update it. I can guarantee you I&#39;d update it twice and then leave it for several years.&lt;/p&gt;
&lt;p&gt;It frustrates me how easy this would be if all these services had APIs that you could just pull your own data from. If our lives are online now, and our data is helping them personalise their services and make more money through recommendations, the least they could do is provide that data in a consumable format. It&#39;s not exactly feasible to do a monthly &lt;a href=&quot;https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/individual-rights/right-of-access/&quot;&gt;&lt;abbr title=&quot;Data Subject Access Request&quot;&gt;DSAR&lt;/abbr&gt;&lt;/a&gt; to get my Netflix viewing history.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>I miss Twitter</title>
      <link href="https://localghost.dev/blog/i-miss-twitter/"/>
      <updated>2023-01-14T00:00:00Z</updated>
      <id>https://localghost.dev/blog/i-miss-twitter/</id>
      <content type="html">&lt;p&gt;I&#39;m still sad about Twitter.&lt;/p&gt;
&lt;p&gt;I think what has emerged for me since switching to Mastodon is an altogether healthier relationship with social media. I&#39;m not checking it constantly and I&#39;m not obsessing over how my tweets are doing. I can post things without worrying that idiots have saved searches for key words/names and are going to come and shit all over the replies.&lt;/p&gt;
&lt;p&gt;And it feels pathetic to be mourning the loss of a social media site. We know social media is Bad and the algorithm is Bad and it&#39;s all just late stage capitalism Bad Bad Bad. And there are plenty of people on Mastodon celebrating its demise.&lt;/p&gt;
&lt;p&gt;But I miss it. I miss the vibes of tech Twitter, I miss the  community feel that I just haven&#39;t felt on Mastodon in the same way. I&#39;m worried that not having a Twitter presence is going to affect my speaking career and mean I don&#39;t get as many conference invitations. I&#39;m angry that one billionaire man-child was able to take this community away from us.&lt;/p&gt;
&lt;p&gt;There are still quite a few people sticking around Twitter as it clings on for dear life but I just can&#39;t bring myself to go back to a site that&#39;s unbanned some really high-profile transphobes, bigots, misogynists and racists. And a literal human trafficker, it turns out.&lt;/p&gt;
&lt;h3 id=&quot;mastodon-is-fine-i-guess&quot; tabindex=&quot;-1&quot;&gt;Mastodon is fine I guess&lt;/h3&gt;
&lt;p&gt;While it&#39;s been a &lt;em&gt;generally&lt;/em&gt; positive experience so far, IMO Mastodon isn&#39;t quite the paradise that people have made it out to be: as a platform (decentralised though it may be) that was relatively tiny for a long time before suddenly blowing up recently, I&#39;ve encountered some users on there who resent that and think that if you&#39;re new, you shouldn&#39;t have an opinion. In a way I get it: Twitter&#39;s demise was the start of Mastodon&#39;s &lt;a href=&quot;https://en.wikipedia.org/wiki/Eternal_September&quot;&gt;Eternal September&lt;/a&gt;. But let&#39;s not kid ourselves: Mastodon still has mansplainers, it has racists, it has arseholes. It&#39;s just harder for them to find your posts.&lt;/p&gt;
&lt;p&gt;I saw one post about how people should put politics-related content under content warnings (CWs), and when I disagreed (because politics is people&#39;s lived experience) the author told me that I&#39;d only had my account a few weeks and that I should listen to the people who had been there for years. And that anything about Black people being silenced/experiencing racism on Mastodon had been &amp;quot;proven to not be true&amp;quot;. They provided no evidence to back this up - I know, I&#39;m as shocked as you are.&lt;/p&gt;
&lt;p&gt;Notably, in the two months I&#39;ve been on Mastodon my feed has remained predominantly male and white, despite trying to diversify my follows. A lot of people I followed initially have returned to Twitter as their other communities (e.g. Black Twitter, trans Twitter) have remained there.&lt;/p&gt;
&lt;h3 id=&quot;shouting-into-the-void&quot; tabindex=&quot;-1&quot;&gt;Shouting into the void&lt;/h3&gt;
&lt;p&gt;I miss the reach I used to have on Twitter. Talking about issues I cared about, like semantic HTML and mental health, and knowing that lots of people would see it (even if generally I got more engagement on the shitposts).&lt;/p&gt;
&lt;p&gt;Glad as I am that the personal website is having a renaissance – you know how much I love personal websites – for me that fulfils a different function from sites like Twitter. This site is my own space on the internet, but it&#39;s me shouting into the void in a way that Twitter wasn&#39;t.&lt;/p&gt;
&lt;p&gt;Mostly I&#39;m just sad that we lost the community that we had. And I hope we can rebuild it somehow.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>2022: The year in lists</title>
      <link href="https://localghost.dev/blog/2022-the-year-in-lists/"/>
      <updated>2022-12-30T00:00:00Z</updated>
      <id>https://localghost.dev/blog/2022-the-year-in-lists/</id>
      <content type="html">&lt;p&gt;I don&#39;t usually do these end-of-year reflection posts, but at a time where I feel like I&#39;m finally starting to hit flat land again after a year of climbing hills, it seems like a nice thing to do, and a way for me to reflect on my own achievements.&lt;/p&gt;
&lt;p&gt;I&#39;ve spent a &lt;em&gt;lot&lt;/em&gt; of time mulling over the bad things that happened this year so I want to focus on the good stuff.&lt;/p&gt;
&lt;p&gt;For that reason, there won&#39;t be much mention of COVID. I want to make it perfectly clear that I see it as very much still a thing, I got it for the first time this autumn and it completely wiped me out. Now that the post-COVID immunity has worn off somewhat, I&#39;ll once again be wearing masks in crowded places and on public transport well into 2023.&lt;/p&gt;
&lt;p&gt;Skip to bits you care about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#the-year-in&quot;&gt;The year in...&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#career-decisions&quot;&gt;...career decisions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#conferences&quot;&gt;...conferences&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#travel&quot;&gt;...travel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#books&quot;&gt;...books&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#music&quot;&gt;...music&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#video-games&quot;&gt;...video games&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#learning-things&quot;&gt;...learning things&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#blog-posts&quot;&gt;...blog posts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/2022-the-year-in-lists/#the-web&quot;&gt;...the web&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-year-in&quot; tabindex=&quot;-1&quot;&gt;The year in...&lt;/h2&gt;
&lt;h3 id=&quot;career-decisions&quot; tabindex=&quot;-1&quot;&gt;...career decisions&lt;/h3&gt;
&lt;p&gt;Last year was the year I left my job at Monzo and joined a startup, and this year I &lt;a href=&quot;https://localghost.dev/blog/when-going-back-doesn-t-mean-going-backwards/&quot;&gt;did the opposite&lt;/a&gt;. I made some tough decisions and got really burned out, but coming back to Monzo in August was amazing and has been just what I needed to help me understand more about what I want out of my career. Thankfully, I&#39;ve recovered from the burnout I was suffering from earlier this year.&lt;/p&gt;
&lt;h3 id=&quot;conferences&quot; tabindex=&quot;-1&quot;&gt;...conferences&lt;/h3&gt;
&lt;p&gt;This year I found myself speaking at &lt;em&gt;seven&lt;/em&gt; conferences... and attending one!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;March&lt;/strong&gt;, I showed &lt;a href=&quot;https://cityjsconf.org/&quot;&gt;CityJS London&lt;/a&gt; how to build a virtual &amp;quot;piano&amp;quot; with the Web Audio API.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;April&lt;/strong&gt;, attending &lt;a href=&quot;https://heypresents.com/&quot;&gt;All Day Hey&lt;/a&gt; in Leeds made me fall in love with the web all over again.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;May&lt;/strong&gt; I took my virtual piano talk to my first international conference, &lt;a href=&quot;https://www.bejs.io/conf&quot;&gt;BeJS&lt;/a&gt; in Brussels!&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;June&lt;/strong&gt; I spoke at &lt;a href=&quot;https://2022.uxlondon.com/speakers/sophie-koonin/&quot;&gt;UX London&lt;/a&gt; about the importance of clear writing in product development, based on a &lt;a href=&quot;https://incident.io/blog/use-your-words-the-importance-of-clear-writing-product-development&quot;&gt;blog post I&#39;d written for incident.io&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;July&lt;/strong&gt; I opened &lt;a href=&quot;https://skillsmatter.com/conferences/13770-fullstack-exchange-2022#overview&quot;&gt;FullStack eXchange&lt;/a&gt; in London with a keynote about the &lt;a href=&quot;https://skillsmatter.com/skillscasts/17629-sophie-koonin&quot;&gt;history of personal websites&lt;/a&gt;. My laptop completely crashed at the beginning of the talk, so I had to restart my computer while everyone watched...!&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;October&lt;/strong&gt;, from the glorious Barbican Centre in London, I showed the lovely folks of &lt;a href=&quot;https://2022.stateofthebrowser.com/&quot;&gt;State of the Browser 2022&lt;/a&gt; how to &lt;a href=&quot;https://localghost.dev/blog/building-a-website-like-it-s-1999-in-2022/&quot;&gt;build a website like it&#39;s 1999&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;November&lt;/strong&gt; I achieved a career goal of speaking at &lt;a href=&quot;https://ffconf.org/&quot;&gt;FFConf&lt;/a&gt; in Brighton, delivering a &lt;a href=&quot;https://www.youtube.com/watch?v=vGYm9VdfJ8s&amp;amp;list=PLZy5V2JKDfX9afwuEl1NolNpvd0yNWc8E&amp;amp;index=5&quot;&gt;love letter to the personal website&lt;/a&gt;. It was everything I&#39;d hoped for, and I&#39;m so proud of it. If you&#39;ve never been, it&#39;s one of the loveliest communities around.&lt;/li&gt;
&lt;li&gt;And finally in &lt;strong&gt;December&lt;/strong&gt; – the week before Christmas! – I travelled to Málaga in Spain for the inaugural &lt;a href=&quot;https://weyweyweb.com/&quot;&gt;WeyWeyWeb&lt;/a&gt; to play that virtual piano again, &lt;a href=&quot;https://youtu.be/YkKYuQBjmtA&quot;&gt;this time with added Web MIDI&lt;/a&gt;. I very nearly didn&#39;t make it thanks to a combination of train strikes and snow, but I&#39;m glad I did!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At times I was a bit overwhelmed with conference prep, but ultimately I had a great time, and I can&#39;t wait for the conferences that 2023 will bring. (my inbox is always open!)&lt;/p&gt;
&lt;h3 id=&quot;travel&quot; tabindex=&quot;-1&quot;&gt;...travel&lt;/h3&gt;
&lt;p&gt;International travel picked up again this year, with trips to Brussels and Spain for conferences, and Scotland, France and Iceland for holidays.&lt;/p&gt;
&lt;p&gt;Iceland was absolutely incredible, I really can&#39;t recommend it enough. A week of driving around, taking photos and being surrounded by the most incredible landscapes I&#39;ve ever seen.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://localghost.dev/img/blog/2022-recap/skogafoss.webp&quot;&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/2022-recap/skogafoss.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/2022-recap/skogafoss.jpeg&quot; alt=&quot;A photo of Skógafoss, a waterfall in Iceland. The waterfall is wide, set between two moss-covered rocks. There is a lot of spray in the air and the weather is overcast.&quot; /&gt;
&lt;/picture&gt;
&lt;/a&gt;
&lt;figcaption&gt;Skógafoss, Iceland
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/2022-recap/sunset.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/2022-recap/sunset.jpeg&quot; alt=&quot;A bright orange sunset, looking out onto the water with some very dark stretches of beach at the bottom, so dark they are black.&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Sunset at Akranes
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I finally got the Eurostar (to Brussels) for the first time since it&#39;s been at St Pancras and honestly I now see why everyone&#39;s so obsessed with it. It&#39;s SO GOOD.&lt;/p&gt;
&lt;p&gt;I also spent some time in Suffolk with friends, and we went birdwatching at RSBP Minsmere, where we saw an amazing murmuration of starlings!&lt;/p&gt;
&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/2022-recap/murmurations.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/2022-recap/murmurations.jpeg&quot; alt=&quot;A flock of starlings moving together in a mushroom cloud shape, above a marsh. The starlings look like little black specks, almost like insects. There are larger gulls flying around.&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Murmuration at RSPB Minsmere
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id=&quot;books&quot; tabindex=&quot;-1&quot;&gt;...books&lt;/h3&gt;
&lt;p&gt;Last year in an attempt to de-Amazon my life a bit I swapped out my Kindle for a &lt;a href=&quot;https://uk.kobobooks.com/products/kobo-libra-h2o&quot;&gt;Kobo Libra&lt;/a&gt;, which has built-in Overdrive support. So I&#39;ve been enjoying renting ebooks from various local libraries!&lt;/p&gt;
&lt;p&gt;A few of the novels I particularly enjoyed this year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/the-four-winds-the-number-one-bestselling-richard-judy-book-club-pick/9781529054583&quot;&gt;&lt;em&gt;The Four Winds&lt;/em&gt;&lt;/a&gt; by Kristin Hannah&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/seven-husbands-of-evelyn-hugo-the-sunday-times-bestseller/9781398515697&quot;&gt;&lt;em&gt;The Seven Husbands of Evelyn Hugo&lt;/em&gt;&lt;/a&gt; by Taylor Jenkins Reid&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/going-postal-discworld-novel-33/9780552167680&quot;&gt;&lt;em&gt;Going Postal&lt;/em&gt;&lt;/a&gt; by Terry Pratchett&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/the-book-thief/9781909531611&quot;&gt;&lt;em&gt;The Book Thief&lt;/em&gt;&lt;/a&gt; by Markus Zusak&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/if-i-had-your-face-assured-bold-and-electrifying-taylor-jenkins-reid-bestselling-author-of-malibu-rising-9780241986356/9780241986356&quot;&gt;&lt;em&gt;If I Had Your Face&lt;/em&gt;&lt;/a&gt; by Frances Cha&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/the-dictionary-of-lost-words-a-reese-witherspoon-book-club-pick/9781529113228&quot;&gt;&lt;em&gt;The Dictionary of Lost Words&lt;/em&gt;&lt;/a&gt; by Pip Williams&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/the-song-of-achilles-9781408891384/9781408891384&quot;&gt;&lt;em&gt;The Song of Achilles&lt;/em&gt;&lt;/a&gt; by Madeleine Miller&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/how-beautiful-we-were-a-novel-9781838851378/9781838851378&quot;&gt;&lt;em&gt;How Beautiful We Were&lt;/em&gt;&lt;/a&gt; by Imbolo Mbue&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/free-food-for-millionaires/9781801105323&quot;&gt;&lt;em&gt;Free Food For Millionaires&lt;/em&gt;&lt;/a&gt; by Min Jin Lee&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the last month or so I picked up a copy of &lt;a href=&quot;https://www.oreilly.com/library/view/the-staff-engineers/9781098118723/&quot;&gt;The Staff Engineer&#39;s Path&lt;/a&gt; by Tanya Reilly. It&#39;s full of really useful advice and scenarios for anyone who&#39;s either looking to get to Staff, or understand more about what that role entails. Since it&#39;s a work-related book it&#39;s not the kind I tend to devour in an evening, so I&#39;m making my way through it VERY slowly, but it&#39;s definitely a useful one.&lt;/p&gt;
&lt;h3 id=&quot;music&quot; tabindex=&quot;-1&quot;&gt;...music&lt;/h3&gt;
&lt;p&gt;I can&#39;t listen to music with lyrics while I work, so it&#39;s lo-fi beats to relax and/or debug to all the way, but here are some particularly good albums (new and old) that I enjoyed this year. It seems to be 2003 again with some of the new releases and I&#39;m honestly living for it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://carlyraejepsen.lnk.to/TheLoneliestTime&quot;&gt;Carly Rae Jepsen – The Loneliest Time&lt;/a&gt; Album of the year 1000%. If you still think she only did &amp;quot;Call Me Maybe&amp;quot;, do yourself a favour and listen to her last three albums.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wetleg.bandcamp.com/album/wet-leg&quot;&gt;Wet Leg – Wet Leg&lt;/a&gt; Superb mid-00s style indie.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.maggielindemann.com/products/suckerpunch-digital-album&quot;&gt;Maggie Lindemann – SUCKERPUNCH&lt;/a&gt; – Evanescence meets Avril Lavigne meets my teenage self. Makes me feel old, but it&#39;s a vibe nonetheless.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://editors.bandcamp.com/album/ebm&quot;&gt;Editors – EBM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://muna.bandcamp.com/album/muna&quot;&gt;MUNA – MUNA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://caitlinrose.bandcamp.com/album/cazimi&quot;&gt;Caitlin Rose – Cazimi&lt;/a&gt; – her 2013 album The Stand-In is one of my all-time faves&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lacunacoil.com/project/comalies-xx/&quot;&gt;Lacuna Coil – Comalies XX&lt;/a&gt; – rearranged and re-recorded versions of the original. The new version of Swamped is &lt;em&gt;everything&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;video-games&quot; tabindex=&quot;-1&quot;&gt;...video games&lt;/h3&gt;
&lt;p&gt;One thing that didn&#39;t change this year is that I played a shit-ton of video games. Here are some highlights.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.playstation.com/en-gb/games/horizon-forbidden-west/&quot;&gt;Horizon: Forbidden West&lt;/a&gt; – Horizon: Zero Dawn is one of my all-time favourites, and this didn&#39;t disappoint! The world-building is incredible, it&#39;s absolutely stunning, and I love hunting around for datapoints to discover even more lore.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tunicgame.com/&quot;&gt;Tunic&lt;/a&gt; – lovely Zelda-esque game with some REALLY creative puzzles. Really loved this one.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playwonderlands.2k.com/&quot;&gt;Tiny Tina&#39;s Wonderlands&lt;/a&gt; – based on my favourite Borderlands 2 DLC, Tiny Tina&#39;s Assault on Dragon Keep, this was a delightful (and hilarious) D&amp;amp;D-themed action game. Borderlands remains the only FPS-style game I&#39;ve ever enjoyed. Disappointing DLC that I didn&#39;t even bother with, but I got a lot of hours out of this game!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cult-of-the-lamb.com/&quot;&gt;Cult of the Lamb&lt;/a&gt; was great fun for a while, but also had some serious bugs that made the game really frustrating (these have been patched now). It also &lt;em&gt;finally&lt;/em&gt; got me to like roguelikes, which resulted in me finally playing...&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.supergiantgames.com/games/hades/&quot;&gt;Hades&lt;/a&gt;. I&#39;m pretty bad at it, but everything about this game rules.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bethesda.net/en/game/deathloop&quot;&gt;Deathloop&lt;/a&gt; was a lot of fun, even if I did enable a LOT of accessibility settings! Fascinating story, great setting, and very clever puzzles.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.somethingwemade.se/toem/&quot;&gt;TOEM: A photo adventure&lt;/a&gt; which was extremely cute and I loved it&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nintendo.co.uk/Games/Nintendo-Switch-download-software/Carto-1859331.html&quot;&gt;Carto&lt;/a&gt; – wonderful little indie game&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chicorygame.com/&quot;&gt;Chicory: A Colorful Tale&lt;/a&gt; – a lovely allegory for depression and making the world seem more beautiful again.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stray.game/&quot;&gt;Stray&lt;/a&gt; – another vote for beautiful world-building and lovely storytelling.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ea.com/en-gb/games/it-takes-two&quot;&gt;It Takes Two&lt;/a&gt; – hooray, finally a couch co-op game! The story was absolute dogshit and we hated everyone in it, but the gameplay was excellent and made up for it all. Really, really creative co-op.&lt;/li&gt;
&lt;li&gt;Shamefully I&#39;ve only just started &lt;a href=&quot;https://returntomonkeyisland.com/&quot;&gt;Return to Monkey Island&lt;/a&gt;, but I&#39;m loving it so far obviously, even if the new art style is a little jarring and I miss being able to make Guybrush say &amp;quot;I&#39;m not picking that up.&amp;quot;&lt;/li&gt;
&lt;li&gt;I&#39;ve also spent the last few weeks replaying Skyrim – 11 years later! – and it still holds up. Incredible. You could probably run it on an electric screwdriver at this point. (Still buggy as shit though, even on PS5)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;ve noticed that game devs have been adding a lot more accessibility settings to games in the last few years. Accessibility isn&#39;t just about being able to play games if you&#39;re disabled, it&#39;s about &lt;em&gt;everyone being able to enjoy the game&lt;/em&gt;. I&#39;m... aggressively okay at video games, and so for games like Hades and Deathloop that have really challenging combat, the only way I can get through it without dying over and over and over again at the same place (and inevitably rage-quitting) is to turn the difficulty down. I don&#39;t always want to play on easy mode, though. Lots of games now have more granular accessibility settings, so you can turn down the damage you receive, or make it so that you get a little stronger every time you die. This means I don&#39;t feel like I&#39;m walking through the game with no challenge at all, I can still enjoy the story, and play it at a level of challenge that suits me.&lt;/p&gt;
&lt;h3 id=&quot;learning-things&quot; tabindex=&quot;-1&quot;&gt;...learning things&lt;/h3&gt;
&lt;p&gt;Some of the things I learned this year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how to create shadow effects with CSS &lt;code&gt;perspective&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;how to use the Web MIDI API to hook up a MIDI controller to the browser&lt;/li&gt;
&lt;li&gt;basic music production – recording, mixing and mastering&lt;/li&gt;
&lt;li&gt;about the &lt;a href=&quot;https://moderncss.dev/practical-uses-of-css-math-functions-calc-clamp-min-max/#clamp&quot;&gt;CSS function &lt;code&gt;clamp()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how to make &lt;a href=&quot;https://www.atsukoskitchen.com/japanese-cooking-classes/&quot;&gt;okonomiyaki and takoyaki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;blog-posts&quot; tabindex=&quot;-1&quot;&gt;...blog posts&lt;/h3&gt;
&lt;p&gt;I wrote a whopping SIX posts this year! It may not sound that much, but it&#39;s a record since I&#39;ve had this site. I&#39;m hoping I can keep momentum going into next year.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/start-at-the-beginning-the-importance-of-learning-the-basics/&quot;&gt;Start at the beginning: the importance of learning the basics&lt;/a&gt; – frustrated with the number of people advocating for skipping learning HTML/CSS and just doing React/Tailwind, I wrote this article about why that&#39;s a terrible idea.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/burnout-a-cautionary-tale-and-a-plea-to-take-a-break/&quot;&gt;Burnout, a cautionary tale (and a plea to take a break)&lt;/a&gt; – I&#39;d taken a break from work, as I was in a bad place and completely exhausted.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/when-going-back-doesn-t-mean-going-backwards/&quot;&gt;When going back doesn&#39;t mean going backwards&lt;/a&gt; – the successor to the previous post. Going back to my old job, and feelings about everything that had happened.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/everything-i-googled-in-a-week-as-a-senior-software-engineer/&quot;&gt;Everything I googled in a week as a senior software engineer&lt;/a&gt; – an updated version of the original I wrote in 2019, to show that I still google everything!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/building-a-website-like-it-s-1999-in-2022/&quot;&gt;Building a website like it&#39;s 1999, in 2002&lt;/a&gt; – the written version of the talk I gave at State of the Browser this year. It went pretty viral a few days ago as someone posted it on Hacker News, which was both cool and terrifying.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/preparing-for-conferences/&quot;&gt;Preparing for conferences&lt;/a&gt; – a summary of my process for preparing to give a conference talk, from talk preparation to delivery.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-web&quot; tabindex=&quot;-1&quot;&gt;...the web&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I left Twitter for Mastodon, and &lt;a href=&quot;https://social.lol/@sophie/109597731355994610&quot;&gt;I&#39;m not actually sure I like it more than Twitter&lt;/a&gt;. But I don&#39;t want to go back to a platform that&#39;s unbanned some of the most detestable people in recent history.&lt;/li&gt;
&lt;li&gt;I joined &lt;a href=&quot;https://omg.lol/&quot;&gt;omg.lol&lt;/a&gt;, and you should too. Until 1 Jan 2023, it&#39;s $5 a &lt;em&gt;year&lt;/em&gt; for a pastebin (paste.lol), URL shortener (url.lol), Mastodon instance (social.lol), email forwarder (@omg.lol) and soon a weblog (weblog.lol)! I now have a fancy &lt;a href=&quot;https://sophie.omg.lol/&quot;&gt;&amp;quot;web card&amp;quot;&lt;/a&gt; I can link people to if they want to get in touch. It&#39;s just lovely vibes all round.&lt;/li&gt;
&lt;li&gt;It feels like there&#39;s been a bit of a personal website resurgence, which is lovely – especially considering the topics of the talks I&#39;ve given this year! I gave this site a bit of a facelift, with five new themes including some very silly ones. I also moved it to &lt;a href=&quot;https://neocities.org/&quot;&gt;neocities&lt;/a&gt; and joined some webrings because WHY NOT.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;there-you-have-it&quot; tabindex=&quot;-1&quot;&gt;There you have it &lt;!-- omit in toc --&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s been nice to spend some time reflecting on the good things that happened this year despite all the bad. I&#39;m hoping to keep the conference momentum going, so if you know any that are looking for speakers, let me know! Here&#39;s hoping 2023 brings new opportunities and more personal website joy.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Preparing for conferences</title>
      <link href="https://localghost.dev/blog/preparing-for-conferences/"/>
      <updated>2022-12-14T00:00:00Z</updated>
      <id>https://localghost.dev/blog/preparing-for-conferences/</id>
      <content type="html">&lt;p&gt;I&#39;ve been speaking at conferences and meetups on and off for nearly five years now, and a few people have asked me what the process is for preparing a talk. So I thought I&#39;d share how I approach it.&lt;/p&gt;
&lt;p&gt;For me, the talk process is a circle:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/circle-of-talks.png&quot; alt=&quot;A circular flow diagram. The top box says &amp;quot;YEAH LET&#39;S DO A TALK!&amp;quot;, which flows to a box that says &amp;quot;Oh no, I have to prepare the talk.&amp;quot;. That box flows to one that says &amp;quot;This is the worst. I hate this. Why did I agree to this?&amp;quot;. The one after that says &amp;quot;Okay! Ready! Let&#39;s do this!&amp;quot;, and the box after that says &amp;quot;THAT WAS AMAZING! I want to do it again!&amp;quot;. That box points back round to the first box, in a circle.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;It&#39;s the circle of talks.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;It repeats around and around forever. There is &lt;em&gt;always&lt;/em&gt; a point at which I hate my talk and wish I&#39;d never done it, but there is also always a point where I&#39;m having the time of my life. Giving talks is great fun, but it&#39;s also hard work.&lt;/p&gt;
&lt;p&gt;(As a sort-of aside: that hard work is why you should never speak at a conference that isn&#39;t at &lt;em&gt;least&lt;/em&gt; paying for your travel/accommodation or taking you to dinner. Your time is worth money.)&lt;/p&gt;
&lt;p&gt;I&#39;ll break my process down into a set of steps, but the &lt;abbr title=&quot;too long; didn&#39;t read&quot;&gt;tl;dr&lt;/abbr&gt; is: &lt;strong&gt;practise&lt;/strong&gt;. Your audience deserve to watch a talk that you&#39;ve rehearsed and aren&#39;t just making up on the spot. Not only will it show if you haven&#39;t practised, you&#39;re much more likely to run over time as well. The practise part isn&#39;t optional!&lt;/p&gt;
&lt;p&gt;The timelines are super rough, but generally I&#39;d start around 2-2.5 months before the talk. I&#39;ve done talks in shorter timeframes than that, but I wouldn&#39;t recommend it – taking longer means I don&#39;t have to dedicate all of my free time to it, I can space the work out a lot more.&lt;/p&gt;
&lt;h2 id=&quot;initial-planning-about-8-10-weeks-before-the-conference&quot; tabindex=&quot;-1&quot;&gt;Initial planning: about 8-10 weeks before the conference&lt;/h2&gt;
&lt;p&gt;To begin with: identify the overarching theme(s) of the talk. What are the main points I want to convey? No more than 3 or 4.&lt;/p&gt;
&lt;p&gt;I make short-form notes for content ideas – bullet points or post-it notes – jotting down any ideas that come to mind, and sorting them into themes afterwards.&lt;/p&gt;
&lt;p&gt;Previously, I&#39;ve used notecards blue-tacked to the wall with ideas on, so I can shuffle them around and group them.&lt;/p&gt;
&lt;p&gt;At this point, I&#39;ll also do research. Depending on the topic, I might read blog posts, articles, I even read someone&#39;s PhD thesis once. I might pull out some good quotes, or just write down some general ideas from those.&lt;/p&gt;
&lt;h2 id=&quot;content-about-6-8-weeks-before&quot; tabindex=&quot;-1&quot;&gt;Content: about 6-8 weeks before&lt;/h2&gt;
&lt;p&gt;Taking my notes, and writing more detail for each point in a Notion doc. What do I have to say about each of these ideas? Sometimes I spot how they flow into each other, or I might find that one of them doesn&#39;t fit with the others any more after I&#39;ve written some blurb.&lt;/p&gt;
&lt;p&gt;I treat it a bit like a blog post at this point, writing as if I&#39;m talking to someone. It helps me to solidify my ideas as a talk rather than just random sentences.&lt;/p&gt;
&lt;h2 id=&quot;slides-about-4-6-weeks-before&quot; tabindex=&quot;-1&quot;&gt;Slides: about 4-6 weeks before&lt;/h2&gt;
&lt;p&gt;Depending on my needs, I&#39;ll either use Keynote or Google Slides. I prefer Keynote, but if I need to be able to switch to a browser quickly, Google Slides is better. I used Reveal once which was useful for code and embeds, but it took me ages to build the slides so I probably won&#39;t use it very often.&lt;/p&gt;
&lt;p&gt;The first version of the slides always has the notes containing the full sentences from the content planning stage, because I&#39;ll use these for the first few run-throughs.&lt;/p&gt;
&lt;p&gt;If my talk needs any code snippets, I&#39;ll screenshot them from VSCode so they look pretty (or, if I&#39;m using Reveal, I&#39;ll put them straight in the slides).&lt;/p&gt;
&lt;p&gt;This part is also about editing, editing, editing. Especially for shorter talks, it&#39;s as much about what you leave out as it is what you put in.&lt;/p&gt;
&lt;p&gt;Not gonna lie: at this point I start to hate my talk. I&#39;ll come around to it again, but right now I&#39;ll be fed up with it and full of regret. It&#39;s really hard work, and I find making slides pretty tedious. Prepare to have some moments like this – it will get better, though!&lt;/p&gt;
&lt;h2 id=&quot;run-throughs-from-3-4-weeks-before&quot; tabindex=&quot;-1&quot;&gt;Run-throughs: from 3-4 weeks before&lt;/h2&gt;
&lt;p&gt;Early on, I&#39;ll run my slides through with my husband who&#39;s usually pretty good at spotting things that don&#39;t fit, or things I can leave out. I always recommend doing a run-through with a partner, friend or colleague!&lt;/p&gt;
&lt;p&gt;I sometimes run talks at work as well – we have a weekly slot for engineering talks that anyone can give. If you don&#39;t have that, perhaps you could introduce it at your company?&lt;/p&gt;
&lt;p&gt;I&#39;ll do three or four run-throughs a week, after work, where I shut myself in our little office and just deliver the talk to nobody. When we have a dog staying, it&#39;s a bit easier as I can give the talk to him. (He&#39;s never particularly interested in what I have to say, though.)&lt;/p&gt;
&lt;h2 id=&quot;the-week-of-the-conference&quot; tabindex=&quot;-1&quot;&gt;The week of the conference&lt;/h2&gt;
&lt;p&gt;I don&#39;t tend to practise my talk in the days leading up to the conference. Often it&#39;s because I&#39;m travelling or busy in the evening with a speakers&#39; dinner. But I also think it&#39;d just stress me out. By the time the conference arrives, I&#39;ve practised it enough that I feel happy with it.&lt;/p&gt;
&lt;h2 id=&quot;delivering-the-talk&quot; tabindex=&quot;-1&quot;&gt;Delivering the talk&lt;/h2&gt;
&lt;p&gt;I&#39;m not going to go into a huge amount of detail here – the &lt;a href=&quot;https://localghost.dev/blog/things-experienced-speakers-wish-they-d-known&quot;&gt;post I wrote when I was preparing for my first conference in 2018&lt;/a&gt; covers a lot of that ground – but the main thing I&#39;ve learned is that I get super nervous as I&#39;m waiting to go on stage, and then when I get on stage it just goes away and I have a great time.&lt;/p&gt;
&lt;p&gt;If you&#39;ve practised enough, you shouldn&#39;t need to stare at your speaker notes – just glance at them for a prompt. Sometimes you&#39;ll find you won&#39;t need to look at them at all.&lt;/p&gt;
&lt;p&gt;Afterwards, you&#39;ll probably feel amazing and want to do more talks, and thus the cycle starts again. (Conference organisers: I&#39;m at my most suggestible in the week after I&#39;ve just given a talk.)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Building a website like it&#39;s 1999... in 2022</title>
      <link href="https://localghost.dev/blog/building-a-website-like-it-s-1999-in-2022/"/>
      <updated>2022-10-23T00:00:00Z</updated>
      <id>https://localghost.dev/blog/building-a-website-like-it-s-1999-in-2022/</id>
      <content type="html">&lt;p&gt;&lt;strong&gt;Edit May 2023:&lt;/strong&gt; the Bridgy integration with Twitter no longer works due to Twitter shutting off API access so some functionality won&#39;t be working any more!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This is a written version of a talk I gave at State of the Browser 2022 in October 2022!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Motion warning&lt;/strong&gt;: This page contains quite a few animations, but if you have reduced motion turned on, they won&#39;t play.&lt;/p&gt;
&lt;h2 id=&quot;the-web-used-to-be-weirder&quot; tabindex=&quot;-1&quot;&gt;The web used to be weirder&lt;/h2&gt;
&lt;p&gt;I&#39;m on a bit of a mission this year to bring back the spirit of the old web. The creativity and flair of the late 90s and early 2000s. Back then, there were no rules – you put whatever you wanted on a webpage, because it was your space to do as you please.&lt;/p&gt;
&lt;p&gt;And for a whole generation of internet users, having a website was the cool thing to do. It&#39;s just what you did back then. We&#39;re talking pre-social media, pre-web 2.0 – the good old fashioned static personal home page.&lt;/p&gt;
&lt;p&gt;Sites like Geocities, Angelfire, Tripod and Expage offered free static hosting for all, and the number of personal websites boomed. Some hosts offered drag-and-drop website builders so you didn&#39;t even have to learn HTML.&lt;/p&gt;
&lt;p&gt;We might look back on these websites now and laugh – they look ridiculous compared to the sleek and minimalist sites we&#39;re used to nowadays. But I actually think we&#39;ve gone too far in the other direction, and now so many websites look the same. These old personal websites were a reflection of yourself.&lt;/p&gt;
&lt;p&gt;Some of these websites were for family to share photos and updates...&lt;/p&gt;
&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/geocities1.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/geocities1.png&quot; alt=&quot;A brightly coloured website that says &#39;Welcome to Tom &amp; Sherry&#39;s Proud Grandparents page. The Proud Grandparents page was created to show pictures of our grandchildren to family and friends, and an occasional Web surfer. The grandkids, our pride and joy, and their parents have made us very proud. Okay, let&#39;s see the pictures!&#39;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;a href=&quot;https://geocities.restorativland.org/Heartland/Ridge/1217/&quot;&gt;The Proud Grandparents Page&lt;/a&gt;
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;...while others were full of graphics to share and use on your own site...&lt;/p&gt;
&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/geocities2.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/geocities2.png&quot; alt=&quot;Lisa&#39;s graphics&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;a href=&quot;https://www.oocities.org/siliconvalley/haven/1520/&quot;&gt;Lisa&#39;s Graphics&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;...and some were fansites. Look at those frames! I got this screenshot mid-&lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt;-scroll as well.&lt;/p&gt;
&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/geocities3.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/geocities3.png&quot; alt=&quot;A Final Fantasy fansite&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;a href=&quot;https://www.oocities.org/hcdohl/&quot;&gt;Mognet Central&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I played a game a couple of years ago called &lt;a href=&quot;https://www.hypnospace.net/&quot;&gt;Hypnospace Outlaw&lt;/a&gt;, which is a completely bonkers game where you&#39;re a moderator of a version of the 90s web that you access in your sleep. The homepages in this game were directly inspired by Geocities websites (there&#39;s a &lt;a href=&quot;https://noclippodcast.net/episodes/2021/5/22/noclip-pocket-e42-big-winrar-energy-hypnospace-outlaw&quot;&gt;really good episode of Noclip&lt;/a&gt; about it) and it made me so nostalgic. I really recommend it if you haven&#39;t played it already! It really captures the spirit of the time – the personality and weirdness that made these sites so special.&lt;/p&gt;
&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/hypnospace.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/hypnospace.png&quot; alt=&quot;A screenshot from the video game Hypnospace Outlaw, with a website called &#39;Linda&#39;s Library of Weird&#39;.&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;a href=&quot;https://www.hypnospace.net/&quot;&gt;Hypnospace Outlaw&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&quot;let-s-bring-back-the-weird&quot; tabindex=&quot;-1&quot;&gt;Let&#39;s bring back the weird&lt;/h2&gt;
&lt;p&gt;I&#39;d love to see this spirit return today – the experimental and fun side of the web. My goal is to show you how we can be just as creative today but using &lt;strong&gt;modern and accessible methods&lt;/strong&gt;. Because, as fun as they were, old websites were a &lt;em&gt;nightmare&lt;/em&gt; for accessibility. We didn&#39;t really use semantic HTML, we used tables for &lt;em&gt;layouts&lt;/em&gt; (instead of, y&#39;know, tabular data), everything was constantly flashing and moving. Luckily for us, the modern web allows us to be just as creative while still considering the user at the other end of the browser.&lt;/p&gt;
&lt;p&gt;So naturally, I built a &lt;a href=&quot;https://sophieswebsite1999.neocities.org/&quot;&gt;90s-style website&lt;/a&gt;, with some of my favourite old web tropes. I used as much modern HTML, CSS and JS as I could. Let&#39;s take a look through some of the features and how we might recreate them!&lt;/p&gt;
&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/site.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/site.png&quot; alt=&quot;A screenshot of my 90s-style website&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;a href=&quot;https://sophieswebsite1999.neocities.org/&quot;&gt;Sophie&#39;s Homepage&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;animated-gi-fs&quot; tabindex=&quot;-1&quot;&gt;Animated GIFs&lt;/h2&gt;
&lt;p&gt;GeoCities sites were absolutely littered with GIFs. Flames, construction workers, dividers, even animated bullet points. Animations were a lot of fun, and almost an art form to squeeze so much into such a tiny filesize.&lt;/p&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/cameronsworld.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/cameronsworld.png&quot; alt=&quot;A screenshot of cameronsworld.net featuring many space-themed GIFs&quot; /&gt;
&lt;/picture&gt;
&lt;p&gt;This is a screenshot from &lt;a href=&quot;https://cameronsworld.net/&quot;&gt;cameronsworld.net&lt;/a&gt;, which is a beautiful archive of GeoCities GIFs, and an artwork in itself.&lt;/p&gt;
&lt;p&gt;Nowadays it&#39;s easier than ever to put animations on our sites, whether that&#39;s still the humble GIF (internet is so much faster these days), more modern formats like &lt;code&gt;webm&lt;/code&gt; and &lt;code&gt;gifv&lt;/code&gt;, or even SVG animation with CSS or libraries like GreenSock. But we can do better still.&lt;/p&gt;
&lt;p&gt;The standard code to include an image hasn&#39;t changed much since the olden days:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IMG&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;SRC&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flames.gif&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;picture&gt;
    &lt;source srcset=&quot;https://localghost.dev/img/geocities/flames.gif&quot; media=&quot;(prefers-reduced-motion: no-preference)&quot; /&gt;
     &lt;img src=&quot;https://localghost.dev/img/geocities/static/flames.png&quot; alt=&quot;Animated flames&quot; /&gt;
&lt;/picture&gt;
&lt;p&gt;In those days, of course, we wrote all our HTML in capitals because that was what you did for some reason. XHTML, maybe? Anyway, the consequence of this is that everyone sees the GIF whether they like it or not. For people with epilepsy, vestibular disorders, or anything where motion causes sickness, autoplaying GIFs are a big problem. Luckily, wcan fix this today, with something we didn&#39;t have back then!&lt;/p&gt;
&lt;h3 id=&quot;the-prefers-reduced-motion-media-query&quot; tabindex=&quot;-1&quot;&gt;The &lt;code&gt;prefers-reduced-motion&lt;/code&gt; media query&lt;/h3&gt;
&lt;p&gt;Using this media query, we can only play the GIF if the user doesn&#39;t have reduced motion turned on on their computer – so everyone can enjoy our trash website, regardless of their access needs.&lt;/p&gt;
&lt;h3 id=&quot;harnessing-media-queries-with-the-picture-element&quot; tabindex=&quot;-1&quot;&gt;Harnessing media queries with the &lt;code&gt;picture&lt;/code&gt; element&lt;/h3&gt;
&lt;p&gt;The HTML5 &lt;code&gt;picture&lt;/code&gt; element allows us to specify an image, and then potential alternative sources for it.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;underconstruction.gif&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-reduced-motion: no-preference)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
     &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;underconstruction.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Under construction&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above code snippet, we&#39;ve got the &lt;code&gt;img&lt;/code&gt; tag as before, but this time it&#39;s showing a &lt;em&gt;still&lt;/em&gt; version of the GIF, which I made by opening the GIF in Preview, pulling out the first frame and saving it as a PNG. The &lt;code&gt;source&lt;/code&gt; tag contains the URL of the GIF, and will only kick in if its &lt;code&gt;media&lt;/code&gt; attribute is satisfied. So if you don&#39;t have reduced motion enabled, the source of the image will be replaced by the animated GIF version. Magic!&lt;/p&gt;
 &lt;picture&gt;
    &lt;source srcset=&quot;https://localghost.dev/img/geocities/consbar.gif&quot; media=&quot;(prefers-reduced-motion: no-preference)&quot; /&gt;
     &lt;img src=&quot;https://localghost.dev/img/geocities/static/consbar.png&quot; alt=&quot;Under construction&quot; /&gt;
&lt;/picture&gt;
&lt;h2 id=&quot;text-effects&quot; tabindex=&quot;-1&quot;&gt;Text effects&lt;/h2&gt;
&lt;p&gt;Remember &lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt;? It made text scroll across the screen, like ticker tape.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;marquee-wrapper&quot;&gt;&lt;span class=&quot;marquee&quot;&gt;Wheee!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Or those of you with Netscape would have had the infamous &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; tag, which makes text blink in and out of view...&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;blink&quot;&gt;This is awful&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Ultimately, text effects like this don&#39;t belong in body text. Even if you don&#39;t have any access needs to speak of, it makes reading text really hard. There&#39;s a good reason they were both deprecated.&lt;/p&gt;
&lt;p&gt;Instead, I thought, why not have fun with headers? In days of yore we&#39;d make cool text-based headers in whatever graphics programs we could get our hands on – or even just MS Word. Text with flames, rainbow fonts, you name it.&lt;/p&gt;
&lt;p&gt;These days we can recreate this magic using CSS instead of using an image! And the great news is, because it&#39;s normal text with CSS doing the heavy lifting, it&#39;s still totally accessible.&lt;/p&gt;
&lt;p&gt;For my next trick, I&#39;m drawing inspiration from an OG 90s classic: Microsoft WordArt.&lt;/p&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/wordart-99.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/wordart-99.jpeg&quot; alt=&quot;MS WordArt selection gallery&quot; /&gt;
&lt;/picture&gt;
&lt;h3 id=&quot;word-art-but-make-it-css&quot; tabindex=&quot;-1&quot;&gt;WordArt, but make it CSS&lt;/h3&gt;
&lt;p&gt;While not strictly from the 90s web, WordArt for me harks back to the aesthetic of 90s maximalism, and definitely fits aesthetically with what I&#39;m trying to do.&lt;/p&gt;
&lt;p&gt;I&#39;m going to show you how to recreate two of my classic favourite WordArt styles using modern CSS.&lt;/p&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/wordart-purple.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/wordart-purple.png&quot; alt=&quot;The word &#39;wordart&#39; in Impact font, skewed to point up and to the right, filled in with a purple gradient and a light purple drop shadow&quot; /&gt;
&lt;/picture&gt;
&lt;h4 id=&quot;gradient-fill-text-with-background-clip&quot; tabindex=&quot;-1&quot;&gt;Gradient-fill text with &lt;code&gt;background-clip&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;You can&#39;t colour text with a gradient (yet) in CSS, but you &lt;em&gt;can&lt;/em&gt; give an element a gradient fill &lt;em&gt;background&lt;/em&gt;. Using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/background-clip&quot;&gt;&lt;code&gt;background-clip&lt;/code&gt;&lt;/a&gt; property we can control where the background shows. Specifically, we can set &lt;code&gt;background-clip: text&lt;/code&gt; to make the background only show wherever there&#39;s text in the element.&lt;/p&gt;
&lt;p&gt;Then, if we make the actual text transparent, only the gradient background will show through.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;183deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #6000CA 10%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #CA00CD 70%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;background-clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;-webkit-background-clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Impact&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;purple-wordart-base&quot;&gt;WordArt&lt;/p&gt;
&lt;p&gt;Pretty!&lt;/p&gt;
&lt;p&gt;Then let&#39;s add a &lt;code&gt;transform&lt;/code&gt; property to make it look a bit more like the real deal.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;skewY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-8deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scaleY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1.3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scaleX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;purple-wordart-base purple-wordart-skewed&quot;&gt;WordArt&lt;/p&gt;
&lt;h4 id=&quot;adding-the-drop-shadow&quot; tabindex=&quot;-1&quot;&gt;Adding the drop shadow&lt;/h4&gt;
&lt;p&gt;Now we need to add the light purple drop shadow. But if we try to use the &lt;code&gt;text-shadow&lt;/code&gt; property, it shows up on top of the text!&lt;/p&gt;
&lt;p class=&quot;purple-wordart-base purple-wordart-skewed purple-wordart-text-shadow&quot;&gt;WordArt&lt;/p&gt;
&lt;p&gt;This is because we&#39;re really looking at the background – the actual text is transparent and sitting on top. If I change the colour of the text to the body colour, you&#39;ll see what I mean:&lt;/p&gt;
&lt;p class=&quot;purple-wordart-base purple-wordart-skewed purple-wordart-text-shadow purple-wordart-text-black&quot;&gt;WordArt&lt;/p&gt;
&lt;p&gt;To get around this, we&#39;ll need a wrapper element that contains the shadow, so it appears behind the text-shaped gradient background.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;purple-wordart-wrapper&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;purple-wordart&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;WordArt&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&#39;ll add a &lt;strong&gt;drop-shadow filter&lt;/strong&gt; to the wrapper element. This adds a drop-shadow the same shape as the element&#39;s children, and because the background is clipped to the text in the child &lt;code&gt;span&lt;/code&gt;, the drop-shadow will follow that shape too!&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.wordart-wrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;drop-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;2px 2px 0px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;130&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 140&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 251&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result:&lt;/p&gt;
&lt;span class=&quot;purple-wordart-wrapper&quot;&gt;
&lt;span class=&quot;purple-wordart-base purple-wordart-skewed&quot;&gt;WordArt&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Uncanny!&lt;/p&gt;
&lt;p&gt;For my second WordArt recreation, I&#39;m bringing back my old childhood favourite – the rainbow one. I remember using this one all over my primary school homework.&lt;/p&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/wordart-rainbow.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/wordart-rainbow.png&quot; alt=&quot;MS Wordart screenshot, the word &#39;WordArt&#39; written in rainbow gradient text with a grey 3D shadow.&quot; /&gt;
&lt;/picture&gt;
&lt;p&gt;We&#39;ve got another gradient fill here, so we&#39;ll use &lt;code&gt;background-clip: text&lt;/code&gt; again to get the same effect, and chuck on a &lt;code&gt;transform&lt;/code&gt; to get the right shape.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  90deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  #9c00ff&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  #ff0000&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  #ff8800&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  #ffff00&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  #02be02&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  #0000ff&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  #4f00ff&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  #9c00ff
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;background-clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;-webkit-background-clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Arial Black&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scaleY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1.5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scaleX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.6&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;transform-origin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class=&quot;rainbow constrain-width&quot;&gt;WordArt&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We&#39;ll use a wrapper element to create the shadow again, but this time it&#39;s a little different. This has more of a 3D effect, where the shadow kind of flattens and goes to the left, as if we&#39;re looking at the WordArt from the front.&lt;/p&gt;
&lt;p&gt;CSS can do that!&lt;/p&gt;
&lt;h4 id=&quot;getting-some-perspective&quot; tabindex=&quot;-1&quot;&gt;Getting some perspective&lt;/h4&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.wrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Arial Black&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;perspective&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 150px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;perspective-origin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bottom center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use the &lt;code&gt;perspective&lt;/code&gt; property to put us into a kind of &amp;quot;3D mode&amp;quot;. It tells the browser, &amp;quot;act as though I&#39;m standing this far away from the element&amp;quot;. In our case, 150px.&lt;/p&gt;
&lt;p&gt;We then set the &lt;code&gt;perspective-origin&lt;/code&gt; property to determine what position we&#39;re looking at the element from. I want it to seem like we&#39;re in front of it, at the bottom.&lt;/p&gt;
&lt;p&gt;What this will do is change the way that transformations apply to the element, taking into account the perspective to manipulate it along the Z-axis as well as X and Y.&lt;/p&gt;
&lt;p&gt;To create a shadow effect I&#39;ll target the &lt;code&gt;wrapper::before&lt;/code&gt; pseudoelement, and set its content to &amp;quot;WordArt&amp;quot; to mirror the text. This will make the &amp;quot;shadow&amp;quot; text appear behind the rainbow gradient. Then I&#39;ll apply some transformations to skew the &amp;quot;shadow&amp;quot; – that &lt;code&gt;perspective&lt;/code&gt; property on the &lt;code&gt;wrapper&lt;/code&gt; element will change the way it rotates and skews.&lt;/p&gt;
&lt;p&gt;(This one needs a bit more hacky wrangling when you change the font size, and I use fluid typescales so I&#39;m going to embed a Codepen a bit further down instead of rendering it inline!)&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.wrapper::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;WordArt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;35%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;60deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;skewX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;65deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
  &lt;span class=&quot;token function&quot;&gt;scaleY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;2.8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scaleX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transform-origin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bottom right&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right now, the CSS is hardcoded to have a shadow that says &amp;quot;WordArt&amp;quot;. If we want to use this text style for other things too, how can we dynamically set the shadow text content? With the CSS &lt;code&gt;attr()&lt;/code&gt; function!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;attr()&lt;/code&gt; gets the content of a given attribute for an element. I&#39;ve called mine &lt;code&gt;data-content&lt;/code&gt;. So, in our &lt;code&gt;wrapper::before&lt;/code&gt; rule, &lt;code&gt;content: &#39;WordArt&#39;&lt;/code&gt; becomes &lt;code&gt;content: attr(data-content)&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.wrapper::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data-content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;60deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;skewX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;60deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
  &lt;span class=&quot;token function&quot;&gt;scaleY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;2.8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scaleX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transform-origin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bottom right&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And render the HTML with the attribute:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;rainbow-wrapper&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Rainbow&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;rainbow&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Rainbow&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can write different words!&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;265&quot; data-theme-id=&quot;dark&quot; data-default-tab=&quot;result&quot; data-user=&quot;sophiekoonin&quot; data-slug-hash=&quot;qBKmzmR&quot; data-preview=&quot;true&quot; data-pen-title=&quot;Rainbow text&quot;&gt;
  &lt;span&gt;See the Pen 
    &lt;a href=&quot;https://codepen.io/sophiekoonin/pen/qBKmzmR&quot;&gt;
      Rainbow text&lt;/a&gt; by &lt;a href=&quot;https://codepen.io/sophiekoonin&quot;&gt;@sophiekoonin&lt;/a&gt;
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;It&#39;s not perfect, but I think that looks good enough for a throwback site header!&lt;/p&gt;
&lt;h2 id=&quot;music&quot; tabindex=&quot;-1&quot;&gt;Music&lt;/h2&gt;
&lt;p&gt;In 2001, I had a NeoPets shop. As soon as you loaded the page, you&#39;d be greeted with the dulcet tones of Teenage Dirtbag, in MIDI form.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;EMBED&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;SRC&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mysong.mid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;HIDDEN&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;yes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autostart&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Autoplay, naturally, and looping. Definitely hidden, so the music was just &lt;em&gt;there&lt;/em&gt;. But it was super distracting, quite disorientating, and you couldn&#39;t turn it off.&lt;/p&gt;
&lt;p&gt;Modern browsers block autoplaying audio, for good reason. It&#39;s extremely annoying. Thankfully, the HTML5 &lt;code&gt;audio&lt;/code&gt; element gives us a bit more control.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;audio&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Play music&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;controls&lt;/span&gt; 
  &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/soundtrack.webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;audio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can still have audio on your website! Just make it &lt;em&gt;opt-in&lt;/em&gt;. If it&#39;s part of the experience you want to create, that&#39;s totally fine, as long as your viewer is okay with it playing.&lt;/p&gt;
&lt;p&gt;&lt;audio aria-label=&quot;Play music&quot; controls=&quot;&quot; src=&quot;https://localghost.dev/extras/soundtrack.webm&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;Make sure to add a label – whether external or &lt;code&gt;aria-label&lt;/code&gt; – to tell the user what it does.&lt;/p&gt;
&lt;p&gt;The controls that show up are the browser default – what&#39;s rendered above will look different depending on whether you&#39;re in Chrome, Firefox, etc. But you can use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API#controlling_sound&quot;&gt;Web Audio API&lt;/a&gt; to customise the controls, by rendering some pretty buttons and using JavaScript to make them control playing audio.&lt;/p&gt;
&lt;h2 id=&quot;cursor-trails&quot; tabindex=&quot;-1&quot;&gt;Cursor trails&lt;/h2&gt;
&lt;h3 id=&quot;then-dynamic-html&quot; tabindex=&quot;-1&quot;&gt;Then: Dynamic HTML&lt;/h3&gt;
&lt;p&gt;Back in the day, a cursor trail was a real flex. It said, &amp;quot;look at what I can do with JavaScript!&amp;quot;. (Or in my case, what &lt;a href=&quot;https://dynamicdrive.com/&quot;&gt;dynamicdrive.com&lt;/a&gt; could do with JavaScript.)&lt;/p&gt;
&lt;p&gt;This was known as Dynamic HTML: not necessarily a technology in its own right, but a collection of technologies (a bit like we use the term JAMstack now). HTML, CSS and JavaScript – but a very old version of JavaScript. Everything was client-side at this time, because we didn&#39;t have AJAX/client HTTP requests yet. And the implementations differed significantly between the two major browsers of the time, Internet Explorer and Netscape. (This is a period known as the &#39;Browser Wars&#39;, and there&#39;s a &lt;a href=&quot;https://thehistoryoftheweb.com/browser-wars/&quot;&gt;good summary of it on The History Of The Web&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;It led to stuff like this, from a &lt;a href=&quot;https://www.dynamicdrive.com/dynamicindex13/star.htm&quot;&gt;Dancing Stars animated cursor trail&lt;/a&gt;. This cursor adds a trail of seven 3px-wide yellow &amp;quot;stars&amp;quot; (tiny squares) that appear to follow your cursor around. I can&#39;t show you a preview, because the script doesn&#39;t work any more.&lt;/p&gt;
&lt;p&gt;First we had to check whether the script was running in IE, or Netscape, because the implementation would be completely different. In IE, we&#39;d check for the existence of &lt;code&gt;document.all&lt;/code&gt;, a function which returned all the elements in the DOM.&lt;/p&gt;
&lt;p&gt;If this was present, we&#39;d then call &lt;code&gt;document.write&lt;/code&gt; (a function we also &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/write&quot;&gt;shouldn&#39;t use any more&lt;/a&gt;) to insert several little 3px &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; squares into the DOM.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;all&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;starsDiv&quot;&lt;/span&gt; 
  style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;position:absolute;top:0px;left:0px&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&#39;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;xy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; xy &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; xy&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&quot;position&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;3px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;height&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;3px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;background&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;#&lt;span class=&quot;token constant&quot;&gt;FFFF00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    font&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;size&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;2px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;visibility&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;visible&quot;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&#39;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;/div&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Netscape didn&#39;t have &lt;code&gt;div&lt;/code&gt;s, it had &lt;code&gt;LAYER&lt;/code&gt;s. This is basically the same thing with a different name, because browser creators at the time liked causing pain.&lt;/p&gt;
&lt;p&gt;(Side note, this is why some people used to refer to &lt;code&gt;divs&lt;/code&gt; as &amp;quot;div layers&amp;quot; – it encompasses both elements.)&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;LAYER&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;a0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; 
  &lt;span class=&quot;token attr-name&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;10&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;TOP&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;10&lt;/span&gt; 
  &lt;span class=&quot;token attr-name&quot;&gt;VISIBILITY&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;SHOW&lt;/span&gt; 
  &lt;span class=&quot;token attr-name&quot;&gt;BGCOLOR&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#FFFF00&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; 
  &lt;span class=&quot;token attr-name&quot;&gt;CLIP&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0,0,3,3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;LAYER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;document.layers&lt;/code&gt; returned something, we knew we were in Netscape, and could do Netscape things. In this case:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;captureEvents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MOUSEMOVE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The full script is available at &lt;a href=&quot;http://www.dynamicdrive.com/dynamicindex13/star.htm&quot;&gt;DynamicDrive.com&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;now-canvas-and-request-animation-frame&quot; tabindex=&quot;-1&quot;&gt;Now: Canvas and &lt;code&gt;requestAnimationFrame&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://tholman.com/cursor-effects&quot;&gt;Tim Holman&lt;/a&gt; has done the hard work for us here, and recreated 90s-style cursor effects with modern JavaScript and HTML.&lt;/p&gt;
&lt;video controls=&quot;&quot;&gt;
 &lt;source src=&quot;https://localghost.dev/img/blog/build-1999/star-cursor.webm&quot; type=&quot;video/webm&quot; /&gt;
    Video of a cursor trail with many small coloured stars following the cursor.
    &lt;a href=&quot;https://localghost.dev/img/blog/build-1999/star-cursor.webm&quot;&gt;Download the video as .webm&lt;/a&gt;
&lt;/video&gt;
&lt;p&gt;Tim&#39;s cursors use canvas, which is much more performant than rendering individual elements into the DOM. You also get much more fine-grained control over how elements are positioned. Can you imagine trying to render this many stars into the DOM in the old script? Using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;canvas API&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame&quot;&gt;&lt;code&gt;requestAnimationFrame&lt;/code&gt;&lt;/a&gt; – the function that allows us to batch up animations and efficiently update them before the browser&#39;s next repaint – we can render lots of little stars, making them fade in and out in lovely ways.&lt;/p&gt;
&lt;h3 id=&quot;media-queries-work-in-java-script-too&quot; tabindex=&quot;-1&quot;&gt;Media queries work in JavaScript, too!&lt;/h3&gt;
&lt;p&gt;Of course, cursor trails mean animations, and not everyone wants to see those. The good news is, we can use media queries in JS, just like we do in CSS and HTML &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; prefersReducedMotionQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; 
	window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;prefers-reduced-motion&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
					
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;prefersReducedMotionQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	cursor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By calling &lt;code&gt;window.matchMedia&lt;/code&gt; with the name of the query we&#39;re interested in (&lt;code&gt;prefers-reduced-motion&lt;/code&gt;), we can check the value of the user&#39;s motion settings and only init the cursor if they &lt;em&gt;don&#39;t&lt;/em&gt; have reduced motion enabled.&lt;/p&gt;
&lt;p&gt;You can even add an event listener to the media query, so that when its value changes we can dynamically initialise or destroy the cursor accordingly.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;prefersReducedMotionQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;change&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefersReducedMotionQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		cursor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		cursor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;webrings&quot; tabindex=&quot;-1&quot;&gt;Webrings&lt;/h2&gt;
&lt;p&gt;In the days before search engines were particularly good, how did you find similar websites? Webrings, of course. A webring is a collection of websites based around a shared interest or topic. Webrings offered a sense of belonging to a community, and gave you a fancy plaque to put on your website.&lt;/p&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/webring.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/webring.png&quot; alt=&quot;A webring plaque for Calvin and Hobbes-themed sites, with links to previous, next and random sites in the ring, and links to the index page.&quot; /&gt;
&lt;/picture&gt;
&lt;p&gt;Each site in the webring would have a plaque like this so you could navigate the ring, and there&#39;d be a backend somewhere (probably written in Perl) that did all the hard work of calculating which site came where in the ring.&lt;/p&gt;
&lt;p&gt;I built my own &lt;a href=&quot;https://sotb22-webring.neocities.org/&quot;&gt;webring for State of the Browser 2022 attendees&lt;/a&gt;, with a Google Sheets backend (for time-saving/live demo reasons) and a Cloudflare Worker on top of that to work out which site to send people to. It checks the value of &lt;code&gt;request.referrer&lt;/code&gt; to see where the call is coming from, looks up that URL in the list of sites, and returns the next or previous one accordingly.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; pathname &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchAndParseCsv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; referrer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;referrer
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For something a little more permanent/professional, I recommend checking out &lt;a href=&quot;https://mxb.dev/blog/webring-kit/&quot;&gt;Max Böck&#39;s webring kit&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;guestbooks&quot; tabindex=&quot;-1&quot;&gt;Guestbooks&lt;/h2&gt;
&lt;p&gt;Last but not least, the humble guestbook! In the days before social media, this was how we showed our appreciation for webmasters. Rather than just building a guestbook I thought I&#39;d do something a little different for the talk. It&#39;s a guestbook all right, but it&#39;s powered by Twitter!&lt;/p&gt;
&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://localghost.dev/img/blog/build-1999/guestbook.webp&quot; type=&quot;image/webp&quot; /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/build-1999/guestbook.png&quot; alt=&quot;A screenshot of my guestbook, with messages from conference attendees.&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;a href=&quot;https://sophieswebsite1999.neocities.org/guestbook&quot;&gt;View the guestbook&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Instead, I used a technology called &lt;strong&gt;webmentions&lt;/strong&gt;: a protocol to notify a website when someone else links to them, such as on their own website or on Twitter. Webmentions are collected as a feed (a bit like RSS) and associated with a domain name or host. I put meta tags in the &lt;code&gt;head&lt;/code&gt; of my site to indicate that I&#39;m on the lookout for webmentions.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;https://webmention.io/&quot;&gt;webmention.io&lt;/a&gt; to collect those webmentions for me, though it&#39;s totally possible to set up your own server to do so. On this website (localghost) I collect webmentions at build time and publish them underneath the pages, but on the demo website for this talk I have a client-side script to fetch mentions as I wanted to be able to demo them live.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;https://brid.gy/&quot;&gt;brid.gy&lt;/a&gt; to collect mentions from Twitter and send them to &lt;a href=&quot;http://webmention.io/&quot;&gt;webmention.io&lt;/a&gt;, and then my site queries &lt;a href=&quot;http://webmention.io/&quot;&gt;webmention.io&lt;/a&gt; to get the feed of mentions.&lt;/p&gt;
&lt;h2 id=&quot;go-forth-and-build-weird-stuff&quot; tabindex=&quot;-1&quot;&gt;Go forth and build weird stuff!&lt;/h2&gt;
&lt;p&gt;The web is an amazing platform brimming with opportunities to be creative and experimental. I&#39;d love to see what you build – if you mention this page on your own site or Twitter, the webmentions will appear below, or tag me &lt;code&gt;@type__error&lt;/code&gt;!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Everything I googled in a week as a senior software engineer</title>
      <link href="https://localghost.dev/blog/everything-i-googled-in-a-week-as-a-senior-software-engineer/"/>
      <updated>2022-10-15T00:00:00Z</updated>
      <id>https://localghost.dev/blog/everything-i-googled-in-a-week-as-a-senior-software-engineer/</id>
      <content type="html">&lt;p&gt;Three years ago I wrote a post called &lt;a href=&quot;https://localghost.dev/blog/everything-i-googled-in-a-week-as-a-professional-software-engineer/&quot;&gt;Everything I googled in a week as a professional software engineer&lt;/a&gt;, and it clearly resonated with people, because it went pretty viral. It &lt;em&gt;still&lt;/em&gt; gets most of the pageviews on this website.&lt;/p&gt;
&lt;p&gt;Well, a lot has changed in three years: I got promoted and I&#39;m now a senior engineer and lead the web engineering discipline at &lt;a href=&quot;https://monzo.com/&quot;&gt;Monzo&lt;/a&gt;. But one thing hasn&#39;t changed: I still google a lot, every single day. Here&#39;s what I googled in a week, 2022 edition.&lt;/p&gt;
&lt;p&gt;Obvious disclaimer: this is slightly edited as I&#39;ve removed most of the non-work-related ones.&lt;/p&gt;
&lt;p&gt;Some of these search terms might make you laugh and think &amp;quot;how did you not know that?&amp;quot;. Well, there are several reasons you might not know something (choose all that apply):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you&#39;ve never used it before&lt;/li&gt;
&lt;li&gt;you&#39;ve used it before but can&#39;t remember&lt;/li&gt;
&lt;li&gt;it&#39;s changed since the last time you used it&lt;/li&gt;
&lt;li&gt;you&#39;re tired&lt;/li&gt;
&lt;li&gt;you&#39;re distracted&lt;/li&gt;
&lt;li&gt;you&#39;re human&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More than ever, we&#39;re constantly surrounded by new information. It&#39;s impossible to remember everything all of the time.&lt;/p&gt;
&lt;p&gt;I hope this makes you feel a bit better if you ever feel bad that you have to google something &amp;quot;obvious&amp;quot;.&lt;/p&gt;
&lt;h2 id=&quot;monday&quot; tabindex=&quot;-1&quot;&gt;Monday&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;slack channel bookmarks&lt;/em&gt; – trying to find some documentation on where to find channel bookmarks on mobile, as my friend couldn&#39;t find them.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;carbonara&lt;/em&gt; – my manager made the very lofty claim in standup that his carbonara was the best, so I googled a picture to make a &amp;quot;carbonara king&amp;quot; emoji of him&lt;/p&gt;
&lt;p&gt;&lt;em&gt;directory tree cli&lt;/em&gt; - how to render a directory tree in a CLI&lt;/p&gt;
&lt;p&gt;&lt;em&gt;react-toastify&lt;/em&gt; - a useful notification library&lt;/p&gt;
&lt;p&gt;&lt;em&gt;anchor dataset&lt;/em&gt; - accessing data attributes in JS&lt;/p&gt;
&lt;p&gt;&lt;em&gt;mdn element dataset&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;waitfornextupdate&lt;/em&gt; - trying to remember if this testing-library function was just for hooks. (It is.)&lt;/p&gt;
&lt;p&gt;The next few are a direct result of the fact that in JS you still can&#39;t automatically download a file in a cross-browser way without creating an anchor element. There&#39;s the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/downloads/download&quot;&gt;downloads API&lt;/a&gt;, but Safari doesn&#39;t support it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;jest how to test detached elements&lt;/em&gt; – &lt;code&gt;jest-dom&lt;/code&gt; can only see what&#39;s in the DOM under test, so if an element is detached from the DOM, it&#39;s not going to be able to test it. But this didn&#39;t immediately occur to me.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;code&gt;document.createElement(&#39;a&#39;)&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;code&gt;document.createElement(&#39;a&#39;)&lt;/code&gt; detached&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;react-hook-form&lt;/em&gt; - we&#39;re trying to add this to some of our web properties, as they don&#39;t have any form management and &lt;code&gt;react-hook-form&lt;/code&gt; is a really nice abstraction.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;spread types may only be created from object types&lt;/em&gt; - I kept getting this error and I couldn&#39;t work out why, and it turns out we had an old version of &lt;code&gt;react-hook-form&lt;/code&gt; installed in a different app (just monorepo things) which had different return types&lt;/p&gt;
&lt;p&gt;&lt;em&gt;react-hook-form &amp;quot;spread types may only be created from object types&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;tuesday&quot; tabindex=&quot;-1&quot;&gt;Tuesday&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;slack channel links in API&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;gilts&lt;/em&gt; – they were &lt;a href=&quot;https://www.ftadviser.com/investments/2022/10/10/boe-to-expand-gilt-intervention-as-obr-confirms-forecast-date/&quot;&gt;in the news&lt;/a&gt; and I didn&#39;t actually know what they were.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;js accessing nested object key&lt;/em&gt; – I momentarily couldn&#39;t remember whether you could access nested object keys like &lt;code&gt;myObj[&#39;key1.key2&#39;]&lt;/code&gt;. (you can&#39;t, because the entire string is interpreted as one key)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;intensifies emoji maker&lt;/em&gt; – I went with &lt;a href=&quot;https://makeemoji.com/&quot;&gt;MakeEmoji&lt;/a&gt; which has a pleasing number of options&lt;/p&gt;
&lt;p&gt;&lt;em&gt;modal accessibility&lt;/em&gt; – I was having a discussion with someone about why modals aren&#39;t accessible. It turns out there is some nuance to it, and that they aren&#39;t necessarily completely inaccessible, but they require a fair bit of work to make them accessible.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;modal window accessibility&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I hope you can feel the frustration in the next few search queries, in which I struggle with Storybook and then find that my problem isn&#39;t actually documented. I knew that there was a world in which you can write JSDoc comments above a component, and have them show up in Storybook docs, because we did that at my previous job. The docs don&#39;t seem to mention this at all, and it wasn&#39;t working for some reason. It turns out that it doesn&#39;t work with default export components, for some reason, but this isn&#39;t documented anywhere.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;storybook docs markdown comments&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;storybook story description&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;storybook docs&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;storybook comments&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;storybook comment&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;storybook JSdoc&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;wednesday&quot; tabindex=&quot;-1&quot;&gt;Wednesday&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;mark hoppus&lt;/em&gt; – I mentioned that I was trying to get blink-182 tickets (I failed) and we were debating how old they are now.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;lofi girl&lt;/em&gt; – during retro we put music on while people are writing their Retrium tickets, and I thought this was the appropriate vibe.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;apollo query oncompleted&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;useLazyQuery&lt;/em&gt; – trying to figure out if this returns the return value, or &lt;code&gt;void&lt;/code&gt;. Turns out the latest version returns the return value, and the version we&#39;re on doesn&#39;t :(&lt;/p&gt;
&lt;p&gt;&lt;em&gt;sentry github&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&#39;RequestSessionStatus&#39; is not exported from &#39;@sentry/types&#39;&lt;/em&gt; – this turned out to not be a problem with the Sentry lib, but actually a yarn.lock mismatch (just monorepo things).&lt;/p&gt;
&lt;h2 id=&quot;thursday&quot; tabindex=&quot;-1&quot;&gt;Thursday&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;document.write&lt;/em&gt; – I knew this was deprecated, but I wanted to find something to back up my PR comment.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;apollo server request size&lt;/em&gt; – Getting 412s from our apollo server and trying to figure out how to bump the max request size.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;apollo-server-koa&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;apollo server request size site:stackoverflow.com&lt;/em&gt; - a useful tip for googling is that you can restrict searches to a particular site using the &lt;code&gt;site:&lt;/code&gt; param.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;mdn element dataset&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;open new window and set inner html&lt;/em&gt; – trying to find a suitable replacement for &lt;code&gt;document.write&lt;/code&gt; for the person who requested it. We went for &lt;code&gt;window.open()&lt;/code&gt; and &lt;code&gt;myWindow.document.documentElement.innerHTML = myHtml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;mock clock golang&lt;/em&gt; – figuring out the best way to mock the &lt;code&gt;time.Now()&lt;/code&gt; function in Go. There are various ways of doing it across our codebase, I wasn&#39;t sure which one was the most up-to-date way, but I figured it out eventually.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;graphql server request entity too large&lt;/em&gt; – still trying to fix the apollo server request limit&lt;/p&gt;
&lt;p&gt;&lt;em&gt;doggo ipsum&lt;/em&gt; – my &lt;a href=&quot;https://doggoipsum.com/&quot;&gt;favourite ipsum generator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;apollo server body parser config&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;apollo server body parser config koa&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;koa&lt;/em&gt; - I finally realised we need to set the body parser config on the server itself, not the apollo-server wrapper.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;koa request size&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;britney spears albums&lt;/em&gt; – trying to find as many Britney track titles as possible to cram into a pun-filled gratitude post for my colleague who helped us out.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;ronseal&lt;/em&gt; – I like to google pictures of &lt;a href=&quot;https://ronseal.com/&quot;&gt;Ronseal&lt;/a&gt; and put them in PR descriptions when the title describes exactly what the PR does (i.e. it &lt;a href=&quot;https://en.wikipedia.org/wiki/Does_exactly_what_it_says_on_the_tin&quot;&gt;does what it says on the tin&lt;/a&gt;). I think I got this habit from &lt;a href=&quot;https://b3ta.com/dictionary/define/Ronseal/&quot;&gt;b3ta&lt;/a&gt; back in the day.&lt;/p&gt;
&lt;h2 id=&quot;friday&quot; tabindex=&quot;-1&quot;&gt;Friday&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;read a symbolic link&lt;/em&gt; – I&#39;m so shit at symbolic links lolol&lt;/p&gt;
&lt;p&gt;&lt;em&gt;npm service status&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;apollo async oncompleted&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;window.history&lt;/em&gt; – looking for the arguments for &lt;code&gt;history.push&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I think the next few are particularly amusing given that I &lt;em&gt;literally gave a conference talk&lt;/em&gt; on &lt;code&gt;redux-saga&lt;/code&gt; in 2018, but it&#39;s been so long since I touched any code containing sagas that I completely forgot how they work:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;saga execute non-saga function&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;saga execute non-redux function&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;redux-saga effects&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;More ipsum generators, because my colleagues enjoyed doggo ipsum:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://cupcakeipsum.com/&quot;&gt;cupcake ipsum&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://www.loremipsums.nl/lorem-ipsum-origineel/veggie-ipsum/&quot;&gt;veggie ipsum&lt;/a&gt;&lt;/em&gt; – I &lt;em&gt;love&lt;/em&gt; that I also googled this in the previous post as well, I promise it&#39;s a coincidence&lt;/p&gt;
&lt;p&gt;&lt;em&gt;ts-command-line-args&lt;/em&gt; - onto some CLI building now!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;node.js interactive shell&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;node.js interactive shell select&lt;/em&gt; – I was looking for a CLI library that lets you select different options with the arrow keys. I found &lt;a href=&quot;https://github.com/SBoudrias/Inquirer.js&quot;&gt;inquirer.js&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;types/inquirer&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;ts-command-line-args&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;should you symlink from destination&lt;/em&gt; – I can never remember what order to do symlinks in&lt;/p&gt;
&lt;p&gt;&lt;em&gt;TS2464&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;next-images&lt;/em&gt; - checking that some of our plugins are still relevant.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;nextjs document&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;react-hook-form radio buttons&lt;/em&gt; - sometimes you just need a good example. Turns out it was easier than I thought, and I just needed to forward a ref to our radio button component.&lt;/p&gt;
&lt;h2 id=&quot;looking-back&quot; tabindex=&quot;-1&quot;&gt;Looking back&lt;/h2&gt;
&lt;p&gt;You might say &amp;quot;well, you googled fewer things this week than you did that week in 2019!&amp;quot;. For one thing, I have more meetings now than I did back then. I&#39;m in a different team, working on different things.&lt;/p&gt;
&lt;p&gt;It also depends from week to week what I&#39;m working on; last week I spent a lot of time building data export in Go, and so my search history was full of frustrated queries like &lt;code&gt;golang readseeker from buffer&lt;/code&gt; and &lt;code&gt;create a file from string golang&lt;/code&gt;. (I still help out on backend tickets when it&#39;s needed.)&lt;/p&gt;
&lt;p&gt;Some of the stuff I googled back then I can remember how to do without looking it up now, but some of it I definitely can&#39;t (e.g. I still can&#39;t get my brain to retain some more complex CSS grid things). For example, I&#39;d 100% still have to google all of these from the last post:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;whitespace regex&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;regex not letter&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;js date&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;grid minmax&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There you have it – I still google loads of stuff. To finish, I&#39;ll leave you with what I said in the post from 2019:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What I&#39;m trying to show with all this is that you can do something 100 times but still not remember how to do it off the top of your head. Never be ashamed of googling, even if it seems like the most basic thing you&#39;re looking up.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
    </entry>
    
    <entry>
      <title>When going back doesn&#39;t mean going backwards</title>
      <link href="https://localghost.dev/blog/when-going-back-doesn-t-mean-going-backwards/"/>
      <updated>2022-08-27T00:00:00Z</updated>
      <id>https://localghost.dev/blog/when-going-back-doesn-t-mean-going-backwards/</id>
      <content type="html">&lt;p&gt;Hey internet! Last time we spoke, I was in a bad place. Burnt out and off work. Well, in May I made the decision to quit my job at the startup after 7 months, and in June I accepted a new job at... my old job. So I&#39;m back at Monzo, &lt;a href=&quot;https://twitter.com/type__error/status/1554859381875003393&quot;&gt;working at the bank&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You don&#39;t often hear about people going back to jobs they&#39;ve left. It turns out it&#39;s really nice, because you already have friends there and people are happy to see you come back. For me, leaving Monzo was always a pull, not a push: this exciting opportunity came up at &lt;a href=&quot;http://incident.io/&quot;&gt;incident.io&lt;/a&gt;, and I thought I&#39;d give it a go.&lt;/p&gt;
&lt;p&gt;Accepting that a job isn&#39;t working out can be a difficult pill to swallow. I really wanted to make it work, and I think a lot of the burnout I experienced was because it wasn&#39;t the right job for me. Being part of an early-stage startup seemed to be the hot thing to do, and this particular one is run by people I really respect. I had to come to terms with the fact that early-stage startups aren&#39;t really my cup of tea: I realised that I work best in bigger organisations, where I can take more of a leadership role and do larger-scale work that improves things for the people around me.&lt;/p&gt;
&lt;p&gt;I also realised that I didn&#39;t really want to market myself as a full-stack developer any more. The week I got back from my time off, I went to &lt;a href=&quot;https://heypresents.com/conferences/2022&quot;&gt;All Day Hey&lt;/a&gt; in Leeds and I left with this incredible sense of clarity. It reminded me of everything I love about the web. And it made me finally accept the fact that, well, I don&#39;t like backend development very much.&lt;/p&gt;
&lt;p&gt;See, I&#39;ve always had a degree of internalised self-doubt about being web developer, as if it means I&#39;m somehow not a &amp;quot;proper&amp;quot; developer for not being a backend dev. (Never mind that if anyone said that to me, I&#39;d give them an earful.)&lt;/p&gt;
&lt;p&gt;I&#39;ve been building websites for over 20 years, but when I learnt Java during my masters I thought I&#39;d become a backend dev, because that was what Real Programmers do. After my career took me onto a React/Node project at John Lewis, then joining Monzo as a full-stack web engineer, I clung on to the &amp;quot;full-stack&amp;quot; title. Doing backend too meant that I could still consider myself a Serious Dev while still doing the fun web stuff as well.&lt;/p&gt;
&lt;p&gt;That &amp;quot;fun web stuff&amp;quot; included teaching people about accessibility, improving testing, architecting and building out brand new web apps, upgrading countless libraries, implementing microfrontends in a large React monolith, architecting and building component libraries. It involved becoming recognised as a conference speaker, and being invited to conferences about web development in the UK and abroad. It involved being promoted to Web Discipline Lead at Monzo, where I could be the &amp;quot;public face&amp;quot; of web internally at Monzo, defining and introducing engineering standards across the organisation. But it&#39;s okay because I was still doing backend engineering to make me a Proper Developer...!&lt;/p&gt;
&lt;p&gt;Oh dear.&lt;/p&gt;
&lt;p&gt;At All Day Hey, when I watched Andy Bell&#39;s brilliant talk &lt;a href=&quot;https://heypresents.com/talks/be-the-browser-s-mentor-not-its-micromanager&quot;&gt;Be The Browser&#39;s Mentor, Not Its Micromanager&lt;/a&gt;, I was &lt;em&gt;so excited&lt;/em&gt; by what I&#39;d just seen. I hadn&#39;t felt that much enthusiasm about what I did for months – sorry, but the introduction of Generics in Go doesn&#39;t hold a candle to modern CSS utility functions and what they mean for responsive website design 💅&lt;/p&gt;
&lt;p&gt;I realised that I hadn&#39;t been doing enough of what I love, and I LOVE THE WEB. Web development is awesome. (And I &lt;em&gt;am&lt;/em&gt; a Proper Developer.) Sadly the company&#39;s engineering needs didn&#39;t really match up with my specialism.&lt;/p&gt;
&lt;p&gt;The week after, I handed in my notice. I was sad, but we all agreed it was the right thing, and everyone was super supportive.&lt;/p&gt;
&lt;p&gt;Ultimately, the 7 months at the startup were well spent: this experience helped me to understand what I really wanted for my career – and I realised it was pretty much what I was doing before. Plus, I met some excellent people, and learnt a lot from them. (I also built a pretty sweet component library, and I hope they&#39;re getting good use out of it.)&lt;/p&gt;
&lt;p&gt;This isn&#39;t a cautionary tale: it&#39;s a reassurance that it&#39;s okay to make the wrong decision. The wrong decision is not necessarily a bad decision, and going back to my old job isn&#39;t a backwards step – I picked up where I left off and I&#39;m heading in the same direction I was when I left. I don&#39;t have any regrets about the past year – I learnt a lot, I have a lot of love for the folks at &lt;a href=&quot;http://incident.io/&quot;&gt;incident.io&lt;/a&gt;, and they&#39;re building something amazing. It just wasn&#39;t the right thing for me, and that&#39;s okay.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Burnout, a cautionary tale (and a plea to take a break)</title>
      <link href="https://localghost.dev/blog/burnout-a-cautionary-tale-and-a-plea-to-take-a-break/"/>
      <updated>2022-04-16T00:00:00Z</updated>
      <id>https://localghost.dev/blog/burnout-a-cautionary-tale-and-a-plea-to-take-a-break/</id>
      <content type="html">&lt;p&gt;It&#39;s Easter and I&#39;m off work til the end of the month. It was originally just going to be for a few days as I&#39;d used up most of my holiday already, but after I burst into tears at my manager on Wednesday during our 1:1 when he asked me how a project was going, he suggested I might need a bit longer (good manager).&lt;/p&gt;
&lt;p&gt;I really love my job, I love what I do and I love my colleagues. Before the pandemic - and even during it - I threw myself at my work and learnt as much as I possibly could. And from my beginnings learning Java at university, I learnt JavaScript, then React, then Kotlin. Then I moved onto Monzo and picked up Go. I was Good At Things.&lt;/p&gt;
&lt;p&gt;But for the last few months I&#39;ve found that nothing goes in any more. I can&#39;t absorb new information. People have to explain things several times, and my head feels like it&#39;s full of cotton wool. It makes me feel like an idiot. I&#39;d tell myself I was better than this and I needed to just &lt;em&gt;focus&lt;/em&gt; and maybe I was just getting too distracted.&lt;/p&gt;
&lt;p&gt;When I sit down to do web development, it&#39;s like autopilot and I can steam through it, but anything backend seems to be beyond my capability at the moment. Which is frustrating because there&#39;s a lot of backend development in my current role. I used to love it, what gives? I&#39;m out of practice, sure, but it&#39;s like there&#39;s this barrier that stops me from learning anything new.&lt;/p&gt;
&lt;p&gt;Weeeelllll, now I can put a name to that barrier: burnout. FFS.&lt;/p&gt;
&lt;p&gt;Was it my job, I thought? Was I unhappy in my new role? Well, the thought of going back to my old job didn&#39;t make me feel any better. Nor would going anywhere else. Switching to a new job certainly came with the stress of starting from scratch, and working at a startup is a whole other vibe. But ultimately I do like it, and I knew that in any other circumstances I&#39;d be thriving there.&lt;/p&gt;
&lt;p&gt;So let&#39;s look at everything else.&lt;/p&gt;
&lt;p&gt;The last two years have been among the worst of my life. I&#39;m sure they have for many of you, too. I spent so long unable to do some of the things I love, in a lockdown that could have been a lot shorter if our government wasn&#39;t equal parts inept and corrupt. The world is permanently on fire. I had to take a hiatus from reading the news because it was all too much. Plus I&#39;ve had my own depression to contend with and other family things that I won&#39;t go into, which have made everything that much more difficult.&lt;/p&gt;
&lt;h2 id=&quot;don-t-be-like-me-please-take-a-break&quot; tabindex=&quot;-1&quot;&gt;Don&#39;t be like me, please take a break&lt;/h2&gt;
&lt;p&gt;I wrote a blog post in 2020 about &lt;a href=&quot;https://localghost.dev/blog/give-yourself-a-break-lessons-from-burnout/&quot;&gt;giving yourself a break&lt;/a&gt;, but apparently I didn&#39;t take enough of my own advice.&lt;/p&gt;
&lt;p&gt;For someone who goes on about work-life balance and mental health, I&#39;m not actually very good at practising what I preach. (Surprise.)&lt;/p&gt;
&lt;p&gt;I&#39;d figured that because I never work late or out-of-hours, I&#39;d avoid burnout. But that&#39;s just one contributing factor.&lt;/p&gt;
&lt;p&gt;And when I take time off I usually feel like I need to fill it with Stuff. Projects, outings, day trips, holidays. Making the most of your time off. I&#39;ve never been good at doing &lt;em&gt;nothing&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A couple of weeks ago I was ill with a mystery virus that seemed an awful lot like covid but was negative on all the tests, and a few mornings that week I&#39;d woken up feeling loads better and started working. By the afternoon I had to give up and down tools again because I felt so ill.&lt;/p&gt;
&lt;p&gt;I thought I&#39;d be making myself useful by working when I felt better, but instead I was being more disruptive than anything else. People didn&#39;t know if I was going to finish the thing I was working on, so they didn&#39;t pick it up, and stuff kind of stopped and started. If I&#39;d just taken the rest of the week and shoved my laptop in a cupboard, I&#39;d have actually got the rest I needed.&lt;/p&gt;
&lt;h2 id=&quot;heed-my-words-adventurer&quot; tabindex=&quot;-1&quot;&gt;Heed my words, adventurer!&lt;/h2&gt;
&lt;p&gt;I am not writing this post to garner sympathy, I&#39;m sharing because I want to be transparent about the fact that this doesn&#39;t &amp;quot;just happen to other people&amp;quot;. I have been running on fumes for months, and it culminated in me crying in a coffee shop in front of my extremely kind and patient manager.&lt;/p&gt;
&lt;p&gt;If you are feeling like things are harder these days, that&#39;s because they are. If you&#39;re not functioning at 100% and find you&#39;re pushing yourself even harder because of it, please stop. I want you to take a minute to check on yourself. Are you okay? Do you need to take some time off?&lt;/p&gt;
&lt;p&gt;(Also, never be ashamed of crying at work. We&#39;ve all been there. Normalise crying at work. It&#39;s a sign that something is wrong, but the crying itself isn&#39;t anything to be ashamed of.)&lt;/p&gt;
&lt;p&gt;I believe in working hard, but I do not believe in working hard at the expense of your physical and mental health.&lt;/p&gt;
&lt;p&gt;So now I&#39;m on a break until May, and I&#39;m going to go for walks, go birdwatching, do jigsaws, plant some vegetables in the allotment, play video games and make a big fuss of my father-in-law&#39;s dog Boris.&lt;/p&gt;
&lt;p&gt;If you&#39;ve got some holiday, please take it.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/boris.JPG&quot; alt=&quot;A brown and white Staffordshire bull terrier lies on the sofa with his limbs extended. He is greying round the muzzle, and has what looks like a smile. He&#39;s lying next to a PS5 controller.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;My plans.&lt;/figcaption&gt;&lt;/figure&gt;
</content>
    </entry>
    
    <entry>
      <title>Start at the beginning: the importance of learning the basics</title>
      <link href="https://localghost.dev/blog/start-at-the-beginning-the-importance-of-learning-the-basics/"/>
      <updated>2022-01-02T00:00:00Z</updated>
      <id>https://localghost.dev/blog/start-at-the-beginning-the-importance-of-learning-the-basics/</id>
      <content type="html">&lt;p&gt;If you&#39;re an early-career developer, Twitter is overflowing with people tweeting great tips -- and some absolute rubbish -- about how to improve your skills and become better at your job. I&#39;ve spoken to more than a few people who&#39;ve asked me, &amp;quot;how should I start?&amp;quot;. And I tell everyone the same thing: &lt;strong&gt;learn the basics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;However you learn best -- book, video, interactive tutorial -- &lt;strong&gt;you need to learn HTML and CSS  before you can call yourself a web developer&lt;/strong&gt;. I don&#39;t think that&#39;s a particularly controversial statement.&lt;/p&gt;
&lt;p&gt;Once you start getting into interactive website territory, with API calls and fancy stuff, that&#39;s where you need JavaScript (JS) knowledge. More specifically, &lt;strong&gt;vanilla JS&lt;/strong&gt;: plain JS with no additional frameworks or plugins. The JS that your browser understands without having to do any pre-processing. It makes working with frameworks a whole lot easier, and it&#39;ll help you to know when &lt;em&gt;not&lt;/em&gt; to use a framework (and avoid making users download massive JS bundles when all you need is a tiny bit of code). Browsers have come a &lt;em&gt;long&lt;/em&gt; way, and a lot of what we might have needed to use &lt;a href=&quot;https://babeljs.io/&quot;&gt;Babel&lt;/a&gt; to do even just a couple of years ago is now natively supported in the big 4 browsers (Chrome, Firefox, Edge and Safari).&lt;/p&gt;
&lt;h2 id=&quot;where-i-started&quot; tabindex=&quot;-1&quot;&gt;Where I started&lt;/h2&gt;
&lt;p&gt;I started building websites when I was ten years old. I was lucky enough to grow up with computers in the house, and had a book called &amp;quot;Make Your Own Webpage&amp;quot; which taught me the basics of HTML (as it was in 1999). You can even &lt;a href=&quot;https://archive.org/details/makeyourownwebpa00pede&quot;&gt;read it on archive.org&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/make-your-own-webpage.jpg&quot; alt=&quot;The front cover of the book &#39;Make Your Own Webpage, a guide for kids, from the Creators of Internet for Kids!&#39; by Ted Pedersen and Francis Moss. The cover features a CRT monitor and beige desktop computer with a computer mouse that looks like a real mouse, on a mousemat that looks like a slice of cheese.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;A few years later, I built my personal website full of webrings and stolen gifs with the help of &lt;a href=&quot;http://lissaexplains.com/&quot;&gt;Lissa Explains It All&lt;/a&gt;, the ultimate HTML bible for anyone on the internet in 2002. (It&#39;s still online today, but pretty out of date!)&lt;/p&gt;
&lt;p&gt;Fast-forward through LiveJournal, Greymatter, Blogger, Wordpress, pretty much any pre-2010 blogging site/CMS framework you can imagine -- and now I&#39;m building websites (or web apps) as my actual job, using React. A fair bit has changed -- but not as much as you&#39;d think.&lt;/p&gt;
&lt;p&gt;I&#39;m telling you this because I &lt;strong&gt;still use the knowledge I learned when I was ten&lt;/strong&gt; every day at work. HTML tags are still HTML tags, they&#39;re still written much the same way -- HTML5 got rid of some of the ones I used to use (pour one out for &lt;code&gt;marquee&lt;/code&gt;) and introduced some new ones, but the fundamentals are still the same. Most of the websites that I built in 1999 would still render today.&lt;/p&gt;
&lt;p&gt;My first professional web development experience was a React app. I&#39;d just about heard of React at that point, but I&#39;d recently learned JavaScript and Node.JS which gave me a good foundation to build upon. I brushed up on some newer CSS concepts I hadn&#39;t used, but all the HTML and CSS of my past web adventures came right back. The good thing about this stuff is that it&#39;s all over the internet, so when you inevitably forget how to do something really simple, you can just google it.&lt;/p&gt;
&lt;h2 id=&quot;frameworks-serve-a-purpose-but-they-don-t-replace-the-basics&quot; tabindex=&quot;-1&quot;&gt;Frameworks serve a purpose, but they don&#39;t replace the basics&lt;/h2&gt;
&lt;p&gt;People might tell you to learn React, because everyone&#39;s using React. I write React every day, and a lot of that is just JavaScript and HTML. If you don&#39;t know either of those, you&#39;re going to have a bad time -- or you&#39;ll end up with highly specialised framework-specific knowledge that will bite you later on when you need to use a different framework. Lots of people are using React, sure -- but a lot more people aren&#39;t. A far, far larger proportion of the web runs on Wordpress, for example. Lots of sites are built in plain old HTML, CSS and JS, like the &lt;a href=&quot;https://emojinator.fun/&quot;&gt;emojinator&lt;/a&gt; and this very website you&#39;re reading.&lt;/p&gt;
&lt;p&gt;Besides, in a few years we&#39;ll probably using the newest, hottest framework and React will become the butt of jokes in the way that Angular has. Or you&#39;ll land a job at your dream company and find they use Vue, or some state management library from 2011 which has no documentation. I&#39;m not saying don&#39;t learn React -- I&#39;m saying learn the basics first, so you can apply your knowledge to all manner of other frameworks and tools.&lt;/p&gt;
&lt;p&gt;The same goes for CSS. There was a particularly bad take floating around Twitter recently where someone suggested that junior developers should skip CSS and just learn &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt;. I have used Tailwind, and I dislike it for various reasons, but I can see use cases for it such as quick prototyping and a set of nice defaults in the absence of specialist design. However, it doesn&#39;t and shouldn&#39;t &amp;quot;replace&amp;quot; CSS. Tailwind is literally just a library of someone else&#39;s CSS classes, except you have a separate class for nearly every rule instead of writing all your rules together in reusable classes for each component. Instead of learning hundreds of class names, why not learn the CSS rules they translate to and write one CSS class with multiple selectors? When you go to your next job or project and find they don&#39;t use Tailwind, it&#39;ll slow you down as you need to learn the selectors all over again.&lt;/p&gt;
&lt;h2 id=&quot;recommended-resources&quot; tabindex=&quot;-1&quot;&gt;Recommended resources&lt;/h2&gt;
&lt;p&gt;I&#39;d recommend going in the order HTML, CSS, JS. That way, you can build something in HTML, add CSS to it as you learn it, and finally soup it up with your new-found JS knowledge.&lt;/p&gt;
&lt;p&gt;If you&#39;re a backend engineer who only touches the frontend occasionally, you don&#39;t need to go too deep -- but you need a decent grasp of which element does what (&lt;a href=&quot;https://localghost.dev/0221/06/the-right-tag-for-the-job-why-you-should-use-semantic-html/&quot;&gt;semantic HTML&lt;/a&gt;) so you can make sure you&#39;re using the right elements and not creating any accessibiliy problems.&lt;/p&gt;
&lt;h3 id=&quot;learning-html&quot; tabindex=&quot;-1&quot;&gt;Learning HTML&lt;/h3&gt;
&lt;p&gt;I usually recommend these tutorials which provide a really nice overview of different types of elements. Try using some of the tags in &lt;a href=&quot;https://codepen.io/pen/&quot;&gt;Codepen&lt;/a&gt;, and watch things render before your eyes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics&quot;&gt;Getting started with HTML - MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://.freecodecamp.org/learn/responsive-web-design/#basic-html-and-html5&quot;&gt;FreeCodeCamp&#39;s Responsive Web Design course - Basic HTML and HTML5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Learn from how other people do it. Web pages are so bloated with scripts and analytics these days that it&#39;s hard to just right-click and &amp;quot;View Source&amp;quot; in the same way that I did back in the early 00s -- I learnt a lot of HTML by nicking other people&#39;s code! But you can use browser dev tools to look at the structure of the page (the Document Object Model or DOM, as it&#39;s known):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/open/#elements&quot;&gt;Chrome dev tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector&quot;&gt;Firefox inspector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;learning-css&quot; tabindex=&quot;-1&quot;&gt;Learning CSS&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/pen/&quot;&gt;Codepen&lt;/a&gt; comes in useful again here to easily write and apply CSS styles to HTML, and have it update instantly.&lt;/p&gt;
&lt;p&gt;Again, MDN has a great beginners&#39; guide. CSS Tricks is my favourite place for useful CSS knowledge (I referred back to their Flexbox cheatsheet for YEARS) and really clearly written guides.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Free&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/CSS_basics&quot;&gt;CSS basics -- MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/almanac/&quot;&gt;CSS Almanac -- CSS Tricks&lt;/a&gt; -- a guide to nearly every selector and property in CSS&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/guides/&quot;&gt;CSS Tricks guides&lt;/a&gt; for in-depth info&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Paid&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/css-fundamentals&quot;&gt;CSS fundamentals on egghead.io&lt;/a&gt; by Tyler Clark (video)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;learning-java-script&quot; tabindex=&quot;-1&quot;&gt;Learning JavaScript&lt;/h3&gt;
&lt;p&gt;You want to start with vanilla JS. Having a solid foundation in that will make it easier to use frameworks and libraries in the future.&lt;/p&gt;
&lt;p&gt;How in-depth you go is up to you, and depends on how much interactive stuff you want to build on your website. If you just want a plain static site with a bit of markdown, you probably don&#39;t need JavaScript. This website has a tiny bit of JS on that controls dark mode; without that, it&#39;d just be HTML and CSS (though I did use a static site generator to build it).&lt;/p&gt;
&lt;p&gt;Besides the obligatory MDN article, these resources have been recommended by folks on &lt;a href=&quot;https://twitter.com/type__error/status/1477734460741369860&quot;&gt;Twitter&lt;/a&gt; - there are more suggestions in the replies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Free&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics&quot;&gt;Getting started with JavaScript -- MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://freecodecamp.org/&quot;&gt;FreeCodeCamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://javascript.info/&quot;&gt;javascript.info&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wesbos.com/javascript&quot;&gt;Wes Bos&#39;s Beginner JS Notes &amp;amp; Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://javascript30.com/&quot;&gt;Wes Bos -- 30 Day Vanilla JS Coding Challenge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Paid&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vanillajsacademy.com/essentials/&quot;&gt;Vanilla JS Academy&lt;/a&gt; (structured course)&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
      <title>The right tag for the job: why you should use semantic HTML</title>
      <link href="https://localghost.dev/blog/the-right-tag-for-the-job-why-you-should-use-semantic-html/"/>
      <updated>2021-06-06T00:00:00Z</updated>
      <id>https://localghost.dev/blog/the-right-tag-for-the-job-why-you-should-use-semantic-html/</id>
      <content type="html">&lt;p&gt;I&#39;ve come across a lot of websites in my career (and in daily browsing) that are straight-up inaccessible. If you&#39;ve ever worked on a project that is riddled with accessibility issues, you&#39;ll know that fixing these problems is a mammoth task - it needs &lt;em&gt;time&lt;/em&gt;, it needs &lt;em&gt;people&lt;/em&gt;, it needs &lt;em&gt;prioritisation&lt;/em&gt;... it costs money, basically. As I mentioned in my blog post &lt;a href=&quot;https://localghost.dev/2020/10/7-myths-designers-and-developers-believe-about-web-accessibility/&quot;&gt;7 myths designers and developers believe about web accessibility&lt;/a&gt;, retrofitting accessibility is &lt;em&gt;hard&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The key is to &lt;em&gt;start out&lt;/em&gt; with the right tools for the job - or rather, the right &lt;em&gt;tags&lt;/em&gt; for the job. In every website, underneath the layers of CSS, the bloated frameworks, the 45945 API calls, there&#39;s good old HTML, and that&#39;s the key to accessible websites. Specifically, &lt;b&gt;semantic HTML&lt;/b&gt;.&lt;/p&gt;
&lt;h2 id=&quot;semantic-html-conveys-meaning&quot; tabindex=&quot;-1&quot;&gt;Semantic HTML conveys meaning&lt;/h2&gt;
&lt;p&gt;Semantics is the study of meaning, and semantic HTML is exactly that - HTML tags that convey meaning. They tell the browser - and assistive technology such as screenreaders - about the structure of the page, and how it should behave when you interact with those elements. A button should let you click on it. An ordered list should have numbers.&lt;/p&gt;
&lt;p&gt;Sighted people rely on visual information to tell them about the structure of a page. Based on our prior knowledge of UI conventions, we can identify where a header is, which parts of the page are buttons or form elements, and what the title of the page is, for example. But if you don&#39;t get that visual information, how can you tell what&#39;s on the page?&lt;/p&gt;
&lt;p&gt;Assistive technology such as screenreaders will take the HTML markup of the page and present it to the user either by reading it out audibly, or sending the output to something like a &lt;a href=&quot;https://en.wikipedia.org/wiki/Refreshable_braille_display&quot;&gt;braille display&lt;/a&gt;. They&#39;ll use the different types of HTML tags in the document to present structured information to the user, allowing them to navigate by headings, or cycle through the links in the page.  So it matters what HTML tags we use, to make sure the screenreaders get the complete picture.&lt;/p&gt;
&lt;h2 id=&quot;example-a-non-semantic-news-site&quot; tabindex=&quot;-1&quot;&gt;Example: a non-semantic news site&lt;/h2&gt;
&lt;p&gt;Take this mocked-up news site (as you can see, graphic design is my passion). I&#39;ve built it using mostly &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements, with a couple of &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;s as I wanted some nice big headings. My menu at the top is a series of &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s containing links, and the footer at the bottom is the same.&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;265&quot; data-theme-id=&quot;dark&quot; data-default-tab=&quot;result&quot; data-user=&quot;sophiekoonin&quot; data-slug-hash=&quot;jOBxyVB&quot; data-preview=&quot;true&quot; data-pen-title=&quot;Semantic HTML blog post example: no semantics&quot;&gt;
  &lt;span&gt;See the Pen 
    &lt;a href=&quot;https://codepen.io/sophiekoonin/pen/jOBxyVB&quot;&gt;
      Semantic HTML blog post example: no semantics&lt;/a&gt; by &lt;a href=&quot;https://codepen.io/sophiekoonin&quot;&gt;@sophiekoonin&lt;/a&gt;
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;If you&#39;re a sighted user like me, it looks perfectly fine. I can see a menu, a header, some titles and a footer. The byline and image caption is nicely italicised, and there are some nice buttons and fancy SVG checkboxes on the cookie banner.&lt;/p&gt;
&lt;p&gt;But let&#39;s take a look at this page from the perspective of assistive tech, which relies on the underlying HTML markup to establish the structure of the document. I&#39;m using the screenreader software &lt;a href=&quot;https://support.apple.com/en-gb/guide/voiceover/welcome/mac&quot;&gt;VoiceOver&lt;/a&gt;, which is built into MacOS, but if you&#39;re on Windows you can use &lt;a href=&quot;https://www.nvaccess.org/download/&quot;&gt;NVDA&lt;/a&gt; which is free to download.&lt;/p&gt;
&lt;h3 id=&quot;landmarks&quot; tabindex=&quot;-1&quot;&gt;Landmarks&lt;/h3&gt;
&lt;p&gt;Screenreader software such as VoiceOver, JAWS and NVDA provides a way for users to navigate to different sections on the page based on &lt;b&gt;landmarks&lt;/b&gt;. So, you could skip directly to the footer, or directly to the main part of the site. This relies on these landmarks being signposted through semantic tags such as &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;,&lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;. We haven&#39;t used any of these tags in the example, so screen readers wouldn&#39;t be able to identify any landmarks.&lt;/p&gt;
&lt;p&gt;When I run VoiceOver and press Ctrl+Option+Shift+I to tell me about the page, it says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Page contains 14 links 2 headings&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When I properly mark up the header, footer and main sections of the page, with an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; around the content of the news article itself, VoiceOver allows me to navigate between them with Ctrl+Option+right/left arrow.  It tells me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Page contains 14 links 2 headings 3 landmarks 1 article&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;menus&quot; tabindex=&quot;-1&quot;&gt;Menus&lt;/h3&gt;
&lt;p&gt;If a screenreader user is trying to find the menu in this news page, they&#39;re going to have to go through all the links on the page to try and find one that seems like it could be a menu link. The &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; wrapping the menu is completely ignored by the screenreader, so all they hear is each individual link.  There&#39;s nothing to tell them which part is the menu. People shouldn&#39;t have to tab through the page incessantly to find out how to navigate to a different part of the website.&lt;/p&gt;
&lt;p&gt;Prior to HTML5, the best way to mark up menus with lists of links was an unordered list - &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;. Now we have the &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; tag, short for navigation.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;nav&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;menu&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;News&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Politics&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;World&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Sport&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Tech&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;nav&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Technically, you don&#39;t have to have a list inside the &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; - you can just put links in. I personally like to keep the list inside - the nice thing about lists is that some screen readers will read out how many items there are, so the user knows how long to keep tabbing through. It&#39;s also good for backwards compatibility in case any older screen readers don&#39;t understand &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Lists can be styled easily (including removing bullet points), and &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; (list item) elements can contain links and buttons.&lt;/p&gt;
&lt;p&gt;You can add &lt;code&gt;aria-label&lt;/code&gt; attributes to your &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; elements to give even more information about the purpose of the menu.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;nav&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;main navigation&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s how VoiceOver interprets all of this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;main navigation, navigation&lt;/p&gt;
&lt;p&gt;list, 5 items&lt;/p&gt;
&lt;p&gt;link, Sport, 4 of 5&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Whether you use &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; or not isn&#39;t a hard-and-fast rule. I noted that &lt;a href=&quot;https://gov.uk/&quot;&gt;gov.uk&lt;/a&gt; (an example of amazing accessible design) doesn&#39;t actually use them at all, sticking with unordered lists. As long as you use one and/or the other, you&#39;ll be fine.&lt;/p&gt;
&lt;p&gt;As an aside, I recommend using the Dev Tools inspector to look through the source for &lt;a href=&quot;http://gov.uk/&quot;&gt;gov.uk&lt;/a&gt; to see how they use ARIA attributes, as they use them very well.&lt;/p&gt;
&lt;h3 id=&quot;checkboxes&quot; tabindex=&quot;-1&quot;&gt;Checkboxes&lt;/h3&gt;
&lt;p&gt;These checkboxes aren&#39;t real checkboxes - they&#39;re &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements with CSS &lt;code&gt;:before&lt;/code&gt; pseudoselectors containing an SVG checkbox icon. VoiceOver reads them out as plain text, leaving you with no idea you can even click them.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chocolate chip&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&#39;s super annoying that you can&#39;t style the default checkbox in browsers, but there are ways around it that still let you use &lt;code&gt;&amp;lt;input type=&amp;quot;checkbox&amp;quot;&amp;gt;&lt;/code&gt;. The same goes for radio buttons.&lt;/p&gt;
&lt;p&gt;If we put the input back in and &lt;em&gt;visually&lt;/em&gt; hide it (leaving the input element in the DOM), we get a much more helpful readout:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;choc-chip&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;choc-chip&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;visually-hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;pretty-checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-hidden&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Chocolate chip
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(NB. you could even just have the actual SVG inline here)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chocolate chip, unticked, checkbox&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We hide the &amp;quot;pretty&amp;quot; checkbox from screen readers, because they will pick up the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The input itself has a CSS class of &lt;code&gt;&amp;quot;visually-hidden&amp;quot;&lt;/code&gt;, which looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.visually-hidden&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1px 1px 1px 1px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clip-path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;white-space&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nowrap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can&#39;t simply use &lt;code&gt;visibility: hidden&lt;/code&gt; or &lt;code&gt;display: none&lt;/code&gt; as these attributes actually apply to screenreaders as well. Anything hidden that way will also be hidden to screenreaders. This &lt;code&gt;visually-hidden&lt;/code&gt; class makes sure that elements with this styling are invisible to sighted users, but still visible to assistive tech.&lt;/p&gt;
&lt;p&gt;For more information on this approach, check out &lt;a href=&quot;https://www.sarasoueidan.com/blog/inclusively-hiding-and-styling-checkboxes-and-radio-buttons/&quot;&gt;Sara Soueidan&#39;s guide to inclusively hiding &amp;amp; styling checkboxes and radio buttons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For even more semantic goodness, we can now wrap our checkboxes in a &lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt;, which groups together related form-fields. We can then attach the &amp;quot;Which of these cookies can we use?&amp;quot; text to the fieldset by making it a &lt;code&gt;&amp;lt;legend&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;fieldset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;legend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Which of these cookies can we use?&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;legend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;choc-chip&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;choc-chip&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;pretty-checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-hidden&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Chocolate chip
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;oatmeal-raisin&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;oatmeal-raisin&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;pretty-checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Oatmeal &lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;amp;&quot;&gt;&amp;amp;amp;&lt;/span&gt; raisin
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;fieldset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;VoiceOver:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Which of these cookies can we use?, group&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;buttons&quot; tabindex=&quot;-1&quot;&gt;Buttons&lt;/h3&gt;
&lt;p&gt;The &amp;quot;buttons&amp;quot; - actually &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements with added &lt;code&gt;onclick&lt;/code&gt; attributes - are recognised as &amp;quot;Clickable&amp;quot; by Voiceover. Not all screenreader software is able to identify clickable elements in this way.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Accept all, clickable&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, if I&#39;m navigating through the page using only the keyboard, I can&#39;t select one because the browser doesn&#39;t know it&#39;s a button.&lt;/p&gt;
&lt;p&gt;Sure, we &lt;em&gt;could&lt;/em&gt; go adding a &lt;code&gt;tabindex&lt;/code&gt; attribute to these buttons to make them tabbable, but then you run the risk of messing up the natural tabbing order of the document. The best solution is to &lt;strong&gt;use a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element&lt;/strong&gt;. That&#39;s what it&#39;s for, and you can style it pretty much any way you like using CSS. You can even &lt;a href=&quot;https://codepen.io/sophiekoonin/pen/oNZdebX&quot;&gt;make a button look like a link, and vice versa&lt;/a&gt;, which is something I do a lot when building a website to a design spec.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;  &lt;span class=&quot;token selector&quot;&gt;.button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 6px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;align-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.8rem 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #420a55&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.button:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #8d6c99&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Accept all, button&lt;/p&gt;
&lt;p&gt;You are currently on a button... to click this button, press Ctrl+Option+Space.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;headings&quot; tabindex=&quot;-1&quot;&gt;Headings&lt;/h3&gt;
&lt;p&gt;We&#39;ve got three H1 elements on the page: the site name, the article headline, and the cookie banner. A site should only ever have one &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; element - it&#39;s the main heading that tells you what the page is about. Any other headings should be nested underneath, or possibly not even headings at all (in the case of &amp;quot;The Woofer Times&amp;quot;, this doesn&#39;t need to be an actual heading).&lt;/p&gt;
&lt;p&gt;There&#39;s a subheading within the article which is simply bold text. That should be a lower-level nested heading, in this case an &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Screenreaders allow users to navigate by headings, so it&#39;s important they&#39;re in a logical order.&lt;/p&gt;
&lt;p&gt;If you want big text, style it with CSS rather than relying on the browser default size of heading elements.&lt;/p&gt;
&lt;h3 id=&quot;bold-and-italic-text&quot; tabindex=&quot;-1&quot;&gt;Bold and italic text&lt;/h3&gt;
&lt;p&gt;The byline and the image caption are displayed in italics using the &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; tag. However, screen readers may actually read this out differently because they expect the &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; tag to signal &lt;b&gt;emphasis&lt;/b&gt; - that&#39;s what &lt;code&gt;em&lt;/code&gt; means. So the tone of voice might change as it&#39;s being read out, with additional stress. The italics in the byline and caption are a stylistic choice, so we should italicise it with CSS rather than &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; tag still exists alongside the &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; tag, but represents text that stands out from the rest of the body, such as scientific names, thoughts, technical terms and idiomatic terms from other languages:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;The red fox, &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Vulpes vulpes&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;, is commonly found... 
It was a case of &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;fr&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;déjà vu&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;.
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Sure&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;, she thought, &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;that must be it&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;.

I &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;really&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; don&#39;t like this book.
Do it &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;now&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;!
No, &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;I&#39;m&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Spartacus.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same goes for any bold text - &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; indicates significance or keywords and may also result in the screenreader voice changing when read out, so use CSS if the effect is purely presentational.&lt;/p&gt;
&lt;p&gt;Somewhat confusingly, both &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; are used in slightly different situations despite both rendering as bold text in browsers - &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; &amp;quot;indicates that its contents have strong importance, seriousness, or urgency&amp;quot; (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong&quot;&gt;MDN&lt;/a&gt;), where as &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; is used to draw attention without the urgency (e.g. for relevant terms in a blog post like this one).&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;I won&#39;t tell you again, &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;do not disconnect the fire alarm&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;.

This is known as &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;semantic HTML&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NB. Frustratingly, Markdown doesn&#39;t seem to distinguish between these elements, so everything is &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt;.  If you want to use &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; you&#39;ll have to add them in as HTML within your Markdown.&lt;/p&gt;
&lt;h3 id=&quot;image-caption&quot; tabindex=&quot;-1&quot;&gt;Image caption&lt;/h3&gt;
&lt;p&gt;It&#39;s clear to sighted users that the text under the image is a caption, but not to screen readers - there&#39;s nothing associating the two at all.&lt;/p&gt;
&lt;p&gt;We can fix that by wrapping the image in a &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; element, then adding a &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; underneath the image.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;figure&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;a lovely fluffy samoyed&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://codepen-assets.s3.eu-west-2.amazonaws.com/alex-russell-saw-Dj8cxyi9ink-unsplash-1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;figcaption&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&quot;The most angery pupper I have ever seen&quot; Photo: &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://unsplash.com/@alexrussellsaw&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Alex Russell-Saw&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;figcaption&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;figure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;VoiceOver says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;The most angery pupper I have ever seen&amp;quot; Photo: Alex Russell-Saw, figure&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;before then reading out the image information:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a lovely fluffy samoyed, image&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;you-can-still-use-divs&quot; tabindex=&quot;-1&quot;&gt;You can still use &lt;code&gt;div&lt;/code&gt;s!&lt;/h2&gt;
&lt;p&gt;As I demonstrated above, &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements can be extremely misused. That&#39;s not to say you shouldn&#39;t use them &lt;em&gt;at all&lt;/em&gt;: on the contrary. But you should only use them for their intended purpose: entirely meaningless containers. I use them to group elements together which I then style with CSS. Or I&#39;ll wrap the entire page in a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and make it into a flex container for my &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;. &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s are great, as long as you don&#39;t use them in place of more meaningful elements.&lt;/p&gt;
&lt;p&gt;You can use the Dev Tools inspector on this very website and see the way I&#39;ve used &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s presentationally: for example, all the stars in the header are divs which are totally hidden from screenreaders, because they&#39;re entirely decorative.&lt;/p&gt;
&lt;h2 id=&quot;the-same-news-site-with-semantic-tags&quot; tabindex=&quot;-1&quot;&gt;The same news site, with semantic tags&lt;/h2&gt;
&lt;p&gt;I&#39;ve recreated the same site using semantic HTML tags, and it looks pretty much exactly the same (save some different margins here and there that are easy enough to fix).&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;265&quot; data-theme-id=&quot;dark&quot; data-default-tab=&quot;result&quot; data-user=&quot;sophiekoonin&quot; data-slug-hash=&quot;yLMjMgQ&quot; data-preview=&quot;true&quot; data-pen-title=&quot;Semantic HTML blog post example: semantic HTML&quot;&gt;
  &lt;span&gt;See the Pen 
    &lt;a href=&quot;https://codepen.io/sophiekoonin/pen/yLMjMgQ&quot;&gt;
      Semantic HTML blog post example: semantic HTML&lt;/a&gt; by &lt;a href=&quot;https://codepen.io/sophiekoonin&quot;&gt;@sophiekoonin&lt;/a&gt;
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;strong&gt;There is still a lot of room for improvement&lt;/strong&gt;. ARIA attributes come into play here, to add extra information about the roles of each part of the page. I&#39;m just illustrating the semantic elements in this example.&lt;/p&gt;
&lt;p&gt;Here are the main differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the page is broken up into &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;, with an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; round the main page content&lt;/li&gt;
&lt;li&gt;there is only one &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; on the page, because it signifies the main heading that tells you what the page is about&lt;/li&gt;
&lt;li&gt;the &amp;quot;Accept all&amp;quot; and &amp;quot;Close&amp;quot; buttons are actual &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; elements&lt;/li&gt;
&lt;li&gt;the checkboxes are &lt;code&gt;&amp;lt;input type=&amp;quot;checkbox&amp;quot;&amp;gt;&lt;/code&gt;, with &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;s on each one
&lt;ul&gt;
&lt;li&gt;they&#39;re also inside a &lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt; with a &lt;code&gt;&amp;lt;legend&amp;gt;&lt;/code&gt; explaining what the checkboxes are for&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;the menu links are in an unordered list, &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;, wrapped in a &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the subheadings in the article are now &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; (the first level of nesting under the main heading)&lt;/li&gt;
&lt;li&gt;the byline and caption are styled italic through CSS, rather than &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; tags&lt;/li&gt;
&lt;li&gt;I&#39;ve used a &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; for the image, with a &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; for the caption to indicate it&#39;s a caption for that image&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;semantics-from-the-start-accessibility-from-the-start&quot; tabindex=&quot;-1&quot;&gt;Semantics from the start = accessibility from the start&lt;/h2&gt;
&lt;p&gt;Using semantic HTML as building blocks for a website will give you a lovely accessible foundation upon which to add your fancy CSS and whizzy JavaScript. You can use semantic elements in JS frameworks, too - I write React every day at work, and always use semantic elements. Your users (and/or your customers) will thank you for it.&lt;/p&gt;
&lt;h2 id=&quot;find-out-more-about-semantic-html&quot; tabindex=&quot;-1&quot;&gt;Find out more about semantic HTML&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element&quot;&gt;MDN: HTML Elements reference&lt;/a&gt; - with explanations of each element&#39;s purpose&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn-the-web.algonquindesign.ca/topics/html-semantics-cheat-sheet&quot;&gt;Semantic HTML cheat sheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
      <title>A typical day: pandemic edition</title>
      <link href="https://localghost.dev/blog/a-typical-day-pandemic-edition/"/>
      <updated>2021-01-18T00:00:00Z</updated>
      <id>https://localghost.dev/blog/a-typical-day-pandemic-edition/</id>
      <content type="html">&lt;p&gt;&lt;em&gt;This is a series started by &lt;a href=&quot;http://cdevroe.com/2021/01/07/my-typical-day/&quot;&gt;Colin Devroe&lt;/a&gt;. Some other lovely folks have written their own, including &lt;a href=&quot;https://www.sarasoueidan.com/desk/typical-day/&quot;&gt;Sara Soueidan&lt;/a&gt; and &lt;a href=&quot;https://www.cassie.codes/posts/my-typical-day/&quot;&gt;Cassie Evans&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This post would have looked very different prior to March 2020. Before then, I&#39;d be out at events, meetups or social meetings several nights a week, in the office every day seeing people, and commuting to and from work. Needless to say, as someone who gets energy from being around others, the change to working from home full-time and not going out has had a massive impact on my mental health and my depression has got much worse. I don&#39;t think I would have been nearly as &lt;a href=&quot;https://localghost.dev/2020/12/give-yourself-a-break-lessons-from-burnout/&quot;&gt;burnt out&lt;/a&gt; as I was in December if all of this hadn&#39;t been going on. So this is my &amp;quot;current normal&amp;quot;.&lt;/p&gt;
&lt;h2 id=&quot;my-typical-day&quot; tabindex=&quot;-1&quot;&gt;My typical day&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;08:00 - 09:00:&lt;/strong&gt; wake up, about an hour later than I used to. I&#39;ll usually sit in bed for a bit drinking coffee, talking to my husband, and checking the news/Twitter/notifications (it&#39;s a habit I&#39;m trying to break, but not doing very well).&lt;/p&gt;
&lt;p&gt;At some point during this hour I&#39;ll scrape myself out of bed and shower, get dressed (I can&#39;t work in PJs!) and find some breakfast. If I&#39;m honest, this is usually about 15 mins before I start work, because my bed is extremely comfortable and it&#39;s cold in the house.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;09:00&lt;/strong&gt;: catch up on Slack messages, see if there&#39;s anything that needs my attention, say hi to the team.&lt;/p&gt;
&lt;p&gt;We usually have a quick standup at 10ish, then I get stuck into whatever I&#39;m working on. My days are often peppered with 1:1s, planning meetings and various other things - PR reviews, answering questions on Slack, triaging bugs. At the moment I find it hard to focus for long periods of time so whatever I&#39;m doing I&#39;ll inevitably be distracted by notifications (I need to get into the habit of turning off Slack while I work).&lt;/p&gt;
&lt;p&gt;Depending on what I&#39;m working on, I might be doing web work in React or backend work in Go, or I might be writing a proposal for a feature or technical change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:00 - 13:00&lt;/strong&gt;: lunchtime. Quite an early lunch, a relic from when I used to be up at 7, eating breakfast at 7:30 and hungry by 12. I&#39;ll often go for a walk in the park and listen to a podcast (&lt;a href=&quot;https://darknetdiaries.com/&quot;&gt;Darknet Diaries&lt;/a&gt;, &lt;a href=&quot;https://switchedonpop.com/&quot;&gt;Switched On Pop&lt;/a&gt; and &lt;a href=&quot;https://strongsongspodcast.com/&quot;&gt;Strong Songs&lt;/a&gt; are my favourites) or chat to my husband. On especially good days, we might go down to our garage where we have a little gym set up - I&#39;ve been doing some free weights.&lt;/p&gt;
&lt;p&gt;I miss buying lunch, which is undisputably the most annoying meal to have to make.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:00 - 18:00:&lt;/strong&gt; the rest of my working day - more coding and 1:1s. I block out parts of the day in my calendar so that I have uninterrupted time with no meetings.&lt;/p&gt;
&lt;p&gt;At points I&#39;ll go downstairs to refill my drink and pester my husband.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:00:&lt;/strong&gt; I log off from work. I&#39;m very strict about what time I finish work - never after 6pm.&lt;/p&gt;
&lt;p&gt;Once every couple of weeks I have a therapy session via video call, which has been massively helpful this year.&lt;/p&gt;
&lt;p&gt;I&#39;m a creative person and I originally imagined I&#39;d spend a lot of the free time I gained from not commuting doing things like making music or building cool things, but in reality I just browse the internet or play a video game.&lt;/p&gt;
&lt;p&gt;I&#39;ve been thinking a lot about &lt;a href=&quot;https://butyoudontlooksick.com/articles/written-by-christine/the-spoon-theory/&quot;&gt;spoon theory&lt;/a&gt; recently. Originally conceived as an analogy to living with chronic illness, I think it fits perfectly with the way my depression has been manifesting itself this year: I just haven&#39;t got enough spoons right now to do all the things I want to do. So often when I feel I should be being productive or doing something creative, all I&#39;ll have the energy to do is just sit down and play video games. Over Xmas it was Assassin&#39;s Creed: Valhalla, then a lot of &lt;a href=&quot;https://www.stardewvalley.net/&quot;&gt;Stardew Valley&lt;/a&gt;, and I&#39;ve racked up an embarrassing number of hours on &lt;a href=&quot;https://www.twopointhospital.com/&quot;&gt;Two Point Hospital&lt;/a&gt; over the last few weeks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:00:&lt;/strong&gt; time to make dinner, or continue to veg out while my husband makes dinner. Or we give up and order a takeaway.&lt;/p&gt;
&lt;p&gt;We&#39;ve been making an extra effort to eat at the table over the last few months, which is nice because we actually talk to each other and pay attention to what we&#39;re eating instead of just zoning out in front of the TV 🙈&lt;/p&gt;
&lt;p&gt;Generally after dinner we&#39;ll watch TV, or continue playing video games next to each other because we&#39;re adults and make sensible choices. Sometimes I&#39;ll pick up whatever embroidery I have in progress and do it while we watch TV - I&#39;m currently embroidering my wedding flowers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://localghost.dev/img/blog/typical-day/embroidery.jpg&quot; alt=&quot;&quot; title=&quot;An embroidery in progress featuring statice, a peony, roses and a gerbera&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;https://localghost.dev/img/blog/typical-day/wedding-flowers.jpg&quot; alt=&quot;&quot; title=&quot;A photo of my wedding bouquet&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve started keeping a little journal of good things that happen every day. They can be as minor as &amp;quot;made a &lt;a href=&quot;https://www.bbcgoodfood.com/recipes/aubergine-tomato-halloumi-pie&quot;&gt;really nice pie&lt;/a&gt;&amp;quot; or something like &amp;quot;got really positive feedback on the proposal I wrote&amp;quot;. I tend to fixate on negative things, so having written evidence that good stuff is still happening should hopefully anchor me a bit and remind me of the positive side. I recommend it! Plus it&#39;s an excuse to buy a notebook and some nice pens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:00:&lt;/strong&gt; time for bed. Over the last six years I&#39;ve been going to bed at 11pm so religiously that I actually get sleepy at 11pm automatically. I try and read a bit before bed (currently making my way through Elena Ferrante&#39;s Neapolitan Novels) but usually I only manage a few pages before I conk out.&lt;/p&gt;
&lt;p&gt;I feel like this wasn&#39;t a particularly exciting or enlightening typical day, but really, how can it be? Like &lt;a href=&quot;https://www.cassie.codes/posts/my-typical-day/&quot;&gt;Cassie&lt;/a&gt; says: &amp;quot;If you&#39;re reading these posts - whatever your day looks like - whether you smashed through a huge to-do list or not. You&#39;re doing good, remember to be kind to yourself.&amp;quot;.&lt;/p&gt;
&lt;p&gt;All we can really do is make it through the day, focus on the tiny things that make us happy, and know that there is a light at the end of the tunnel. At the time of writing, 5.8% of the UK population have received their first dose of the vaccine (including my dad, hey dad!). Good news is out there, it&#39;s just not as clickbaity as the bad news, so we see less of it.&lt;/p&gt;
&lt;p&gt;Now, if you&#39;ll excuse me, I have a fake hospital to run.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Give yourself a break: lessons from burnout</title>
      <link href="https://localghost.dev/blog/give-yourself-a-break-lessons-from-burnout/"/>
      <updated>2020-12-16T00:00:00Z</updated>
      <id>https://localghost.dev/blog/give-yourself-a-break-lessons-from-burnout/</id>
      <content type="html">&lt;p&gt;I started writing this post a few days ago, and was so exhausted I couldn&#39;t actually be bothered to finish it, which tells you a lot really. And if you&#39;re too exhausted to read another blog post, here&#39;s a summary: have a rest. Go and do something nice. Tech can wait.&lt;/p&gt;
&lt;p&gt;A couple of weeks ago (possibly - I have no concept of time any more) I tweeted this, which seemed to resonate with folks:&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/type__error&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1474717155983773700/sXRdF5e3_normal.jpg&quot; alt=&quot;Avatar for type__error&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/type__error&quot;&gt;Sophie Koonin &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/type__error/status/1335329159200055303&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;the only coding I&#39;ve done outside of work in quite a long time is a small bit of JS to apply hats to emoji. I don&#39;t know how anyone does tech stuff out of hours, it&#39;s exhausting enough doing it IN hours&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=1335329159200055303&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;160 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Dec 5, 2020 09:04:15 (UTC)&quot; href=&quot;https://twitter.com/type__error/status/1335329159200055303&quot;&gt;10:04 PM · Dec 5, 2020&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;(A plug for &lt;a href=&quot;https://emojinator.fun/&quot;&gt;emojinator&lt;/a&gt;, the site in question where you can add hats to emoji)&lt;/p&gt;
&lt;p&gt;Twitter is awash with fantastically talented people building incredible stuff and sharing it, as well as those insufferable grifters who tweet things like &amp;quot;15 things you must learn to be a good developer #100daysofcode&amp;quot; and then hawk you their ebook. &amp;quot;Thought leaders&amp;quot; (don&#39;t you just hate that term) are churning out article after article of brilliant leadership advice, technical deep-dives, creative things to do in CSS, you name it. I don&#39;t know about you, but the article count on my RSS reader has been increasing for months and I just have &lt;em&gt;not&lt;/em&gt; been reading anything. I can barely focus at the moment.&lt;/p&gt;
&lt;p&gt;I became a tech lead in September, which is something I&#39;ve been wanting to do for quite a while. One day I&#39;ll write about what I&#39;ve learned as a new tech lead, but now is not the time, because I don&#39;t think I&#39;ve been able to really succeed in my role due to the constant being-on-fire-ness of this entire year. Instead, have some lessons from the trash fire of 2020 that we can take over into the slightly-smaller-but-still-burning trash fire of 2021.&lt;/p&gt;
&lt;h2 id=&quot;lesson-number-1-give-yourself-a-break&quot; tabindex=&quot;-1&quot;&gt;Lesson number 1: give yourself a break&lt;/h2&gt;
&lt;p&gt;As a new tech lead, I&#39;ve felt like I&#39;m letting my team down because I don&#39;t have the mental capacity to sit and read about the things I need to improve on, like observability and monitoring. I&#39;ve had a copy of the SRE Workbook on my laptop waiting to be read, which I&#39;ve barely touched.&lt;/p&gt;
&lt;p&gt;Where did all these expectations come from? Myself, mostly. Nobody is peering over my shoulder going &amp;quot;why aren&#39;t you reading about Prometheus?&amp;quot;. (Actually, my team have been nothing but supportive this entire year, and I love them all.)&lt;/p&gt;
&lt;p&gt;The coronavirus pandemic has meant that my &lt;a href=&quot;https://shechoir.com/london&quot;&gt;choir&lt;/a&gt; hasn&#39;t been able to meet since March. The choir I&#39;ve been a part of for seven years, have been co-running for five of those years, and which forms a huge part of my own identity. I&#39;m an extrovert who gets energy from being around people (but not in a Colin Robinson kind of way). Most of us have barely seen our friends and family. This year has been intensely difficult. I&#39;m sure you&#39;ve all experienced something similar.&lt;/p&gt;
&lt;p&gt;On top of that, the political situation is dire. As well as the absolute corrupt shambles of a government who have done such a terrible job of handling the COVID pandemic that we have the highest death rate in Europe, there&#39;s a big red countdown timer on the UK government website that counts down to Brexit when I&#39;m looking up just what the hell being in Tier 3 actually means. Many, many people have lost loved ones, there&#39;s a whole wave of anti-vaccine &amp;quot;Keep Britain Free&amp;quot; zealots popping up all over the place, and the UK has been nicknamed &amp;quot;TERF island&amp;quot; because of the rampant transphobia being spouted by formerly respectable public figures. And that&#39;s just on our doorsteps in the UK.&lt;/p&gt;
&lt;p&gt;The SRE Workbook can wait, basically. We have enough on our collective plates.&lt;/p&gt;
&lt;h2 id=&quot;lesson-number-2-if-it-benefits-your-work-it-can-be-done-in-working-hours&quot; tabindex=&quot;-1&quot;&gt;Lesson number 2: if it benefits your work, it can be done in working hours&lt;/h2&gt;
&lt;p&gt;Perhaps you used to watch conference talks on the daily commute, or read technical books in bed. I definitely had a copy of The Manager&#39;s Path next to the bed that I was chipping away at at one point. But now that commute is gone for many of us, and I don&#39;t know about you but the only thing I have the energy for in the evenings is playing video games or idly browsing the internet.&lt;/p&gt;
&lt;p&gt;I believe quite strongly that if you are learning something in order to become better at your job, you should be doing that within working hours. In my team, we all know that &lt;em&gt;technically&lt;/em&gt; it&#39;s encouraged to take time out to learn something - we get a budget to do just that - but we rarely ever do it. To that end, I actually scheduled 2 hours a week in the whole team&#39;s calendar on a Friday for &amp;quot;Reading time&amp;quot;. They can use that time however they want: they can watch videos, try something out, read a book. They can take more time, or less time. They can do it on a different day, or skip it entirely. All it is is the explicit permission to take some time out of your week to learn something, &lt;em&gt;if you want&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;(I don&#39;t know if they actually take the time: I haven&#39;t asked. I just want them to know they can, in writing.)&lt;/p&gt;
&lt;h2 id=&quot;lesson-number-3-some-things-are-boring-and-that-s-okay&quot; tabindex=&quot;-1&quot;&gt;Lesson number 3: some things are boring and that&#39;s okay&lt;/h2&gt;
&lt;p&gt;Kubernetes does not interest me. I have the utmost respect for the infra folks on Twitter who seem super intelligent and knowledgeable about it, but I cannot bring myself to be interested in it. For a while I felt like not being into this stuff made me a lesser developer somehow. But my specialism lies elsewhere and there are things I&#39;m good at that people who do Kubernetes for a living aren&#39;t (and I have it on good authority that &amp;quot;do Kubernetes&amp;quot; is a technical term). That&#39;s how well-functioning teams work.&lt;/p&gt;
&lt;p&gt;I know enough &lt;code&gt;kubectl&lt;/code&gt; to get a list of pods and SSH into one of them (but mainly because I have the zsh autocomplete plugin and it fills it in for me based on my past commands). That&#39;s all I need: most of the actual Kubernetes wizardry is automated or managed by people who specialise in it (in my case, our excellent Infrastructure Platform team). A good developer or tech lead will know when it&#39;s the right time to delegate to someone who knows more about it than they do.&lt;/p&gt;
&lt;h2 id=&quot;lesson-number-4-mental-health-is-more-important-than-kp-is&quot; tabindex=&quot;-1&quot;&gt;Lesson number 4: mental health is more important than KPIs&lt;/h2&gt;
&lt;p&gt;I&#39;ll caveat this section with the fact that I know many people work at companies where mental health is not something to be discussed, or something that people really seem to take into account. And I also know it&#39;s not as simple as &amp;quot;find another job&amp;quot; and that should never be the only solution.&lt;/p&gt;
&lt;p&gt;Instead, this is an appeal to senior leadership and management. Your employees are not inanimate batteries you plug in to power your company. They are humans, with complicated lives, families they haven&#39;t been able to see for months, and children having to stay home from school because someone in their class tested positive for COVID. Their partner might have lost their job this year. They may be struggling with working from home, missing being around people in the office, or they might be finding video calls even more exhausting than real-life meetings. If you&#39;ve sadly had to make redundancies this year, are you expecting the reduced number of people to do the same amount of work as before?&lt;/p&gt;
&lt;p&gt;If you&#39;re a team lead, check in on your team. Are they working long hours? Encourage them to sign off earlier. I highly recommend this &lt;a href=&quot;https://twitter.com/kkukshtel/status/1338240765605109762&quot;&gt;Twitter thread&lt;/a&gt; about why working late a couple of hours a week can lead to completely unrealistic expectations of how long projects take to complete.&lt;/p&gt;
&lt;p&gt;Schedule 1:1s with them and ask them how they&#39;re feeling about things. Encourage them to share their workload if they have too much: having more on your plate than you can cope with right now is not a sign of failure.&lt;/p&gt;
&lt;p&gt;Mental health should be taken as seriously as physical health. We have trained &lt;a href=&quot;https://mhfaengland.org/&quot;&gt;mental health first aiders&lt;/a&gt; who can provide a swift response over Slack, an Employee Assistance Program for access to counselling and legal information, and people can take mental health days if they are struggling. I&#39;ve taken three afternoons off at the last minute in the past three weeks because I was so exhausted with &lt;em&gt;[gestures wildly]&lt;/em&gt; everything, and my team were very understanding about it. I know I am privileged in that regard, and I am grateful.&lt;/p&gt;
&lt;p&gt;Ultimately, the productivity and success of the company will suffer more if your team burn out. People will start taking extended leave, or they&#39;ll quit. It&#39;s a choice between delaying a product release by a day and the mental health of a human being.&lt;/p&gt;
&lt;p&gt;--&lt;/p&gt;
&lt;p&gt;Sorry folks, I wrote a long article when I promised you you didn&#39;t have to read articles. Go and have a cup of tea and look out of the window for a bit.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/type__error&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1474717155983773700/sXRdF5e3_normal.jpg&quot; alt=&quot;Avatar for type__error&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/type__error&quot;&gt;Sophie Koonin &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/type__error/status/1335331963243257857&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;I guess what I&#39;m saying is I&#39;m really tired, it&#39;s okay if you&#39;re really tired, let&#39;s all be really tired and accept that the Google SRE Workbook will stay untouched on the shelf for a few more weeks&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=1335331963243257857&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;20 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Dec 5, 2020 09:15:23 (UTC)&quot; href=&quot;https://twitter.com/type__error/status/1335331963243257857&quot;&gt;10:15 PM · Dec 5, 2020&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;</content>
    </entry>
    
    <entry>
      <title>7 myths designers and developers believe about web accessibility</title>
      <link href="https://localghost.dev/blog/7-myths-designers-and-developers-believe-about-web-accessibility/"/>
      <updated>2020-10-11T00:00:00Z</updated>
      <id>https://localghost.dev/blog/7-myths-designers-and-developers-believe-about-web-accessibility/</id>
      <content type="html">&lt;p&gt;In an ideal world, being &amp;quot;good at accessibility&amp;quot; wouldn&#39;t make you stand out from the crowd. Companies wouldn&#39;t be hiring accessibility experts to help them unpick and untangle the inaccessible products they&#39;ve been building for years. Speaking about web accessibility at a conference would be as unnecessary as getting up on stage and giving a talk on how to write HTML.&lt;/p&gt;
&lt;p&gt;But we don&#39;t live in that world, and the web is full of inaccessible websites. Try navigating the &lt;a href=&quot;https://libertylondon.com/&quot;&gt;Liberty London&lt;/a&gt; website with just your keyboard - can you add something to your basket?&lt;/p&gt;
&lt;p&gt;I want to bust a few myths that I&#39;ve come across over the years, in the hope that accessibility will stop being something to &amp;quot;deal with later&amp;quot; and start being something that people think about from the start.&lt;/p&gt;
&lt;h2 id=&quot;myth-1-the-majority-of-your-users-don-t-have-access-needs&quot; tabindex=&quot;-1&quot;&gt;Myth 1: The majority of your users don&#39;t have access needs&lt;/h2&gt;
&lt;p&gt;Repeat after me: there&#39;s no such thing as a &amp;quot;typical&amp;quot; or &amp;quot;normal&amp;quot; user.&lt;/p&gt;
&lt;p&gt;When we&#39;re designing and building websites or apps, it&#39;s all too easy to forget that we are coming from a narrow perspective. My own abilities, experiences and knowledge shape how I interact with a website, and my experience may be totally different from yours. So even if you know instinctively that a particular icon means &amp;quot;download&amp;quot;, others might not make that connection.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/accessibility-myths/spiderman-point.jpg&quot; alt=&quot;Spiderman pointing at himself&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Is this how you see your users?&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Microsoft&#39;s brilliant &lt;a href=&quot;https://www.microsoft.com/design/inclusive/&quot;&gt;Inclusive Design Toolkit&lt;/a&gt; warns of the consequences of this selective thinking:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“If we use our own abilities and biases as a starting point, we end up with products designed for people of a specific gender, age, language ability, tech literacy, and physical ability. Those with specific access to money, time, and a social network.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As a non-disabled, computer-literate developer with a Macbook and fibre internet, I need to make sure I&#39;m not only building apps for people like me. All my users will have different requirements, different technology and different backgrounds. As the Inclusive Design Toolkit puts it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“When it comes to people, there’s no such thing as “normal.” The interactions we design with technology depend heavily on what we can see, hear, say, and touch. Assuming all those senses and abilities are fully enabled all the time creates the potential to ignore much of the range of humanity.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;myth-2-accessibility-is-optional&quot; tabindex=&quot;-1&quot;&gt;Myth 2: Accessibility is optional&lt;/h2&gt;
&lt;p&gt;It&#39;s actually the law, in both the UK and the USA (I can&#39;t speak for other countries, but I&#39;d be keen to hear from anyone who knows about any legislation in other places).&lt;/p&gt;
&lt;p&gt;In the UK, The Equality Act 2010 makes it illegal to discriminate against people with disabilities - and that includes by accident! It doesn’t explicitly refer to websites, but the Equality and Human Rights Commission’s (EHRC) Code of Practice does list websites as one of the services to the public that should be considered covered by the Equality Act.&lt;/p&gt;
&lt;p&gt;According to the EHRC, an organisation &amp;quot;is responsible for ensuring that reasonable adjustments have been made where needed, for example by changing the size of the font, to ensure that disabled users are able to get the information, without being placed at a substantial disadvantage (even if the [organization] employs an external organisation to build and maintain its website).&amp;quot;&lt;/p&gt;
&lt;p&gt;The EHRC may:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Conduct formal investigations&lt;/li&gt;
&lt;li&gt;Serve non-discrimination notices&lt;/li&gt;
&lt;li&gt;Act over persistent discrimination&lt;/li&gt;
&lt;li&gt;Issue &lt;a href=&quot;https://www.equalityhumanrights.com/sites/default/files/employercode.pdf&quot;&gt;Codes of Practice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Help someone prosecute a company - this requires someone to actually sue the company, which there&#39;s no case law for at the moment - but that could change any time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the US, there&#39;s the Americans with Disabilities Act (ADA). Famously, &lt;a href=&quot;https://www.bbc.co.uk/news/technology-46894463&quot;&gt;Domino&#39;s Pizza was sued&lt;/a&gt; by a blind person who couldn&#39;t use the website with a screenreader. According to US law firm &lt;a href=&quot;https://www.adatitleiii.com/2020/04/the-curve-has-flattened-for-federal-website-accessibility-lawsuits/&quot;&gt;Seyfarth&lt;/a&gt;, in 2019 there were 2,256 web accessibility lawsuits filed.&lt;/p&gt;
&lt;h2 id=&quot;myth-3-access-needs-come-from-permanent-disabilities&quot; tabindex=&quot;-1&quot;&gt;Myth 3: Access needs come from permanent disabilities&lt;/h2&gt;
&lt;p&gt;The truth is, anyone at any time can have access needs, and they can be permanent, temporary or situational. Temporary impairments might be due to a medical condition, and situational impairments may result from the environment around us - a situation we&#39;re in.&lt;/p&gt;
&lt;p&gt;Some examples of permanent conditions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;partial or full blindness&lt;/li&gt;
&lt;li&gt;having a limb amputated&lt;/li&gt;
&lt;li&gt;learning difficulties, such as dyslexia&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A temporary impairment could be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;visual aura from a migraine&lt;/li&gt;
&lt;li&gt;repetitive strain injury making it difficult to use a mouse&lt;/li&gt;
&lt;li&gt;cognitive processing difficulties (also known as brain fog) following an illness&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Situational impairments might include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a bright light causing glare on the screen&lt;/li&gt;
&lt;li&gt;loud noise, making it difficult to concentrate&lt;/li&gt;
&lt;li&gt;slow internet&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even if you consider yourself not to have any form of disability, you could find yourself with a temporary or situational impairment at any point.&lt;/p&gt;
&lt;p&gt;Often when developers &lt;em&gt;do&lt;/em&gt; think about accessibilty, they think of screen readers and people with visual impairments. That&#39;s a start, but there’s a spectrum of what constitutes disability. The important thing to note is that it doesn’t necessarily translate to some kind of personal health condition.&lt;/p&gt;
&lt;p&gt;According to the World Health Organisation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Disability is not just a health problem. It is a complex phenomenon, reflecting the interaction between features of a person’s body and features of the society in which [they live].&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So much of the world around us can influence whether or not we have, or consider ourselves to have, a disability. Summed up perfectly in this Tweet by &lt;a href=&quot;https://twitter.com/amandaleduc/status/1300083052710899712&quot;&gt;Amanda Leduc&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/AmandaLeduc&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1412041452675383297/qD9K8Hh4_normal.jpg&quot; alt=&quot;Avatar for AmandaLeduc&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/AmandaLeduc&quot;&gt;Amanda Leduc ♿️ &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/AmandaLeduc&quot;&gt;@AmandaLeduc&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/AmandaLeduc/status/1300083052710899712&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;If you wear corrective lenses of any kind, you have a disability. And guess what: your disability has already been accommodated, which is part of why you might not see yourself as disabled.&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=1300083052710899712&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;152136 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Aug 30, 2020 02:48:48 (UTC)&quot; href=&quot;https://twitter.com/AmandaLeduc/status/1300083052710899712&quot;&gt;04:48 PM · Aug 30, 2020&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;h2 id=&quot;myth-4-accessibility-is-a-barrier-to-good-design&quot; tabindex=&quot;-1&quot;&gt;Myth 4: Accessibility is a barrier to good design&lt;/h2&gt;
&lt;p&gt;I&#39;ve heard this one a lot. And as a web developer I feel like I’m often turning around to designers and saying “Sorry, I can’t do that, because it’s not accessible”. (The best designers will then turn around and say, &amp;quot;okay, let&#39;s find something that works better.&amp;quot;)&lt;/p&gt;
&lt;p&gt;It’s easy to imagine that in order for design to be truly accessible you have to build sites that look like the &lt;a href=&quot;http://info.cern.ch/hypertext/WWW/TheProject.html&quot;&gt;first-ever web page&lt;/a&gt;, but that’s not the case.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/accessibility-myths/accessible-web-page.png&quot; alt=&quot;A screenshot of the first ever web page on cern.ch: white background, black text, blue hyperlinks, no styling whatsoever.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Is this... accessible design?&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The reality is that accessible design &lt;em&gt;is&lt;/em&gt; good design - and vice versa.&lt;/p&gt;
&lt;p&gt;When it comes down to it, can we really call a design good if it doesn’t work for everyone? You can build the sleekest, prettiest user interface, but if someone who uses the keyboard to get around the internet can’t see what they’re doing, are we really going to call that good design?&lt;/p&gt;
&lt;p&gt;As Jessie Hausler, the Director of Product Accessibility at Salesforce, wrote in his article &lt;em&gt;&lt;a href=&quot;https://medium.com/salesforce-ux/7-things-every-designer-needs-to-know-about-accessibility-64f105f0881b&quot;&gt;7 Things Every Designer Needs to Know about Accessibility&lt;/a&gt;&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Accessibility will not force you to make a product that is ugly, boring, or cluttered. It will introduce a set of constraints to incorporate as you consider your design.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we can work within the constraints of the technology we&#39;re building for, or the constraints of what&#39;s in style, we can work within the constraints of accessible design.&lt;/p&gt;
&lt;h2 id=&quot;myth-5-accessibility-is-hard-to-implement&quot; tabindex=&quot;-1&quot;&gt;Myth 5: Accessibility is hard to implement&lt;/h2&gt;
&lt;p&gt;This one has a grain of truth in: accessibility can be difficult to implement &lt;em&gt;retrospectively&lt;/em&gt;. If you&#39;ve built an entire web app without considering accessibility, it can take a lot of time (and cost money) to go back and fix it up so that it&#39;s accessible.&lt;/p&gt;
&lt;p&gt;But if you consider accessibility from the &lt;em&gt;start&lt;/em&gt;, factoring it into your designs and the way you write your code, it doesn&#39;t have to be difficult. Accessibility by default is a lot easier than accessibility after the fact.&lt;/p&gt;
&lt;p&gt;As web developers, by sticking to some best practices in HTML we&#39;re already halfway there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;using semantic HTML elements such as &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; to mark up the different parts of the document (I&#39;ll write more about this soon!)&lt;/li&gt;
&lt;li&gt;using &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; for buttons with &lt;code&gt;onclick&lt;/code&gt; attributes, and &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; for links to different pages (and not adding &lt;code&gt;onclick&lt;/code&gt; attributes to anything except buttons!)&lt;/li&gt;
&lt;li&gt;nesting headings correctly, from &lt;code&gt;h1&lt;/code&gt; through to &lt;code&gt;h6&lt;/code&gt; , with only one &lt;code&gt;h1&lt;/code&gt; on the page&lt;/li&gt;
&lt;li&gt;keeping HTML tags for markup, and using CSS for style (rather than, say, using a &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag because you want big text)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many of the things that will help some groups of users with access needs will also benefit others: for example, good semantic HTML is great for screenreader users, but also helpful for keyboard or &lt;a href=&quot;https://gettecla.com/blogs/news/introduction-to-assistive-switches&quot;&gt;adaptive switch&lt;/a&gt; users, as with the right tags the browser knows what should be focusable and what shouldn&#39;t. Labels on forms make them easier to read and understand for people of all cognitive abilities, and they are good for screenreader users too.&lt;/p&gt;
&lt;p&gt;Add an accessibility checklist to your JIRA tickets or pull request templates: make it part of your definition of done. Look out for best practices in your code reviews.&lt;/p&gt;
&lt;h2 id=&quot;myth-6-react-apps-are-inherently-inaccessible&quot; tabindex=&quot;-1&quot;&gt;Myth 6: React apps are inherently inaccessible&lt;/h2&gt;
&lt;p&gt;I seem to follow two camps of people on Twitter: people in the React community, and people who hate the React community. I am by no means a hardcore React stan (and I think the community can be as toxic as any popular programming language/framework community can be) but I use it every day at work and I think it&#39;s just as good as some of the alternatives out there.&lt;/p&gt;
&lt;p&gt;One of the arguments against React that I hear most frequently is that React produces inaccessible websites which are &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s all the way down. &lt;strong&gt;It doesn&#39;t.&lt;/strong&gt; If React apps are a mess of un-semantic &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s, it&#39;s because the developer put them there. The issue is not with the framework, but the lack of semantic HTML knowledge of the developers who use it: you can write any HTML you like in JSX.&lt;/p&gt;
&lt;p&gt;Ultimately, screen readers tend to ignore &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; components, as they&#39;re generic containers. If we use semantic HTML elements for the actual content of the page, there shouldn&#39;t be a problem at all from a markup perspective.&lt;/p&gt;
&lt;p&gt;React components can&#39;t return more than one element, so with older versions of React you had to wrap multiple elements in a container &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. This would have added a few extra containers to the page. But the introduction of &lt;a href=&quot;https://reactjs.org/docs/fragments.html&quot;&gt;React Fragments&lt;/a&gt; solved that, and usually doesn&#39;t result in any additional elements being added to the DOM.&lt;/p&gt;
&lt;p&gt;Another thing to watch out for is screen readers not alerting users when content on the page changes. This isn&#39;t a React problem specifically but a &lt;a href=&quot;https://codeburst.io/building-accessible-single-page-apps-2ea3e4fbbc01&quot;&gt;single-page application (SPA) problem&lt;/a&gt;, and it can be solved with ARIA live regions. You&#39;d still have this problem if you built your app in vanilla JavaScript.&lt;/p&gt;
&lt;p&gt;Ideally, your SPA should also work with JavaScript turned off - with React we have tools like &lt;a href=&quot;https://localghost.dev/blog/7-myths-designers-and-developers-believe-about-web-accessibility/nextjs.org/&quot;&gt;Next.js&lt;/a&gt; which produce isomorphic (server- and client-side) applications.&lt;/p&gt;
&lt;h2 id=&quot;myth-7-automated-testing-will-catch-all-accessibility-problems&quot; tabindex=&quot;-1&quot;&gt;Myth 7: Automated testing will catch all accessibility problems&lt;/h2&gt;
&lt;p&gt;People love to show off their Lighthouse scores, don&#39;t they?&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/accessibility-myths/lighthouse-badges.png&quot; alt=&quot;A screenshot of a github repo that offers Lighthouse badges for your repository or website so you can brag about your site&#39;s awesome Lighthouse performance&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Is it though?&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; is an open-source automated testing tool that you can run as a Node module, from the CLI, from Chrome or as part of your CI checks. It checks for performance, accessibility, progressive web app performance, best practices and SEO. It&#39;s a great tool, but unfortunately it&#39;s often treated as a silver bullet. A high Lighthouse score is a good thing, but it doesn&#39;t mean that you&#39;ve &amp;quot;done&amp;quot; accessibility - there are always things that Lighthouse won&#39;t be able to detect.&lt;/p&gt;
&lt;p&gt;The best way to test accessibility is to approach it from all sides. Automated tooling such as Lighthouse or &lt;a href=&quot;https://www.deque.com/axe/&quot;&gt;axe&lt;/a&gt;) can and should be part of your development process, as it&#39;ll provide a quick feedback loop for common accessibility problems in your markup and CSS. But what these tools don&#39;t account for are things like the quirks of different screenreader software (and boy, do they have quirks), or the other kinds of assistive technology people will use to access your website. Lighthouse can&#39;t tell you whether your site is still legible when it&#39;s zoomed in 600%, or whether you can interact with the various parts of the app with a keyboard as you would with a mouse. The Accessibility in Government blog has a great article about &lt;a href=&quot;https://accessibility.blog.gov.uk/2017/02/24/what-we-found-when-we-tested-tools-on-the-worlds-least-accessible-webpage/&quot;&gt;what they found when they tested automated tooling on the world&#39;s least accessible webpage&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before you merge your PR, check how your new feature behaves with screenreaders: Macs and iPhones come with &lt;a href=&quot;https://www.apple.com/uk/accessibility/iphone/vision/&quot;&gt;VoiceOver&lt;/a&gt;, Android has &lt;a href=&quot;https://support.google.com/accessibility/android/answer/6283677?hl=en&quot;&gt;TalkBack&lt;/a&gt;, Linux has &lt;a href=&quot;https://help.gnome.org/users/orca/stable/introduction.html.en&quot;&gt;Orca&lt;/a&gt;, and you can download the open-source &lt;a href=&quot;https://www.nvaccess.org/download/&quot;&gt;NVDA&lt;/a&gt; screenreader for Windows. Does it read out everything it&#39;s supposed to? Is it reading out anything it&#39;s &lt;em&gt;not&lt;/em&gt; supposed to? Are the headings in the right order?&lt;/p&gt;
&lt;p&gt;Ultimately, there’s no substitute for getting people with actual access needs to use the website or app. You can do some user testing, commission a formal audit, and/or make sure you have ways for users to give you feedback. Include disabled users in your UX research upfront, as well, so that you&#39;re building something accessible right from the start.&lt;/p&gt;
&lt;h2 id=&quot;it-s-your-turn&quot; tabindex=&quot;-1&quot;&gt;It&#39;s your turn&lt;/h2&gt;
&lt;p&gt;If you encounter any of these myths, &lt;strong&gt;you can (and should) challenge them&lt;/strong&gt;. A lot of the time, these myths stick around because accessibility just isn&#39;t being talked about. Be the one to bring it up, and get others on board too. Bake accessibility into your design systems and your ways of working.&lt;/p&gt;
&lt;h2 id=&quot;resources-and-further-reading&quot; tabindex=&quot;-1&quot;&gt;Resources &amp;amp; further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/design/inclusive/&quot;&gt;Microsoft Inclusive Design Toolkit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/salesforce-ux/7-things-every-designer-needs-to-know-about-accessibility-64f105f0881b&quot;&gt;7 Things Every Designer Needs to Know about Accessibility - Jessie Hausler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.deque.com/accessible-design/&quot;&gt;Deque - Accessible Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.deque.com/accessible-development/&quot;&gt;Deque - Accessible Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marcysutton.com/&quot;&gt;Marcy Sutton&lt;/a&gt; - blog posts and talks from an accessibility expert&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://accessibility.blog.gov.uk/2017/02/24/what-we-found-when-we-tested-tools-on-the-worlds-least-accessible-webpage/&quot;&gt;What we found when we tested tools on the world&#39;s least accessible webpage&lt;/a&gt; - Accessibility in Government Blog&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
      <title>Defending yourself against cross-site scripting attacks with Content-Security-Policy</title>
      <link href="https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/"/>
      <updated>2020-05-03T00:00:00Z</updated>
      <id>https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/</id>
      <content type="html">&lt;p&gt;I spent an entire day last week wrestling with a PDF-rendering library in React which was refusing to work in production. Locally it ran just fine, but as soon as we built our app in production mode, it wasn&#39;t doing anything. Looking at the console, the errors it was spitting out made my heart sink. I&#39;d seen these before.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://localghost.dev/img/blog/csp/eval-error.png&quot;&gt;&lt;img src=&quot;https://localghost.dev/img/blog/csp/eval-error.png&quot; alt=&quot;Console error: Refused to evaluate a string as JavaScript because &#39;unsafe-eval&#39; is not an allowed source of script in the following Content Security Policy directive: script-src &#39;self&#39;.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;The dreaded console error&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;This was an error caused by our &lt;code&gt;Content-Security-Policy&lt;/code&gt; (CSP) header, telling our browser that something in the library should be blocked - specifically, an &lt;code&gt;eval()&lt;/code&gt; function within one of the dependencies of our PDF-rendering library. I did what any self-respecting developer would do and complained at anyone who would listen - okay, I posted on Twitter - and then discovered that a lot of other developers weren&#39;t aware of what CSP was or what it was for. So I thought I&#39;d write a post about it.&lt;/p&gt;
&lt;p&gt;The error messages I was getting were protecting me against a common web security vulnerability - &lt;strong&gt;cross-site scripting attacks&lt;/strong&gt; (XSS). As a web developer, it&#39;s really important to be aware of what XSS is and how to prevent it.&lt;/p&gt;
&lt;h2 id=&quot;contents&quot; tabindex=&quot;-1&quot;&gt;Contents &lt;!-- omit in toc --&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#cross-site-scripting-xss&quot;&gt;Cross-site scripting (XSS)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#stored-xss&quot;&gt;Stored XSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#reflected-xss&quot;&gt;Reflected XSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#dom-based-xss&quot;&gt;DOM-based XSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#preventing-xss&quot;&gt;Preventing XSS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#content-security-policy-is-a-set-of-rules-about-permitted-content-on-a-website&quot;&gt;Content-Security-Policy is a set of rules about permitted content on a website&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#fetch-directives&quot;&gt;Fetch directives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#document-directives&quot;&gt;Document directives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#navigation-directives&quot;&gt;Navigation directives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#sources&quot;&gt;Sources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#unsafe-inline-and-the-risks-of-inline-styles&quot;&gt;&lt;code&gt;unsafe-inline&lt;/code&gt; and the risks of inline styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#scripts-unsafe-inline-and-unsafe-eval-why-eval-is-evil&quot;&gt;Scripts, &lt;code&gt;unsafe-inline&lt;/code&gt; and &lt;code&gt;unsafe-eval&lt;/code&gt;: why &lt;code&gt;eval()&lt;/code&gt; is evil&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#how-to-create-a-csp-header&quot;&gt;How to create a CSP header&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#adding-to-a-csp-header&quot;&gt;Adding to a CSP header&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#styled-components-and-unsafe-inline&quot;&gt;styled-components and &lt;code&gt;unsafe-inline&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#environment-based-csp&quot;&gt;Environment-based CSP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#the-moral-of-the-story&quot;&gt;The moral of the story&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#references--further-reading&quot;&gt;References &amp;amp; further reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cross-site-scripting-xss&quot; tabindex=&quot;-1&quot;&gt;Cross-site scripting (XSS)&lt;/h2&gt;
&lt;p&gt;XSS involves someone injecting malicious code into an unsuspecting website, which then executes on the victim&#39;s computer.&lt;/p&gt;
&lt;p&gt;This injected code can do things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;copy your cookies and send them to the attacker&lt;/li&gt;
&lt;li&gt;get information about your location or data from your webcam etc&lt;/li&gt;
&lt;li&gt;grab session tokens from local storage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On websites that store sensitive information, such as banking or shopping websites, XSS vulnerabilities could allow someone to impersonate you by stealing your access token and using it to log in as you - which means gold-plated toilet seats for them, and a nice credit card bill for you.&lt;/p&gt;
&lt;p&gt;The Open Web Application Security Project (OWASP) recognises three different methods of XSS: &lt;strong&gt;stored&lt;/strong&gt;, &lt;strong&gt;reflected&lt;/strong&gt; and &lt;strong&gt;DOM-based&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;stored-xss&quot; tabindex=&quot;-1&quot;&gt;Stored XSS&lt;/h3&gt;
&lt;p&gt;Stored XSS refers to malicious code sent in the server response from something like a database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: on a website allowing people to submit comments to discuss news articles, a user submits the following comment:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Great stuff guys!&amp;lt;script&amp;gt;/* sneaky code */&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the website isn&#39;t sanitizing any user input (stripping it of any HTML tags) and is rendering the comment directly into the DOM, that script will execute as soon as you load the page it&#39;s on. Before you know it, your browser has posted comments all over the site declaring your support for some deeply unpleasant organisation.&lt;/p&gt;
&lt;p&gt;There&#39;s a really interesting &lt;a href=&quot;https://darknetdiaries.com/episode/61/&quot;&gt;episode of the podcast Darknet Diaries&lt;/a&gt; featuring Samy, the (accidental) creator of a MySpace worm that used XSS to get people&#39;s profiles to automatically add him as a friend. This is a great example of stored XSS, because it all started with something he posted on his own profile (which was then stored in MySpace&#39;s database).&lt;/p&gt;
&lt;h3 id=&quot;reflected-xss&quot; tabindex=&quot;-1&quot;&gt;Reflected XSS&lt;/h3&gt;
&lt;p&gt;This is where malicious user input in a request is sent back in the immediate server response, executing on the receiving client&#39;s browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: someone posts a link online to a shopping website, with a search term in the URL so it takes you directly to the catalog page. But they&#39;ve also included some &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags in the query string.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://niche-tshirts.com/shirts?q=extreme+ironing&amp;lt;script&amp;gt;/* sneaky code */&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;When the server receives the request, it takes the entire query string and uses that as the search parameter. In the HTML that it returns, it renders:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;You searched for: amateur paleontology&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* sneaky code */&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, your poor unsuspecting browser will execute what&#39;s between those script tags, and the attacker will be able to gain access to your access token and anything else they&#39;ve managed to scrape.&lt;/p&gt;
&lt;h3 id=&quot;dom-based-xss&quot; tabindex=&quot;-1&quot;&gt;DOM-based XSS&lt;/h3&gt;
&lt;p&gt;Finally, DOM-based XSS is when JavaScript running on the page uses data from somewhere the attacker can control, such as &lt;code&gt;window.location&lt;/code&gt; (the URL of the page). Say we have some JS on our page which takes &lt;code&gt;window.location&lt;/code&gt; and then executes some function with it:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://niche-tshirts.com/shirts?q=spelunking&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; searchTerm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;q&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;p&gt;You searched for: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;searchTerm&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/p&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If our attacker from before sends another link with the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags in the query string, these will be rendered into the DOM when the browser sets the inner HTML content of the &lt;code&gt;#content&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;Unlike the previous two examples, this all happens client-side - the server isn&#39;t involved at all.&lt;/p&gt;
&lt;h3 id=&quot;preventing-xss&quot; tabindex=&quot;-1&quot;&gt;Preventing XSS&lt;/h3&gt;
&lt;p&gt;Some of the methods we can use to prevent XSS attacks on our websites include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sanitizing user input, making sure there are no HTML tags in it&lt;/li&gt;
&lt;li&gt;escape certain characters (like &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, and &lt;code&gt;&amp;quot;&lt;/code&gt;) with HTML entity encoding (e.g. &lt;code&gt;&amp;amp;amp;&lt;/code&gt; for &amp;amp;) to prevent them being executed&lt;/li&gt;
&lt;li&gt;not rendering arbitrary content from the server as HTML - render it as plain text&lt;/li&gt;
&lt;li&gt;check the Content-Type is correct - only &lt;code&gt;text/html&lt;/code&gt; if you are &lt;em&gt;definitely&lt;/em&gt; returning HTML&lt;/li&gt;
&lt;li&gt;use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies&quot;&gt;HttpOnly cookies&lt;/a&gt; for sensitive data so that the JavaScript &lt;code&gt;Document.cookie&lt;/code&gt; API can&#39;t access them&lt;/li&gt;
&lt;li&gt;and... set up a &lt;code&gt;Content-Security-Policy&lt;/code&gt; header!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;content-security-policy-is-a-set-of-rules-about-permitted-content-on-a-website&quot; tabindex=&quot;-1&quot;&gt;Content-Security-Policy is a set of rules about permitted content on a website&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt; is an additional layer of security on a site, preventing things like malicious scripts from being run by defining strict rules about what content you can and can&#39;t have on a website. It can be one of the HTTP headers that we can send from the web server to the client (browser), or a meta tag in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of an HTML page. A browser will read the CSP and check whether the scripts, stylesheets and various other resources it&#39;s executing or displaying conform to the rules in the policy. If not, it won&#39;t load them.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/csp/gandalf.png&quot; alt=&quot;Gandalf from Lord of the Rings saying &#39;you shall not pass&#39;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;CSP is basically Browser Gandalf.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;A CSP meta tag may look a bit like this, for a site with Google Analytics and some Twitter/Facebook embeds, and images from a CDN:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;http-equiv&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Content-Security-Policy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;default-src &#39;none&#39;; manifest-src &#39;none&#39;; prefetch-src &#39;none&#39;; worker-src &#39;none&#39;; object-src &#39;self&#39;; font-src *; connect-src &#39;self&#39; https://www.google-analytics.com; img-src &#39;self&#39; https://some-cdn.com; script-src &#39;self&#39; https://platform.twitter.com https://www.google-analytics.com https://connect.facebook.net https://staticxx.facebook.com; style-src &#39;self&#39; https://platform.twitter.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/csp/csp-error.png&quot; alt=&quot;Console error: Refused to load the script &#39;http://evil.example.com/evil.js&#39; because it violates the following Content Security Policy directive: script-src &#39;self&#39; https://apis.google.com&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;An example Content-Security-Policy error from Google&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The CSP header or meta tag content is always a string containing a semicolon-separated list of rules.&lt;/p&gt;
&lt;p&gt;A policy has a list of &lt;strong&gt;directives&lt;/strong&gt; which are usually suffixed with &lt;code&gt;-src&lt;/code&gt;, and refer to different kinds of rules for resources and content on the page.&lt;br /&gt;
Some common categories of directive are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#fetch-directives&quot;&gt;fetch directives&lt;/a&gt;: which resource types may be loaded, and where from&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#document-directives&quot;&gt;document directives&lt;/a&gt;: what properties the document or worker may have&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://localghost.dev/blog/defending-yourself-against-cross-site-scripting-attacks-with-content-security-policy/#navigation-directives&quot;&gt;navigation directives&lt;/a&gt;: where a user on the page can navigate to/submit a form to&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next to each directive we specify one or more permitted &lt;strong&gt;sources&lt;/strong&gt; for each of these directives.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;script-src: &#39;self&#39; https://othersite.com;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- directive: source source; --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I won&#39;t go through every single directive - there are a lot - but I&#39;ll outline some of the most common ones, and what rules they might enforce. Plus, the good old caveat that Internet Explorer only supports &lt;em&gt;one&lt;/em&gt; directive (&lt;code&gt;sandbox&lt;/code&gt;), and that&#39;s with the legacy &lt;code&gt;X-Content-Security-Policy&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;While the HTTP header and meta tags are mostly the same, the main differences are that HTTP headers have wider browser support, support all of the directives (meta tags support &lt;em&gt;most&lt;/em&gt; of the directives) and may be cached by proxies (meta tags won&#39;t be).&lt;/p&gt;
&lt;p&gt;As long as you&#39;re employing multiple methods of defence against XSS, including CSP, you can protect yourself (yes, &lt;em&gt;even&lt;/em&gt; in IE). It&#39;s important to emphasise here that CSP is &lt;em&gt;one way&lt;/em&gt; of protecting your website against XSS - it&#39;s not enough on its own.&lt;/p&gt;
&lt;h3 id=&quot;fetch-directives&quot; tabindex=&quot;-1&quot;&gt;Fetch directives&lt;/h3&gt;
&lt;p&gt;These directives govern which resource types may be loaded, and where from. Things like images, scripts, styles and frames will be defined here.&lt;/p&gt;
&lt;p&gt;Any of these directives may also have a wildcard value (&lt;code&gt;*&lt;/code&gt;), which basically means &amp;quot;anything goes&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;image-src&lt;/code&gt;: Permitted sources for any images/favicons on the page.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;script-src&lt;/code&gt;: Permitted sources for any JavaScript on the page.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;style-src&lt;/code&gt;: What kind of CSS it&#39;s allowed to load, and where from.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;font-src&lt;/code&gt;: where fonts specified by the CSS &lt;code&gt;@font-face&lt;/code&gt; rule may be fetched from (e.g. Google Fonts)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;frame-src&lt;/code&gt;: where iframes may load content from (e.g. Stripe elements)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;connect-src&lt;/code&gt;: hosts that your JavaScript may make requests to (e.g. an API, with &lt;code&gt;fetch&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;default-src&lt;/code&gt;: the fallback if any of the rules don&#39;t match the resource&lt;/p&gt;
&lt;h3 id=&quot;document-directives&quot; tabindex=&quot;-1&quot;&gt;Document directives&lt;/h3&gt;
&lt;p&gt;Document directives restrict the use of plugins (such as with &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt;) and let us enable sandbox mode.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;plugin-types&lt;/code&gt;: allows you to define permitted &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types&quot;&gt;MIME types&lt;/a&gt; for embedded media and elements using &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; tags. These days, you rarely need to use &lt;code&gt;embed&lt;/code&gt;, so I don&#39;t expect you&#39;ll come across this one very much.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sandbox&lt;/code&gt;: literally the only directive that IE supports! This allows you to restrict the browser environment on the page, effectively blocking &lt;em&gt;everything&lt;/em&gt; - popups, scripts, forms, you name it. It has different sources from the rest of the directives - you can specify exactly what should be enabled in sandbox mode. Take a look at the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox&quot;&gt;MDN documentation on sandbox&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h3 id=&quot;navigation-directives&quot; tabindex=&quot;-1&quot;&gt;Navigation directives&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;form-action&lt;/code&gt;: if you&#39;re using traditional HTML forms with &lt;code&gt;action&lt;/code&gt; attributes, this directive specifies the URIs you&#39;re allowed to submit to.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;frame-ancestors&lt;/code&gt;: specifies which pages or URIs are allowed to embed iframes and other frame/embeddable content.&lt;/p&gt;
&lt;h3 id=&quot;sources&quot; tabindex=&quot;-1&quot;&gt;Sources&lt;/h3&gt;
&lt;p&gt;Most directives have the same possible list of sources. With the exception of &lt;strong&gt;host source&lt;/strong&gt; and &lt;strong&gt;scheme source&lt;/strong&gt;, they are all specified in single quotes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&#39;self&#39;&lt;/code&gt; - anything from the same host is fine. So a CSP for localghost.dev with &lt;code&gt;image-src: &#39;self&#39;&lt;/code&gt; and &lt;code&gt;script-src: &#39;self&#39;&lt;/code&gt; would be allowed to display images and run scripts from localghost.dev.&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;host source&lt;/strong&gt; : external hosts that the resource can come from.
&lt;ul&gt;
&lt;li&gt;For example, &lt;code&gt;style-src: &amp;quot;https://myhost.com&amp;quot;&lt;/code&gt; allows you to include a stylesheet from &lt;a href=&quot;http://myhost.com/&quot;&gt;myhost.com&lt;/a&gt;, e.g. &lt;code&gt;&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://myhost.com/style.css&amp;quot;/&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you often use content delivery networks (CDNs), you&#39;ll need to specify them here. You can also use &lt;strong&gt;wildcards&lt;/strong&gt; for the subdomain of the URL, and restrict things to specific &lt;strong&gt;ports&lt;/strong&gt; as well. The &lt;strong&gt;protocol&lt;/strong&gt; matters, so a host source with &lt;code&gt;https://&lt;/code&gt; will not allow anything from &lt;code&gt;http://&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;e.g. &lt;code&gt;connect-src: &#39;self&#39; https://*.localghost.dev&lt;/code&gt; will allow anything over HTTPS from a subdomain of this site.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;scheme source&lt;/strong&gt;: what scheme (protocol) it&#39;s okay to fetch resources from. Chiefly &lt;code&gt;https:&lt;/code&gt; or &lt;code&gt;http:&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&#39;unsafe-inline&#39;&lt;/code&gt;: whether inline &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags are allowed. I&#39;ve only really used this for &lt;code&gt;script-src&lt;/code&gt; and &lt;code&gt;style-src&lt;/code&gt; before. I&#39;ll talk more about why inline style and script tags are a security risk later in the article.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&#39;unsafe-eval&#39;&lt;/code&gt;: whether or not JS is allowed to use the &lt;code&gt;eval()&lt;/code&gt; function, which executes arbitrary code from whatever string is passed in. I&#39;ll go into more detail about why this is a bad thing in a later section.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nonce&lt;/code&gt; - you may specify an &lt;strong&gt;unguessable&lt;/strong&gt; cryptographic nonce (a base-64 encoded string) which you can then pass into inline styles or scripts as an attribute to allow them to load on the page. This means you can use inline &lt;code&gt;&amp;lt;style nonce=&amp;quot;mysecretstring&amp;quot;&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags without indiscriminately allowing &lt;em&gt;all&lt;/em&gt; inline styles - effectively marking only a specific set of inline styles as &amp;quot;safe&amp;quot;. This &lt;strong&gt;must&lt;/strong&gt; be randomly generated and unguessable, otherwise it&#39;s basically pointless as anyone can guess the hash and pop it in their injected scripts.&lt;/li&gt;
&lt;li&gt;hashes - e.g. &lt;code&gt;sha256-&amp;lt;your hash value&amp;gt;&lt;/code&gt;. This is a base64-encoded representation of your inline styles or scripts, so the browser can check the hash against its own hash of the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; to make sure it&#39;s the real deal. Anything that doesn&#39;t match the hash will be ignored. Hashes are static, while nonces are generated server-side on every page load.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;none&lt;/code&gt; - no URLs match, no nothing may be loaded at all. For example, I don&#39;t use iframes on this site at all, so I&#39;d have &lt;code&gt;frame-src: none&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;unsafe-inline-and-the-risks-of-inline-styles&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;unsafe-inline&lt;/code&gt; and the risks of inline styles&lt;/h3&gt;
&lt;p&gt;But why would we have to explicitly enable &lt;code&gt;unsafe-inline&lt;/code&gt; for &lt;code&gt;style-src&lt;/code&gt;, and why are inline &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags considered &amp;quot;unsafe&amp;quot;?&lt;/p&gt;
&lt;p&gt;There are actually a few vulnerabilities that can be triggered using CSS. They&#39;re rare and, yes, less harmful than JS-based XSS attacks, but it&#39;s still worth protecting yourself against them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;messing up your site&#39;s styles - on a big-name website or app, this can cause reputational damage&lt;/li&gt;
&lt;li&gt;injecting styles into the page to add unsolicited content or offensive text&lt;/li&gt;
&lt;li&gt;disguising user-generated content to look like official content, e.g. a fake login link&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these can be prevented with &lt;code&gt;unsafe-inline&lt;/code&gt;, meaning the browser will only pay attention to the styles from your defined stylesheets.&lt;/p&gt;
&lt;p&gt;Ultimately, allowing &lt;code&gt;unsafe-inline&lt;/code&gt; is a calculated risk: if you really can&#39;t get around it with a nonce or a hash, then you can enable this knowing that you are introducing a slight vulnerability, but it&#39;s highly unlikely to be anything super severe.&lt;/p&gt;
&lt;p&gt;There&#39;s a helpful &lt;a href=&quot;https://stackoverflow.com/a/31759553&quot;&gt;StackOverflow answer&lt;/a&gt; which explains this in more detail.&lt;/p&gt;
&lt;h3 id=&quot;scripts-unsafe-inline-and-unsafe-eval-why-eval-is-evil&quot; tabindex=&quot;-1&quot;&gt;Scripts, &lt;code&gt;unsafe-inline&lt;/code&gt; and &lt;code&gt;unsafe-eval&lt;/code&gt;: why &lt;code&gt;eval()&lt;/code&gt; is evil&lt;/h3&gt;
&lt;p&gt;While enabling &lt;code&gt;unsafe-inline&lt;/code&gt; for styles is not the end of the world, it&#39;s a bad idea to allow it for scripts. A lot of harm can be done with an inline script that&#39;s been injected into your code - with a &lt;code&gt;connect-src&lt;/code&gt; directive defined as well it would mean that that script couldn&#39;t connect to any other sources, but it could still cause havoc, such as pretending to be you and updating user-generated content.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eval()&lt;/code&gt;, on the other hand, should be avoided at all costs. It takes a string as a parameter, and blindly executes it as Javascript. There&#39;s literally a section in the MDN article about &lt;code&gt;eval()&lt;/code&gt; called &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Never_use_eval!&quot;&gt;&amp;quot;Never use eval()!&amp;quot;&lt;/a&gt;. This opens up a whole host of security vulnerabilities - if &lt;code&gt;unsafe-eval&lt;/code&gt; is enabled, anyone can execute arbitrary code in your application. Yikes.&lt;/p&gt;
&lt;h2 id=&quot;how-to-create-a-csp-header&quot; tabindex=&quot;-1&quot;&gt;How to create a CSP header&lt;/h2&gt;
&lt;p&gt;In a static site, you can add a meta tag to your site&#39;s &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;http-equiv&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Content-Security-Policy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;default-src &#39;none&#39;; manifest-src &#39;none&#39;; prefetch-src &#39;none&#39;; worker-src &#39;none&#39;; object-src &#39;self&#39;; font-src *; connect-src &#39;self&#39; https://www.google-analytics.com; img-src &#39;self&#39; https://some-cdn.com; script-src &#39;self&#39; &#39;sha256-flsjfljlkfjaspdjfsdkgs&#39; https://platform.twitter.com https://www.google-analytics.com https://connect.facebook.net https://staticxx.facebook.com; style-src &#39;self&#39; &#39;nonce-adsfasdfasfsadf&#39; https://platform.twitter.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re running an app with an Express server, you can easily set it as one of the response headers with &lt;code&gt;helmet-csp&lt;/code&gt; - check out the &lt;a href=&quot;https://helmetjs.github.io/docs/csp/&quot;&gt;Helmet docs&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;The safest method is to have the most restrictive CSP possible, and only add new sources if you&#39;re completely sure. Include a &lt;code&gt;default-src: &#39;none&#39;&lt;/code&gt; so that if no rule matches a resource, it will be blocked.&lt;/p&gt;
&lt;h2 id=&quot;adding-to-a-csp-header&quot; tabindex=&quot;-1&quot;&gt;Adding to a CSP header&lt;/h2&gt;
&lt;p&gt;When is it safe to add new sources to a CSP header? Perhaps you&#39;ve included an image or stylesheet in your code, and your browser is refusing to render it because it violates the CSP. Before adding the URL as a source, consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;do you trust this site?&lt;/li&gt;
&lt;li&gt;how specific can you make the URI? (avoid wildcards unless it&#39;s a host you control)&lt;/li&gt;
&lt;li&gt;could you host the file on an already approved source?&lt;/li&gt;
&lt;li&gt;could you add the CSS file to your repo instead?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;styled-components-and-unsafe-inline&quot; tabindex=&quot;-1&quot;&gt;styled-components and &lt;code&gt;unsafe-inline&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If you&#39;re using &lt;code&gt;styled-components&lt;/code&gt;, which renders &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags into the page, rather than enabling &lt;code&gt;unsafe-inline&lt;/code&gt; for styles you can define a nonce by setting it as a Webpack global (&lt;code&gt;__webpack_nonce__&lt;/code&gt;). The caveats: this is apparently undocumented, and only works with server-side rendered code. So you might have to enable &lt;code&gt;unsafe-inline&lt;/code&gt; for that one.&lt;/p&gt;
&lt;h3 id=&quot;environment-based-csp&quot; tabindex=&quot;-1&quot;&gt;Environment-based CSP&lt;/h3&gt;
&lt;p&gt;If you run different environments for development and production, you may want to consider serving different CSP headers for different environments. For example, your &lt;code&gt;image-src&lt;/code&gt; directive might point to a non-prod CDN if &lt;code&gt;process.env.NODE_ENV !== &#39;production&lt;/code&gt;, but the production CDN otherwise. Similarly, if you use a local server at &lt;code&gt;localhost:6060&lt;/code&gt; as an API for development but your production app points to a hosted API somewhere else, you might want to add &lt;code&gt;localhost:6060&lt;/code&gt; to your CSP &lt;em&gt;only for the development environment&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;There are some frameworks, such as Hugo and Next.JS, which rely on inline scripts for hot reloading. In this case, it&#39;s fine to add &lt;code&gt;unsafe-inline&lt;/code&gt; to your &lt;code&gt;script-src&lt;/code&gt; &lt;strong&gt;in development&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For example, if you use Hugo and want to enable live reloading with your CSP, as well as scripts imported from the site itself, you can use &lt;code&gt;.Site.IsServer&lt;/code&gt; (though this only works if you use server mode for development and not production):&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;http-equiv&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Content-Security-Policy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;default-src &#39;none&#39;; [...] script-src &#39;self&#39; {{ if eq .Site.IsServer true }}&#39;unsafe-inline&#39;{{ end}} {{ .Site.BaseURL }};&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-moral-of-the-story&quot; tabindex=&quot;-1&quot;&gt;The moral of the story&lt;/h2&gt;
&lt;p&gt;So, how did I get around the &lt;code&gt;unsafe-eval&lt;/code&gt; warnings I was seeing in the console?&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/csp/ron-swanson.webp&quot; alt=&quot;A gif of Ron Swanson throwing his computer into a dumpster&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Unfortunately... the only solution was to chuck out that library and find something else. There are no workarounds for a library that uses &lt;code&gt;eval&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When adding a CSP header to a site, start out with the principle of least privilege: only permit &lt;em&gt;exactly&lt;/em&gt; what you need, nothing more. When in doubt, be overly restrictive, and see what console errors you&#39;re getting.&lt;/p&gt;
&lt;p&gt;If you&#39;re working on a site or app that has a CSP header set, don&#39;t be tempted to add sources just to make console errors go away. Make sure you know exactly what you&#39;re allowing.&lt;/p&gt;
&lt;p&gt;And promise me you will never, ever enable &lt;code&gt;unsafe-eval&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;references-and-further-reading&quot; tabindex=&quot;-1&quot;&gt;References &amp;amp; further reading&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cloudflare.com/learning/security/threats/cross-site-scripting/&quot;&gt;Cloudflare - What is Cross-Site Scripting?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy&quot;&gt;MDN - Content-Security-Policy&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting&quot;&gt;MDN - Cross-Site Scripting&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://owasp.org/www-community/attacks/xss/&quot;&gt;OWASP - Cross-Site Scripting&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://owasp.org/www-community/attacks/DOM_Based_XSS&quot;&gt;OWASP - DOM-based XSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Google&#39;s &lt;a href=&quot;https://csp-evaluator.withgoogle.com/&quot;&gt;CSP Evaluator&lt;/a&gt; can check how watertight your CSP is (but is not exhaustive).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/security/csp&quot;&gt;Google Web Fundamentals - CSP&lt;/a&gt;&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>ffconf 2019: future friends beside the seaside</title>
      <link href="https://localghost.dev/blog/ffconf-2019-future-friends-beside-the-seaside/"/>
      <updated>2019-11-16T00:00:00Z</updated>
      <id>https://localghost.dev/blog/ffconf-2019-future-friends-beside-the-seaside/</id>
      <content type="html">&lt;p&gt;Now in its 11th year, &lt;a href=&quot;https://ffconf.org/&quot;&gt;ffconf&lt;/a&gt; is one of the biggest events in the conference calendar for web developers across the UK (and further afield). Yet somehow I&#39;ve managed to miss every one since I got into tech, because of some reason or another - last year I was at the week-long blockchain sales pitch that is Web Summit - so I was understandably very excited to finally be going. It takes place at the lovely &lt;a href=&quot;https://www.picturehouses.com/cinema/duke-of-york-s-picturehouse&quot;&gt;Duke of York&#39;s Picturehouse&lt;/a&gt; in Brighton - the first conference I&#39;ve attended that had cupholders in the seats! (Fun fact, I applied for a job there once.)&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054723761_90697f5ae7_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Photo: Trys Mudford&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;One of the best things about attending conferences is seeing friendly faces from the community, and this particular subset of the JS community is so lovely. It does give you hope (and a refreshing break from some of the other conferences and meetups I&#39;ve attended in the past few years which have had 97% more posturing). I spend a lot of time in the London bubble so it&#39;s great to meet folks from other tech communities, and going to the &lt;a href=&quot;https://asyncjs.com/&quot;&gt;Async&lt;/a&gt; meetup the night before was a great way to do that.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054939337_0d28f8a8c9_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;JS (and real-life) friends! (photo: Trys Mudford)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;(I also got to meet &lt;a href=&quot;https://noopkat.com/&quot;&gt;Suz Hinton&lt;/a&gt; at the afterparty and didn&#39;t make a fool of myself, so that is always a plus. She&#39;s extremely lovely.)&lt;/p&gt;
&lt;p&gt;What made these talks &lt;em&gt;especially&lt;/em&gt; good was that they focused more on people and stories. My favourite kind of conference talk is one where people show something amazing they built, and share their experiences of building it (a good example from JSConf EU 2019 is &lt;a href=&quot;https://www.youtube.com/watch?v=ZsBAkSxwU5c&quot;&gt;Bringing back dialup: the internet over SMS&lt;/a&gt; by Alexandra Sunderland). I don&#39;t need to come away with an exact step-by-step guide on how to do the thing (I&#39;ll never remember it all anyway), and it might not even be a practical thing for me to ever attempt, but I want to know all about it because it&#39;s amazing.&lt;/p&gt;
&lt;p&gt;I&#39;ve been thinking a lot recently about the &lt;em&gt;reasons&lt;/em&gt; that people learn to code - and in fact, I submitted to the ffconf CFP with a talk about this, which I&#39;m going to try and polish up and submit elsewhere. When I started doing web development, I didn&#39;t really know what I was doing or why I was doing it - I thought it&#39;d be cool to make a website, and how else was I going to get my Neopets shop to play music and have a flashy background?&lt;/p&gt;
&lt;p&gt;It seems like a lot of the coding for creativity&#39;s sake has been lost in favour of learning the latest, greatest technology in order to advance your career and to be able to say that you&#39;re using it. I could go on, and I probably will in another blog post, but seeing people up on stage doing ridiculous and beautiful things with code gives me hope for creative coding (and, most importantly, it inspires me to do some too).&lt;/p&gt;
&lt;p&gt;I came home from ffconf and restyled my entire website with ludicrous link effects and a gradient border. It&#39;s not quite making jewellery from SVGs but it&#39;s a start.&lt;/p&gt;
&lt;p&gt;Here&#39;s a bit of what I took away from each of the ffconf talks. I haven&#39;t included many photos because my phone&#39;s camera is atrocious in low light, but I&#39;ve borrowed some nice pictures from the official photos by Trys Mudford on &lt;a href=&quot;https://flickr.com/photos/remysharp/albums/72157711751196107&quot;&gt;Remy Sharp&#39;s Flickr&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;sharon-steed-engaging-empathy&quot; tabindex=&quot;-1&quot;&gt;Sharon Steed - Engaging Empathy&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Empathy: the thing you have to do to not be an asshole in the office.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Opening the conference was author and communications consultant Sharon Steed. She spoke about her experiences of growing up with and living with a stutter - and the difficulties she&#39;s had with others not taking her seriously. She&#39;s a fantastic storyteller, and as she shared with us her challenges and achievements it felt like she was taking us with her on her journey.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054724506_16d5135ec3_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Sharon Steed (photo: Trys Mudford)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;She compared empathy to love; &amp;quot;the thing everyone understands, but nobody can explain&amp;quot;. It&#39;s intangible, but it&#39;s also a choice: we can &lt;em&gt;choose&lt;/em&gt; to have empathy for others (and we should). (Her tales of new love were accompanied by &lt;em&gt;that&lt;/em&gt; picture of Britney and Justin in denim, which was an all around win for me.)&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/ffconf/sharonsteed-1.jpg&quot; alt=&quot;Photo of Sharon Steed giving a conference talk&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;So, what to take away from Sharon&#39;s talk? We should &lt;strong&gt;foster environments of collaboration and inclusion&lt;/strong&gt;. People need to feel safe in order to speak honestly - having open lines of communication and being transparent from the start will help this. She also pointed out that teams don&#39;t always need to work in the same way: people should be able to do things on their own terms.&lt;/p&gt;
&lt;p&gt;Sharon also described what she called the &amp;quot;Key Empathy Behaviours&amp;quot;: patience, perspective and connection. As long as we are being present and remembering the &lt;strong&gt;why&lt;/strong&gt; of what we&#39;re doing, understanding where others are coming from, and speak for &lt;strong&gt;intention&lt;/strong&gt; and not impact, we&#39;ll be able to engage empathy and make everyone feel like they belong.&lt;/p&gt;
&lt;p&gt;ffconf talk page: &lt;a href=&quot;https://ffconf.org/talks/empathy/&quot;&gt;Engaging Empathy&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;amina-adewusi-what-does-it-take-to-become-a-developer-in-2020&quot; tabindex=&quot;-1&quot;&gt;Amina Adewusi - What does it take to become a developer in 2020?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;We need a recruitment revolution.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Amina&#39;s talk focused on the barriers to entry for under-represented people in the industry: a topic that has come up time and time again but doesn&#39;t really seem to be getting any better (especially for people of colour).&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054726391_72d4de9b7c_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Amina Adewusi (photo: Trys Mudford)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Her words were powerful, and the measured way she spoke really conveyed the gravity of what she was saying. In the last 11 years, the proportion of women and non-white students in computer science has not improved: last year, only 20% of computer science students were female, and only 15% were non-white. And a computer science degree is already a privileged platform to launch your career from. What about those who don&#39;t go down that path?&lt;/p&gt;
&lt;p&gt;She spoke of the generosity of strangers: the bootcamp alumni who all answered her questions when she contacted them out of the blue, happy to give advice. And while bootcamps are a great way to fast-track your career into tech, they&#39;re also very exclusionary for anyone with caring responsibilities, and they can be very expensive. She spoke of her experiences raising a baby while also learning to code, which frankly blew my mind - learning to code is really hard (don&#39;t let anyone tell you otherwise!) so to do that while also looking after a very young child is just phenomenal.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/ffconf/aminaadewusi-1.jpg&quot; alt=&quot;Photo of Amina Adewusi giving a conference talk&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;For those who self-teach, it&#39;s hard to get a job once you&#39;ve learned the skills. More often than not, recruitment is unforgiving, with high expectations, long interview processes and unreasonable deadlines (Amina recalled being sent a tech test on a Friday, with the expectation she&#39;d send it back on the Monday!). It&#39;s not just the time it takes, either - the interview process can be very intimidating and sometimes discriminatory.&lt;/p&gt;
&lt;p&gt;As Amina put it: &lt;strong&gt;this is a call to action&lt;/strong&gt;. How can we, as experienced engineers, help the next generation of developers get a foothold? Some of her excellent suggestions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mentor at organisations like &lt;a href=&quot;https://www.codebar.io/&quot;&gt;Codebar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;pair with new engineers&lt;/li&gt;
&lt;li&gt;give engineers time to work on open source&lt;/li&gt;
&lt;li&gt;be mindful when recruiting - don&#39;t assume everyone has all the time in the world&lt;/li&gt;
&lt;li&gt;observe the &lt;a href=&quot;https://www.ericholscher.com/blog/2017/aug/2/pacman-rule-conferences/&quot;&gt;&amp;quot;pacman&amp;quot; rule&lt;/a&gt; when talking to people at events&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ffconf talk page: &lt;a href=&quot;https://ffconf.org/talks/what-does-it-take/&quot;&gt;What does it take to become a developer in 2020?&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;alice-bartlett-getting-more-from-git&quot; tabindex=&quot;-1&quot;&gt;Alice Bartlett - Getting more from Git&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;My process is &#39;try stuff until it works and don&#39;t ask any questions&#39;.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Somehow, Alice turned a pretty dry subject - git - into one of the most relatable and entertaining talks I&#39;ve ever seen.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054211838_fb160b70ec_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Alice Bartlett (photo: Trys Mudford)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;With entertaining anecdotes about the meaningless commit messages she&#39;d write early in her career (we&#39;ve all been guilty of that, and you should see the nonsense commits I&#39;ve made to this very website) she highlighted the importance of considering who else is going to be reading all of it - including Future You. A commit message should be a single unit of work that makes sense in isolation, and that will tell you &lt;em&gt;why&lt;/em&gt; the change is made. So no more &amp;quot;fix bug&amp;quot; commits.&lt;/p&gt;
&lt;p&gt;Of course, it&#39;s not always possible to commit code with this in mind, and many of us approach coding by trying stuff out and going all over the place, so git offers us some handy ways of rewriting history so that we can make the commits sensible &lt;em&gt;after&lt;/em&gt; the fact.&lt;/p&gt;
&lt;p&gt;Here, Alice took us through a brief history of git (including a very entertaining swipe at Linus Torvalds and his &lt;a href=&quot;https://gizmodo.com/linux-founder-takes-some-time-off-to-learn-how-to-stop-1829105667&quot;&gt;controversy&lt;/a&gt;: &amp;quot;the creator of Linux and git, two projects he named after himself&amp;quot;). Many attendees in the room hadn&#39;t had the... pleasure of working with non-distributed source control systems like SVN, so she took us through how git differs and why it&#39;s so helpful for things like testing out changes and controlling the quality of code that&#39;s merged in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Why are we so bad at git?&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/ffconf/alicebartlett-git.jpg&quot; alt=&quot;Picture of LEGO heads that says &#39;you are in a detached HEAD state&#39;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Everything is confusing&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;A lot of the problem is the jargon and the impenetrable docs, Alice argued - and I agree with her there, having never read a man page that I understood.&lt;/p&gt;
&lt;p&gt;And then, using simple and clear language, Alice took us through the process of rewriting git history using interactive rebase (&lt;code&gt;git rebase --i&lt;/code&gt;). It&#39;s a super useful tool, allowing you to squash multiple commits into one, reword commit messages and change the order they are applied in. She also showed us &lt;code&gt;git add --patch&lt;/code&gt; which lets you choose which &amp;quot;hunks&amp;quot; of your code to commit and which to leave unstaged. I&#39;m not going to go into too much detail here, but the video is definitely worth a watch when it comes out.&lt;/p&gt;
&lt;p&gt;Some top git tips here to take away:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t just link to Slack posts or tickets in the PR - give a bit of background info&lt;/li&gt;
&lt;li&gt;...but don&#39;t write an essay about the change, keep it concise&lt;/li&gt;
&lt;li&gt;commit messages should be meaningful and say &lt;em&gt;why&lt;/em&gt; the change happened&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ffconf talk page: &lt;a href=&quot;https://ffconf.org/talks/getting-more-from-git/&quot;&gt;Getting more from Git&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;laura-kalbag-8-unbelievable-things-you-never-knew-about-tracking&quot; tabindex=&quot;-1&quot;&gt;Laura Kalbag - 8 Unbelievable Things You Never Knew About Tracking&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;We&#39;re asked to give up everything, or get nothing.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Laura highlighted the ridiculous amount of information that various sites have about all of us just through embedded scripts and images. And even though this data is anonymised, a study showed it was possible to actually re-identify people from this data based on their browsing habits. Scary, right?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054212948_48bb29c813_w.jpg&quot; alt=&quot;&quot; title=&quot;Laura Kalbag (photo: Trys Mudford&quot; /&gt;)&lt;/p&gt;
&lt;p&gt;She touched on the Cambridge Analytica scandal - &amp;quot;tracking affects democracy&amp;quot; - and how companies can manipulate people through precise targeting based on their interests, knowing exactly what will make them tick. Facebook&#39;s latest ad campaign got a shout out for being totally hypocritical - when you choose to share something with &amp;quot;Only Me&amp;quot; or &amp;quot;Friends&amp;quot; you&#39;re also sharing it with Facebook!&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/ffconf/laurakalbag-1.jpg&quot; alt=&quot;Photo of Laura Kalbag giving a conference talk&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Companies make it worse by having incomprehensible terms and conditions, filled with legal jargon. When you consider that 1 in 6 people in England have very poor literacy skills (source: &lt;a href=&quot;https://literacytrust.org.uk/parents-and-families/adult-literacy/&quot;&gt;National Literacy Trust&lt;/a&gt;) it&#39;s especially problematic. Sites like Facebook are a lifeline for many people, but if they can&#39;t understand what they&#39;re signing up for is it really consent?&lt;/p&gt;
&lt;p&gt;While we can&#39;t escape technology - Laura argued there is no longer a distinction between &amp;quot;online life&amp;quot; and &amp;quot;real life&amp;quot; - we must all consider the &lt;strong&gt;impact our products will have&lt;/strong&gt; outisde of the immediate interface. She described how we can build more ethical tech, by making things that function without users&#39; personal info, furthering the human experience rather than for corporate profit. I find this a really interesting and difficult dilemma, as in our society it&#39;s really difficult (if not impossible) to build a sustainable product that isn&#39;t for-profit in some way. It would require pretty significant government support, which isn&#39;t going to happen any time soon.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Friends don&#39;t let friends get tracked by corporations.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I knew from the title that this talk would be worrying, but I didn&#39;t anticipate just how unsettled I&#39;d feel afterwards! As someone whose life has been basically entirely managed by Google products for years, I realised I needed to make a change. Laura suggested &lt;a href=&quot;https://switching.software/&quot;&gt;switching.software&lt;/a&gt; as a resource for alternatives to common &amp;quot;big tech&amp;quot; products, which is a great place to start.&lt;/p&gt;
&lt;p&gt;ffconf talk page: &lt;a href=&quot;https://ffconf.org/talks/privacy/&quot;&gt;8 Unbelievable Things You Never Knew About Tracking&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;harry-roberts-from-milliseconds-to-millions-a-look-at-the-numbers-powering-web-performance&quot; tabindex=&quot;-1&quot;&gt;Harry Roberts - From Milliseconds to Millions: A Look at the Numbers Powering Web Performance&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;How do you &lt;em&gt;know&lt;/em&gt; the site is slow?&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A talk with a rather different tone from the last one - a focus on Harry&#39;s experience as a web performance consultant. Performance is definitely one of those things I know I should know more about, so I was geared up for the next couple of talks to help me boost that knowledge a little bit.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054730331_c8ba40ded7_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Harry Roberts (photo: Trys Mudford)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Harry had the great suggestion of keeping a list of questions that you can ask all potential clients, so that you can better understand the problem. In his case, things like: &amp;quot;How do you know the site is slow?&amp;quot;. You could do this for any kind of consulting work - knowing exactly what to ask is hugely important. You can shape these questions over time, knowing exactly where to look next time and what doesn&#39;t work.&lt;/p&gt;
&lt;p&gt;I must admit I was a little put off when he suggested we &amp;quot;leave our ethics at the door&amp;quot; given the talk he&#39;d just followed, and some of the talk seemed a little too focused on the clients and not on the stories behind it. But this was, after all, a talk on numbers.&lt;/p&gt;
&lt;p&gt;I found it particularly interesting how he identified a performance issue localised to Venezuela, and got all the engineers on the team to use a simulated Venezuelan internet connection to get them to think about things like network conditions and how it might be for other countries. We often take our internet speed for granted, and even when Virgin Media is at its worst, we still have vastly better internet than much of the world. Much of the world&#39;s population is dependent on mobile internet, so are our apps really suitable for everyone?&lt;/p&gt;
&lt;p&gt;Harry advocated for normalising performance - making it part of our everyday work. It can be a hard thing to argue for, when your managers are pushing for more new features, but if you can show them the numbers, it&#39;s hard to argue.&lt;/p&gt;
&lt;p&gt;ffconf talk page: &lt;a href=&quot;https://ffconf.org/talks/from-milliseconds-to-millions/&quot;&gt;From Milliseconds to Millions: A Look at the Numbers Powering Web Performance&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;anna-migas-effortless-performance-debugging&quot; tabindex=&quot;-1&quot;&gt;Anna Migas - Effortless Performance Debugging&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;If [the browser] fails to load the frames fast enough, we&#39;ll experience jank.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Continuing my crash course in performance, Anna showed us with a live demo how we can use the power of Chrome Dev Tools to debug some common performance issues. Using the Profiler tab she showed how we can identify how loading is progressing and the performance of animations during interactions. You can see at-a-glance what&#39;s taking the longest time to load, plus it also has some helpful metrics such as the time to &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint&quot;&gt;First Meaningful Paint&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054944937_d87fd371d8_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Anna Migas (photo: Trys Mudford)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;In the spirit of the Live Demo, Anna was going to show us how the Lighthouse integration in Chrome can audit performance issues on web apps, but sadly technology was not on our side and Lighthouse wouldn&#39;t work (it seemed it wasn&#39;t just Anna though). Despite this hiccup, there were still a lot of really useful tips I took away from her talk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Look through Network tab to see what&#39;s been downloaded, and in what order&lt;/li&gt;
&lt;li&gt;Block elements using the request blocking tab to see the impact of not loading them&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;font-display: swap&lt;/code&gt; to show a fallback font before the font is loaded&lt;/li&gt;
&lt;li&gt;Using a lazy-loading library to only load pictures as you need them&lt;/li&gt;
&lt;li&gt;Using an image&#39;s intrinsic ratio to show placeholders and prevent text from jumping around as images load&lt;/li&gt;
&lt;li&gt;Splitting CSS into a critical path stylesheet and others that can be loaded separately&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/ffconf/annamigas-1.jpg&quot; alt=&quot;Photo of Anna Migas giving a conference talk&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;ffconf talk page: &lt;a href=&quot;https://ffconf.org/talks/effortless-performance-debugging/&quot;&gt;Effortless Performance Debugging&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;charlotte-dann-taking-the-web-off-the-screen&quot; tabindex=&quot;-1&quot;&gt;Charlotte Dann - Taking The Web Off The Screen&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;You&#39;d think that art is an expressive process, and adding this binary middle step seems incongruous, but... computers are better adapted to the design process than any traditional artistic medium.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I actually didn&#39;t make many notes for this talk because I was so engrossed in the incredible visuals Charlotte was showing us. As I mentioned before, I&#39;m a big fan of coding for artistic reasons, even if I don&#39;t really do it myself much. I&#39;m not particularly &amp;quot;good&amp;quot; at SVG or any kind of web animation, so it blows me away when I see what people can do with canvas, SVG and JS.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054950652_645f381878_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Charlotte Dann (photo: Trys Mudford)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;In Charlotte&#39;s case, she bridges the gap between digital and physical art, using what she designs using generative CSS to produce prints and even intricate jewellery. She&#39;s made jigsaw puzzles which she&#39;s laser cut out, got a pen plotter to draw abstract designs onto pieces of paper for that precise yet hand-drawn look.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;nth-child&lt;/code&gt; pseudoselectors, she showed us how you can create an abstract pattern from a series of squares and circles. She&#39;s done a few of these, and used them as wall art for her studio!&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/ffconf/charlottedann-1.jpg&quot; alt=&quot;Photo of Charlotte Dann giving a conference talk&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;She&#39;s also the founder of &lt;a href=&quot;https://hexatope.io/&quot;&gt;Hexatope&lt;/a&gt;, a web app that lets you draw beautiful patterns on a hexagonal grid and get them cast into silver and gold jewellery. The designs are incredible, and it&#39;s really fun to play with.&lt;/p&gt;
&lt;p&gt;I absolutely love hearing about things like this, and it&#39;s inspired me to get more involved in creative coding. If only people spent as much time doing things like this as they did arguing about which framework is best.&lt;/p&gt;
&lt;p&gt;ffconf talk page: &lt;a href=&quot;https://ffconf.org/talks/taking-the-web-off-the-screen/&quot;&gt;Taking The Web Off The Screen&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;suz-hinton-adventures-in-reinventing-interfaces&quot; tabindex=&quot;-1&quot;&gt;Suz Hinton - Adventures in reinventing interfaces&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;I want less mass-produced surveillance bullshit and more Harry Potter magic.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was super excited for Suz&#39;s talk, as I&#39;ve followed her online for some time and I think her hardware projects are amazing, so it was great to see some of them demoed. She demonstrated how we can use web APIs to interact with non-standard interfaces like printers and other hardware.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://live.staticflickr.com/65535/49054951142_95abc9eb9e_w.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Suz Hinton (photo: Trys Mudford)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Using a thermal receipt printer, the Web Media API and the Web USB API, Suz recreated her very own Gameboy Printer! She also showed off her web app that lets you upload Arduino programs directly to the Arduino via the browser, which made me think I should dig out the Arduino I&#39;ve had in a drawer for several years and never used. (We&#39;ve all got a drawer like that.)&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/ffconf/suzhinton-1.jpg&quot; alt=&quot;Photo of Suz Hinton giving a conference talk&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;The tone of the talk shifted considerably when Suz started talking about ownership over computers and data. She cited a 1985 ruling by the Federal Communications Commission which allocated the 900MHz frequency band to industrial, scientific and medical uses. Amateur radio users were allowed to use the band if they liked, as long as it didn&#39;t interfere with the protected uses.&lt;/p&gt;
&lt;p&gt;However, now the FCC have proposed that private companies should be able to use these bands. And guess who&#39;s jumped straight in there? Everyone&#39;s favourite Big Bad Retailer, Amazon.&lt;/p&gt;
&lt;p&gt;Amazon&#39;s new &lt;a href=&quot;https://techcrunch.com/2019/09/25/amazon-sidewalk-is-a-new-long-range-wireless-network-for-your-stuff/&quot;&gt;Sidewalk&lt;/a&gt; network functions on this 900MHz band. Even the 700-odd Amazon employees testing the products managed to form a localised network on the 900MHz frequency stretching across much of the Los Angeles Basin. Meaning they&#39;ve basically made a frequency jammer. (Bear in mind frequency jammers are illegal in many countries.) If hundreds of thousands of Amazon customers start buying these products which operate on this frequency, what&#39;s going to happen to all those medical devices that rely on this band?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;This is the scariest thing I&#39;ve ever heard.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At this point Suz started crying, which really drove home just how doomed we&#39;ll be if this stuff keeps happening. &amp;quot;We are pretending that these devices are supposed to be helping people, and these people have no idea that they are actually contributing to a privately owned network that companies like this can do whatever they want with.&amp;quot;&lt;/p&gt;
&lt;p&gt;It&#39;s our job to make sure that we are fighting against things like this - &amp;quot;we&#39;ve done a bad job at educating people&amp;quot;. We should be building more &amp;quot;Harry Potter magic&amp;quot; instead of buying corporate home technology, Suz argued - she namechecked &lt;a href=&quot;http://samanthagoldste.in/&quot;&gt;Samantha Goldstein&lt;/a&gt;, who built a stained glass panel that goes cloudy if it&#39;s raining.&lt;/p&gt;
&lt;p&gt;ffconf talk page: &lt;a href=&quot;https://ffconf.org/talks/nerdverse/&quot;&gt;Adventures in reinventing interfaces&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;reflections&quot; tabindex=&quot;-1&quot;&gt;Reflections&lt;/h2&gt;
&lt;p&gt;I came away from ffconf inspired and filled with a desire to build weird stuff: I want to learn to do things with hardware, make pretty patterns with CSS, and teach my niece how to do the same just for the hell of it.&lt;/p&gt;
&lt;p&gt;I also came away feeling terrified by corporate surveillance and the absolute disregard for ethics in technology under the guise of convenience.&lt;/p&gt;
&lt;p&gt;Being a developer isn&#39;t just about building flashy stuff and sending it off into the world and moving onto the next thing: we&#39;ve got a responsibility to make sure that the tech we&#39;re building is doing good, not harm, and that people are educated about the implications if we do start building creepy stuff like Sidewalk. As Laura Kalbag put it: &lt;strong&gt;be the advocate, and question the default&lt;/strong&gt;. We know what these products and these companies are doing, so it&#39;s our job to tell everyone about it.&lt;/p&gt;
&lt;p&gt;ffconf is a small, family-run conference, with passionate speakers who have a story to tell. I&#39;d love to see more community-focused conferences like this - it&#39;s all too common nowadays to go to a big conference and end up listening to sales pitch after sales pitch when all you want to do is learn something.&lt;/p&gt;
&lt;p&gt;I feel like I should probably get rid of the Google Home that&#39;s sitting in my living room right now.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Everything I googled in a week as a professional software engineer</title>
      <link href="https://localghost.dev/blog/everything-i-googled-in-a-week-as-a-professional-software-engineer/"/>
      <updated>2019-09-02T00:00:00Z</updated>
      <id>https://localghost.dev/blog/everything-i-googled-in-a-week-as-a-professional-software-engineer/</id>
      <content type="html">&lt;p&gt;&lt;strong&gt;Update 15/10/2022:&lt;/strong&gt; I&#39;ve written a new version of this! &lt;a href=&quot;https://localghost.dev/blog/everything-i-googled-in-a-week-as-a-senior-software-engineer/&quot;&gt;Everything I googled in a week as a senior software engineer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In an attempt to dispel the idea that if you have to google stuff you&#39;re not a proper engineer, this is a list of nearly everything I googled in a week at work, where I&#39;m a software engineer with several years&#39; experience.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/ground.png&quot; alt=&quot;A google search for &amp;quot;threw it on the ground&amp;quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Happy birthday to the ground.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Obviously these weren&#39;t all googled in a row (although you can probably spot that a few were), but throughout the day. I can&#39;t remember the context of everything I was googling, but hopefully it&#39;ll make you feel a little better next time you have to google something.&lt;/p&gt;
&lt;h2 id=&quot;monday&quot; tabindex=&quot;-1&quot;&gt;Monday&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;npm react-testing-library&lt;/code&gt; - during a React upgrade, looking at dependencies to see latest versions and checking for breaking changes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Expecting a parsed GraphQL document. Perhaps you need to wrap the query string in a &amp;quot;gql&amp;quot; tag?&lt;/code&gt; - said React upgrade then started causing some super fun errors.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;react-apollo release notes&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;react-apollo/test-utils&lt;/code&gt; - tests were throwing some odd errors with our graphQL components.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;undo a rebase&lt;/code&gt; - oops.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;react testing library apollo &amp;quot;invariant violation&amp;quot;&lt;/code&gt; - package upgrades are so much fun!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jest silence warnings&lt;/code&gt; - don&#39;t judge me, ok?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;semantic HTML contact details&lt;/code&gt; - wanted to check if the &lt;code&gt;&amp;lt;address&amp;gt;&lt;/code&gt; tag was relevant here&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aa contrast checker&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;temporary visual impairment&lt;/code&gt; - fact checking for an accessibility talk I was giving that week&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dominos accessibility&lt;/code&gt; - popcorn.gif&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shame gif&lt;/code&gt; - an important part of any presentation&lt;/p&gt;
&lt;h2 id=&quot;tuesday&quot; tabindex=&quot;-1&quot;&gt;Tuesday&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;javascript get array of unique dates&lt;/code&gt; - if I have an array of &lt;code&gt;Date&lt;/code&gt;s, how can I filter them so they are unique? (&lt;code&gt;reduce&lt;/code&gt;, naturally, but I can rarely use that without googling it first)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;date to locale string&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;js date to locale string&lt;/code&gt; - after I got a load of Java results&lt;/p&gt;
&lt;p&gt;&lt;code&gt;alternatives to Moment.js&lt;/code&gt; - it&#39;s large&lt;/p&gt;
&lt;p&gt;&lt;code&gt;group array items by date&lt;/code&gt; - more &lt;code&gt;reduce&lt;/code&gt; fun&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sort object keys javascript&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;react fragment keys&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next link&lt;/code&gt; - needed a reminder of how to use the Link component in Next.JS&lt;/p&gt;
&lt;p&gt;&lt;code&gt;React.Children.only expected to receive a single React element child.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;visual studio code disable autocomplete html&lt;/code&gt; - it keeps autoclosing HTML elements on the same line, and I still can&#39;t switch it off&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dt dd dl&lt;/code&gt; - couldn&#39;t remember what the example use for these was.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;html nested sections&lt;/code&gt; - is it ok to have &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; inside &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;display dl in pairs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;veggie ipsum&lt;/code&gt; - the best lorem ipsum generator&lt;/p&gt;
&lt;p&gt;&lt;code&gt;css keyframes&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;css animate underline text&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dl vs ul&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;react generating keys&lt;/code&gt; - should I use some kind of hash, or should I use data in the props? (I ended up constructing a string with unique timestamp data)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;css checkbox&lt;/code&gt; - can we style checkboxes yet? (no)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;flexbox center span&lt;/code&gt; - it was 17:24 and I was tired by this point&lt;/p&gt;
&lt;p&gt;&lt;code&gt;grid minmax&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;flexible grid row&lt;/code&gt; - I don&#39;t have a whole lot of CSS Grid experience, so I always end up googling a ton with this.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;grid row height auto&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cauliflower shortage&lt;/code&gt; - someone told me about this and I panicked&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next.js hooks&lt;/code&gt; - we can use them, right? (we can, and I did)&lt;/p&gt;
&lt;h2 id=&quot;wednesday&quot; tabindex=&quot;-1&quot;&gt;Wednesday&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;cors&lt;/code&gt; - today is going to be bleak&lt;/p&gt;
&lt;p&gt;&lt;code&gt;the corrs&lt;/code&gt; - once I hit some CORS errors I decided I needed to make a meme, and I needed to find the perfect image. It took a surprisingly long time.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/thecorrs-search.png&quot; alt=&quot;Many many Google search results for The Corrs&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/thecors.jpg&quot; alt=&quot;A photoshopped version of the Corrs&#39; album with the title &amp;quot;No Access-Control-Allow-Origin header is present on the requested resource&amp;quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;Worth it.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;git patch trailing whitespace&lt;/code&gt; - I was sent a git patch with some whitespace that prevented it from actually patching&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jsx annotation&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;web api fetch preflight&lt;/code&gt; - in my CORS adventures I wanted to read up a bit more about preflight requests.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;web api fetch origin header&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;discriminated union flow&lt;/code&gt; - trying to diagnose problems with my Flow types.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;whitespace regex&lt;/code&gt; - is it &lt;code&gt;\w&lt;/code&gt;? (no, that&#39;s a word - it&#39;s &lt;code&gt;\s&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;regex not letter&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pat butcher emoji&lt;/code&gt; - what can I say, I google important things&lt;/p&gt;
&lt;p&gt;&lt;code&gt;woman shouting at cat&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;google oauth&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next.js authentication&lt;/code&gt; - sometimes it&#39;s helpful to google stuff to see if anyone has written examples of how to do common flows in the framework or tool that you&#39;re using&lt;/p&gt;
&lt;p&gt;&lt;code&gt;component displayname&lt;/code&gt; - do I need to do this with my higher-order components?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nextCookie&lt;/code&gt; - starting to mess around with oauth cookies&lt;/p&gt;
&lt;p&gt;&lt;code&gt;reading cookies in react&lt;/code&gt; - there must be a better way than &lt;code&gt;document.cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;js-cookie npm&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookies-js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;universal-cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;google oauth cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;🍪&lt;/p&gt;
&lt;h2 id=&quot;thursday&quot; tabindex=&quot;-1&quot;&gt;Thursday&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&amp;quot;log in with google&amp;quot; localhost&lt;/code&gt; - was having all sorts of problems getting this to work&lt;/p&gt;
&lt;p&gt;&lt;code&gt;httpserverrequest javascript&lt;/code&gt; - I have a feeling this was something to do with Flow types&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nextjs flowtypes&lt;/code&gt; - yep, there you go&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;quot;python-social-auth&amp;quot; react&lt;/code&gt; - trying to figure out if the django backend I was working with would play nicely with my React frontend&lt;/p&gt;
&lt;p&gt;&lt;code&gt;google social login&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vary header&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get cookie from 302&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;google social login cookies&lt;/code&gt; - I was having a really fun time with this as you can tell&lt;/p&gt;
&lt;p&gt;&lt;code&gt;google oauth cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;python-social-auth set-cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;python-social-auth site:stackoverflow.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;python-social-auth react site:stackoverflow.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;django&lt;/code&gt; - I think I gave up at this point and just googled Django because I have literally never used it&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetch send cookies&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;testing same origin credentials locally&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cross origin cookies&lt;/code&gt; - spoiler alert: not a thing&lt;/p&gt;
&lt;p&gt;&lt;code&gt;useState default value&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;quot;The value of the &#39;Access-Control-Allow-Origin&#39; header in the response must not be the wildcard &#39;*&#39; when when the request&#39;s credentials mode is &#39;include&#39;.&lt;/code&gt; - googling error messages is only second to console.log() as a debugging method&lt;/p&gt;
&lt;p&gt;&lt;code&gt;useState with empty array&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;react hooks initial state empty array&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;quot;Provisional headers are shown&amp;quot;&lt;/code&gt; - this is where the requests weren&#39;t going through and I couldn&#39;t see what the actual headers being sent were&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetch send cookies&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;how to see request headers in chrome&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetch not sending cookies&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Thursday was a whole lot of fun D:&lt;/p&gt;
&lt;h2 id=&quot;friday&quot; tabindex=&quot;-1&quot;&gt;Friday&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;provisional headers are shown&lt;/code&gt; - still at it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sending cookies from localhost&lt;/code&gt; - have I mentioned that I hate cookies?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;editing host file&lt;/code&gt; - desperate times (and it didn&#39;t even work)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sending cookies with different domain&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getinitialprops functional component&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getting cookies from document&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;js find&lt;/code&gt; - to check the usage and return types&lt;/p&gt;
&lt;p&gt;&lt;code&gt;string contains&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;string methods js&lt;/code&gt; - I can&#39;t keep any of these in my head&lt;/p&gt;
&lt;p&gt;&lt;code&gt;js string methods&lt;/code&gt; - 20 mins later&lt;/p&gt;
&lt;p&gt;&lt;code&gt;js fetch manual cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;django react cookies localhost&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;django react cookies localhost site:stackoverflow.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;httponly cookie&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;django httponly&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;async await promise.all&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nextjs port&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;google appengine node ports&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next rename static&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;install gcloud cli&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;method patch&lt;/code&gt; - couldn&#39;t remember what the HTTP method &lt;code&gt;PATCH&lt;/code&gt; does.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nextjs env&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next.js environment variables&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next js docs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;editing data with hooks&lt;/code&gt; - literally no idea what I was trying to google here but this was past 5pm so I was evidently quite tired&lt;/p&gt;
&lt;p&gt;&lt;code&gt;react form submit&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dayjs&lt;/code&gt; - I needed the documentation again.&lt;/p&gt;
&lt;p&gt;What I&#39;m trying to show with all this is that you can do something 100 times but still not remember how to do it off the top of your head. Never be ashamed of googling, even if it seems like the most basic thing you&#39;re looking up. I can never remember how &lt;code&gt;Date&lt;/code&gt; works.&lt;br /&gt;
I&#39;ve built plenty of forms in React but couldn&#39;t remember how &lt;code&gt;onSubmit&lt;/code&gt; worked on the Friday evening at 5:30pm. I constantly have to google JS string methods. Cookies are terrible. (Incidentally, we fixed the cookie issue by running everything in a docker container and tunneling with &lt;code&gt;ngrok&lt;/code&gt;, so everything&#39;s on the same domain.)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Questions to ask at an engineering interview</title>
      <link href="https://localghost.dev/blog/questions-to-ask-at-an-engineering-interview/"/>
      <updated>2019-08-22T00:00:00Z</updated>
      <id>https://localghost.dev/blog/questions-to-ask-at-an-engineering-interview/</id>
      <content type="html">&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/silicon-valley-crushed-it.png&quot; alt=&quot;A still from the HBO series Silicon Valley in which two interviewers are asking a candidate &#39;it says here on your resume that from 2010 to 2011, you crushed it?&#39;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Tech job interviews are often different flavours of the same thing, regardless of where you apply. Interviewers are likely to ask you questions about your experiences, perhaps a hypothetical question about what you might do in a certain situation, or delve into some of the how-it-works stuff under the hood of whatever programming language you&#39;ll be using. However, there&#39;s one question that you can &lt;em&gt;guarantee&lt;/em&gt; will come up:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Do you have any questions for us?&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(At this point you&#39;ll probably sigh with relief - internally, at least - as you&#39;ve reached the end of the interview.)&lt;/p&gt;
&lt;p&gt;It might be tempting to just skip over this with a &amp;quot;no, thank you&amp;quot; - maybe the job description gave you enough information for now - but you should always ask &lt;strong&gt;at least one question&lt;/strong&gt;. It&#39;s a surprisingly important part of the interview. Firstly, it&#39;s an opportunity for you to squash any worries you might have, or find out the answers to anything that wasn&#39;t quite clear before. Secondly, it&#39;s a chance for you to peek into how things &lt;em&gt;really&lt;/em&gt; are at Company X before you get too far in and realise you&#39;ve Made A Huge Mistake. An interview should be as much you interviewing &lt;em&gt;them&lt;/em&gt; as it is them interviewing you, as you need to find out whether the company is a good fit for &lt;em&gt;you&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Of course, you don&#39;t have to wait until the very end of the interview to ask questions - it&#39;s great to ask relevant questions throughout (though be careful that you don&#39;t end up answering every question with another question)!&lt;/p&gt;
&lt;h2 id=&quot;what-your-questions-say-about-you&quot; tabindex=&quot;-1&quot;&gt;What your questions say about you&lt;/h2&gt;
&lt;p&gt;This part of the interview is not just for your benefit: the questions you ask tell the interviewer more about you than you might think. For starters, it shows how engaged you are, what your priorities are and the kind of things you value in your role. It&#39;s an opportunity to show off your knowledge and experience by asking questions tailored to the work that you&#39;re looking for.&lt;/p&gt;
&lt;p&gt;How many questions is too many? It depends how much time you&#39;ve got left, but don&#39;t be afraid to turn up with a list. I brought a notebook into mine, and I took notes while they answered.&lt;/p&gt;
&lt;p&gt;So, what kind of questions might you ask? Obviously it&#39;s a chance for you to get answers to things that weren&#39;t clear about the application process or the job itself. You can ask about the kind of learning and development opportunities, or ask the old favourite of &amp;quot;what do you like most about working here&amp;quot;? Don&#39;t be afraid to spin questions that the interviewer asked you back around on them, as long as it&#39;s relevant and reasonable.&lt;/p&gt;
&lt;p&gt;My favourite interview question tactic is to think about things from my previous roles that frustrate you or that you know you don&#39;t want to experience again. Asking questions about these things can give you an idea of whether they do things differently at this place, or whether you&#39;re walking into the same job all over again. (Alternatively, if there&#39;s something you particuarly enjoy about your current role, it&#39;s a good way of finding out whether it&#39;ll be the same at this new place too.)&lt;/p&gt;
&lt;p&gt;I&#39;ll give you some questions that I might ask as a starter (and in some cases, what these questions are &lt;em&gt;really&lt;/em&gt; asking). I&#39;m not saying you should ask all of these questions - that would probably take all afternoon - but feel free to pick and choose depending on where you&#39;re applying and what&#39;s important to you. They&#39;re here as a starting point.&lt;/p&gt;
&lt;h2 id=&quot;the-how-does-this-work-here-questions&quot; tabindex=&quot;-1&quot;&gt;The &amp;quot;how does this work here?&amp;quot; questions&lt;/h2&gt;
&lt;p&gt;These are the questions based on your past experience. It&#39;s a great way to understand more about processes at the company, as well as making sure you won&#39;t end up working at somewhere that&#39;s exactly the same as your last place (unless that&#39;s something you want).&lt;/p&gt;
&lt;h3 id=&quot;working-in-product-teams&quot; tabindex=&quot;-1&quot;&gt;Working in product teams&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;How closely do the dev team work with business stakeholders/customers?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Is this a case of &amp;quot;IT vs The Business&amp;quot; or are engineers embedded throughout the company? I&#39;ve worked in both situations, and it&#39;s much nicer being considered a core part of the business rather than a &amp;quot;supporting function&amp;quot; building a product for someone else&#39;s business needs. Is there a product manager on the team who represents the needs of the customer? Are there regular demos with relevant stakeholders? Do they do any user testing?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How does work come in to the team?&lt;/p&gt;
&lt;p&gt;How involved are engineers with deciding what to build?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Am I going to be involved in the decision making and the shaping of what we&#39;re building or am I going to be blindly picking up JIRA tickets? Is it an iterative development process, or is it a case of &amp;quot;requirements come in, code comes out 6 months later&amp;quot;?&lt;/p&gt;
&lt;p&gt;It took a bit of getting used to, but now I love being involved right at the beginning, helping to decide what it is we&#39;re going to build and how we&#39;re going to build it. It&#39;s related to the first question in that it shows that engineering is a core part of the company and not just something that you do to achieve the thing for the business.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Who makes technical decisions? How much independence do product teams have to make technical decisions?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This question is about autonomy in what you&#39;re building. Is it going to be a case of &amp;quot;here is the work, go and do it&amp;quot; or &amp;quot;how shall we build this? What do you think?&amp;quot;&lt;/p&gt;
&lt;p&gt;Some degree of uniformity is vital, especially if you&#39;re working with lots of microservices. But having strategies for the way you work and the technologies you use is one thing; being told exactly what to do and having no independence or flexibility is another thing entirely.&lt;/p&gt;
&lt;h3 id=&quot;code-deployment-and-infrastructure&quot; tabindex=&quot;-1&quot;&gt;Code, deployment and infrastructure&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Who looks after the live applications?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Is it &amp;quot;you build it, you run it&amp;quot; or is it &amp;quot;you build it, and throw it over the wall&amp;quot;? Do engineers have ownership of the apps they build, and are they trusted to look after them?&lt;/p&gt;
&lt;p&gt;It&#39;s also worth finding out how on-call works, because if that&#39;s something that you can accommodate, it&#39;s a great opportunity to learn about the tech and how things work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What&#39;s your deployment process like?&lt;/p&gt;
&lt;p&gt;What does an average path to production look like for a service or application?&lt;/p&gt;
&lt;p&gt;Do you practice continuous integration?&lt;/p&gt;
&lt;p&gt;How often do you release to production?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Can I deploy stuff myself on an ad-hoc basis, or is it once a week/every three months? Is it going to be six months before you let me put it into the wild?&lt;/p&gt;
&lt;p&gt;This is about finding out how much friction there is in the path to production. The more obstacles and Process With A Capital P, the more obstacles to innovation and experimentation. &lt;em&gt;That said&lt;/em&gt;, there is a happy medium, and I don&#39;t believe you should be firing off software into prod without due diligence, some process and quality control, especially when you&#39;re working with people&#39;s data. It&#39;s worth finding out how the company handles that delicate balance.&lt;/p&gt;
&lt;p&gt;Bonus points for continuous deployment.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Say I wanted to spin up a new service. How would I do that?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Would I need to get permission from the Change Advisory Board in a distant ivory tower? Would I need to write numerous documents and have handover meetings for deployment and maintenance?&lt;/p&gt;
&lt;p&gt;The answer I got in my Monzo interview was &amp;quot;there&#39;s a command line script to generate a new service&amp;quot; and I nearly wept with joy.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How do you do version control?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If they say &amp;quot;Subversion&amp;quot;, it&#39;s time to question why. Is there a big beast of a monolith that was written in Java 6 and nobody has quite had the guts to move it off SVN because they aren&#39;t &lt;em&gt;quite&lt;/em&gt; sure where all the code lives, and do you really want to work with that every day?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How are architectural decisions made?&lt;/p&gt;
&lt;p&gt;Do you have an architecture team? What do they do?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Do you have people whose job title is literally &amp;quot;Architect&amp;quot;? If so, are they hands-on and technical, or do they come in and tell you how to build stuff and then leave again? Am I going to be allowed to make my own decisions about the structure of the software I&#39;m building?&lt;/p&gt;
&lt;p&gt;I&#39;ve definitely met some good architects, but really things like solution architecture should be done by the team building the actual thing.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What&#39;s your testing strategy?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Do your developers do any testing? (Conversely, are you obsessed with as high test coverage as possible?) And if there&#39;s pretty poor test coverage, are they open to improving it or do they see it as a waste of time?&lt;/p&gt;
&lt;h2 id=&quot;the-how-is-it-really-questions&quot; tabindex=&quot;-1&quot;&gt;The &amp;quot;how is it really?&amp;quot; questions&lt;/h2&gt;
&lt;p&gt;The questions that help you delve into day-to-day life at the company.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What does a typical day look like for you?&lt;/p&gt;
&lt;p&gt;What are the working hours like here?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Will I be working until 8pm, or can I go to my clay pigeon shooting evening class without getting Meaningful Glances from my colleagues behind their screens?&lt;/p&gt;
&lt;p&gt;Perhaps I&#39;m naïve, but I firmly believe in a work-life balance. I was very lucky to have that in my previous job, and I have it in my current job. I don&#39;t believe working later makes you a better contributor, as I think the more tired and burned out you get the worse your work is going to be in the long run. I want to know what kind of hours people keep so I can factor it into my decision.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Are there many parents here?&lt;/p&gt;
&lt;p&gt;How does parental leave work here?&lt;/p&gt;
&lt;p&gt;What facilities do you have for new parents?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This one is not a question I&#39;ve asked, as I don&#39;t have children and don&#39;t plan to have them any time soon, but if you&#39;re a parent or at that stage in your life where it&#39;s something you&#39;re thinking about, you want to know whether it&#39;s a parent-friendly workplace, whether there&#39;s anywhere to go and breastfeed, and what kind of caregiver leave they offer.&lt;/p&gt;
&lt;p&gt;I know that some places can be a bit funny if women of childbearing age ask about this kind of thing in interviews; if that&#39;s the case, I would question whether it&#39;s really somewhere you want to work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What kind of diversity and inclusion initiatives do you have here?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Does the company take diversity and inclusion seriously? Many (if not most) tech companies have traditionally very poor diversity in terms of gender and ethnicity - and often age, too. Trying to hire from a more diverse pool of candidates is one thing, but how are they trying to make things better in order to get people to stay? Are they hiring a diversity and inclusion lead? Do they have groups or networks?&lt;/p&gt;
&lt;h2 id=&quot;the-where-can-i-go-from-here-questions&quot; tabindex=&quot;-1&quot;&gt;The &amp;quot;where can I go from here?&amp;quot; questions&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;What kind of learning and development opportunities are there for employees?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A good way to find out whether they have a learning budget, or whether you can ask for books/courses/conference tickets.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do you ever host meetups or community events?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Perhas not important to everyone, but as someone who owes a lot of my professional development to the community as a whole, it&#39;s really important that wherever I work has a good community spirit.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do people here ever speak at conferences?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This one is a bit less helpful as (especially if it&#39;s a small team) it could be that the employees don&#39;t &lt;em&gt;want&lt;/em&gt; to speak at a conference, and that&#39;s totally fine. But if it&#39;s a huge company and nobody really speaks at conferences, it kind of raises the question of whether anyone does anything worth talking about (or, if they are, whether the company isn&#39;t keen on letting them talk about it.)&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I hope this has given you an idea of the kind of questions you might want to ask at an interview, but more importantly, the value you can get from asking them. If you&#39;ve got any more thoughts or ideas for interview questions, share this on Twitter with the link below and join the conversation!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
      <title>Why software engineers should know their audience</title>
      <link href="https://localghost.dev/blog/why-software-engineers-should-know-their-audience/"/>
      <updated>2019-04-06T00:00:00Z</updated>
      <id>https://localghost.dev/blog/why-software-engineers-should-know-their-audience/</id>
      <content type="html">&lt;p&gt;When you&#39;re talking about what you&#39;re working on, do you ever stop to think about what you&#39;re saying and whether the person you&#39;re talking to can actually understand it?&lt;/p&gt;
&lt;p&gt;There&#39;s more to being a software engineer than coding. What people often underestimate are the so-called &amp;quot;soft skills&amp;quot; (the worst term for something that&#39;s really vital) - the way that you interact with others around you. In particular, how you communicate what you&#39;re doing to people with different skillsets.&lt;/p&gt;
&lt;p&gt;Imagine you&#39;re in a standup and someone gives this update:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&#39;ll synthesize the haptic DDR3 network, that should shut down the SDD capacitor!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This came straight from the brilliant &lt;a href=&quot;http://shinytoylabs.com/jargon/&quot;&gt;Hollywood Jargon Generator&lt;/a&gt;, but to anyone who doesn&#39;t have as much technical experience as you, that might as well have been what you just said.&lt;/p&gt;
&lt;h2 id=&quot;u-wot-m8&quot; tabindex=&quot;-1&quot;&gt;u wot m8&lt;/h2&gt;
&lt;p&gt;One of the best pieces of advice I ever got was from a tech lead who took all of the engineers aside after a standup where we&#39;d been discussing how we were getting on with the new authentication flow we were writing. She told us to make sure we didn&#39;t go into too much technical detail in the standup, so that everyone could understand it. Sounds so simple, right?&lt;/p&gt;
&lt;p&gt;We&#39;d just spent ten minutes with the rest of the team - including our product manager and the marketing lead - and we&#39;d been talking about refresh tokens and token expiry. It had meant absolutely nothing to several of the people there. The more we mentioned these arcane technical concepts, the less the rest of the team wanted to come along to standups because they felt it was irrelevant or above them in some way.&lt;/p&gt;
&lt;p&gt;&amp;quot;But it&#39;s a standup!&amp;quot; I hear you cry. &amp;quot;You&#39;re only meant to give a quick high-level update anyway!&amp;quot;&lt;/p&gt;
&lt;p&gt;You&#39;d be surprised how easy it is to start talking in jargon without even realising for even the smallest of updates. Next time you give a standup update, ask yourself: if I weren&#39;t technical, would I know what I was talking about?&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://localghost.dev/img/blog/xkcd-eli5.png&quot; alt=&quot;&amp;quot;Two characters are talking. Left: What&#39;ve you been up to? Right: Doing tons of math for my thesis. Left: Can you explain it like I&#39;m five? Right: Oh my God, where are your parents?&amp;quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;figcaption&gt;source: xkcd&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;How much technical detail do you really need to go into? If your standup tends to be &amp;quot;here&#39;s what I did yesterday and here&#39;s what I&#39;m doing today&amp;quot;, do you need to say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We&#39;re still trying to get the auth service to store the user&#39;s refresh token in the database and check it against the incoming requests.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;or can you just say&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We&#39;re still trying to get authentication working in the backend.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To you the first example might look pretty easy to understand, but that&#39;s probably because you&#39;re an engineer. You know what a refresh token is. You know what a request is in this context. Non-technical folk probably knows what a database is, and what authentication is, but that&#39;s about it, and they&#39;ve switched off by the end of the sentence. By keeping the technical detail to a minimum you&#39;ll be providing an update that the rest of the team understand, so they know what everyone&#39;s working on.&lt;/p&gt;
&lt;p&gt;What about if there&#39;s a particular technical thing that&#39;s blocking you that you wanted to mention in standup? Either mention that you&#39;re generally blocked in the update and approach one of your fellow engineers afterwards, or maybe have a 5-minute dev-only standup after the main team standup where you can go into more technical detail.&lt;/p&gt;
&lt;p&gt;Here&#39;s another example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some of our ElasticSearch nodes went down last night, it turns out we are still using the old cluster so we&#39;re going to migrate to the new cluster today.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;vs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our search system went down last night, it turns out we&#39;re using an old server* so we&#39;re going to move it to the newer one today.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;* I know this isn&#39;t technically the same thing, but to someone who doesn&#39;t know what a cluster is, it literally doesn&#39;t matter. You can go into more detail in the relevant dev standup/slack channel/coffee break.&lt;/p&gt;
&lt;h2 id=&quot;use-real-world-analogies&quot; tabindex=&quot;-1&quot;&gt;Use real-world analogies&lt;/h2&gt;
&lt;p&gt;Keeping your language simple works for technical people too. If you&#39;re mentoring another engineer or helping someone work through a problem, being able to explain technical concepts in an easy-to-understand way and relating them to tangible concepts in the real world can really help others to learn from you. You&#39;ll find it a useful skill for giving talks - there&#39;s a real difference between &amp;quot;show me how&amp;quot; and &amp;quot;make me understand&amp;quot;. For example, in a talk about &lt;a href=&quot;https://redux-saga.js.org/&quot;&gt;redux-saga&lt;/a&gt; I used pictures of dogs to illustrate the library of effects it provides. In pretty much every object-oriented programming language tutorial there is some kind of &lt;code&gt;Person&lt;/code&gt; or &lt;code&gt;Car&lt;/code&gt; class to help you understand the concept of an object, and the tutorials about composition and inheritance usually talk about different species of animals.&lt;/p&gt;
&lt;p&gt;With the auth token example, I told my team a story about a secret members&#39; club involving an ever-changing secret handshake and a badge that let you learn the new secret handshake, which went down well and helped the others to get what we were trying to do, even though my product manager thought I was talking about a sex club.&lt;/p&gt;
&lt;h2 id=&quot;yeah-so-we-really-need-to-reticulate-the-splines-in-our-codebase&quot; tabindex=&quot;-1&quot;&gt;Yeah, so we really need to reticulate the splines in our codebase&lt;/h2&gt;
&lt;p&gt;It&#39;s not just standups where this skill comes in handy: in your day-to-day role, explaining technical things clearly and simply might be the thing that convinces them why you should take a break from delivering new features to go back and tackle the mountain of tech debt that&#39;s been piling up:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We need to upgrade a lot of the packages, check for security vulnerabilities and replace a lot of deprecated code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What does &amp;quot;deprecated&amp;quot; mean? What are packages in this context?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We&#39;ve got some old code that needs updating because soon it won&#39;t work any more, and it might pose a security risk.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It sounds so basic, but to a non-engineer, that&#39;s all they need to know. They might ask some questions about what kind of risk it poses, or what &amp;quot;won&#39;t work any more&amp;quot; means, but it&#39;s a lot easier to follow.&lt;/p&gt;
&lt;h2 id=&quot;challenge-yourself-to-keep-it-simple&quot; tabindex=&quot;-1&quot;&gt;Challenge yourself to keep it simple&lt;/h2&gt;
&lt;p&gt;It&#39;s not always easy to translate the hard stuff in easy terms, especially when you&#39;re working on things that involve kubernetes (true story). I still haven&#39;t found a good layman&#39;s explanation for what Docker does. But next time you&#39;re in a standup, listen carefully to what your fellow engineers say and then catch up with your other team members afterwards to ask if they followed everything. If they say yes, then you&#39;re fine, and you can just carry on as you were. But if they say no, consider getting your team to challenge themselves to deliver very simple standup for a a week. Make up ridiculous stories to illustrate migrating ElasticSearch clusters. At the end, ask those same people whether they feel like they know more about what you&#39;ve been up to.&lt;/p&gt;
&lt;p&gt;And if all else fails, there&#39;s my old favourite fallback:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Everything&#39;s slightly broken, but I&#39;m working on making it not broken.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
    </entry>
    
    <entry>
      <title>Things experienced speakers wish they&#39;d known:</title>
      <link href="https://localghost.dev/blog/things-experienced-speakers-wish-they-d-known/"/>
      <updated>2018-01-24T00:00:00Z</updated>
      <id>https://localghost.dev/blog/things-experienced-speakers-wish-they-d-known/</id>
      <content type="html">&lt;p&gt;Congratulations, you&#39;ve been accepted to speak at a conference!&lt;/p&gt;
&lt;p&gt;But once the initial euphoria wears off, you might find the impostor syndrome starting to set in. What if I&#39;m not good enough? Do I know enough about this topic? What if the audience hates me?&lt;/p&gt;
&lt;p&gt;That&#39;s me right now. I&#39;m currently preparing my first ever conference talk for &lt;a href=&quot;https://www.reactfest.uk/&quot;&gt;ReactFest 2018&lt;/a&gt;, which I was encouraged to apply to by the excellent folk at &lt;a href=&quot;https://www.meetup.com/en-AU/ReactJS-Girls-London/&quot;&gt;ReactJS Girls London&lt;/a&gt;. Having gained quite a few new followers on Twitter after my talk was announced, I thought I&#39;d canvas a bit for helpful hints for a first time speaker. I figured a few people might respond.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/type__error&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1474717155983773700/sXRdF5e3_normal.jpg&quot; alt=&quot;Avatar for type__error&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/type__error&quot;&gt;Sophie Koonin &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/type__error/status/955554649666355200&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;People who have spoken at tech conferences: what&#39;s one thing you wish you&#39;d known before your first talk?&lt;br /&gt;&lt;br /&gt;(RT for reach please?)&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955554649666355200&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;751 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 09:35:56 (UTC)&quot; href=&quot;https://twitter.com/type__error/status/955554649666355200&quot;&gt;10:35 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;I got so many fantastic tips and suggestions—including some from experienced speakers like &lt;a href=&quot;https://www.twitter.com/sarah_edo&quot;&gt;Sarah Drasner&lt;/a&gt;, &lt;a href=&quot;https://www.twitter.com/dan_abramov&quot;&gt;Dan Abramov&lt;/a&gt; and &lt;a href=&quot;https://www.twitter.com/anjuan&quot;&gt;Anjuan Simmons&lt;/a&gt; — that one user suggested I turn it into a blog post. So here it is. (Thanks to &lt;a href=&quot;https://www.twitter.com/sayzlim&quot;&gt;@sayzlim&lt;/a&gt; for the suggestion!)&lt;/p&gt;
&lt;p&gt;These tips have come from the tech community, but I think the majority of them can apply to any public speaking event.&lt;/p&gt;
&lt;h2 id=&quot;preparing-your-talk-slides-and-content&quot; tabindex=&quot;-1&quot;&gt;Preparing your talk: Slides and content&lt;/h2&gt;
&lt;p&gt;You&#39;ve got the outline of your talk that you submitted to the Call for Papers, but how do you turn it into a full talk? Whether your talk is full-length or a shorter lightning talk, it can be quite stressful when you&#39;re deciding what should go in and what you should leave out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Find someone with speaking experience to help you put together your talk.&lt;/strong&gt; The organisers of ReactFest put me in touch with a lovely volunteer who offered to give me some pointers. Find a friend, mentor or colleague with experience of giving conference talks, and ask if they will help you out.&lt;/p&gt;
&lt;p&gt;One thing that came up repeatedly on Twitter was the concept of storytelling. Rather than just listing ideas or treating the talk like a tutorial, you want to &lt;strong&gt;tell a story&lt;/strong&gt;. This makes the talk so much more engaging, and more likely to be remembered.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/markdalgleish&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/754886061872979968/BzaOWhs1_normal.jpg&quot; alt=&quot;Avatar for markdalgleish&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/markdalgleish&quot;&gt;🧁 Mark Dalgleish &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/markdalgleish&quot;&gt;@markdalgleish&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/markdalgleish/status/955688673911103493&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/threepointone&quot;&gt;@threepointone&lt;/a&gt; Share a story, not a series of facts. Took me far too long to figure that one out.&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955688673911103493&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;20 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 06:28:30 (UTC)&quot; href=&quot;https://twitter.com/markdalgleish/status/955688673911103493&quot;&gt;07:28 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1336281436685541376/fRSl8uJP_normal.jpg&quot; alt=&quot;Avatar for dan_abramov&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;дэн &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;@dan_abramov&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/dan_abramov/status/955591200769232896&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; It helps if you have one idea to center your talk around. Like in Inception movie, what’s the one thing you want to put in their heads? This helps you decide what to cut (and you might have to cut things when you realize you’re running over the day before the talk).&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955591200769232896&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;55 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 12:01:10 (UTC)&quot; href=&quot;https://twitter.com/dan_abramov/status/955591200769232896&quot;&gt;01:01 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;When it comes to putting a deck together, most people tend to use Google Slides, Keynote or PowerPoint, but tech folks might also like to try out &lt;a href=&quot;https://revealjs.com/#/&quot;&gt;Reveal.JS&lt;/a&gt;, which lets you write fully responsive slides in HTML or Markdown (perfect for code snippets!). I used it for a talk I gave at a meetup, and had the entire talk hosted on GitHub Pages just in case I had to use someone else&#39;s computer (which, in the end, I did!).&lt;/p&gt;
&lt;p&gt;Once you&#39;ve got the content nailed, you can focus on the design. Don&#39;t worry about being too fancy — you want your audience to focus on what you are saying rather than what&#39;s on the slides. &lt;strong&gt;Too many words on the slides will distract the audience, as will too many animated gifs!&lt;/strong&gt; You can always create another version of your deck with more details on for sharing afterwards, as suggested by &lt;a href=&quot;https://www.twitter.com/henrihelvetica&quot;&gt;@henrihelvetica&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/johnwards&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1504834746907607046/byLeFAmk_normal.jpg&quot; alt=&quot;Avatar for johnwards&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/johnwards&quot;&gt;John Wards &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/johnwards&quot;&gt;@johnwards&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/johnwards/status/955786243605843970&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Take all those points off the slide and move to speaker notes, replace with cat* picture that represents that section.&lt;br /&gt;&lt;br /&gt;People will focus on you rather than reading ahead and becoming bored.&lt;br /&gt;&lt;br /&gt;*other options are available&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955786243605843970&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;27 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 12:56:12 (UTC)&quot; href=&quot;https://twitter.com/johnwards/status/955786243605843970&quot;&gt;01:56 PM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Keep your slide theme nice and simple, and consider whether the theme you have chosen will be easily readable from the back of the room with a crappy projector. Put your name and Twitter handle (if you have one) at the bottom of every slide because people will forget it if you only put it at the beginning (thanks &lt;a href=&quot;https://www.twitter.com/claireinez&quot;&gt;@claireinez&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;If you&#39;re putting code in your slides, &lt;strong&gt;don&#39;t go overboard.&lt;/strong&gt; Sometimes it&#39;s inevitable — for example if you&#39;re talking about how a particular tool or library works —but as &lt;a href=&quot;https://www.twitter.com/alexmcpherson&quot;&gt;@alexmcpherson&lt;/a&gt; suggests, it&#39;s best to keep them short, 8–10 lines of code maximum. You can always share a repo or a CodePen link with your audience afterwards.&lt;/p&gt;
&lt;h2 id=&quot;practice-makes-perfect&quot; tabindex=&quot;-1&quot;&gt;Practice makes perfect&lt;/h2&gt;
&lt;p&gt;Something that came up over and over again: &lt;strong&gt;rehearse your talk!&lt;/strong&gt; Do it on your own, give it to your cat, make your colleagues listen to it and get their feedback. I got my fiancé to listen to my talk about &lt;a href=&quot;https://redux-saga.js.org/&quot;&gt;redux-saga&lt;/a&gt;, and even though he didn&#39;t know the content, he gave me some really helpful feedback about &lt;em&gt;how&lt;/em&gt; I was delivering the talk. &lt;strong&gt;Your manner of delivery is just as important as what you&#39;re saying.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Local developer meetups can be a fantastic place to &lt;strong&gt;try out your talk&lt;/strong&gt; before the conference and gather some feedback/see how it lands — a bit like how comedians try out new material in smaller venues before festivals and tours.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/jevakallio&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1530342542075695104/u546yO_I_normal.jpg&quot; alt=&quot;Avatar for jevakallio&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/jevakallio&quot;&gt;Jani Eväkallio &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/jevakallio&quot;&gt;@jevakallio&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/jevakallio/status/955557640821706752&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Rehearse three times:&lt;br /&gt;- First alone&lt;br /&gt;- Second with supportive audience (partner, friends, colleagues)&lt;br /&gt;- Finally, a smaller local meetup&lt;br /&gt;&lt;br /&gt;By the conference you&#39;ll know your timings &amp;amp; punchlines by heart and have ironed out any kinks, so you can focus on having fun&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955557640821706752&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;153 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 09:47:49 (UTC)&quot; href=&quot;https://twitter.com/jevakallio/status/955557640821706752&quot;&gt;10:47 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Rehearsing your talk will also help you to get a feel for &lt;strong&gt;how long it takes to run&lt;/strong&gt;. Your talk will be strictly timeboxed, so make sure you get your key points across in the time that you have.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1336281436685541376/fRSl8uJP_normal.jpg&quot; alt=&quot;Avatar for dan_abramov&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;дэн &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;@dan_abramov&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/dan_abramov/status/955589928011927553&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; If you haven’t practiced you will run over. My first talk at a local meetup was supposed to be 30 minutes, I only got through half of the slides. This is solved by practice: actually rehearse the whole talk (to an empty room, then to an audience).&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955589928011927553&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;56 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 11:56:07 (UTC)&quot; href=&quot;https://twitter.com/dan_abramov/status/955589928011927553&quot;&gt;12:56 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Don&#39;t be tempted to script out your entire talk, as it&#39;ll come across as a bit robotic (plus put a whole lot more pressure on you to learn it). Instead, become familiar with the key points of your talk and &lt;strong&gt;deliver those in a more improvised, natural way&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;technology-isn-t-always-your-friend&quot; tabindex=&quot;-1&quot;&gt;Technology isn&#39;t always your friend&lt;/h2&gt;
&lt;p&gt;Make sure your talk works offline — you never know what&#39;s going to happen. I&#39;ve been to a tech conference before with no wifi or sockets for power. As &lt;a href=&quot;https://twitter.com/Naomi_Freeman/status/955565518488031234&quot;&gt;@naomi_freeman&lt;/a&gt; suggests: &amp;quot;Don&#39;t trust the wifi. Download any online content BEFORE travelling.&amp;quot;&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/shanselman&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1133122333290291200/xV9gO-D6_normal.jpg&quot; alt=&quot;Avatar for shanselman&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/shanselman&quot;&gt;Scott Hanselman 🇺🇦 &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/shanselman&quot;&gt;@shanselman&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/shanselman/status/955967486846951424&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; test your talk in Airplane mode&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955967486846951424&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;13 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 24, 2018 12:56:24 (UTC)&quot; href=&quot;https://twitter.com/shanselman/status/955967486846951424&quot;&gt;01:56 AM · Jan 24, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Dan Abramov suggests having a &lt;strong&gt;backup of the slides online&lt;/strong&gt; that will run on someone else&#39;s laptop. Google Slides can be run from anywhere if you share the link, or you can host your Reveal.JS talk somewhere like GitHub Pages. Make sure you are comfortable giving your talk &lt;strong&gt;without prompts&lt;/strong&gt;, as you might not be able to access your speaker notes.&lt;/p&gt;
&lt;p&gt;If you can, find out beforehand what the setup will be like at the conference. The projector they are using, the size of the room and the equipment available will all affect things like font size, colour scheme and audio.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/Paul_Bone&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1227157516125007872/wGv0kVwc_normal.jpg&quot; alt=&quot;Avatar for Paul_Bone&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/Paul_Bone&quot;&gt;Paul Bone &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/Paul_Bone&quot;&gt;@Paul_Bone&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/Paul_Bone/status/955662979504029696&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/MsAmandaDean&quot;&gt;@MsAmandaDean&lt;/a&gt; Whether the projector is DVI/VGA/HDMI etc. Whether it&#39;s 4:3 or 16:9. Whether the room is light or dark (should I use light slides or dark ones).&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955662979504029696&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;2 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 04:46:24 (UTC)&quot; href=&quot;https://twitter.com/Paul_Bone/status/955662979504029696&quot;&gt;05:46 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/CynthiaSavard&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/920814970522259457/RjXTRlt3_normal.jpg&quot; alt=&quot;Avatar for CynthiaSavard&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/CynthiaSavard&quot;&gt;Cynthia Savard &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/CynthiaSavard&quot;&gt;@CynthiaSavard&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/CynthiaSavard/status/955895439232028673&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/CallbackWomen&quot;&gt;@CallbackWomen&lt;/a&gt; Bring all of the dongles, bring your own clicker (usb) , plan for no presenter notes, always email yourself a pdf backup version&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955895439232028673&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;4 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 08:10:07 (UTC)&quot; href=&quot;https://twitter.com/CynthiaSavard/status/955895439232028673&quot;&gt;09:10 PM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Don&#39;t assume the organisers will provide the adaptor you need to connect your laptop! &lt;strong&gt;Bring it yourself just to be safe&lt;/strong&gt; (and label it with your name so nobody steals it).&lt;/p&gt;
&lt;h2 id=&quot;to-demo-or-not-to-demo&quot; tabindex=&quot;-1&quot;&gt;To demo or not to demo?&lt;/h2&gt;
&lt;p&gt;This was a bit of a divisive one — should you have a live demo in your talk?&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/khanght&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1469049047981772803/wOmnrmf__normal.jpg&quot; alt=&quot;Avatar for khanght&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/khanght&quot;&gt;Khang Hoang &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/khanght&quot;&gt;@khanght&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/khanght/status/955595332993216512&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/dan_abramov&quot;&gt;@dan_abramov&lt;/a&gt; Demos won’t work&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955595332993216512&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;2 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 12:17:36 (UTC)&quot; href=&quot;https://twitter.com/khanght/status/955595332993216512&quot;&gt;01:17 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1336281436685541376/fRSl8uJP_normal.jpg&quot; alt=&quot;Avatar for dan_abramov&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;дэн &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;@dan_abramov&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/dan_abramov/status/955596853457969152&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/khanght&quot;&gt;@khanght&lt;/a&gt; &lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Mine worked tho&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955596853457969152&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;3 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 12:23:38 (UTC)&quot; href=&quot;https://twitter.com/dan_abramov/status/955596853457969152&quot;&gt;01:23 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;A demo can be a fantastic way of, well, demonstrating a technical concept. But there&#39;s always a risk that something will go wrong on the night — whether it&#39;s down to nerves, technical issues or just plain bad luck. You can avoid this by &lt;strong&gt;making a recording of it beforehand&lt;/strong&gt; that you can play to the audience and narrate what you&#39;re doing. Or have a &lt;strong&gt;working copy&lt;/strong&gt; that you can check out from Git in case of emergency (thanks to &lt;a href=&quot;https://twitter.com/wilslau&quot;&gt;Laura Wilson&lt;/a&gt; for that tip).&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1281071936605323266/wc1KRZLK_normal.jpg&quot; alt=&quot;Avatar for sarah_edo&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;Sarah Drasner &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;@sarah_edo&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/sarah_edo/status/955561991329923072&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/threepointone&quot;&gt;@threepointone&lt;/a&gt; make &#39;em work offline and have a backup screen recording of it in action&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955561991329923072&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;168 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 10:05:06 (UTC)&quot; href=&quot;https://twitter.com/sarah_edo/status/955561991329923072&quot;&gt;11:05 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Mac users can use &lt;a href=&quot;https://getkap.co/&quot;&gt;Kap&lt;/a&gt; to record screen captures in video or GIF format. There&#39;s also &lt;a href=&quot;https://launchpad.net/kazam&quot;&gt;Kazam&lt;/a&gt; for Linux and &lt;a href=&quot;https://camstudio.org/&quot;&gt;CamStudio&lt;/a&gt; for Windows.&lt;/p&gt;
&lt;p&gt;In some cases screen captures might even be better than live demos:&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/kyleshevlin&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/786039150667411456/t_0mWTZk_normal.jpg&quot; alt=&quot;Avatar for kyleshevlin&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/kyleshevlin&quot;&gt;Kyle Shevlin &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/kyleshevlin&quot;&gt;@kyleshevlin&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/kyleshevlin/status/955562633989562368&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/sarah_edo&quot;&gt;@sarah_edo&lt;/a&gt; &lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/threepointone&quot;&gt;@threepointone&lt;/a&gt; Yes, I don&#39;t recommend any live coding in your first one. No reason to add one more thing to be nervous about. And I&#39;ve seen Sarah&#39;s recordings, they work great for talks! She can describe what&#39;s going on without having to type at the same time.&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955562633989562368&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;41 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 10:07:40 (UTC)&quot; href=&quot;https://twitter.com/kyleshevlin/status/955562633989562368&quot;&gt;11:07 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;And as &lt;a href=&quot;https://twitter.com/fforres/status/955624874298494976&quot;&gt;@fforres&lt;/a&gt; points out, don&#39;t rely on your audience to be sensible — if your demo involves live input from the audience, be warned, people might send in something rude!&lt;/p&gt;
&lt;h2 id=&quot;on-the-day&quot; tabindex=&quot;-1&quot;&gt;On the day&lt;/h2&gt;
&lt;p&gt;The dress code at tech conferences can be pretty casual, but if you&#39;re wearing a dress, make sure it has pockets as you&#39;ll need to attach a mic battery pack! &lt;a href=&quot;https://www.twitter.com/knitcodemonkey&quot;&gt;@knitcodemonkey&lt;/a&gt; says: &amp;quot;Wear something with a collar and a waistband for the mic. Wear comfy shoes.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bring a water bottle&lt;/strong&gt; and don&#39;t forget to drink from it to keep your voice happy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Take off your lanyard&lt;/strong&gt; or any heavy jewellery before you go on, because microphones will pick up the sound of it rattling around.&lt;/p&gt;
&lt;p&gt;Make sure your laptop is ready for the talk: close all other programs, have the slides up and ready, and turn all notifications off. Put your laptop on airplane mode if you can. If you have a smart watch or phone on you, don&#39;t forget to silence that as well.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/HenriHelvetica&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/960605708202004481/MMNCgNgM_normal.jpg&quot; alt=&quot;Avatar for HenriHelvetica&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/HenriHelvetica&quot;&gt;Henri Helvetica 🚀 👨🏾‍🚀 🇭🇹 &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/HenriHelvetica&quot;&gt;@HenriHelvetica&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/HenriHelvetica/status/955733006349512704&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/keithjgrant&quot;&gt;@keithjgrant&lt;/a&gt; &lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; The lanyard bit is 100%! Add jewelry to that.&lt;br /&gt;Btw, ✈️ mode AND shut off wifi.&lt;br /&gt;Someone who loved my talk was kindly tweeting non stop about it and I kept getting notifications on my watch - I actually thought it was an emergency 🚨.&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955733006349512704&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;8 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 09:24:40 (UTC)&quot; href=&quot;https://twitter.com/HenriHelvetica/status/955733006349512704&quot;&gt;10:24 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1336281436685541376/fRSl8uJP_normal.jpg&quot; alt=&quot;Avatar for dan_abramov&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;дэн &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;@dan_abramov&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/dan_abramov/status/955595076092260352&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/acdlite&quot;&gt;@acdlite&lt;/a&gt; And turn the notifications off! Otherwise you risk exposing private messages hopping on the screen during the talk 😛&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955595076092260352&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;20 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 12:16:34 (UTC)&quot; href=&quot;https://twitter.com/dan_abramov/status/955595076092260352&quot;&gt;01:16 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;As you&#39;re about to go on, you might feel the nerves. The audience is waiting! Take a deep breath and &lt;strong&gt;try to radiate confidence&lt;/strong&gt;, even if it doesn&#39;t come naturally to you. The audience won&#39;t know you&#39;re faking confidence (and soon you won&#39;t be). You know your stuff, you love your topic, you&#39;ve rehearsed it time and time again — &lt;strong&gt;you are ready&lt;/strong&gt;. Enjoy it.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1336281436685541376/fRSl8uJP_normal.jpg&quot; alt=&quot;Avatar for dan_abramov&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;дэн &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;@dan_abramov&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/dan_abramov/status/955590579148279809&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Peak scary is ten seconds before the talk. I didn’t anticipate it to be this scary. But once you get into your element (by talking about things you care about) the fear goes away completely, it even becomes fun. At least it did for me.&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955590579148279809&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;69 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 11:58:42 (UTC)&quot; href=&quot;https://twitter.com/dan_abramov/status/955590579148279809&quot;&gt;12:58 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/jevakallio&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1530342542075695104/u546yO_I_normal.jpg&quot; alt=&quot;Avatar for jevakallio&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/jevakallio&quot;&gt;Jani Eväkallio &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/jevakallio&quot;&gt;@jevakallio&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/jevakallio/status/955555697223196672&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Projecting confidence is as good as having it. Even if you are not 100% happy with your talk, nobody knows what it was supposed to look like, so just rock your stuff and don&#39;t apologise for a thing&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955555697223196672&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;31 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 09:40:06 (UTC)&quot; href=&quot;https://twitter.com/jevakallio/status/955555697223196672&quot;&gt;10:40 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Be aware you might not be able to see anyone in the audience…&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/NikkitaFTW&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1407810876875419657/1PZPCpvD_normal.jpg&quot; alt=&quot;Avatar for NikkitaFTW&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/NikkitaFTW&quot;&gt;Sara Vieira &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/NikkitaFTW&quot;&gt;@NikkitaFTW&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/NikkitaFTW/status/955704311312519168&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/ReactFest&quot;&gt;@ReactFest&lt;/a&gt; One thing I didn&#39;t know and really surprised me is that you probably will not be able to see anything, the lights pointing at the stage will most likely almost blind you 🙁&lt;br /&gt;And you are going to rock!&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955704311312519168&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;4 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 07:30:38 (UTC)&quot; href=&quot;https://twitter.com/NikkitaFTW/status/955704311312519168&quot;&gt;08:30 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Move around a bit&lt;/strong&gt;, rather than standing still behind the podium. It can be tempting to just keep talking — don&#39;t forget to pause now and again.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1281071936605323266/wc1KRZLK_normal.jpg&quot; alt=&quot;Avatar for sarah_edo&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;Sarah Drasner &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;@sarah_edo&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/sarah_edo/status/955562218615025664&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Breathe.&lt;br /&gt;&lt;br /&gt;(seriously)&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955562218615025664&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;26 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 10:06:01 (UTC)&quot; href=&quot;https://twitter.com/sarah_edo/status/955562218615025664&quot;&gt;11:06 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/_davideast&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1429504343875670016/zjwrjcGY_normal.jpg&quot; alt=&quot;Avatar for _davideast&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/_davideast&quot;&gt;David East &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/_davideast&quot;&gt;@_davideast&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/_davideast/status/955597614560378880&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Plan out when you&#39;re going to pause during your talk. &quot;Letting it land&quot; is really important for the audience. It also keeps you from rambling at light speed (a habit I still have to unlearn for each and every talk)&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955597614560378880&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;39 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 12:26:40 (UTC)&quot; href=&quot;https://twitter.com/_davideast/status/955597614560378880&quot;&gt;01:26 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1336281436685541376/fRSl8uJP_normal.jpg&quot; alt=&quot;Avatar for dan_abramov&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;дэн &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/dan_abramov&quot;&gt;@dan_abramov&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/dan_abramov/status/955593635390808064&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/acdlite&quot;&gt;@acdlite&lt;/a&gt; Whenever you get nervous just take a pause. Breathe. Drink some water. It’s empowering to realize you can even make an awkward pause and everyone will just sit silently because you control the room 😎&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955593635390808064&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;75 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 12:10:51 (UTC)&quot; href=&quot;https://twitter.com/dan_abramov/status/955593635390808064&quot;&gt;01:10 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Be mindful how you address the audience — don&#39;t just fall back on &amp;quot;guys&amp;quot; considering your audience is likely to be a mix of genders (thanks &lt;a href=&quot;https://twitter.com/fforres/status/955624874298494976&quot;&gt;@fforres&lt;/a&gt;). Jokes are a fantastic way of lightening the mood and keeping the audience engaged, but avoid any risqué/potentially inappropriate jokes.&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/fuzzychef&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1450166432654712833/_tvvPyQ8_normal.jpg&quot; alt=&quot;Avatar for fuzzychef&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/fuzzychef&quot;&gt;Berkubernetus &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/fuzzychef&quot;&gt;@fuzzychef&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/fuzzychef/status/955660680765427712&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/Heronymus&quot;&gt;@Heronymus&lt;/a&gt; Risque&#39; jokes are a *really* bad idea in a public presentation.&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955660680765427712&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;3 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 04:37:16 (UTC)&quot; href=&quot;https://twitter.com/fuzzychef/status/955660680765427712&quot;&gt;05:37 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Don&#39;t be put off if you make a mistake! If you leave something out or stumble over something, nobody will know. Keep going!&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/MandaLaceyS&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1520675351/me_normal.png&quot; alt=&quot;Avatar for MandaLaceyS&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/MandaLaceyS&quot;&gt;Amanda Stockwell &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/MandaLaceyS&quot;&gt;@MandaLaceyS&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/MandaLaceyS/status/955606916163604480&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/justjessdc&quot;&gt;@justjessdc&lt;/a&gt; You&#39;ll notice your own &quot;mistakes&quot; 100 times more than anyone else. But no one else knows what you were planning to say. So if you take a breath and keep going, probably no one else will know there was even a blip. Also, bring extra clothes. I spilled coffee😂&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955606916163604480&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;29 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 01:03:37 (UTC)&quot; href=&quot;https://twitter.com/MandaLaceyS/status/955606916163604480&quot;&gt;02:03 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/_s_hari&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1515077650557149195/ZoR5WdFD_normal.jpg&quot; alt=&quot;Avatar for _s_hari&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/_s_hari&quot;&gt;Santosh Hari 😷 &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/_s_hari&quot;&gt;@_s_hari&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/_s_hari/status/955760274623131649&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Prep! Whatever the content, prep till you can wake up in the middle of the night and do the talk from memory&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955760274623131649&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;2 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 11:13:01 (UTC)&quot; href=&quot;https://twitter.com/_s_hari/status/955760274623131649&quot;&gt;12:13 PM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;And don&#39;t forget the most important thing:&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/justinfagnani&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/378800000808710206/2dbdaa1cb7b0db02f997aea5b40f29b8_normal.jpeg&quot; alt=&quot;Avatar for justinfagnani&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/justinfagnani&quot;&gt;Justin Fagnani &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/justinfagnani&quot;&gt;@justinfagnani&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/justinfagnani/status/955556013943373824&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; &lt;a href=&quot;https://twitter.com/threepointone&quot;&gt;@threepointone&lt;/a&gt; Everyone&#39;s rooting for you&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955556013943373824&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;18 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 09:41:21 (UTC)&quot; href=&quot;https://twitter.com/justinfagnani/status/955556013943373824&quot;&gt;10:41 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/damovisa&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/982382127089041408/dPUE3zMX_normal.jpg&quot; alt=&quot;Avatar for damovisa&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/damovisa&quot;&gt;Damian Brady 🥑 💉 💉 💉 #AlwaysWasAlwaysWillBe &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/damovisa&quot;&gt;@damovisa&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/damovisa/status/955721090147303424&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; The audience is on your side. They want you to succeed.&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955721090147303424&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;14 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 08:37:18 (UTC)&quot; href=&quot;https://twitter.com/damovisa/status/955721090147303424&quot;&gt;09:37 AM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/jonskeet&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/783726102996922368/4OTYaJ-o_normal.jpg&quot; alt=&quot;Avatar for jonskeet&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/jonskeet&quot;&gt;Jon Skeet &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/jonskeet&quot;&gt;@jonskeet&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/jonskeet/status/955574730421088257&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; If you enjoy yourself (and your topic) then other people will enjoy the talk. Enthusiasm is viral. People remember good talks. People forget bad talks. People really remember and hate on *nasty* (sexist, racist etc) talks; never be worse than yourself just to be memorable.&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955574730421088257&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;33 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 10:55:44 (UTC)&quot; href=&quot;https://twitter.com/jonskeet/status/955574730421088257&quot;&gt;11:55 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;You&#39;re at the conference because you love your topic, and so does everyone else there. You&#39;re all on the same team. As &lt;a href=&quot;https://twitter.com/tlakomy/status/955557815292227585&quot;&gt;@tlakomy&lt;/a&gt; assures me: &amp;quot;It&#39;s going to be okay&amp;quot;.&lt;/p&gt;
&lt;h2 id=&quot;taking-questions&quot; tabindex=&quot;-1&quot;&gt;Taking questions?&lt;/h2&gt;
&lt;p&gt;Sarah Drasner recommends not taking questions during your first talk:&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1281071936605323266/wc1KRZLK_normal.jpg&quot; alt=&quot;Avatar for sarah_edo&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;Sarah Drasner &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/sarah_edo&quot;&gt;@sarah_edo&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/sarah_edo/status/955564180970528768&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; oh! a better one: don&#39;t take questions your first time. I don&#39;t recommend it because you get done with the tada! and then it just kind of tapers off from there. plus the best questions aren&#39;t performative, they&#39;re the ones that happen off the stage face to face&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955564180970528768&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;65 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 22, 2018 10:13:48 (UTC)&quot; href=&quot;https://twitter.com/sarah_edo/status/955564180970528768&quot;&gt;11:13 PM · Jan 22, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;That said, it&#39;s a personal choice — if you feel comfortable (and time allows it) you can take as many questions as you want. An excellent tip from &lt;a href=&quot;https://www.twitter.com/bradwaynemartin&quot;&gt;@bradwaynemartin&lt;/a&gt;: &lt;strong&gt;always repeat the questions back to the audience&lt;/strong&gt;, as the microphone won&#39;t pick them up.&lt;/p&gt;
&lt;p&gt;But if anyone does challenge any decisions you made, you can fire this straight back:&lt;/p&gt;
&lt;blockquote class=&quot;tweet-card&quot;&gt;&lt;div class=&quot;tweet-header&quot;&gt;&lt;a class=&quot;tweet-profile&quot; href=&quot;https://twitter.com/hpcprogrammer&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/profile_images/1532932743939727360/x4owY3LL_normal.jpg&quot; alt=&quot;Avatar for hpcprogrammer&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;tweet-author&quot;&gt;&lt;a class=&quot;tweet-author-name&quot; href=&quot;https://twitter.com/hpcprogrammer&quot;&gt;Fernanda Foertter | 🇧🇷🇺🇸🥑👩🏽‍💻🤓 &lt;/a&gt;&lt;a class=&quot;tweet-author-handle&quot; href=&quot;https://twitter.com/hpcprogrammer&quot;&gt;@hpcprogrammer&lt;/a&gt;&lt;/div&gt;&lt;a class=&quot;tweet-bird&quot; href=&quot;https://twitter.com/hpcprogrammer/status/955779489140703232&quot;&gt;&lt;div class=&quot;tweet-bird-icon&quot; role=&quot;presentation&quot; aria-label=&quot;View on Twitter&quot; title=&quot;View on Twitter&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;tweet-body&quot;&gt;&lt;a href=&quot;https://twitter.com/type__error&quot;&gt;@type__error&lt;/a&gt; Best answer I ever gave to a “why would you do it this way” was “why not? Seemed like a good place to start. Next question?” I don’t know why it took me so long to learn it&lt;/p&gt;&lt;div class=&quot;tweet-images&quot;&gt;&lt;/div&gt;&lt;div class=&quot;tweet-footer&quot;&gt;&lt;a class=&quot;tweet-like&quot; href=&quot;https://twitter.com/intent/like?tweet_id=955779489140703232&quot;&gt;&lt;div class=&quot;tweet-like-icon&quot; aria-label=&quot;Like&quot; title=&quot;Like&quot; role=&quot;img&quot;&gt;&lt;/div&gt;&lt;span class=&quot;tweet-like-count&quot;&gt;21 &lt;/span&gt;&lt;/a&gt;&lt;a class=&quot;tweet-date&quot; title=&quot;Time Posted: Jan 23, 2018 12:29:22 (UTC)&quot; href=&quot;https://twitter.com/hpcprogrammer/status/955779489140703232&quot;&gt;01:29 PM · Jan 23, 2018&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;h2 id=&quot;good-luck&quot; tabindex=&quot;-1&quot;&gt;Good luck!&lt;/h2&gt;
&lt;p&gt;To all first-time or aspiring speakers: you&#39;ve got this!.&lt;/p&gt;
&lt;p&gt;If you&#39;re looking to get into the world of conference speaking, a great place to start is local developer meetups (if you can&#39;t find one, start one!). You might even start by giving talks at work to your colleagues. The more you do it, and the more feedback you gather, the more comfortable you&#39;ll become.&lt;/p&gt;
&lt;p&gt;I also recommend taking a look at Jo Francetti&#39;s &lt;a href=&quot;https://medium.com/samsung-internet-dev/public-speaking-for-beginners-8bdee16123ba&quot;&gt;guide to Public Speaking For Beginners&lt;/a&gt; and Emily Webber&#39;s &lt;a href=&quot;https://github.com/ewebber/whatspeakersneed/blob/master/index.md&quot;&gt;list of things that speakers need from conference organisers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you to everyone who contributed to the enormous Twitter thread that led to the creation of this post. If I have forgotten to credit anyone, or you would prefer not to be included, please get in touch.&lt;/p&gt;
</content>
    </entry>
</feed>