<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Danny Smith - Articles</title><description>Latest articles from Danny Smith</description><link>https://danny.is/</link><item><title>How my personal video platform works</title><link>https://danny.is/writing/loomclone-part-2/</link><guid isPermaLink="true">https://danny.is/writing/loomclone-part-2/</guid><pubDate>Fri, 24 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My &lt;a href=&quot;/writing/loomclone-part-1&quot;&gt;introduction&lt;/a&gt; to &lt;em&gt;LoomClone&lt;/em&gt; was pretty sparse on technical detail so I’m gonna dive into a bit of that here, specifically how the mac app and backend API work together for the lifecycle of a recording. You should go read the intro if you haven’t already or this won’t make much sense.&lt;/p&gt;
&lt;p&gt;I think the best way to explain this is to walk through the lifecycle of a recording one step at a time.&lt;/p&gt;
&lt;h2 id=&quot;1-opening-the-menubar-app&quot;&gt;1. Opening the menubar app&lt;a href=&quot;#1-opening-the-menubar-app&quot; aria-label=&quot;Link to “1. Opening the menubar app”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I open the menubar popover we start some lightweight preview sessions for the selected camera and audio devices and start capturing screenshots of the active display to provide a preview for that. We also begin polling for new devices to keep the dropdowns up-to-date and check the server is reachable and the local data directory is writable. All this is intentionally separate from the actual capture sessions and is torn down once the menubar popover is closed. Its only purpose is to confirm to the user that the chosen sources are set up properly and let them choose a few settings.&lt;/p&gt;
&lt;div class=&quot;grid-wrapper&quot; style=&quot;--columns: 2;--rows: 1;--gap: var(--space-s);&quot; data-astro-cid-lpgbqc46&gt;   &lt;img src=&quot;/_astro/2026-07-06-lc-menubar-app-1.H0mbfP6s_Z1nnkpz.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-menubar-app-1.H0mbfP6s_Z1YUPlj.webp 640w, /_astro/2026-07-06-lc-menubar-app-1.H0mbfP6s_Z1nnkpz.webp 744w&quot; alt=&quot;2026-07-06-lc-menubar-app-1.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 744px) 744px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;744&quot; height=&quot;1282&quot;&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-menubar-app-2.YeGst7Nx_29VvPt.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-menubar-app-2.YeGst7Nx_Z2gx6OW.webp 640w, /_astro/2026-07-06-lc-menubar-app-2.YeGst7Nx_29VvPt.webp 744w&quot; alt=&quot;2026-07-06-lc-menubar-app-2.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 744px) 744px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;744&quot; height=&quot;1618&quot;&gt; &lt;/div&gt;
&lt;h2 id=&quot;2-pressing-record&quot;&gt;2. Pressing record&lt;a href=&quot;#2-pressing-record&quot; aria-label=&quot;Link to “2. Pressing record”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The recording toolbar shows a short countdown while in the background:&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;We immediately start capture sessions for the selected devices so they have time to warm up, check the audio source is actually providing samples and spin up the various writer sessions so they’re warm. We also double-check a few other bits about the video and audio streams we’re getting.&lt;/li&gt;
&lt;li&gt;We hit the server to generate a UUID and unique three-word slug for the video. This creates a new recording in the database and seeds a data directory on the server, returning the UUID and slug to the mac app.&lt;/li&gt;
&lt;li&gt;We use the UUID to create a local data directory and seed it with an &lt;code&gt;init.mp4&lt;/code&gt; to act as an entrypoint for the local &lt;code&gt;m4s&lt;/code&gt; segments.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the countdown finishes we initiate an internal &lt;em&gt;metronome&lt;/em&gt; to help keep things in sync and begin the &lt;strong&gt;writer&lt;/strong&gt; processes.&lt;/p&gt;
&lt;h2 id=&quot;3-during-recording&quot;&gt;3. During Recording&lt;a href=&quot;#3-during-recording&quot; aria-label=&quot;Link to “3. During Recording”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We use two capture sessions: one for the screen and another for the camera+audio together (or just the audio if there’s no camera feed). These feed an orchestrator which maintains a metronome &amp;amp; recording clock, handles user pausing/unpausing by pausing the clock, and deals with things like frame caching.&lt;/p&gt;
&lt;p&gt;There’s a ton of fiddly stuff in here to gracefully handle video sources which deliver frames unpredictably. Every video frame and audio sample is timestamped using the &lt;em&gt;hardware capture time&lt;/em&gt; it arrived with rather than the wall clock, so variable latency between capture and reaching my code is irrelevant for timing sync. When camera &amp;amp; mic are both selected they share a single capture session so are inherently aligned to sub-millisecond accuracy. While the metronome ticks along at the target frame rate, it’s just a &lt;em&gt;budget&lt;/em&gt;: on each tick we emit whatever the source has actually delivered. So a camera that can only manage 24fps produces 24fps output rather than the horrible corrupted mess I ended up with from my initial metronome implementation.&lt;/p&gt;
&lt;aside aria-label=&quot;Aside: Some cameras are lying bastards&quot; class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;h3 class=&quot;title&quot; data-astro-cid-lnlu5god&gt;Aside: Some cameras are lying bastards&lt;/h3&gt; &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;My Sony ZV-1 advertises itself as a 30fps camera but actually only delivers &lt;strong&gt;about&lt;/strong&gt; 24. When I told macOS to &lt;em&gt;hold&lt;/em&gt; 30fps, its capture layer tried to fabricate the missing frames and in doing so corrupted the timestamps on every frame, shifting them several seconds into the past and completely desyncing the audio. The fix was to stop telling the camera how fast to run and just take whatever it gives us. Cameras that can actually sustain their advertised rate (like the built-in FaceTime one) were never affected, which made it a &lt;em&gt;delightfully&lt;/em&gt; confusing bug to track down.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;Anyway….&lt;/p&gt;
&lt;p&gt;During recording we continuously write three media files to disk locally, directly from the capture sessions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;screen.mov&lt;/code&gt; - The “raw” output of the ProRes screen capture session.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;camera.mp4&lt;/code&gt; - The “raw” output of the H.264 camera capture session, ignoring any user adjustments to white balance &amp;amp; brightness, including an audio track.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;audio.m4a&lt;/code&gt; - The “raw” audio from the mic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are never sent to the server, but having them locally means I can always pull them into Final Cut Pro if I decide to do some proper editing later, and have a reliable backup if composition fails for some reason. We also keep a timeline of events in memory and continuously write to a few log and diagnostic files.&lt;/p&gt;
&lt;p&gt;The video we send to the server is &lt;em&gt;composited&lt;/em&gt; from the capture streams using the upload resolution and framerate selected by the user before recording. Camera adjustments for white balance &amp;amp; brightness are applied to the camera feed and both it and the screen feed are downscaled and/or cropped to an appropriate size &amp;amp; aspect ratio, taking into account the chosen stream quality.&lt;/p&gt;
&lt;aside aria-label=&quot;User Actions While Recording&quot; class=&quot;callout  large-heading&quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;👉&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;h3 class=&quot;title&quot; data-astro-cid-lnlu5god&gt;User Actions While Recording&lt;/h3&gt; &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;The following user actions affect the composited recording.&lt;/p&gt;&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;Pressing &lt;strong&gt;pause&lt;/strong&gt; in the toolbar temporarily pauses all writers until &lt;strong&gt;unpause&lt;/strong&gt; is pressed, along with the metronome.&lt;/li&gt;
&lt;li&gt;Pressing &lt;strong&gt;chapter marker&lt;/strong&gt; in the toolbar adds an anonymous marker to the recording timeline which can later be edited in the admin app to create viewer-facing chapter markers. If pressed while paused it’s added at the next &lt;em&gt;unpause&lt;/em&gt; timestamp.&lt;/li&gt;
&lt;li&gt;Switching &lt;strong&gt;mode&lt;/strong&gt; in the toolbar causes the composited recording to swap to the relevant feed (screen or camera). In screen &amp;amp; camera mode the camera feed is rendered as a small circular overlay in a corner of the screen.&lt;/li&gt;
&lt;li&gt;Moving the camera preview from one quadrant to another (eg. bottom-right to top-left) moves it to that corner in the composited output (when in screen &amp;amp; camera mode).&lt;/li&gt;
&lt;/ul&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;As the composited video is generated it’s encoded at an appropriate bitrate and written to disk locally as a series of ~4 second &lt;code&gt;.m4s&lt;/code&gt; segments before being “streamed” to the server via a queue of simple &lt;code&gt;PUT&lt;/code&gt; requests. Failed puts are retried indefinitely with an exponential backoff (and we stop bothering entirely while the network is obviously down), but they’re all sent via a queue so even a ~10 min connection loss just means the queued segments are sent up on reconnection. These &lt;code&gt;PUT&lt;/code&gt;s are idempotent on the server.&lt;/p&gt;
&lt;p&gt;There are a couple of other resilience details worth a mention. If the Metal compositor hangs or returns a bad frame – which can happen under sustained GPU pressure – we detect it, rebuild the context and carry on. And when the screen content isn’t changing for a while (eg. I’m showing a static slide while talking to camera) we emit synthetic &lt;em&gt;keep-alive&lt;/em&gt; frames so the HLS segments never go empty and break the playlist.&lt;/p&gt;
&lt;p&gt;We also keep an eye on the capture sources themselves: if the screen, camera or mic stalls or drops out mid-recording a warning pill pops up in the toolbar so I know something’s wrong. And if the camera in a shared camera+mic capture session dies we fail over to a separate, always-running mic capture session so the audio at least keeps recording.&lt;/p&gt;
&lt;p&gt;The recording timeline is periodically written to a temporary file on disk to facilitate recovery if anything goes wrong.&lt;/p&gt;
&lt;h2 id=&quot;4-pressing-stop&quot;&gt;4. Pressing Stop&lt;a href=&quot;#4-pressing-stop&quot; aria-label=&quot;Link to “4. Pressing Stop”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When we hit stop, the capture sessions and writer processes finish and if the &lt;code&gt;UploadActor&lt;/code&gt; queue isn’t empty it’s given up to ten seconds to finish uploading before remaining segments are marked &lt;em&gt;failed&lt;/em&gt; locally&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;true&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The recording timeline is used to write a local &lt;code&gt;recording.json&lt;/code&gt; to disk which includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic info like UUID, start/end timestamps, duration etc.&lt;/li&gt;
&lt;li&gt;Details of the input sources and raw writers used, including hardware details and the like.&lt;/li&gt;
&lt;li&gt;Details of the writer used for the composited video including the encoder and streaming settings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It includes a timeline of events during recording, each of which has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kind&lt;/code&gt; - type of event (eg &lt;code&gt;segment.uploaded&lt;/code&gt; or &lt;code&gt;modeChange.CamOnly&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;t&lt;/code&gt; - time of event as seconds since metronome &lt;code&gt;T0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wallClock&lt;/code&gt; - datetime of event per OS clock as UTC.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt; - Other useful stuff as an object. This depends on the kind of event.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the event timeline includes segment-related events, &lt;code&gt;recording.json&lt;/code&gt; also contains a separate list of the generated segments, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bytes&lt;/code&gt; - size of the segment&lt;/li&gt;
&lt;li&gt;&lt;code&gt;durationSeconds&lt;/code&gt; - segment duration&lt;/li&gt;
&lt;li&gt;&lt;code&gt;emittedAt&lt;/code&gt; - Metronome time the segment was emitted.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filename&lt;/code&gt; - Filename on local filesystem (eg &lt;code&gt;seg_000.m4s&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index&lt;/code&gt; - Order of segments chronologically as emitted by the compositor. We can use this to deal with any ordering issues later.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uploaded&lt;/code&gt; - True if any &lt;code&gt;PUT&lt;/code&gt; for the segment returned OK, false if not.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This data is written to a local &lt;code&gt;recording.json&lt;/code&gt; and then POSTed to &lt;code&gt;/:uuid/complete&lt;/code&gt; on the server to tell it we’re done recording.&lt;/p&gt;
&lt;details class=&quot;accordion&quot; data-astro-cid-zq5noieg&gt; &lt;summary data-astro-cid-zq5noieg&gt;&lt;span class=&quot;summary-text&quot; data-astro-cid-zq5noieg&gt;Example recording.json&lt;/span&gt;&lt;/summary&gt; &lt;div class=&quot;accordion-content content-trim&quot; data-astro-cid-zq5noieg&gt; &lt;p&gt;Here’s an example (with a bunch of the repetitive data removed).&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;/&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;recording.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;osVersion&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;26.5.1&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;0.1.0&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;encoder&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;audioBitrate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;128000&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;audioCodec&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;aac-lc&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;outputHeight&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1080&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;outputWidth&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1920&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;segmentIntervalSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;targetFPS&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;30&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;videoBitrate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;8000000&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;videoCodec&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;h264&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;videoProfile&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;High&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;events&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;screen.mov&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;raw.writer.started&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:47:20.381Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;audio.m4a&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;raw.writer.started&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:47:20.381Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;recording.committed&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:47:23.223Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;camera.mp4&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;raw.writer.started&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.004191994667053223&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:47:23.227Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4229782&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;durationSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;seg_000.m4s&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;segment.emitted&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4.875511791&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:47:28.056Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;seg_000.m4s&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;segment.uploaded&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4.891072416&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:47:28.071Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;bottomRight&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;to&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;bottomLeft&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;pip.position.changed&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;36.89907425&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:48:00.079Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;screenAndCamera&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;to&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;cameraOnly&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;mode.switched&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;41.911716833&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:48:05.091Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;branch&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;pop&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;deltaMs&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1989.68825&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;monotonicity.rejected&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;44.876459916&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:48:08.056Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;staleDurationSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1.016079208&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;keepalive.emitted&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;56.630524583&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:48:19.810Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;cameraOnly&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;to&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;screenAndCamera&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;mode.switched&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;56.944255875&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:48:20.124Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;recording.stopped&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;159.799141458&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:50:02.977Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;-11800&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;domain&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;AVFoundationErrorDomain&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;The operation could not be completed&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;camera.mp4&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;underlyingCode&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;-16364&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;underlyingDomain&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;NSOSStatusErrorDomain&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;underlyingError&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;The operation couldn’t be completed. (OSStatus error -16364.)&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;raw.writer.failed&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;159.799141458&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:50:03.238Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;diagnostics: iters=7450 emit=3891 skipStale=2703 keepAlive=11 mono=53 neg=7 noSrc=785 peek=0 pop=623 camFrames=3350 (~0.00fps) scrFrames=4721 evictions=2399 compFails=0 dropRate=0.8%&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;160.061301666&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;wallClock&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:50:03.239Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;hardware&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;arch&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;arm64&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;model&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Mac14,9&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;inputs&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;camera&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;advertisedFormats&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;720&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;maxFrameRate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;30.00003000003&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;minFrameRate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;30.00003000003&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;pixelFormat&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;420v&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1280&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;ZV-1&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;selectedFormat&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;activeMaxFrameDurationSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.03333330000003333&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;activeMinFrameDurationSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.03333330000003333&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;didLockRate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;720&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;pixelFormat&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;420v&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1280&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;uniqueID&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;0x2111100054c0de4&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1080&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1920&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;microphone&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;halInputLatencyMs&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;13.75&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Yeti Stereo Microphone&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;uniqueID&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;AppleUSBAudioEngine:Blue Microphones:Yeti Stereo Microphone:ReV8:2&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;preset&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bitrate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;8000000&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1080&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;1080p&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;1080p&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1920&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rawStreams&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;audio&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;audioCodec&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;aac-lc&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bitrate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;192000&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3134595&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;channels&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;audio.m4a&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;sampleRate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;48000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;camera&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bitrate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;12000000&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;11308795&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;failed&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;camera.mp4&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;720&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;videoCodec&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;h264&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1280&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;screen&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bitrate&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;146343794&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2923201587&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;screen.mov&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2160&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;videoCodec&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;prores422proxy&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3840&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;runtime&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;cameraIntervalP50Ms&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;35&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;cameraIntervalP95Ms&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;67&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;effectiveCameraFps&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;20.96381726106132&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;effectiveScreenFps&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;29.5433376983494&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;metronome&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;emitOK&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3891&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;iterations&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;7450&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;keepAliveEmits&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;11&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;monoRejects&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;53&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;skipsStale&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2703&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;outputFps&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;24.41815371721232&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;screenIntervalP50Ms&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;35&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;screenIntervalP95Ms&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;67&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;schemaVersion&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;segments&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4229782&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;durationSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;emittedAt&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4.875511791&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;seg_000.m4s&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;uploaded&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4136495&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;durationSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;emittedAt&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;9.318954291&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;seg_001.m4s&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;uploaded&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1427814&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;durationSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3.0933333333333333&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;emittedAt&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;159.956935541&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;seg_039.m4s&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;40&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;uploaded&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;durationSeconds&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;159.799141458&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;endedAt&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:50:02.977Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;4a1f8229-6a19-4e55-8fd4-af979b706ecd&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;initialMode&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;cameraOnly&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;initialPipPosition&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;bottomRight&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;slug&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;fluffy-moments-flash&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;startedAt&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-06-06T16:47:23.223Z&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{  &amp;quot;app&amp;quot; : {    &amp;quot;build&amp;quot; : &amp;quot;1&amp;quot;,    &amp;quot;osVersion&amp;quot; : &amp;quot;26.5.1&amp;quot;,    &amp;quot;version&amp;quot; : &amp;quot;0.1.0&amp;quot;  },  &amp;quot;encoder&amp;quot; : {    &amp;quot;audioBitrate&amp;quot; : 128000,    &amp;quot;audioCodec&amp;quot; : &amp;quot;aac-lc&amp;quot;,    &amp;quot;outputHeight&amp;quot; : 1080,    &amp;quot;outputWidth&amp;quot; : 1920,    &amp;quot;segmentIntervalSeconds&amp;quot; : 4,    &amp;quot;targetFPS&amp;quot; : 30,    &amp;quot;videoBitrate&amp;quot; : 8000000,    &amp;quot;videoCodec&amp;quot; : &amp;quot;h264&amp;quot;,    &amp;quot;videoProfile&amp;quot; : &amp;quot;High&amp;quot;  },  &amp;quot;events&amp;quot; : [    {      &amp;quot;data&amp;quot; : {        &amp;quot;file&amp;quot; : &amp;quot;screen.mov&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;raw.writer.started&amp;quot;,      &amp;quot;t&amp;quot; : 0,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:47:20.381Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;file&amp;quot; : &amp;quot;audio.m4a&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;raw.writer.started&amp;quot;,      &amp;quot;t&amp;quot; : 0,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:47:20.381Z&amp;quot;    },    {      &amp;quot;kind&amp;quot; : &amp;quot;recording.committed&amp;quot;,      &amp;quot;t&amp;quot; : 0,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:47:23.223Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;file&amp;quot; : &amp;quot;camera.mp4&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;raw.writer.started&amp;quot;,      &amp;quot;t&amp;quot; : 0.004191994667053223,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:47:23.227Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;bytes&amp;quot; : 4229782,        &amp;quot;durationSeconds&amp;quot; : 4,        &amp;quot;filename&amp;quot; : &amp;quot;seg_000.m4s&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;segment.emitted&amp;quot;,      &amp;quot;t&amp;quot; : 4.875511791,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:47:28.056Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;filename&amp;quot;: &amp;quot;seg_000.m4s&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;segment.uploaded&amp;quot;,      &amp;quot;t&amp;quot; : 4.891072416,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:47:28.071Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;from&amp;quot; : &amp;quot;bottomRight&amp;quot;,        &amp;quot;to&amp;quot; : &amp;quot;bottomLeft&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;pip.position.changed&amp;quot;,      &amp;quot;t&amp;quot; : 36.89907425,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:48:00.079Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;from&amp;quot; : &amp;quot;screenAndCamera&amp;quot;,        &amp;quot;to&amp;quot; : &amp;quot;cameraOnly&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;mode.switched&amp;quot;,      &amp;quot;t&amp;quot; : 41.911716833,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:48:05.091Z&amp;quot;    },    // ...    {      &amp;quot;data&amp;quot; : {        &amp;quot;branch&amp;quot; : &amp;quot;pop&amp;quot;,        &amp;quot;deltaMs&amp;quot; : 1989.68825      },      &amp;quot;kind&amp;quot; : &amp;quot;monotonicity.rejected&amp;quot;,      &amp;quot;t&amp;quot; : 44.876459916,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:48:08.056Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;staleDurationSeconds&amp;quot; : 1.016079208      },      &amp;quot;kind&amp;quot; : &amp;quot;keepalive.emitted&amp;quot;,      &amp;quot;t&amp;quot; : 56.630524583,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:48:19.810Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;from&amp;quot; : &amp;quot;cameraOnly&amp;quot;,        &amp;quot;to&amp;quot; : &amp;quot;screenAndCamera&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;mode.switched&amp;quot;,      &amp;quot;t&amp;quot; : 56.944255875,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:48:20.124Z&amp;quot;    },    {      &amp;quot;kind&amp;quot; : &amp;quot;recording.stopped&amp;quot;,      &amp;quot;t&amp;quot; : 159.799141458,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:50:02.977Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;code&amp;quot; : -11800,        &amp;quot;domain&amp;quot; : &amp;quot;AVFoundationErrorDomain&amp;quot;,        &amp;quot;error&amp;quot; : &amp;quot;The operation could not be completed&amp;quot;,        &amp;quot;file&amp;quot; : &amp;quot;camera.mp4&amp;quot;,        &amp;quot;underlyingCode&amp;quot; : -16364,        &amp;quot;underlyingDomain&amp;quot; : &amp;quot;NSOSStatusErrorDomain&amp;quot;,        &amp;quot;underlyingError&amp;quot; : &amp;quot;The operation couldn’t be completed. (OSStatus error -16364.)&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;raw.writer.failed&amp;quot;,      &amp;quot;t&amp;quot; : 159.799141458,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:50:03.238Z&amp;quot;    },    {      &amp;quot;data&amp;quot; : {        &amp;quot;message&amp;quot; : &amp;quot;diagnostics: iters=7450 emit=3891 skipStale=2703 keepAlive=11 mono=53 neg=7 noSrc=785 peek=0 pop=623 camFrames=3350 (~0.00fps) scrFrames=4721 evictions=2399 compFails=0 dropRate=0.8%&amp;quot;      },      &amp;quot;kind&amp;quot; : &amp;quot;error&amp;quot;,      &amp;quot;t&amp;quot; : 160.061301666,      &amp;quot;wallClock&amp;quot; : &amp;quot;2026-06-06T16:50:03.239Z&amp;quot;    }  ],  &amp;quot;hardware&amp;quot; : {    &amp;quot;arch&amp;quot; : &amp;quot;arm64&amp;quot;,    &amp;quot;model&amp;quot; : &amp;quot;Mac14,9&amp;quot;  },  &amp;quot;inputs&amp;quot; : {    &amp;quot;camera&amp;quot; : {      &amp;quot;advertisedFormats&amp;quot; : [        {          &amp;quot;height&amp;quot; : 720,          &amp;quot;maxFrameRate&amp;quot; : 30.00003000003,          &amp;quot;minFrameRate&amp;quot; : 30.00003000003,          &amp;quot;pixelFormat&amp;quot; : &amp;quot;420v&amp;quot;,          &amp;quot;width&amp;quot; : 1280        }      ],      &amp;quot;name&amp;quot; : &amp;quot;ZV-1&amp;quot;,      &amp;quot;selectedFormat&amp;quot; : {        &amp;quot;activeMaxFrameDurationSeconds&amp;quot; : 0.03333330000003333,        &amp;quot;activeMinFrameDurationSeconds&amp;quot; : 0.03333330000003333,        &amp;quot;didLockRate&amp;quot; : true,        &amp;quot;height&amp;quot; : 720,        &amp;quot;pixelFormat&amp;quot; : &amp;quot;420v&amp;quot;,        &amp;quot;width&amp;quot; : 1280      },      &amp;quot;uniqueID&amp;quot; : &amp;quot;0x2111100054c0de4&amp;quot;    },    &amp;quot;display&amp;quot; : {      &amp;quot;height&amp;quot; : 1080,      &amp;quot;id&amp;quot; : 3,      &amp;quot;width&amp;quot; : 1920    },    &amp;quot;microphone&amp;quot; : {      &amp;quot;halInputLatencyMs&amp;quot; : 13.75,      &amp;quot;name&amp;quot; : &amp;quot;Yeti Stereo Microphone&amp;quot;,      &amp;quot;uniqueID&amp;quot; : &amp;quot;AppleUSBAudioEngine:Blue Microphones:Yeti Stereo Microphone:ReV8:2&amp;quot;    }  },  &amp;quot;preset&amp;quot; : {    &amp;quot;bitrate&amp;quot; : 8000000,    &amp;quot;height&amp;quot; : 1080,    &amp;quot;id&amp;quot; : &amp;quot;1080p&amp;quot;,    &amp;quot;label&amp;quot; : &amp;quot;1080p&amp;quot;,    &amp;quot;width&amp;quot; : 1920  },  &amp;quot;rawStreams&amp;quot; : {    &amp;quot;audio&amp;quot; : {      &amp;quot;audioCodec&amp;quot; : &amp;quot;aac-lc&amp;quot;,      &amp;quot;bitrate&amp;quot; : 192000,      &amp;quot;bytes&amp;quot; : 3134595,      &amp;quot;channels&amp;quot; : 2,      &amp;quot;filename&amp;quot; : &amp;quot;audio.m4a&amp;quot;,      &amp;quot;sampleRate&amp;quot; : 48000    },    &amp;quot;camera&amp;quot; : {      &amp;quot;bitrate&amp;quot; : 12000000,      &amp;quot;bytes&amp;quot; : 11308795,      &amp;quot;failed&amp;quot; : true,      &amp;quot;filename&amp;quot; : &amp;quot;camera.mp4&amp;quot;,      &amp;quot;height&amp;quot; : 720,      &amp;quot;videoCodec&amp;quot; : &amp;quot;h264&amp;quot;,      &amp;quot;width&amp;quot; : 1280    },    &amp;quot;screen&amp;quot; : {      &amp;quot;bitrate&amp;quot; : 146343794,      &amp;quot;bytes&amp;quot; : 2923201587,      &amp;quot;filename&amp;quot; : &amp;quot;screen.mov&amp;quot;,      &amp;quot;height&amp;quot; : 2160,      &amp;quot;videoCodec&amp;quot; : &amp;quot;prores422proxy&amp;quot;,      &amp;quot;width&amp;quot; : 3840    }  },  &amp;quot;runtime&amp;quot; : {    &amp;quot;cameraIntervalP50Ms&amp;quot; : 35,    &amp;quot;cameraIntervalP95Ms&amp;quot; : 67,    &amp;quot;effectiveCameraFps&amp;quot; : 20.96381726106132,    &amp;quot;effectiveScreenFps&amp;quot; : 29.5433376983494,    &amp;quot;metronome&amp;quot; : {      &amp;quot;emitOK&amp;quot; : 3891,      &amp;quot;iterations&amp;quot; : 7450,      &amp;quot;keepAliveEmits&amp;quot; : 11,      &amp;quot;monoRejects&amp;quot; : 53,      &amp;quot;skipsStale&amp;quot; : 2703    },    &amp;quot;outputFps&amp;quot; : 24.41815371721232,    &amp;quot;screenIntervalP50Ms&amp;quot; : 35,    &amp;quot;screenIntervalP95Ms&amp;quot; : 67  },  &amp;quot;schemaVersion&amp;quot; : 3,  &amp;quot;segments&amp;quot; : [    {      &amp;quot;bytes&amp;quot; : 4229782,      &amp;quot;durationSeconds&amp;quot; : 4,      &amp;quot;emittedAt&amp;quot; : 4.875511791,      &amp;quot;filename&amp;quot; : &amp;quot;seg_000.m4s&amp;quot;,      &amp;quot;index&amp;quot; : 1,      &amp;quot;uploaded&amp;quot; : true    },    {      &amp;quot;bytes&amp;quot; : 4136495,      &amp;quot;durationSeconds&amp;quot; : 4,      &amp;quot;emittedAt&amp;quot; : 9.318954291,      &amp;quot;filename&amp;quot; : &amp;quot;seg_001.m4s&amp;quot;,      &amp;quot;index&amp;quot; : 2,      &amp;quot;uploaded&amp;quot; : true    },    // ...    {      &amp;quot;bytes&amp;quot; : 1427814,      &amp;quot;durationSeconds&amp;quot; : 3.0933333333333333,      &amp;quot;emittedAt&amp;quot; : 159.956935541,      &amp;quot;filename&amp;quot; : &amp;quot;seg_039.m4s&amp;quot;,      &amp;quot;index&amp;quot; : 40,      &amp;quot;uploaded&amp;quot; : true    }  ],  &amp;quot;session&amp;quot; : {    &amp;quot;durationSeconds&amp;quot; : 159.799141458,    &amp;quot;endedAt&amp;quot; : &amp;quot;2026-06-06T16:50:02.977Z&amp;quot;,    &amp;quot;id&amp;quot; : &amp;quot;4a1f8229-6a19-4e55-8fd4-af979b706ecd&amp;quot;,    &amp;quot;initialMode&amp;quot; : &amp;quot;cameraOnly&amp;quot;,    &amp;quot;initialPipPosition&amp;quot; : &amp;quot;bottomRight&amp;quot;,    &amp;quot;slug&amp;quot; : &amp;quot;fluffy-moments-flash&amp;quot;,    &amp;quot;startedAt&amp;quot; : &amp;quot;2026-06-06T16:47:23.223Z&amp;quot;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt; &lt;/div&gt; &lt;/details&gt;
&lt;p&gt;We also write a local &lt;code&gt;diagnostics.json&lt;/code&gt; full of per-tick timing data and — because a lot of camera and encoder gremlins originate deep inside weird bits of macOS — we grab a slice of the macOS system log for the recording and temporarily write it to disk to help with debugging.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;Because the HLS segments are written to the server as they arrive, and we generated the slug at the beginning, it’s possible to watch the video live at &lt;code&gt;https://v.danny.is/the-generated-slug&lt;/code&gt; &lt;em&gt;while it’s being recorded&lt;/em&gt;. This isn’t really an intentional feature (it’s not a livestreaming platform). We do this so that &lt;strong&gt;when we hit stop we already have a live video on a URL&lt;/strong&gt; and can share it.&lt;/p&gt;&lt;p&gt;At the end of recording the mac app copies the public URL to the clipboard so it can instantly be shared, and it’s also possible to quickly edit a recently-finished video’s title, slug and visibility in the macos menubar app.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;5-when-recordings-finished&quot;&gt;5. When recording’s finished&lt;a href=&quot;#5-when-recordings-finished&quot; aria-label=&quot;Link to “5. When recording’s finished”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the server receives the &lt;code&gt;POST&lt;/code&gt; to &lt;code&gt;/:uuid/complete&lt;/code&gt; it already has all the segments on disk and is serving them as an HLS playlist on the public URL. So the &lt;em&gt;complete&lt;/em&gt; signal just causes &lt;code&gt;recording.json&lt;/code&gt; to be stored server-side, with some of its key data added to the database record. It then kicks off a series of step-by-step post-processing jobs.&lt;/p&gt;
&lt;h3 id=&quot;healing&quot;&gt;Healing&lt;a href=&quot;#healing&quot; aria-label=&quot;Link to “Healing”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Healing is the recovery mechanism for HLS segments that didn’t make it during live recording. The server knows what segments it has on disk and now it has the client’s segment log from &lt;code&gt;recording.json&lt;/code&gt; it can compare the two and ask the client to resend any missing or broken segments. The mac app also re-checks on launch: if a recording from the last few days still has segments that never made it up it quietly finishes healing them in the background.&lt;/p&gt;
&lt;h3 id=&quot;restitching&quot;&gt;Restitching&lt;a href=&quot;#restitching&quot; aria-label=&quot;Link to “Restitching”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The first and simplest post-processing task is stitching the M4S segments into a single &lt;code&gt;source.mp4&lt;/code&gt; using ffmpeg. As soon as we have a valid &lt;code&gt;source.mp4&lt;/code&gt; available, we serve that to viewers instead of the HLS playlist. We also use &lt;code&gt;ffprobe&lt;/code&gt; to extract some metadata from the new MP4 and update the database record.&lt;/p&gt;
&lt;h3 id=&quot;audio-enhancement&quot;&gt;Audio Enhancement&lt;a href=&quot;#audio-enhancement&quot; aria-label=&quot;Link to “Audio Enhancement”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next, we clean up the audio by running it through a chain of ffmpeg filters, writing the result back to the audio track of &lt;code&gt;source.mp4&lt;/code&gt;. I’m still working on getting this right but at the moment the chain involves:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A high pass filter to remove really low frequency noise.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;arnndn&lt;/code&gt; to denoise speech.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;afftdn&lt;/code&gt; to clean up stationary background noise.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;agate&lt;/code&gt; noise gate to silence parts where I’m not speaking.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynaudnorm&lt;/code&gt; for volume levelling within the regions where I &lt;em&gt;am&lt;/em&gt; speaking.&lt;/li&gt;
&lt;li&gt;A final &lt;code&gt;loudnorm&lt;/code&gt; to produce a normalised volume of about -14 LUFS.&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;Despite a lot of tuning this is probably a bit aggressive considering most of my recordings happen in a quiet room with a decent mic. I’m currently iterating on this in a separate little project with a load of representative audio samples.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;We also generate a &lt;code&gt;peaks.json&lt;/code&gt; from the audio levels, used by the video editor to render a nice waveform and suggest quiet areas to trim or cut.&lt;/p&gt;
&lt;h3 id=&quot;thumbnail-candidates&quot;&gt;Thumbnail candidates&lt;a href=&quot;#thumbnail-candidates&quot; aria-label=&quot;Link to “Thumbnail candidates”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Multiple frames are extracted and scored by luminance variance, with the “best” one written to disk as &lt;code&gt;thumbnail.jpg&lt;/code&gt; and used as the video’s poster image. The other candidates are also saved to disk on the server so I can choose a new one in the admin app if I want.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-24-generated-thumbnails-in-admin-interface.Bg1V9aBK_Z1MlOox.webp&quot; srcset=&quot;/_astro/2026-07-24-generated-thumbnails-in-admin-interface.Bg1V9aBK_29NH8d.webp 640w, /_astro/2026-07-24-generated-thumbnails-in-admin-interface.Bg1V9aBK_1jvMef.webp 750w, /_astro/2026-07-24-generated-thumbnails-in-admin-interface.Bg1V9aBK_ZEvqkx.webp 828w, /_astro/2026-07-24-generated-thumbnails-in-admin-interface.Bg1V9aBK_Z1MlOox.webp 840w&quot; alt=&quot;The admin app’s thumbnail picker: a grid of seven auto-extracted candidate frames from the recording, each labelled “auto”, with the first outlined in red and marked “Active” as the chosen poster image.&quot; title caption=&quot;How the generated thumbnail candidates appear in the admin app&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 840px) 840px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;840&quot; height=&quot;335&quot;&gt;
&lt;h3 id=&quot;generating-video-variants&quot;&gt;Generating video variants&lt;a href=&quot;#generating-video-variants&quot; aria-label=&quot;Link to “Generating video variants”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We use &lt;code&gt;source.mp4&lt;/code&gt; to generate downsized variants so we can serve them to viewers who want lower resolution versions. If our source is 1080p we just generate a &lt;code&gt;720p.mp4&lt;/code&gt; version. If it’s 1440p we’ll create both &lt;code&gt;720p.mp4&lt;/code&gt; and &lt;code&gt;1080p.mp4&lt;/code&gt; derivatives. And so on.&lt;/p&gt;
&lt;h3 id=&quot;generating-storyboard-images&quot;&gt;Generating storyboard images&lt;a href=&quot;#generating-storyboard-images&quot; aria-label=&quot;Link to “Generating storyboard images”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For videos longer than 60 seconds we generate a &lt;code&gt;storyboard.jpg&lt;/code&gt; and &lt;code&gt;storyboard.vtt&lt;/code&gt; so the player can use them to show previews when scrubbing the timeline. Longer videos are sampled less frequently than shorter ones so they don’t get too enormous.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-24-storyboard.Bvd4uK8M_Z2htBvr.webp&quot; srcset=&quot;/_astro/2026-07-24-storyboard.Bvd4uK8M_fbAnn.webp 640w, /_astro/2026-07-24-storyboard.Bvd4uK8M_Z1ov3Y5.webp 750w, /_astro/2026-07-24-storyboard.Bvd4uK8M_tnWwL.webp 828w, /_astro/2026-07-24-storyboard.Bvd4uK8M_Z1er1ct.webp 1080w, /_astro/2026-07-24-storyboard.Bvd4uK8M_Z1p4pWf.webp 1280w, /_astro/2026-07-24-storyboard.Bvd4uK8M_1Ufld7.webp 1668w, /_astro/2026-07-24-storyboard.Bvd4uK8M_ZiglDM.webp 2048w, /_astro/2026-07-24-storyboard.Bvd4uK8M_Z2htBvr.webp 2400w&quot; alt=&quot;A grid of small thumbnail frames sampled across a recording, used as scrubbing previews — showing the intro title card, screen shares and talking-to-camera shots.&quot; title caption=&quot;Example of a storyboard image&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2400px) 2400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2400&quot; height=&quot;544&quot;&gt;
&lt;p&gt;We also generate a second, more granular storyboard image and VTT for use in the admin app’s video editor.&lt;/p&gt;
&lt;h3 id=&quot;suggested-edits&quot;&gt;Suggested edits&lt;a href=&quot;#suggested-edits&quot; aria-label=&quot;Link to “Suggested edits”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We look at the &lt;code&gt;peaks.json&lt;/code&gt; and if there are any obviously quiet areas we write a &lt;code&gt;suggested-edits.json&lt;/code&gt; which is picked up by the video editor and used to suggest trims and cuts when it’s first opened. This is deleted when any actual edits are made, and automatically cleaned up after a few days if not.&lt;/p&gt;
&lt;h2 id=&quot;transcription--subtitles&quot;&gt;Transcription &amp;amp; Subtitles&lt;a href=&quot;#transcription--subtitles&quot; aria-label=&quot;Link to “Transcription &amp;amp; Subtitles”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While the post-processing above is done server-side, doing any &lt;strong&gt;AI stuff&lt;/strong&gt; on the server would mean paying for tokens on an external service or beefing up the server enough to run models on it. Since I have a pretty powerful laptop it makes more sense to do this locally instead, so the macOS app includes WhisperKit for transcription and makes use of Apple’s built-in foundation models for title suggestions.&lt;/p&gt;
&lt;p&gt;When a video completes, the macOS app kicks off a task to transcribe the local &lt;code&gt;audio.m4a&lt;/code&gt; using WhisperKit. It uses this and the timing data from &lt;code&gt;recording.json&lt;/code&gt; to produce two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;words.json&lt;/code&gt; containing each word and its start &amp;amp; end timestamp.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;captions.srt&lt;/code&gt; containing timestamped subtitles.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both are stored locally and also sent to the server along with the plain transcription text. The plain transcription is written to the database and &lt;code&gt;captions.srt&lt;/code&gt; is used to provide subtitles in the video players. We only use &lt;code&gt;words.json&lt;/code&gt; in the video editor, but it’s good to keep around as a definitive version of the transcript.&lt;/p&gt;
&lt;h3 id=&quot;suggested-title-and-description&quot;&gt;Suggested title and description&lt;a href=&quot;#suggested-title-and-description&quot; aria-label=&quot;Link to “Suggested title and description”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If transcription completes successfully, the first ~500 words are fed to Apple’s local Foundation Model along with a system prompt asking for a suggested title. The prompt includes some data from &lt;code&gt;recording.json&lt;/code&gt; and the response is checked against some simple &lt;em&gt;is-this-insane?&lt;/em&gt; rules before being sent to the server where it’s used to update the video’s title in the database (unless I’ve already touched it).&lt;/p&gt;
&lt;p&gt;A similar process is used to suggest a description and, if chapter markers exist, suggest titles for each of them.&lt;/p&gt;
&lt;p&gt;This is far from perfect, but it’s better than nothing at all.&lt;/p&gt;
&lt;h2 id=&quot;so-where-do-we-end-up&quot;&gt;So where do we end up?&lt;a href=&quot;#so-where-do-we-end-up&quot; aria-label=&quot;Link to “So where do we end up?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On the server-side, the whole post-processing pipeline is managed by a kind of &lt;em&gt;step registry&lt;/em&gt; which ensures each step happens in order and allows me to manually retrigger the process from various points if I need to. When everything’s finished the admin panel will show a checklist like this.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-24-post-processing-pipeline.DHrnkl09_ZIMTwV.webp&quot; srcset=&quot;/_astro/2026-07-24-post-processing-pipeline.DHrnkl09_Z2d3M3i.webp 640w, /_astro/2026-07-24-post-processing-pipeline.DHrnkl09_ZIMTwV.webp 673w&quot; alt=&quot;The admin panel’s post-processing checklist, listing each step — source video, metadata, audio processed, thumbnail, 720p variant, storyboard, audio peaks, suggested edits, transcript, word timings, suggested title and description — with green ticks for completed steps and dashes for skipped ones.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 673px) 673px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;673&quot; height=&quot;580&quot;&gt;
&lt;p&gt;We’ll end up with the following in our database record…&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;td&gt;UUID, primary key.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a1b2c3d4-e5f6-7890-abcd-ef1234567890&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slug&lt;/td&gt;
&lt;td&gt;Current URL slug. Unique. Old slugs live in &lt;code&gt;slug_redirects&lt;/code&gt; for 301s.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;how-to-use-the-new-dashboard&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;recording&lt;/code&gt;, &lt;code&gt;healing&lt;/code&gt;, &lt;code&gt;processing&lt;/code&gt;, &lt;code&gt;ready&lt;/code&gt;, &lt;code&gt;reprocessing&lt;/code&gt;, &lt;code&gt;processing_failed&lt;/code&gt;, &lt;code&gt;incomplete&lt;/code&gt; or &lt;code&gt;deleting&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ready&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;visibility&lt;/td&gt;
&lt;td&gt;&lt;code&gt;public&lt;/code&gt;, &lt;code&gt;unlisted&lt;/code&gt; or &lt;code&gt;private&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;unlisted&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;title&lt;/td&gt;
&lt;td&gt;Display title. Null until set by the user or the AI suggestion step.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;How to Use the New Dashboard&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;description&lt;/td&gt;
&lt;td&gt;Public-facing description. Null until set.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;notes&lt;/td&gt;
&lt;td&gt;Private notes.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;duration_seconds&lt;/td&gt;
&lt;td&gt;Cached at completion so list views don’t need to sum segment durations.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;187.4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;width&lt;/td&gt;
&lt;td&gt;Pixel width of &lt;code&gt;source.mp4&lt;/code&gt;, from ffprobe.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2560&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;height&lt;/td&gt;
&lt;td&gt;Pixel height of &lt;code&gt;source.mp4&lt;/code&gt;, from ffprobe.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1440&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aspect_ratio&lt;/td&gt;
&lt;td&gt;Width / height, cached for layout work.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.778&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;file_bytes&lt;/td&gt;
&lt;td&gt;Size of &lt;code&gt;source.mp4&lt;/code&gt; in bytes.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;48291840&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;camera_name&lt;/td&gt;
&lt;td&gt;Camera device name captured from &lt;code&gt;recording.json&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FaceTime HD Camera&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;microphone_name&lt;/td&gt;
&lt;td&gt;Mic device name captured from &lt;code&gt;recording.json&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MacBook Pro Microphone&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;recording_health&lt;/td&gt;
&lt;td&gt;Summary of any recording issues (dropped frames, failed segments). Null if clean.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;source&lt;/td&gt;
&lt;td&gt;&lt;code&gt;recorded&lt;/code&gt; (from the macOS app) or &lt;code&gt;uploaded&lt;/code&gt; (via the admin web upload).&lt;/td&gt;
&lt;td&gt;&lt;code&gt;recorded&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;created_at&lt;/td&gt;
&lt;td&gt;Row creation timestamp (ISO-8601).&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2026-04-30T14:22:03.841Z&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;updated_at&lt;/td&gt;
&lt;td&gt;Last update timestamp (ISO-8601).&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2026-04-30T14:29:17.205Z&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;completed_at&lt;/td&gt;
&lt;td&gt;Set the first time the video reaches &lt;code&gt;ready&lt;/code&gt;; never overwritten afterwards.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2026-04-30T14:25:44.012Z&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trashed_at&lt;/td&gt;
&lt;td&gt;Set when the video is moved to the trash bin; null otherwise.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last_edited_at&lt;/td&gt;
&lt;td&gt;Set when edits are committed via the video editor; null if never edited.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;video_transcripts&lt;/code&gt; table will also have a row for the video with the plain text transcript and word count.&lt;/p&gt;
&lt;h4 id=&quot;on-disk&quot;&gt;On Disk&lt;a href=&quot;#on-disk&quot; aria-label=&quot;Link to “On Disk”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Our server’s storage volume will have a directory which looks something like this…&lt;/p&gt;
&lt;file-tree code=&quot;data/a1b2c3d4-e5f6-7890-abcd-ef1234567890/
├── init.mp4                          # HLS initialization segment
├── seg_000.m4s                       # ~4s media segments streamed during recording
├── seg_001.m4s
├── ...
├── seg_046.m4s
├── stream.m3u8                       # HLS playlist referencing init.mp4 + segments
├── recording.json                    # Timeline, events and segment log from client
├── chapters.json                     # Chapter markers if set
└── derivatives/
    ├── source.mp4                    # Stitched single-file MP4 with enhanced audio
    ├── 1080p.mp4                     # Downsampled variant (source is 1440p)
    ├── 720p.mp4                      # Downsampled variant
    ├── thumbnail.jpg                 # Auto-selected best frame
    ├── thumbnail-candidates/
    │   ├── auto-01.jpg               # Other candidate frames
    │   ├── auto-02.jpg
    │   └── auto-03.jpg
    ├── captions.srt                  # Subtitles uploaded by the macOS app
    ├── words.json                    # Per-word transcript timings
    ├── peaks.json                    # Audio waveform peaks
    ├── suggested-edits.json          # Suggested cuts and trims
    ├── storyboard.jpg                # Sprite sheet of preview frames
    ├── storyboard.vtt                # Maps time ranges to sprite regions
    ├── editor-storyboard.jpg         # Higher-density sprite sheet for editor
    └── editor-storyboard.vtt&quot; title=&quot;Server data folder&quot;&gt;&lt;/file-tree&gt;
&lt;p&gt;Our mac will have a directory in &lt;code&gt;~/Application Support/LoomClone/recordings/[UUID]&lt;/code&gt; which looks something like this…&lt;/p&gt;
&lt;file-tree code=&quot;recordings/a1b2c3d4-e5f6-7890-abcd-ef1234567890/
├── init.mp4                          # HLS initialization segment
├── seg_000.m4s                       # Composited ~4s segments
├── seg_001.m4s
├── ...
├── seg_046.m4s
├── screen.mov                        # Raw ProRes screen capture
├── camera.mp4                        # Raw H.264 camera + audio
├── audio.m4a                         # Raw AAC mic audio
├── recording.json                    # Timeline, events and segment log
├── diagnostics.json                  # Recording-time diagnostic snapshot
├── captions.srt
├── words.json
├── os-log.ndjson                     # Copy of relevant OS log data
└── .transcribed                      # Marker to say transcription complete&quot; title=&quot;Local data folder&quot;&gt;&lt;/file-tree&gt;
&lt;p&gt;And when someone visits the public URL for the video they’ll see something like this, complete with subtitles, thumbnail, scrubber previews and the option to play lower-resolution derivatives.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ZPtvEI.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_Z2vNpj5.webp 640w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ebXmY.webp 750w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ZSIC3F.webp 828w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ZcUpHv.webp 1080w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_D5J7s.webp 1280w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_Z2eePb7.webp 1668w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ZPtvEI.webp 1994w&quot; alt=&quot;The public video page for a recording, showing the title, duration and date, a video player with a talking-head thumbnail and playback controls including subtitles, and a description with author details below.&quot; title caption=&quot;Screenshot of public video page&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1994px) 1994px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1994&quot; height=&quot;1844&quot;&gt;
&lt;p&gt;There’s &lt;strong&gt;a lot&lt;/strong&gt; I haven’t covered here – I’ll hopefully get to that in some more focussed articles in the future. If you’re interested in the details of all this &lt;a href=&quot;https://github.com/dannysmith/loom-clone/blob/main/docs/developer/recording-pipeline.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the internal docs on GitHub might be of interest&lt;/a&gt;. Just bear in mind much of it was written by an LLM.&lt;/p&gt;
&lt;section data-footnotes=&quot;true&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a href=&quot;#footnote-label&quot; aria-label=&quot;Link to “Footnotes”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Because we keep these locally and know they failed, we can retry them later as part of the “healing” process. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>An actual homepage for this site (finally)</title><link>https://danny.is/writing/an-actual-homepage/</link><guid isPermaLink="true">https://danny.is/writing/an-actual-homepage/</guid><pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the last month or two I’ve been trying to finally get this site to a place where it feels &lt;em&gt;finished enough for the moment&lt;/em&gt;. For my personal website this basically equates to &lt;strong&gt;nothing annoys me about it and nothing on the backlog feels like it’s hanging over me&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I’ve really enjoyed picking away at this because it’s been a real mixture of random stuff I’m interested in right now and ideas which I recorded as GH issues &lt;strong&gt;years&lt;/strong&gt; ago.&lt;/p&gt;
&lt;p&gt;While I’ve written about some of this, I was having too much fun to stop and cover much, so I can only really point you at the &lt;a href=&quot;/styleguide&quot;&gt;styleguide&lt;/a&gt;, which does a decent job of demonstrating a lot of it. Or the &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/commits/main/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;commit history&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;The few notes &amp;amp; articles about recent work on this site:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/writing/2026-05-28-work-on-this-site&quot;&gt;Some Work on this Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/2026-06-04-this-site-on-atproto&quot;&gt;This site now has Atproto Records&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/writing/speeding-up-astro-builds&quot;&gt;Speeding up Astro builds and improving deployment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/nicer-cover-images&quot;&gt;Nicer Cover Images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/new-meta-pages&quot;&gt;New AI Statement, Privacy Policy and Colophon Pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/less-painful-editing-in-astro&quot;&gt;A less painful editing experience in Astro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/writing/making-this-astro-site-agent-friendly&quot;&gt;Making this Astro site more Agent-Friendly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/site-styleguide-now&quot;&gt;This site has a styleguide now&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/adding-projects-site-plus-few-other-bits&quot;&gt;Adding projects to this site, plus a few other bits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/upgrading-astro-7-switching-satteri&quot;&gt;Upgrading to Astro 7 and switching to Sätteri&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/moving-uses-pages-site&quot;&gt;Moving my “Uses” pages to this site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/theme-aware-mermaid-diagrams-build-time&quot;&gt;Theme-aware Mermaid Diagrams at build-time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/notes/showing-my-toolbox-on-this-site&quot;&gt;Showing my Toolbox on this site&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;Looking back at my task lists and GH issues it’s pretty clear I’ve shied away from two jobs for bloody ages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Putting some &lt;em&gt;proper&lt;/em&gt; thought into the typefaces and how I use them.&lt;/li&gt;
&lt;li&gt;Redesigning the homepage (&lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/issues/25&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#25&lt;/a&gt;, Apr ’25).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Which is unsurprising because they both require Actual Non-Trivial Taste-Based Decisions About My Own Website. Anyone reading this who’s ever had “redesign personal site” on their todo list will know that such decisions are orders of magnitude more difficult than &lt;strong&gt;any&lt;/strong&gt; genuinely-weighty decision required at work. 🤷‍♂️&lt;/p&gt;
&lt;p&gt;So I’m happy to say that I’ve just ticked the last one off. The homepage of this site now contains more than a simple list of links.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-24-new-homepage-light.DeBqJfvl_ZB1NjD.webp&quot; srcset=&quot;/_astro/2026-07-24-new-homepage-light.DeBqJfvl_ZVNcRX.webp 640w, /_astro/2026-07-24-new-homepage-light.DeBqJfvl_2wvV8Y.webp 750w, /_astro/2026-07-24-new-homepage-light.DeBqJfvl_k3uo0.webp 828w, /_astro/2026-07-24-new-homepage-light.DeBqJfvl_ZD7kkI.webp 1080w, /_astro/2026-07-24-new-homepage-light.DeBqJfvl_1hhv3Q.webp 1280w, /_astro/2026-07-24-new-homepage-light.DeBqJfvl_ZV21us.webp 1668w, /_astro/2026-07-24-new-homepage-light.DeBqJfvl_REzX8.webp 2048w, /_astro/2026-07-24-new-homepage-light.DeBqJfvl_1wtcVN.webp 2560w, /_astro/2026-07-24-new-homepage-light.DeBqJfvl_ZB1NjD.webp 3508w&quot; alt=&quot;The redesigned homepage in light mode, showing recent articles and torn-paper note cards&quot; title caption=&quot;The new homepage (light mode)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 3508px) 3508px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;3508&quot; height=&quot;2108&quot;&gt;
  &lt;img src=&quot;/_astro/2026-07-24-new-homepage-dark.BTh5NX59_Tq2wv.webp&quot; srcset=&quot;/_astro/2026-07-24-new-homepage-dark.BTh5NX59_RUW81.webp 640w, /_astro/2026-07-24-new-homepage-dark.BTh5NX59_1fyXuR.webp 750w, /_astro/2026-07-24-new-homepage-dark.BTh5NX59_J1t18.webp 828w, /_astro/2026-07-24-new-homepage-dark.BTh5NX59_s6cN9.webp 1080w, /_astro/2026-07-24-new-homepage-dark.BTh5NX59_Z26LphE.webp 1280w, /_astro/2026-07-24-new-homepage-dark.BTh5NX59_Z4V4US.webp 1668w, /_astro/2026-07-24-new-homepage-dark.BTh5NX59_ZYanqP.webp 2048w, /_astro/2026-07-24-new-homepage-dark.BTh5NX59_Z2cAdwk.webp 2560w, /_astro/2026-07-24-new-homepage-dark.BTh5NX59_Tq2wv.webp 3596w&quot; alt=&quot;The redesigned homepage in dark mode, showing recent articles and torn-paper note cards&quot; title caption=&quot;The new homepage (dark mode)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 3596px) 3596px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;3596&quot; height=&quot;2196&quot;&gt;
&lt;p&gt;I’ve always had a soft spot for the “skeuomorphic” faux Sellotape, stitched corner banners and ripped paper effects which everyone loved twenty years ago. So while the homepage shows recent articles in the usual &lt;em&gt;type-as-design&lt;/em&gt; style, I couldn’t resist showing notes as little bits of dot-grid paper with a torn right edge – both on the homepage and on the &lt;a href=&quot;/notes&quot;&gt;notes&lt;/a&gt; index.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_StyNm.webp&quot; srcset=&quot;/_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_1WEyhq.webp 640w, /_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_1LzA0c.webp 750w, /_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_Z1qaMBP.webp 828w, /_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_2of2Oj.webp 1080w, /_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_Z1gy87o.webp 1280w, /_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_2ej7ki.webp 1668w, /_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_2mUk3S.webp 2048w, /_astro/2026-07-24-notecards-in-note-index-page.Bv60RgAN_StyNm.webp 2348w&quot; alt=&quot;A grid of note cards styled as dot-grid paper with torn right edges&quot; title caption=&quot;The new compact Note cards, as used on the notes index page&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2348px) 2348px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2348&quot; height=&quot;2196&quot;&gt;
&lt;p&gt;Before starting on &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/pull/151&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#151&lt;/a&gt; I tried to lay some groundwork by standardising the &lt;a href=&quot;/styleguide/components#article-card&quot;&gt;various card components&lt;/a&gt; but still ended up tweaking a bunch of stuff while working on the homepage, including the addition of global CSS &lt;strong&gt;view transitions&lt;/strong&gt;. The PR description has a decent list of all the little bits which happened by accident.&lt;/p&gt;
&lt;p&gt;Aside from “clearing the backlog”, it’s been lovely having &lt;strong&gt;actual time&lt;/strong&gt; in this codebase. That might sound weird considering how simple it is – especially since AI has been writing much of the recent code here – but I don’t ever remember having the time and headspace to kinda &lt;em&gt;settle in&lt;/em&gt; to my own site’s code in a kinda intuitive, somehow &lt;em&gt;fundamental&lt;/em&gt; way&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;true&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I still have a ton of fun ideas I wanna play with on this site, but &lt;strong&gt;boy does it feel good&lt;/strong&gt; to finally clear the backlog and have nothing I feel I &lt;em&gt;should&lt;/em&gt; do here. This site was always intended as a playground but it’s hard to really enjoy playing when you’re avoiding that awkward new roundabout &amp;amp; slide you gotta deal with sometime.&lt;/p&gt;
&lt;section data-footnotes=&quot;true&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a href=&quot;#footnote-label&quot; aria-label=&quot;Link to “Footnotes”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;I feel like working with AI coding agents has probably made this possible, because for every session I’ve spent working on this site at least 3/4 of it has been &lt;strong&gt;reading&lt;/strong&gt; and &lt;strong&gt;thinking&lt;/strong&gt;, not &lt;strong&gt;writing code&lt;/strong&gt;. Which is a luxury one rarely gets on a personal website. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Introducing my very own Loom</title><link>https://danny.is/writing/loomclone-part-1/</link><guid isPermaLink="true">https://danny.is/writing/loomclone-part-1/</guid><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;intro-paragraph&quot; data-astro-cid-pmf5gjw4&gt; &lt;p&gt;Over the past couple of months I’ve been building a personal video-sharing platform to replace  &lt;a href=&quot;https://loom.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Loom&lt;/a&gt;, partly so it works how I’ve always wanted Loom to work and partly so I have long-term control over my own videos. Its basic functionality is broadly equivalent to Loom minus the stuff I never use, plus some me-specific extras. Building what I’m currently calling &lt;em&gt;LoomClone&lt;/em&gt; has been mad fun and I’ve learned a &lt;strong&gt;ton&lt;/strong&gt; (this is my first proper native macOS app, among other things).&lt;/p&gt; &lt;/div&gt; &lt;script type=&quot;module&quot; src=&quot;/home/runner/work/dannyis-astro/dannyis-astro/src/components/mdx/IntroParagraph.astro?astro&amp;type=script&amp;index=0&amp;lang.ts&quot;&gt;&lt;/script&gt;
&lt;p&gt;Before we dive in, here’s what one of these videos looks like embedded on this site…&lt;/p&gt;
&lt;figure class=&quot;lcvid&quot; data-lcvid data-astro-cid-73pxir2x&gt;  &lt;div class=&quot;lcvid-player&quot; style=&quot;aspect-ratio: 1920 / 1080;&quot; data-astro-cid-73pxir2x&gt; &lt;button type=&quot;button&quot; class=&quot;lcvid-facade&quot; aria-label=&quot;Play: Lorem Vidsum One - Permanent Demo&quot; data-astro-cid-73pxir2x&gt; &lt;img class=&quot;lcvid-poster&quot; src=&quot;https://v.danny.is/lorem-one/poster.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; data-astro-cid-73pxir2x&gt; &lt;span class=&quot;lcvid-play&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt; &lt;svg viewBox=&quot;0 0 72 72&quot; data-astro-cid-73pxir2x&gt; &lt;circle cx=&quot;36&quot; cy=&quot;36&quot; r=&quot;35&quot; data-astro-cid-73pxir2x&gt;&lt;/circle&gt; &lt;path d=&quot;M29 23 L29 49 L51 36 Z&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;/svg&gt; &lt;/span&gt; &lt;span class=&quot;lcvid-duration-badge&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt;30s&lt;/span&gt; &lt;/button&gt; &lt;template class=&quot;lcvid-template&quot; data-astro-cid-73pxir2x&gt; &lt;media-player title=&quot;Lorem Vidsum One - Permanent Demo&quot; poster=&quot;https://v.danny.is/lorem-one/poster.jpg&quot; crossorigin=&quot;anonymous&quot; playsinline=&quot;true&quot; load=&quot;eager&quot; preload=&quot;auto&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt; &lt;media-provider data-astro-cid-73pxir2x=&quot;true&quot;&gt; &lt;source src=&quot;https://v.danny.is/lorem-one/raw/1080p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;1920&quot; data-height=&quot;1080&quot; data-astro-cid-73pxir2x&gt;&lt;source src=&quot;https://v.danny.is/lorem-one/raw/720p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;1280&quot; data-height=&quot;720&quot; data-astro-cid-73pxir2x&gt; &lt;track src=&quot;https://v.danny.is/lorem-one/captions.vtt&quot; kind=&quot;subtitles&quot; srclang=&quot;en&quot; label=&quot;English&quot; default data-astro-cid-73pxir2x&gt; &lt;media-poster class=&quot;vds-poster&quot; src=&quot;https://v.danny.is/lorem-one/poster.jpg&quot; alt=&quot;Lorem Vidsum One - Permanent Demo&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt;&lt;/media-poster&gt; &lt;/media-provider&gt; &lt;media-video-layout data-astro-cid-73pxir2x=&quot;true&quot;&gt;&lt;/media-video-layout&gt; &lt;/media-player&gt; &lt;/template&gt; &lt;/div&gt; &lt;figcaption class=&quot;lcvid-meta&quot; data-astro-cid-73pxir2x&gt; &lt;span class=&quot;lcvid-detail&quot; data-astro-cid-73pxir2x&gt; &lt;span data-astro-cid-73pxir2x&gt;30s&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt;·&lt;/span&gt; &lt;span data-astro-cid-73pxir2x&gt;18 June 2026&lt;/span&gt; &lt;/span&gt; &lt;a class=&quot;lcvid-open&quot; href=&quot;https://v.danny.is/lorem-one&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-astro-cid-73pxir2x&gt; &lt;span data-astro-cid-73pxir2x&gt;Open&lt;/span&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;12&quot; height=&quot;12&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt; &lt;path d=&quot;M15 3h6v6&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;path d=&quot;M10 14L21 3&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;/svg&gt; &lt;/a&gt; &lt;/figcaption&gt;   &lt;/figure&gt; &lt;script type=&quot;module&quot; src=&quot;/home/runner/work/dannyis-astro/dannyis-astro/src/components/mdx/LCVid.astro?astro&amp;type=script&amp;index=0&amp;lang.ts&quot;&gt;&lt;/script&gt;
&lt;p&gt;I’ve been a &lt;a href=&quot;https://betterat.work/tool/loom&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;huge fan&lt;/a&gt; and paying customer of Loom since their very early days and have always had two main use cases:&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;Quick, short screenshares and talking head videos&lt;/strong&gt; where I hit stop and immediately paste the link into Slack, Notion, Asana, WhatsApp or similar. For this kind of video-based async communication to work, it must be &lt;strong&gt;frictionless&lt;/strong&gt; for both me and viewers. Loom was the first product which could do this reliably.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Longer, more polished videos&lt;/strong&gt; for tutorials, guides, presentations and the like. These often end up embedded in evergreen Notion/Google docs and tend to be widely linked to from all sorts of places. When I say “more polished” I don’t mean the &lt;em&gt;hours-in-final-cut-pro&lt;/em&gt; polish I might put into a marketing video. More like &lt;em&gt;preparing for an All Hands or training session on Zoom&lt;/em&gt;: I’m gonna prep an outline &amp;amp; some slides, tidy up my desktop, make sure all my demos and other windows are in the right place, maybe do a quick run-through &lt;strong&gt;and then record in one take&lt;/strong&gt;. My default approach to this with Loom has always been to line up any slides or apps I need in fullscreen spaces and swipe between them as I record, adding any important talking-head segments by recording separate Looms and then stitching them together in Loom’s web editor.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Loom’s whole business is built on being brilliant at (1) and while they do (2) kinda-okay I’ve always had some gripes about their product, my biggest being…&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;I don’t own the canonical URLs for my videos.&lt;/strong&gt; I’ve got hundreds of historical Loom videos, many of which are embedded in documents, wikis, knowledge bases and archived slack channels in organisations I’ve long-since left. Every one of them points to &lt;code&gt;loom.com/something&lt;/code&gt;, so while I own my actual videos (I can download them), Loom controls what matters most for content on the web: &lt;em&gt;the canonical URLs&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I don’t have much control over the viewer experience.&lt;/strong&gt; Loom controls how my videos are served to viewers: It’s impossible for me to control things like RSS feeds, custom embed players, SEO and text-based endpoints to the degree I want.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The camera feed isn’t first class unless I’m in &lt;em&gt;cam-only&lt;/em&gt; mode.&lt;/strong&gt; Most of my longer videos involve cutting between talking-head and screenshare segments. Loom doesn’t let you do that with a fullscreen, high-quality camera feed mid-recording. This has always been mega frustrating for me.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliability &amp;amp; Recomposability.&lt;/strong&gt; Loom recordings rarely fail, but when they do it’s invariably a 30-minute single take in which I was &lt;em&gt;fucking inspired&lt;/em&gt; and will never repeat quite as well after picking my smashed laptop off the floor. If you’ve been here you &lt;strong&gt;know&lt;/strong&gt; how that feels. And it’s only &lt;em&gt;slightly&lt;/em&gt; less frustrating to realise a whole segment which should’ve been &lt;em&gt;talking-head&lt;/em&gt; is actually a recording of my empty desktop with a voiceover on top. And there’s nothing I can do because the camera feed wasn’t even captured.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The mac app is not a mac app.&lt;/strong&gt; Loom’s macOS app is built on Electron and bundles a custom build of &lt;a href=&quot;https://ffmpeg.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ffmpeg&lt;/a&gt; alongside the usual Chromium. I get why they do this but as I’m always on Apple Silicon, a proper native mac app is likely to be way more performant for heavy stuff like video capture in all sorts of ways (even before taking advantage of lower-level OS APIs).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;All the features I don’t want.&lt;/strong&gt; Any product with a wide audience will have features I don’t want. I’m cool with that even though I can’t turn many of them off. But in 2026, any product like Loom will likely have features nobody at all wants because &lt;em&gt;a)&lt;/em&gt; it can never be finished unless it’s been abandoned, and &lt;em&gt;b)&lt;/em&gt; it’s gonna be &lt;strong&gt;stuffed full of nonsense AI features&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I tried &lt;a href=&quot;/notes/loom-to-cap/&quot;&gt;switching to Cap&lt;/a&gt; because it’s open source and I can use my own domain, but while it isn’t terrible it doesn’t feel like a platform I wanna bet on long-term. And so we arrive at the thought… &lt;strong&gt;what if I built my own?&lt;/strong&gt; 🤔&lt;/p&gt;
&lt;p&gt;Which is just what I’ve been doing…&lt;/p&gt;
&lt;aside aria-label=&quot;Core Principles&quot; class=&quot;callout  &quot; data-callout-color=&quot;blue&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;📜&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;h3 class=&quot;title&quot; data-astro-cid-lnlu5god&gt;Core Principles&lt;/h3&gt; &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;Given the massive potential for scope creep here, I started by defining some core principles. These underpin almost all the product decisions I’ve made along the way.&lt;/p&gt;&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Instant Shareability&lt;/strong&gt;. The moment I stop recording, I need a working URL which I can share immediately. This is what differentiates async video comms tools from other recording and hosting apps.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Never Lose Footage&lt;/strong&gt;. The product must guarantee that footage is recoverable. Recording a 20-minute tutorial and losing it thanks to connection or server problems isn’t okay. The whole thing should be fault-tolerant by design.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Permanent URLs which I own&lt;/strong&gt;. A video URL works forever. If I change a video’s slug, the old URL redirects to the new one. Videos embedded in Notion pages, Google Docs, and knowledge bases years from now must still work. Every video lives on &lt;code&gt;v.danny.is&lt;/code&gt;, a domain I control.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reliability for Viewers&lt;/strong&gt;. When someone clicks a video link, it Just Works regardless of device, location, connection etc. Viewer-facing video delivery must be first-class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Discoverability &amp;amp; Viewer UX&lt;/strong&gt;. I’m a good &lt;em&gt;web citizen&lt;/em&gt;. The public-facing endpoints serve semantic content which works in a variety of contexts. Public videos are SEO-friendly and are discoverable via RSS feeds, LLM-facing endpoints and the like.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;. The whole system is designed with one goal in mind: &lt;strong&gt;allowing me to record, host, and share videos&lt;/strong&gt;. Every feature must be in service of doing these things &lt;em&gt;really well&lt;/em&gt;.  Similarly, the codebase and technology stack should be as simple as possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I’m the Only Creator Here&lt;/strong&gt;. This product is &lt;em&gt;only for me&lt;/em&gt;. Nobody else will use the mac app or admin app. Nobody else will record videos. The only parts where other people matter are the public-facing bits where folks find and watch my videos.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;the-four-moving-parts&quot;&gt;The Four Moving Parts&lt;a href=&quot;#the-four-moving-parts&quot; aria-label=&quot;Link to “The Four Moving Parts”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s helpful to think about LoomClone as four separate things, each with a distinct purpose and set of user needs. While this isn’t quite accurate when it comes to the underlying tech, it’s the framing I’ll be using in this series of articles.&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The macOS recording app&lt;/strong&gt;. A native macOS menubar app for recording videos with as little friction as possible. Its only job is helping me get set up for a recording quickly and then streaming it up to the server once I hit record while dealing properly with all the pausing and mode switching that happens during recording.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The backend API&lt;/strong&gt;. A web API which receives data from the mac app and handles storage, background post-processing, backup, error recovery and the like. Its job is working with the mac app to get recordings on a server and ready for public consumption in a reliable way.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The admin app&lt;/strong&gt;. A web interface for managing, editing and working with my videos, optimised for the workflows I personally care about most. Ideally, I’ll rarely use it but when I do, its job is to make whatever I need as easy and painless as possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The viewer-facing surface&lt;/strong&gt;. Serves videos to viewers in the best way &lt;em&gt;for them&lt;/em&gt; and handles everything else that has to do with publishing, discoverability etc.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We’ll go into detail about &lt;em&gt;how&lt;/em&gt; these work later but for now it’s important to explain &lt;em&gt;exactly what&lt;/em&gt; they do from a user perspective. So the next half of this article is dedicated to the &lt;strong&gt;recording app&lt;/strong&gt; UI and &lt;strong&gt;viewer-facing surface&lt;/strong&gt;, finishing with a 4-minute lightning tour of the &lt;strong&gt;admin app&lt;/strong&gt; and its features.&lt;/p&gt;
&lt;h2 id=&quot;the-macos-recording-app&quot;&gt;The macOS recording app&lt;a href=&quot;#the-macos-recording-app&quot; aria-label=&quot;Link to “The macOS recording app”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The menubar app is deliberately very simple. Clicking it opens a popover like this.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-menubar-app-1.H0mbfP6s_Z1nnkpz.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-menubar-app-1.H0mbfP6s_Z1YUPlj.webp 640w, /_astro/2026-07-06-lc-menubar-app-1.H0mbfP6s_Z1nnkpz.webp 744w&quot; alt=&quot;The LoomClone menubar popover showing display, camera and mic selectors, a recording preview, a sound meter, white balance and brightness sliders, and the starting-mode, quality and framerate controls&quot; title caption=&quot;The pre-recording menubar app&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 744px) 744px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;744&quot; height=&quot;1282&quot;&gt;
&lt;p&gt;Here’s what each control is for…&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Display&lt;/td&gt;
&lt;td&gt;The monitor to use for screen recording&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Camera&lt;/td&gt;
&lt;td&gt;The camera to use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mic&lt;/td&gt;
&lt;td&gt;The microphone to use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preview&lt;/td&gt;
&lt;td&gt;Shows how the recording will look (based on the &lt;em&gt;starting mode&lt;/em&gt; selected below) so I can confirm the right sources are selected and are looking good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sound meter&lt;/td&gt;
&lt;td&gt;For the selected mic so I can check I have decent audio levels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;White balance&lt;/td&gt;
&lt;td&gt;Adjust the camera white balance. I often want to tweak this depending on the current lighting conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brightness&lt;/td&gt;
&lt;td&gt;Adjust the camera brightness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starting mode&lt;/td&gt;
&lt;td&gt;Choose between screen-only, camera-only and picture-in-picture mode. This only affects the preview above and the mode in which the recording will &lt;strong&gt;start&lt;/strong&gt;. I can toggle between these freely while recording.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upload quality&lt;/td&gt;
&lt;td&gt;Choose the quality of the uploaded recording ( 720p, 1080p or 1440p)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upload framerate&lt;/td&gt;
&lt;td&gt;Choose the framerate of the uploaded recording (30 or 60fps)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The UI adapts appropriately to the three selected sources: if camera or display is set to “None” the mode toggle is disabled; the camera controls are hidden when no camera is selected; the sound meter is only visible when a mic is selected. The upload quality and framerate toggles also respect the available feeds – a camera delivering 1080p/30fps with no display selected won’t show higher options for the upload quality because we’d just end up with an upsampled upload.&lt;/p&gt;
&lt;p&gt;Here’s what it looks like with &lt;em&gt;Camera&lt;/em&gt; mode selected and the preview showing the full camera feed. Expanding the &lt;em&gt;Hide from recording&lt;/em&gt; section allows me to hide stuff from the screen recorder.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-menubar-app-2.YeGst7Nx_29VvPt.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-menubar-app-2.YeGst7Nx_Z2gx6OW.webp 640w, /_astro/2026-07-06-lc-menubar-app-2.YeGst7Nx_29VvPt.webp 744w&quot; alt=&quot;The menubar popover in Camera mode showing the full camera-feed preview above an expanded “Hide from recording” section with checkboxes for desktop icons and app windows&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 744px) 744px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;744&quot; height=&quot;1618&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Desktop icons&lt;/td&gt;
&lt;td&gt;When checked, Finder’s desktop icons will be hidden in the screen recording&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Windows&lt;/td&gt;
&lt;td&gt;Any checked apps will have their windows hidden in the screen recording. Currently running apps are shown alongside the five most-recently selected apps (whether they’re running or not)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;strong&gt;Record&lt;/strong&gt; button shows a short countdown and begins recording.&lt;/p&gt;
&lt;h3 id=&quot;the-ui-while-recording&quot;&gt;The UI while recording&lt;a href=&quot;#the-ui-while-recording&quot; aria-label=&quot;Link to “The UI while recording”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;During recording, a floating toolbar is shown at the bottom of the screen.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-recording-overlay.DDSfFKcw_Z1DqtQ.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-recording-overlay.DDSfFKcw_wQVgf.webp 640w, /_astro/2026-07-06-lc-recording-overlay.DDSfFKcw_Ztfjhd.webp 750w, /_astro/2026-07-06-lc-recording-overlay.DDSfFKcw_Z1DqtQ.webp 761w&quot; alt=&quot;The floating recording toolbar with, from left to right, stop, pause, add-chapter-marker, cancel and mode-switcher controls plus a recording-length timer&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 761px) 761px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;761&quot; height=&quot;144&quot;&gt;
&lt;p&gt;From left to right the controls are…&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stop&lt;/td&gt;
&lt;td&gt;Finishes the recording&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pause/Unpause&lt;/td&gt;
&lt;td&gt;Pauses the recording. I use this &lt;em&gt;all the time&lt;/em&gt; during longer recordings while I reorganise windows, switch to different spaces or quickly check my notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add chapter marker&lt;/td&gt;
&lt;td&gt;Adds an anonymous chapter marker. If my recording has multiple chapters to it I can hit this to add markers which can later be named in the web interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cancel&lt;/td&gt;
&lt;td&gt;Cancels the recording and deletes any data already on the server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mode switcher&lt;/td&gt;
&lt;td&gt;Switch between screen-only, camera-only and picture-in-picture mode mid-recording&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recording length&lt;/td&gt;
&lt;td&gt;Shows the length of the recording so far&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;When in a camera mode a draggable preview of the camera feed is shown as an overlay while recording. This is circular when in screen-and-camera and rectangular when in camera-only mode.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-recording-pip.CZMAI0zo_1APKd.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-recording-pip.CZMAI0zo_2cRT82.webp 640w, /_astro/2026-07-06-lc-recording-pip.CZMAI0zo_1oCM22.webp 750w, /_astro/2026-07-06-lc-recording-pip.CZMAI0zo_Z16gY3L.webp 828w, /_astro/2026-07-06-lc-recording-pip.CZMAI0zo_Z1C1Gc4.webp 1080w, /_astro/2026-07-06-lc-recording-pip.CZMAI0zo_5QqvP.webp 1280w, /_astro/2026-07-06-lc-recording-pip.CZMAI0zo_ZKs839.webp 1668w, /_astro/2026-07-06-lc-recording-pip.CZMAI0zo_ZWDfE4.webp 2048w, /_astro/2026-07-06-lc-recording-pip.CZMAI0zo_1oo2pB.webp 2560w, /_astro/2026-07-06-lc-recording-pip.CZMAI0zo_1APKd.webp 3840w&quot; alt=&quot;A recording in picture-in-picture mode with a circular camera preview overlaid in the corner of the screen&quot; title caption=&quot;Recording in camera-and-screen mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 3840px) 3840px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;3840&quot; height=&quot;2160&quot;&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_Z2lVIxa.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_eJtDH.webp 640w, /_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_Z1jCuQL.webp 750w, /_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_Z7E2Qe.webp 828w, /_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_Wsqf9.webp 1080w, /_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_Z1u3idQ.webp 1280w, /_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_Z1s4546.webp 1668w, /_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_7bg3p.webp 2048w, /_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_8mV4s.webp 2560w, /_astro/2026-07-06-lc-recording-camonly.BhEAJu6v_Z2lVIxa.webp 3840w&quot; alt=&quot;A recording in camera-only mode with a large rectangular camera preview&quot; title caption=&quot;Recording in camera-only mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 3840px) 3840px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;3840&quot; height=&quot;2160&quot;&gt;
&lt;p&gt;Neither the camera preview nor toolbar are captured in the actual screen recording, but dragging the camera preview between quadrants of the screen will cause the camera overlay in the final recording to move to that corner of the screen.&lt;/p&gt;
&lt;p&gt;Besides the settings window and a few warnings which pop up if something goes wrong while recording, &lt;strong&gt;this is the whole UI of the macOS app&lt;/strong&gt;.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;Notice all the things which &lt;strong&gt;aren’t&lt;/strong&gt; here. Most apps like this include options for recording individual windows or sections of a screen, or controls for adding effects, audio cleanup and the like. I know I don’t need any of that myself, so it’s not there.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;the-viewer-facing-surface-a-quick-tour&quot;&gt;The viewer-facing surface (a quick tour)&lt;a href=&quot;#the-viewer-facing-surface-a-quick-tour&quot; aria-label=&quot;Link to “The viewer-facing surface (a quick tour)”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As soon as I’m finished recording a video, it’s available on a randomly-generated public URL like &lt;code&gt;https://v.danny.is/three-random-words/&lt;/code&gt;. Here’s what the page for the video at the top of this article looks like.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ZPtvEI.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_Z2vNpj5.webp 640w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ebXmY.webp 750w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ZSIC3F.webp 828w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ZcUpHv.webp 1080w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_D5J7s.webp 1280w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_Z2eePb7.webp 1668w, /_astro/2026-07-06-lc-demo-publicpage-screenshot.Pb1NZBFN_ZPtvEI.webp 1994w&quot; alt=&quot;A video’s public page on v.danny.is showing the title, length and date above a large video player, with the description and footer below&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1994px) 1994px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1994&quot; height=&quot;1844&quot;&gt;
&lt;p&gt;It shows the title, length and date at the top (plus any public tags – more on that later) with a large video player below. The video player works exactly as you’d expect, including closed captions, thumbnail scrubbing and the like. The description and a simple footer are shown underneath.&lt;/p&gt;
&lt;p&gt;While the page is intentionally simple, fully controlling its underlying code means I’ve been able to ensure these URLs embed properly in Notion and Google docs, have appropriate rich preview data when shared, and – for public videos – are discoverable via appropriate SEO, RSS feeds and the like.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-embedding-notion.BClbwKST_ZH8yru.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-embedding-notion.BClbwKST_1JNuxC.webp 640w, /_astro/2026-07-06-lc-embedding-notion.BClbwKST_Z2oIPU5.webp 750w, /_astro/2026-07-06-lc-embedding-notion.BClbwKST_26tD29.webp 828w, /_astro/2026-07-06-lc-embedding-notion.BClbwKST_HtFLj.webp 1080w, /_astro/2026-07-06-lc-embedding-notion.BClbwKST_Z1LCHSk.webp 1280w, /_astro/2026-07-06-lc-embedding-notion.BClbwKST_Z22THUP.webp 1668w, /_astro/2026-07-06-lc-embedding-notion.BClbwKST_ZH8yru.webp 1860w&quot; alt=&quot;A LoomClone video embedded and playing inline within a Notion page&quot; title caption=&quot;A video URL embedded in a notion page&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1860px) 1860px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1860&quot; height=&quot;1396&quot;&gt;
  &lt;img src=&quot;/_astro/2026-07-06-lc-whatsapp-share.CdR_Macd_Z1JMRux.webp&quot; srcset=&quot;/_astro/2026-07-06-lc-whatsapp-share.CdR_Macd_6GC8A.webp 640w, /_astro/2026-07-06-lc-whatsapp-share.CdR_Macd_obwAY.webp 750w, /_astro/2026-07-06-lc-whatsapp-share.CdR_Macd_STVJt.webp 828w, /_astro/2026-07-06-lc-whatsapp-share.CdR_Macd_Z1JMRux.webp 844w&quot; alt=&quot;A LoomClone video link shared in WhatsApp showing a rich preview with thumbnail and title&quot; title caption=&quot;A video URL shared in WhatsApp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 844px) 844px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;844&quot; height=&quot;720&quot;&gt;
&lt;p&gt;Here’s a quick 3-minute run-through of the video player itself, plus a few little easter eggs.&lt;/p&gt;
&lt;figure class=&quot;lcvid&quot; data-lcvid data-astro-cid-73pxir2x&gt;  &lt;div class=&quot;lcvid-player&quot; style=&quot;aspect-ratio: 1920 / 1080;&quot; data-astro-cid-73pxir2x&gt; &lt;button type=&quot;button&quot; class=&quot;lcvid-facade&quot; aria-label=&quot;Play: Demo of LoomClone&apos;s Video Player Page&quot; data-astro-cid-73pxir2x&gt; &lt;img class=&quot;lcvid-poster&quot; src=&quot;https://v.danny.is/bumpy-planets-spend/poster.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; data-astro-cid-73pxir2x&gt; &lt;span class=&quot;lcvid-play&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt; &lt;svg viewBox=&quot;0 0 72 72&quot; data-astro-cid-73pxir2x&gt; &lt;circle cx=&quot;36&quot; cy=&quot;36&quot; r=&quot;35&quot; data-astro-cid-73pxir2x&gt;&lt;/circle&gt; &lt;path d=&quot;M29 23 L29 49 L51 36 Z&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;/svg&gt; &lt;/span&gt; &lt;span class=&quot;lcvid-duration-badge&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt;3m 13s&lt;/span&gt; &lt;/button&gt; &lt;template class=&quot;lcvid-template&quot; data-astro-cid-73pxir2x&gt; &lt;media-player title=&quot;Demo of LoomClone&apos;s Video Player Page&quot; poster=&quot;https://v.danny.is/bumpy-planets-spend/poster.jpg&quot; crossorigin=&quot;anonymous&quot; playsinline=&quot;true&quot; load=&quot;eager&quot; preload=&quot;auto&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt; &lt;media-provider data-astro-cid-73pxir2x=&quot;true&quot;&gt; &lt;source src=&quot;https://v.danny.is/bumpy-planets-spend/raw/1080p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;1920&quot; data-height=&quot;1080&quot; data-astro-cid-73pxir2x&gt;&lt;source src=&quot;https://v.danny.is/bumpy-planets-spend/raw/720p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;1280&quot; data-height=&quot;720&quot; data-astro-cid-73pxir2x&gt; &lt;track src=&quot;https://v.danny.is/bumpy-planets-spend/captions.vtt&quot; kind=&quot;subtitles&quot; srclang=&quot;en&quot; label=&quot;English&quot; default data-astro-cid-73pxir2x&gt; &lt;media-poster class=&quot;vds-poster&quot; src=&quot;https://v.danny.is/bumpy-planets-spend/poster.jpg&quot; alt=&quot;Demo of LoomClone&apos;s Video Player Page&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt;&lt;/media-poster&gt; &lt;/media-provider&gt; &lt;media-video-layout thumbnails=&quot;https://v.danny.is/bumpy-planets-spend/storyboard.vtt&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt;&lt;/media-video-layout&gt; &lt;/media-player&gt; &lt;/template&gt; &lt;/div&gt; &lt;figcaption class=&quot;lcvid-meta&quot; data-astro-cid-73pxir2x&gt; &lt;span class=&quot;lcvid-detail&quot; data-astro-cid-73pxir2x&gt; &lt;span data-astro-cid-73pxir2x&gt;3m 13s&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt;·&lt;/span&gt; &lt;span data-astro-cid-73pxir2x&gt;6 July 2026&lt;/span&gt; &lt;/span&gt;  &lt;/figcaption&gt;   &lt;/figure&gt; 
&lt;p&gt;The only other part of the public surface I should mention is &lt;strong&gt;tag pages&lt;/strong&gt;, but first a note on visibility…&lt;/p&gt;
&lt;aside aria-label=&quot;Visibility&quot; class=&quot;callout  &quot; data-callout-color=&quot;blue&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;👀&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;h3 class=&quot;title&quot; data-astro-cid-lnlu5god&gt;Visibility&lt;/h3&gt; &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;A video’s &lt;em&gt;visibility&lt;/em&gt; can be one of three values:&lt;/p&gt;&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;Unlisted&lt;/strong&gt; is the default. These are publicly available but are not easily &lt;em&gt;discoverable&lt;/em&gt; unless you know the URL. The vast majority of my videos are of this type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Public&lt;/strong&gt; videos are additionally discoverable via things like RSS &amp;amp; JSON feeds, and are indexed by search engines.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private&lt;/strong&gt; videos are not available outside the admin interface and have no public URL. These are intended for personal notes and videos I want to keep private until I’m ready to share them.&lt;/li&gt;
&lt;/ul&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;As we’ll see shortly, &lt;strong&gt;tags&lt;/strong&gt; mainly exist to help organise my recordings in the admin app. But like videos themselves, tags can be &lt;em&gt;private&lt;/em&gt;, &lt;em&gt;unlisted&lt;/em&gt; or &lt;em&gt;public&lt;/em&gt;. And public tags have their own &lt;strong&gt;tag pages&lt;/strong&gt; which serve as public collections (or playlists) which look like this.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_ZtKJkd.webp&quot; srcset=&quot;/_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_mjXsu.webp 640w, /_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_56PmY.webp 750w, /_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_SgmJk.webp 828w, /_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_AQe1f.webp 1080w, /_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_Z1OOw1.webp 1280w, /_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_fSBXX.webp 1668w, /_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_Z1OPwaj.webp 2048w, /_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_1c5v0k.webp 2560w, /_astro/2026-07-07-lc-tagpage-public-demo.CfmOkdB0_ZtKJkd.webp 2682w&quot; alt=&quot;A public tag page on v.danny.is listing several videos as a collection or playlist&quot; title caption=&quot;Example of a tag page&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2682px) 2682px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2682&quot; height=&quot;1496&quot;&gt;
&lt;p&gt;We’ll dig into some of the more interesting features and subtleties of the public surface in subsequent articles, but for now I wanna give you a quick tour of the admin app for all this.&lt;/p&gt;
&lt;h2 id=&quot;the-admin-app-a-lightning-tour&quot;&gt;The admin app, a lightning tour&lt;a href=&quot;#the-admin-app-a-lightning-tour&quot; aria-label=&quot;Link to “The admin app, a lightning tour”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ll do a whole article on the admin web app in due course, but since I’ve now got a fancy video platform I might as well give you a lightning tour in video form! Here’s a four-minute demo…&lt;/p&gt;
&lt;figure class=&quot;lcvid&quot; data-lcvid data-astro-cid-73pxir2x&gt; &lt;p class=&quot;lcvid-title&quot; data-astro-cid-73pxir2x&gt;Admin App Lightning Tour&lt;/p&gt; &lt;div class=&quot;lcvid-player&quot; style=&quot;aspect-ratio: 2560 / 1440;&quot; data-astro-cid-73pxir2x&gt; &lt;button type=&quot;button&quot; class=&quot;lcvid-facade&quot; aria-label=&quot;Play: Admin App Lightning Tour&quot; data-astro-cid-73pxir2x&gt; &lt;img class=&quot;lcvid-poster&quot; src=&quot;https://v.danny.is/shaky-bobcats-talk/poster.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; data-astro-cid-73pxir2x&gt; &lt;span class=&quot;lcvid-play&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt; &lt;svg viewBox=&quot;0 0 72 72&quot; data-astro-cid-73pxir2x&gt; &lt;circle cx=&quot;36&quot; cy=&quot;36&quot; r=&quot;35&quot; data-astro-cid-73pxir2x&gt;&lt;/circle&gt; &lt;path d=&quot;M29 23 L29 49 L51 36 Z&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;/svg&gt; &lt;/span&gt; &lt;span class=&quot;lcvid-duration-badge&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt;4m 23s&lt;/span&gt; &lt;/button&gt; &lt;template class=&quot;lcvid-template&quot; data-astro-cid-73pxir2x&gt; &lt;media-player title=&quot;Admin App Lightning Tour&quot; poster=&quot;https://v.danny.is/shaky-bobcats-talk/poster.jpg&quot; crossorigin=&quot;anonymous&quot; playsinline=&quot;true&quot; load=&quot;eager&quot; preload=&quot;auto&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt; &lt;media-provider data-astro-cid-73pxir2x=&quot;true&quot;&gt; &lt;source src=&quot;https://v.danny.is/shaky-bobcats-talk/raw/1440p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;2560&quot; data-height=&quot;1440&quot; data-astro-cid-73pxir2x&gt;&lt;source src=&quot;https://v.danny.is/shaky-bobcats-talk/raw/1080p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;1920&quot; data-height=&quot;1080&quot; data-astro-cid-73pxir2x&gt;&lt;source src=&quot;https://v.danny.is/shaky-bobcats-talk/raw/720p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;1280&quot; data-height=&quot;720&quot; data-astro-cid-73pxir2x&gt; &lt;track src=&quot;https://v.danny.is/shaky-bobcats-talk/captions.vtt&quot; kind=&quot;subtitles&quot; srclang=&quot;en&quot; label=&quot;English&quot; default data-astro-cid-73pxir2x&gt; &lt;media-poster class=&quot;vds-poster&quot; src=&quot;https://v.danny.is/shaky-bobcats-talk/poster.jpg&quot; alt=&quot;Admin App Lightning Tour&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt;&lt;/media-poster&gt; &lt;/media-provider&gt; &lt;media-video-layout thumbnails=&quot;https://v.danny.is/shaky-bobcats-talk/storyboard.vtt&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt;&lt;/media-video-layout&gt; &lt;/media-player&gt; &lt;/template&gt; &lt;/div&gt; &lt;figcaption class=&quot;lcvid-meta&quot; data-astro-cid-73pxir2x&gt; &lt;span class=&quot;lcvid-detail&quot; data-astro-cid-73pxir2x&gt; &lt;span data-astro-cid-73pxir2x&gt;4m 23s&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt;·&lt;/span&gt; &lt;span data-astro-cid-73pxir2x&gt;6 July 2026&lt;/span&gt; &lt;/span&gt; &lt;a class=&quot;lcvid-open&quot; href=&quot;https://v.danny.is/shaky-bobcats-talk&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-astro-cid-73pxir2x&gt; &lt;span data-astro-cid-73pxir2x&gt;Open&lt;/span&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;12&quot; height=&quot;12&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt; &lt;path d=&quot;M15 3h6v6&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;path d=&quot;M10 14L21 3&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;/svg&gt; &lt;/a&gt; &lt;/figcaption&gt;   &lt;/figure&gt; 
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;a href=&quot;#wrapping-up&quot; aria-label=&quot;Link to “Wrapping up”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the time of writing, LoomClone feels pretty much feature-complete. I’ve still got a little work to do on audio post-processing &amp;amp; recording performance, but the only new features I’m considering are &lt;a href=&quot;https://github.com/dannysmith/loom-clone/issues/7&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;auto-expiry&lt;/a&gt; and &lt;a href=&quot;https://github.com/dannysmith/loom-clone/issues/5&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;watermarking&lt;/a&gt;, both of which are pretty trivial.&lt;/p&gt;
&lt;p&gt;By far the most interesting parts of this project are under-the-hood, but hopefully this introduction to &lt;strong&gt;what&lt;/strong&gt; LoomClone does will set the scene for the next few articles about &lt;strong&gt;how&lt;/strong&gt; it works.&lt;/p&gt;</content:encoded></item><item><title>Vibe coding an iOS app to publish notes here</title><link>https://danny.is/writing/vibe-coding-ios-app-chrome-extension-publish-notes/</link><guid isPermaLink="true">https://danny.is/writing/vibe-coding-ios-app-chrome-extension-publish-notes/</guid><pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I currently publish two kinds of content on this site: &lt;a href=&quot;/writing&quot;&gt;articles&lt;/a&gt; and &lt;a href=&quot;/notes&quot;&gt;notes&lt;/a&gt;. I write all my articles in &lt;a href=&quot;https://astroeditor.danny.is/&quot;&gt;Astro Editor&lt;/a&gt;, with in-progress work committed to &lt;code&gt;main&lt;/code&gt; as drafts and pushed to GitHub regularly. When I’m ready to publish an article I have Claude Code check for spelling errors, flip the draft toggle and – after checking it looks okay on my local dev server – commit and push. Pushes to &lt;code&gt;main&lt;/code&gt; trigger a GitHub action which rebuilds the static site and redeploys it.&lt;/p&gt;
&lt;p&gt;This process works great for articles, especially since I now keep two local copies of this repo: one for working on the site’s code (which is often not on &lt;code&gt;main&lt;/code&gt;) and one for working on content (which is always on &lt;code&gt;main&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;This also works great for notes &lt;a href=&quot;/notes/less-painful-editing-in-astro/&quot;&gt;like this one&lt;/a&gt; which are basically mini-articles, but it’s not ideal for &lt;a href=&quot;/notes/2026-06-06-the-end-of-responsive-images/&quot;&gt;ones like this&lt;/a&gt; which are responses to stuff I found online.&lt;/p&gt;
&lt;p&gt;When I find something interesting online I want to open Astro Editor, create a new note, paste the URL I found into &lt;code&gt;sourceURL&lt;/code&gt; and then type my comments… that’s way too much faff.&lt;/p&gt;
&lt;h2 id=&quot;theres-gotta-be-a-chrome-extension-for-that&quot;&gt;There’s gotta be a chrome extension for that?&lt;a href=&quot;#theres-gotta-be-a-chrome-extension-for-that&quot; aria-label=&quot;Link to “There’s gotta be a chrome extension for that?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I think &lt;a href=&quot;https://github.com/dannysmith/dannyischromeextension&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this&lt;/a&gt; was actually one of the first things I &lt;strong&gt;fully&lt;/strong&gt; vibe-coded. It’s a Chrome sidebar extension which grabs the URL and title of the current tab and lets me add some notes. Hitting &lt;strong&gt;save&lt;/strong&gt; writes a new draft note to my local copy of this site with appropriate frontmatter and filename.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-06-30-chrome-extension.DITnLrqg_Z2qYcKO.webp&quot; srcset=&quot;/_astro/2026-06-30-chrome-extension.DITnLrqg_eH0d8.webp 640w, /_astro/2026-06-30-chrome-extension.DITnLrqg_ZhhptT.webp 750w, /_astro/2026-06-30-chrome-extension.DITnLrqg_2bXOB7.webp 828w, /_astro/2026-06-30-chrome-extension.DITnLrqg_14D0Tj.webp 1080w, /_astro/2026-06-30-chrome-extension.DITnLrqg_1BwL86.webp 1280w, /_astro/2026-06-30-chrome-extension.DITnLrqg_Z10U6lQ.webp 1668w, /_astro/2026-06-30-chrome-extension.DITnLrqg_2cloP8.webp 2048w, /_astro/2026-06-30-chrome-extension.DITnLrqg_2gceXT.webp 2560w, /_astro/2026-06-30-chrome-extension.DITnLrqg_Z2qYcKO.webp 2568w&quot; alt=&quot;chrome-extension.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2568px) 2568px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2568&quot; height=&quot;1858&quot;&gt;
&lt;p&gt;The textarea supports markdown &amp;amp; keyboard shortcuts, and the &lt;strong&gt;Add Highlight&lt;/strong&gt; button grabs the selected text from the page and adds it to the textarea as a blockquote. This works &lt;em&gt;great&lt;/em&gt; when I wanna comment on multiple quotes from an article.&lt;/p&gt;
&lt;p&gt;Notes created this way obviously won’t go live until I’ve removed &lt;code&gt;draft: true&lt;/code&gt;, committed and pushed. I can live with that – it only takes a few seconds and if I forget, it’ll happen when I’m next in the repo.&lt;/p&gt;
&lt;p&gt;I use Astro Editor and this Chrome extension on my desktop machine, and both are designed to work with &lt;strong&gt;local files&lt;/strong&gt; on it. This works because I’ll always have a local copy of my website’s repo, and the tooling to quickly commit and push.&lt;/p&gt;
&lt;p&gt;This is not so on my iPhone…&lt;/p&gt;
&lt;h2 id=&quot;vibe-coding-my-first-personal-ios-app&quot;&gt;Vibe coding my first personal iOS app&lt;a href=&quot;#vibe-coding-my-first-personal-ios-app&quot; aria-label=&quot;Link to “Vibe coding my first personal iOS app”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I do not like writing on my phone and am never gonna use it to work on articles or long notes for this site. The closest I’m likely to get is the scrappy thoughts that might someday make it to this site, for which I use &lt;a href=&quot;https://obsidian.md/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Obsidian&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The only phone-based thing I wanna do re. this website is quickly create (and sometimes publish) &lt;em&gt;short&lt;/em&gt; notes about the thing I’m reading. &lt;strong&gt;And damn have I realised I wanna do that a lot.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I probably do eighty percent of my online reading on my phone and while I have a perfectly good system for clipping stuff into Obsidian, I almost never remember to share it here afterwards.&lt;/p&gt;
&lt;p&gt;What I need is something similar to my chrome extension, but on my phone. And since I don’t have a local copy of this website on my phone it will need to write directly to GitHub.&lt;/p&gt;
&lt;p&gt;So I (mostly) vibe-coded a little iOS app specifically for this…&lt;/p&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;How it works&lt;a href=&quot;#how-it-works&quot; aria-label=&quot;Link to “How it works”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The app ships with a &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/activity-views&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Share Sheet&lt;/a&gt; extension which lets me save a new draft note from pretty much any app. If there’s a URL or title available they’ll be passed to the app along with any notes I type into the share sheet. The notes field will be pre-populated with any selected text.&lt;/p&gt;
&lt;figure class=&quot;figure&quot; data-astro-cid-o2wyvkuk&gt; &lt;div class=&quot;image-wrapper&quot; data-astro-cid-o2wyvkuk&gt;  &lt;picture data-astro-cid-o2wyvkuk=&quot;true&quot;&gt; &lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_Y0e6K.avif 300w, /_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_ZbCtEO.avif 600w&quot; type=&quot;image/avif&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_1zmX6h.webp 300w, /_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_Z22Rcbi.webp 600w&quot; type=&quot;image/webp&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_Z1hg5Q6.jpg 300w, /_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_IQnRP.jpg 600w&quot; type=&quot;image/jpeg&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;  &lt;img src=&quot;/_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_Z1hg5Q6.jpg&quot; srcset=&quot;/_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_Z1hg5Q6.jpg 300w, /_astro/2026-06-30-ios-notes-sharesheet.B_2MUq09_IQnRP.jpg 600w&quot; alt=&quot;The Share Sheet extension with a notes field and source URL&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-cid-o2wyvkuk=&quot;true&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;650&quot;&gt; &lt;/picture&gt; &lt;/div&gt;  &lt;/figure&gt;
&lt;p&gt;Opening this note in the actual app looks like this. The &lt;em&gt;title&lt;/em&gt;, &lt;em&gt;sourceURL&lt;/em&gt; and &lt;em&gt;body&lt;/em&gt; fields have been properly populated and a suitable filename has been generated. We can also see a link preview for the source URL.&lt;/p&gt;
&lt;figure class=&quot;figure&quot; data-astro-cid-o2wyvkuk&gt; &lt;div class=&quot;image-wrapper&quot; data-astro-cid-o2wyvkuk&gt;  &lt;picture data-astro-cid-o2wyvkuk=&quot;true&quot;&gt; &lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_Z10y2iq.avif 300w, /_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_UsiYj.avif 600w&quot; type=&quot;image/avif&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_1R91p0.webp 300w, /_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_Z1jeoFi.webp 600w&quot; type=&quot;image/webp&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_ZKWWm.jpg 300w, /_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_1iGDLJ.jpg 600w&quot; type=&quot;image/jpeg&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;  &lt;img src=&quot;/_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_ZKWWm.jpg&quot; srcset=&quot;/_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_ZKWWm.jpg 300w, /_astro/2026-06-30-ios-notes-new-note-from-sharesheet.BDrZl_7I_1iGDLJ.jpg 600w&quot; alt=&quot;A new note in the app with title, sourceURL and body populated, showing a link preview&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-cid-o2wyvkuk=&quot;true&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;650&quot;&gt; &lt;/picture&gt; &lt;/div&gt;  &lt;/figure&gt;
&lt;p&gt;The body field supports simple markdown formatting and can be expanded for easier writing.&lt;/p&gt;
&lt;figure class=&quot;figure&quot; data-astro-cid-o2wyvkuk&gt; &lt;div class=&quot;image-wrapper&quot; data-astro-cid-o2wyvkuk&gt;  &lt;picture data-astro-cid-o2wyvkuk=&quot;true&quot;&gt; &lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-4.C3D0PjE6_Z1Xdixq.avif 300w, /_astro/2026-06-30-ios-notes-4.C3D0PjE6_1WBNhY.avif 600w&quot; type=&quot;image/avif&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-4.C3D0PjE6_Z1DWaYg.webp 300w, /_astro/2026-06-30-ios-notes-4.C3D0PjE6_1zpm1c.webp 600w&quot; type=&quot;image/webp&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-4.C3D0PjE6_YIx6G.jpg 300w, /_astro/2026-06-30-ios-notes-4.C3D0PjE6_2nqrVI.jpg 600w&quot; type=&quot;image/jpeg&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;  &lt;img src=&quot;/_astro/2026-06-30-ios-notes-4.C3D0PjE6_YIx6G.jpg&quot; srcset=&quot;/_astro/2026-06-30-ios-notes-4.C3D0PjE6_YIx6G.jpg 300w, /_astro/2026-06-30-ios-notes-4.C3D0PjE6_2nqrVI.jpg 600w&quot; alt=&quot;The expanded body field with markdown formatting&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-cid-o2wyvkuk=&quot;true&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;650&quot;&gt; &lt;/picture&gt; &lt;/div&gt;  &lt;/figure&gt;
&lt;p&gt;All new notes are created as &lt;strong&gt;Local Drafts&lt;/strong&gt; and stored as Swift data on the device. These can be created, edited and deleted in the app.&lt;/p&gt;
&lt;h3 id=&quot;getting-notes-to-github&quot;&gt;Getting notes to GitHub&lt;a href=&quot;#getting-notes-to-github&quot; aria-label=&quot;Link to “Getting notes to GitHub”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When the app loads, it fetches all my notes via the GitHub API, parses the relevant frontmatter and stores them in the app. &lt;strong&gt;Published&lt;/strong&gt; notes get a green label while &lt;strong&gt;unpublished&lt;/strong&gt; drafts get an orange one.&lt;/p&gt;
&lt;figure class=&quot;figure&quot; data-astro-cid-o2wyvkuk&gt; &lt;div class=&quot;image-wrapper&quot; data-astro-cid-o2wyvkuk&gt;  &lt;picture data-astro-cid-o2wyvkuk=&quot;true&quot;&gt; &lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-app-1.CVMxsi7__2a7L1D.avif 300w, /_astro/2026-06-30-ios-notes-app-1.CVMxsi7__ZSTHXv.avif 600w&quot; type=&quot;image/avif&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-app-1.CVMxsi7__ZgbHsy.webp 300w, /_astro/2026-06-30-ios-notes-app-1.CVMxsi7__arWp.webp 600w&quot; type=&quot;image/webp&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-app-1.CVMxsi7__Z2gFT6L.jpg 300w, /_astro/2026-06-30-ios-notes-app-1.CVMxsi7__Z1dLI8Y.jpg 600w&quot; type=&quot;image/jpeg&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;  &lt;img src=&quot;/_astro/2026-06-30-ios-notes-app-1.CVMxsi7__Z2gFT6L.jpg&quot; srcset=&quot;/_astro/2026-06-30-ios-notes-app-1.CVMxsi7__Z2gFT6L.jpg 300w, /_astro/2026-06-30-ios-notes-app-1.CVMxsi7__Z1dLI8Y.jpg 600w&quot; alt=&quot;The notes list with green labels for published notes and orange for drafts&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-cid-o2wyvkuk=&quot;true&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;650&quot;&gt; &lt;/picture&gt; &lt;/div&gt;  &lt;/figure&gt;
&lt;p&gt;When I’m done with a local draft I have two options: I can push it to GitHub as a draft or publish it immediately. In both cases the app uses the GitHub API to make a new commit adding the new note to the repo – &lt;em&gt;publish&lt;/em&gt; just removes &lt;code&gt;draft: true&lt;/code&gt; from the frontmatter.&lt;/p&gt;
&lt;p&gt;I originally planned to leave it at this but I can easily imagine pushing a new note and then spotting an error in it, so it’s also possible to edit and delete notes. Published notes can be “unpublished”, which makes a new commit adding &lt;code&gt;draft: true&lt;/code&gt; and unpublished drafts can be published by doing the reverse. Notes can also be permanently deleted via the app, but I can’t see myself doing that very often.&lt;/p&gt;
&lt;figure class=&quot;figure&quot; data-astro-cid-o2wyvkuk&gt; &lt;div class=&quot;image-wrapper&quot; data-astro-cid-o2wyvkuk&gt;  &lt;picture data-astro-cid-o2wyvkuk=&quot;true&quot;&gt; &lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_10jwxW.avif 300w, /_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_2rXnXP.avif 600w&quot; type=&quot;image/avif&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_Ls57S.webp 300w, /_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_uvAxJ.webp 600w&quot; type=&quot;image/webp&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;&lt;source srcset=&quot;/_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_rBymW.jpg 300w, /_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_zIJPD.jpg 600w&quot; type=&quot;image/jpeg&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot;&gt;  &lt;img src=&quot;/_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_rBymW.jpg&quot; srcset=&quot;/_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_rBymW.jpg 300w, /_astro/2026-06-30-ios-notes-3.BGZ_Tz6x_zIJPD.jpg 600w&quot; alt=&quot;Options to push, publish, edit or delete a note&quot; sizes=&quot;(min-width: 768px) 700px, 100vw&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-cid-o2wyvkuk=&quot;true&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;93&quot;&gt; &lt;/picture&gt; &lt;/div&gt;  &lt;/figure&gt;
&lt;p&gt;Any note which exists on GitHub and is later edited in the app is marked as having &lt;em&gt;local changes&lt;/em&gt; and I have the option to either update the note on GitHub with those changes or overwrite the local data with whatever is on GitHub. There’s also a tiny bit of cleverness to deal with notes which have been updated on GitHub and ensure the local data is updated on sync.&lt;/p&gt;
&lt;p&gt;Here’s the code if you wanna have a nose…&lt;/p&gt;
&lt;div class=&quot;bookmark-card cq &quot; data-astro-cid-tcads4ih&gt; &lt;a href=&quot;https://github.com/dannysmith/dannyis-notesapp-ios&quot; class=&quot;bookmark-link&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-astro-cid-tcads4ih&gt; &lt;div class=&quot;bookmark-content&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-title&quot; data-astro-cid-tcads4ih&gt;GitHub - dannysmith/dannyis-notesapp-ios: Vibe-coded personal iOS app. Experiment&lt;/span&gt; &lt;p class=&quot;bookmark-description&quot; data-astro-cid-tcads4ih&gt;Vibe-coded personal iOS app. Experiment. Contribute to dannysmith/dannyis-notesapp-ios development by creating an account on GitHub.&lt;/p&gt; &lt;span class=&quot;bookmark-meta&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-facts&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-fact bookmark-domain&quot; data-astro-cid-tcads4ih&gt; &lt;img class=&quot;bookmark-favicon&quot; src=&quot;/link-previews/v1-7aa6b7ff23a1e45e.webp&quot; alt=&quot;&quot; width=&quot;32&quot; height=&quot;32&quot; loading=&quot;lazy&quot; data-astro-cid-tcads4ih&gt; github.com &lt;/span&gt; &lt;span class=&quot;bookmark-fact&quot; data-astro-cid-tcads4ih&gt;  GitHub &lt;/span&gt; &lt;/span&gt;  &lt;/span&gt; &lt;/div&gt;  &lt;/a&gt; &lt;/div&gt;
&lt;h2 id=&quot;building-apps-for-one&quot;&gt;Building Apps for One&lt;a href=&quot;#building-apps-for-one&quot; aria-label=&quot;Link to “Building Apps for One”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The initial build of this took about two hours, most of which was me getting clear on what I actually wanted and adequately explaining that to Claude Code. I’ve probably spent another ninety minutes or so glancing over some of the code and iterating with Claude on the visual design.&lt;/p&gt;
&lt;p&gt;I’d probably never have bothered building this before AI models got good at coding, but if I had &lt;strong&gt;I would certainly have tried to make it usable for people other than me&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The biggest reason I was able to get this done so quickly is the number of decisions I didn’t need to make because this &lt;strong&gt;only needs to work for me&lt;/strong&gt;. All the info about my Astro site is hard-coded, as are the fields for my &lt;code&gt;notes&lt;/code&gt; schema. I auth with GitHub via a scoped Personal Access Token, not a complex OAuth flow. It’s only been tested on an iPhone 15 Pro because that’s the only device it’ll be used on. The UI is designed to my taste alone. And so on.&lt;/p&gt;
&lt;p&gt;But perhaps most exciting for me is that I’ve just built my first iOS app in under three hours. And while I only hand-wrote about four lines of code, the simplicity inherent in such a tightly-scoped &lt;em&gt;App for One&lt;/em&gt; means I’ve &lt;strong&gt;read and understood&lt;/strong&gt; most of it. That, plus working in a tight feedback loop with Claude Code has allowed me to learn a lot of new things very quickly here.&lt;/p&gt;</content:encoded></item><item><title>Making this Astro site more Agent-Friendly</title><link>https://danny.is/writing/making-this-astro-site-agent-friendly/</link><guid isPermaLink="true">https://danny.is/writing/making-this-astro-site-agent-friendly/</guid><pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For the last few months I’ve been tinkering with improving this site for AI agent &lt;em&gt;consumers&lt;/em&gt; and after reading &lt;a href=&quot;https://dacharycarey.com/2026/05/04/astro-removed-llms-txt/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Dachary Carey’s article on Astro’s removal of &lt;code&gt;llms.txt&lt;/code&gt;&lt;/a&gt; I went down a bit of a rabbit hole, finding &lt;a href=&quot;https://joost.blog/agent-ready/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Joost de Valk’s “agent-ready” piece&lt;/a&gt; along the way. Here’s the result of that little side-quest.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;While I deploy this site to Vercel right now, it’s important that it doesn’t &lt;strong&gt;depend&lt;/strong&gt; on Vercel’s build pipeline or features. I covered this in my &lt;a href=&quot;/writing/speeding-up-astro-builds&quot;&gt;last article&lt;/a&gt; but the main point is this: &lt;strong&gt;I want my static build to work on any server&lt;/strong&gt; – running &lt;code&gt;astro build&lt;/code&gt; should always produce a deployable static site in &lt;code&gt;dist/&lt;/code&gt;.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;existing-agent-friendly-features&quot;&gt;Existing “agent friendly” features&lt;a href=&quot;#existing-agent-friendly-features&quot; aria-label=&quot;Link to “Existing “agent friendly” features”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This site has always had stuff to help machines understand its content: it has sane, predictable URLs, seo-friendly meta tags and &lt;code&gt;&amp;lt;link rel=&amp;quot;canonical&amp;quot;&amp;gt;&lt;/code&gt;. A sitemap is generated by &lt;code&gt;@astrojs/sitemap&lt;/code&gt; and the three RSS feeds are advertised on every page. The markup is semantic and includes an appropriate sprinkling of &lt;a href=&quot;https://microformats.org/wiki/microformats2&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;microformats&lt;/a&gt; and &lt;a href=&quot;https://schema.org/docs/documents.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;schema.org&lt;/a&gt; stuff. All pages have sensible OG data and &lt;a href=&quot;/notes/nicer-cover-images/&quot;&gt;auto-generated OG images&lt;/a&gt;. I also publish &lt;a href=&quot;/notes/2026-06-04-this-site-on-atproto/&quot;&gt;machine-readable records over AT Protocol&lt;/a&gt;. Some of this is legit helpful for AI User Agents but over the last year I’ve also added stuff &lt;em&gt;specifically&lt;/em&gt; for them…&lt;/p&gt;
&lt;h3 id=&quot;markdown-versions-of-my-content&quot;&gt;Markdown versions of my content&lt;a href=&quot;#markdown-versions-of-my-content&quot; aria-label=&quot;Link to “Markdown versions of my content”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Every article and note has a static &lt;code&gt;.md&lt;/code&gt; version so appending &lt;code&gt;.md&lt;/code&gt; to any URL will return a markdown version of the page (&lt;a href=&quot;/writing/making-this-astro-site-agent-friendly.md&quot;&gt;try it for this article&lt;/a&gt;). I also &lt;a href=&quot;/notes/adding-share-features/&quot;&gt;include “copy / open as Markdown” buttons&lt;/a&gt; at the end of every post, though that’s mainly for &lt;em&gt;humans&lt;/em&gt; copypasta’ing into chat interfaces.&lt;/p&gt;
&lt;h3 id=&quot;an-llmstxt-index&quot;&gt;An &lt;code&gt;llms.txt&lt;/code&gt; index&lt;a href=&quot;#an-llmstxt-index&quot; aria-label=&quot;Link to “An llms.txt index”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I generate an &lt;a href=&quot;https://llmstxt.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/a&gt; at build time which includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A brief intro about me and this website.&lt;/li&gt;
&lt;li&gt;Links to important external identities (socials etc).&lt;/li&gt;
&lt;li&gt;What I’m doing now, pulled from my &lt;a href=&quot;/now&quot;&gt;now page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A list of all the &lt;a href=&quot;/writing&quot;&gt;articles&lt;/a&gt; and &lt;a href=&quot;/notes&quot;&gt;notes&lt;/a&gt; as markdown links.&lt;/li&gt;
&lt;li&gt;A list of &lt;a href=&quot;/notes/new-meta-pages&quot;&gt;other pages&lt;/a&gt;, including the &lt;a href=&quot;/styleguide&quot;&gt;styleguide&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see it live &lt;a href=&quot;/llms.txt&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;json-ld&quot;&gt;JSON-LD&lt;a href=&quot;#json-ld&quot; aria-label=&quot;Link to “JSON-LD”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I include a full &lt;a href=&quot;https://json-ld.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;JSON-LD&lt;/a&gt; &lt;code&gt;@graph&lt;/code&gt; of structured data in every page which includes &lt;code&gt;Person&lt;/code&gt;, &lt;code&gt;Organization&lt;/code&gt; and &lt;code&gt;WebSite&lt;/code&gt;, plus &lt;code&gt;BlogPosting&lt;/code&gt; for content pages.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;/&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;application/ld+json&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;quot;@context&amp;quot;: &amp;quot;https://schema.org&amp;quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;quot;@graph&amp;quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &amp;quot;@type&amp;quot;: &amp;quot;Person&amp;quot;, &amp;quot;@id&amp;quot;: &amp;quot;https://danny.is/#person&amp;quot; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &amp;quot;@type&amp;quot;: &amp;quot;WebSite&amp;quot;, &amp;quot;@id&amp;quot;: &amp;quot;https://danny.is/#website&amp;quot; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &amp;quot;@type&amp;quot;: &amp;quot;BlogPosting&amp;quot;, &amp;quot;headline&amp;quot;: &amp;quot;…&amp;quot;, &amp;quot;datePublished&amp;quot;: &amp;quot;…&amp;quot; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;script type=&amp;quot;application/ld+json&amp;quot;&gt;  {    &amp;quot;@context&amp;quot;: &amp;quot;https://schema.org&amp;quot;,    &amp;quot;@graph&amp;quot;: [      { &amp;quot;@type&amp;quot;: &amp;quot;Person&amp;quot;, &amp;quot;@id&amp;quot;: &amp;quot;https://danny.is/#person&amp;quot; },      { &amp;quot;@type&amp;quot;: &amp;quot;WebSite&amp;quot;, &amp;quot;@id&amp;quot;: &amp;quot;https://danny.is/#website&amp;quot; },      { &amp;quot;@type&amp;quot;: &amp;quot;BlogPosting&amp;quot;, &amp;quot;headline&amp;quot;: &amp;quot;…&amp;quot;, &amp;quot;datePublished&amp;quot;: &amp;quot;…&amp;quot; }    ]  }&lt;/script&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;auditing-for-agent-friendliness&quot;&gt;Auditing for Agent Friendliness&lt;a href=&quot;#auditing-for-agent-friendliness&quot; aria-label=&quot;Link to “Auditing for Agent Friendliness”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Dachary Carey’s &lt;a href=&quot;https://www.npmjs.com/package/afdocs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;afdocs&lt;/code&gt; tool&lt;/a&gt; checks against the spec at &lt;a href=&quot;https://agentdocsspec.com/spec/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;agentdocsspec.com&lt;/a&gt;, which is primarily designed to ensure &lt;em&gt;content&lt;/em&gt; is agent-readable and navigable. Cloudflare’s &lt;strong&gt;&lt;a href=&quot;https://isitagentready.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;isitagentready.com&lt;/a&gt;&lt;/strong&gt; is more focussed on conformity with emerging agent &lt;em&gt;discovery&lt;/em&gt; standards. Running &lt;code&gt;bunx afdocs check http://danny.is --verbose&lt;/code&gt; yielded &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/issues/127#issue-4635722651&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;these warnings &amp;amp; failures&lt;/a&gt;, while isitagentready.com gave me a pretty shit score and recommended a bunch of potential improvements.&lt;/p&gt;
&lt;p&gt;As with most automated audit tools, these are blunt instruments with very little context about this website. So the goal here isn’t improving scores, it’s using the data to actually improve the site.&lt;/p&gt;
&lt;h2 id=&quot;better-agent-friendliness&quot;&gt;Better Agent-Friendliness&lt;a href=&quot;#better-agent-friendliness&quot; aria-label=&quot;Link to “Better Agent-Friendliness”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s what I actually did off the back of these two audits:&lt;/p&gt;
&lt;h3 id=&quot;1-update-robotstxt&quot;&gt;1. Update &lt;code&gt;robots.txt&lt;/code&gt;&lt;a href=&quot;#1-update-robotstxt&quot; aria-label=&quot;Link to “1. Update robots.txt”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I updated my &lt;code&gt;robots.txt&lt;/code&gt; to use the &lt;a href=&quot;https://contentsignals.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Content Signals&lt;/a&gt; convention:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#f6f3ea&quot;&gt;User-Agent: *&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#f6f3ea&quot;&gt;Content-Signal: search=yes, ai-input=yes, ai-train=no&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#f6f3ea&quot;&gt;Allow: /&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;User-Agent: *Content-Signal: search=yes, ai-input=yes, ai-train=noAllow: /&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This is a preference signal, so the AI firms will probably ignore it.&lt;/p&gt;
&lt;h3 id=&quot;2-have-every-page-point-at-llmstxt&quot;&gt;2. Have every page point at &lt;code&gt;llms.txt&lt;/code&gt;&lt;a href=&quot;#2-have-every-page-point-at-llmstxt&quot; aria-label=&quot;Link to “2. Have every page point at llms.txt”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My &lt;code&gt;llms.txt&lt;/code&gt; exists purely to tell AI agents how to get what they need from this site, so it’s super important that they can &lt;strong&gt;actually find it&lt;/strong&gt;. The HTML for an article looks something like this:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;h-entry&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;itemscope&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;itemtype&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;https://schema.org/Article&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;head&gt;  ...&lt;/head&gt;&lt;body&gt;  &lt;main&gt;    &lt;article class=&amp;quot;h-entry&amp;quot; itemscope itemtype=&amp;quot;https://schema.org/Article&amp;quot; &gt;      ...    &lt;/article&gt;  &lt;/main&gt;&lt;/body&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Which means any agent using a &lt;code&gt;WebFetch&lt;/code&gt; tool that returns markdown is unlikely to see anything outside of &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;. If we want every fetch to include a pointer to &lt;code&gt;llms.txt&lt;/code&gt;, it’s not enough to have a link in our &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I now insert a &lt;code&gt;LLMDiscoveryNote.astro&lt;/code&gt; component immediately after the opening &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; on every page. It’s pretty simple:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;astro&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;sr-only&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;For AI agents and crawlers: a structured index of this site is available at&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;href&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;https://danny.is/llms.txt&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;https://danny.is/llms.txt&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;p class=&amp;quot;sr-only&amp;quot;&gt;  For AI agents and crawlers: a structured index of this site is available at  &lt;a href=&amp;quot;https://danny.is/llms.txt&amp;quot;&gt;https://danny.is/llms.txt&lt;/a&gt;.&lt;/p&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;The &lt;em&gt;“For AI agents and crawlers”&lt;/em&gt; isn’t just for agents and crawlers. It’ll be read aloud by screen readers so the text is clear about who it’s for. &lt;em&gt;If anyone knows of a reliable way to hide stuff from screen readers but not from crawlers/agents please yell.&lt;/em&gt;&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;We should do the same for our markdown versions. Let’s add this to the top of them all:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;md&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FFDC49;--0fs:italic&quot;&gt;&amp;gt; For the complete site index, see [&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;llms.txt&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49;--0fs:italic&quot;&gt;](&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0td:underline&quot;&gt;https://danny.is/llms.txt&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49;--0fs:italic&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&gt; For the complete site index, see [llms.txt](https://danny.is/llms.txt)&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-serve-markdown-to-uas-asking-for-it&quot;&gt;3. Serve markdown to UAs asking for it&lt;a href=&quot;#3-serve-markdown-to-uas-asking-for-it&quot; aria-label=&quot;Link to “3. Serve markdown to UAs asking for it”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since user agents can send an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Accept&lt;/code&gt; header&lt;/a&gt; with their requests and we already have markdown versions of our content, we should probably serve markdown to anyone requesting plain ol’ &lt;code&gt;/writing/&amp;lt;slug&amp;gt;&lt;/code&gt; URLs with an &lt;code&gt;Accept: text/markdown&lt;/code&gt; header. We can do this for both notes and articles with a little addition to our vercel config:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;jsonc&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;^/writing/([^/]+)/?$&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;has&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; [{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;accept&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.*text/markdown.*&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; }],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;dest&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/writing/$1.md&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;content-type&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;text/markdown; charset=utf-8&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{  &amp;quot;src&amp;quot;: &amp;quot;^/writing/([^/]+)/?$&amp;quot;,  &amp;quot;has&amp;quot;: [{ &amp;quot;type&amp;quot;: &amp;quot;header&amp;quot;, &amp;quot;key&amp;quot;: &amp;quot;accept&amp;quot;, &amp;quot;value&amp;quot;: &amp;quot;.*text/markdown.*&amp;quot; }],  &amp;quot;dest&amp;quot;: &amp;quot;/writing/$1.md&amp;quot;,  &amp;quot;headers&amp;quot;: { &amp;quot;content-type&amp;quot;: &amp;quot;text/markdown; charset=utf-8&amp;quot; }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Browsers never send &lt;code&gt;Accept: text/markdown&lt;/code&gt; so they’re unaffected by this, but we need to make sure “negotiable URLs” like this carry &lt;code&gt;Vary: Accept&lt;/code&gt; so CDNs don’t accidentally serve markdown when they shouldn’t.&lt;/p&gt;
&lt;p&gt;While we’re here, we can also respond to &lt;code&gt;Accept: text/markdown&lt;/code&gt; requests to the root of &lt;code&gt;danny.is&lt;/code&gt; with the contents of our &lt;code&gt;llms.txt&lt;/code&gt; rather than our homepage HTML.&lt;/p&gt;
&lt;h3 id=&quot;4-advertise-the-index-even-more&quot;&gt;4. Advertise the index Even More&lt;a href=&quot;#4-advertise-the-index-even-more&quot; aria-label=&quot;Link to “4. Advertise the index Even More”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since llms.txt is so important, let’s also add a &lt;code&gt;Link&lt;/code&gt; header to every response and a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to every HTML document:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#f6f3ea&quot;&gt;Link: &amp;lt;/llms.txt&amp;gt;; rel=&amp;quot;describedby&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;Link: &lt;/llms.txt&gt;; rel=&amp;quot;describedby&amp;quot;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;rel&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;describedby&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;text/markdown&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;href&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/llms.txt&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;link rel=&amp;quot;describedby&amp;quot; type=&amp;quot;text/markdown&amp;quot; href=&amp;quot;/llms.txt&amp;quot;&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;5-make-it-easier-to-discover-markdown-versions&quot;&gt;5. Make it easier to discover markdown versions&lt;a href=&quot;#5-make-it-easier-to-discover-markdown-versions&quot; aria-label=&quot;Link to “5. Make it easier to discover markdown versions”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our &lt;code&gt;llms.txt&lt;/code&gt; currently includes a big old list of markdown links to all our content, but the URLs point at the &lt;em&gt;HTML&lt;/em&gt; versions. It makes much more sense to use the &lt;code&gt;.md&lt;/code&gt; URLs in this context. So now the links in &lt;code&gt;llms.txt&lt;/code&gt; point to markdown versions if they exist.&lt;/p&gt;
&lt;p&gt;While we’re here, we should make sure content pages include a &lt;code&gt;&amp;lt;link rel=&amp;quot;alternate&amp;quot; type=&amp;quot;text/markdown&amp;quot; href=&amp;quot;&amp;lt;markdown version&amp;gt;.md&amp;quot;&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;6-turn-meta-refresh-redirects-into-propa-http-redirects&quot;&gt;6. Turn meta-refresh redirects into propa HTTP redirects&lt;a href=&quot;#6-turn-meta-refresh-redirects-into-propa-http-redirects&quot; aria-label=&quot;Link to “6. Turn meta-refresh redirects into propa HTTP redirects”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I use redirects in two places on this site:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Those hard-coded in &lt;code&gt;redirects.ts&lt;/code&gt; which forward URLs like &lt;a href=&quot;https://danny.is/meeting&quot;&gt;https://danny.is/meeting&lt;/a&gt; to external services.&lt;/li&gt;
&lt;li&gt;Articles with a &lt;code&gt;redirectURL&lt;/code&gt; frontmatter property which forward to content hosted elsewhere.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I use &lt;a href=&quot;https://docs.astro.build/en/guides/routing/#configured-redirects&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Astro redirects&lt;/a&gt; for the former and meta-refresh for the latter, though since this is a statically generated site they all end up as HTML meta-refresh pages in the generated &lt;code&gt;/dist&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can improve on this by having our &lt;code&gt;astro build&lt;/code&gt; &lt;em&gt;also&lt;/em&gt; generate a host-agnostic &lt;code&gt;dist/redirects.json&lt;/code&gt; containing &lt;strong&gt;all&lt;/strong&gt; of these, in this kinda form:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;redirects&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; [{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/meeting&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;destination&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;https://cal.com/dannysmith&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;302&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; }]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{  &amp;quot;version&amp;quot;: 1,  &amp;quot;redirects&amp;quot;: [{ &amp;quot;source&amp;quot;: &amp;quot;/meeting&amp;quot;, &amp;quot;destination&amp;quot;: &amp;quot;https://cal.com/dannysmith&amp;quot;, &amp;quot;status&amp;quot;: 302 }]}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then as part of our Vercel-specific deployment action we can use this to generate an appropriate &lt;a href=&quot;https://vercel.com/docs/build-output-api&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Build Output API&lt;/a&gt; config which returns proper &lt;code&gt;302&lt;/code&gt; redirects for these routes. The meta-refresh pages stay in the static build for the sake of portability, as does &lt;code&gt;dist/redirects.json&lt;/code&gt; which is the &lt;em&gt;source of truth&lt;/em&gt; about how server-side redirects should work.&lt;/p&gt;
&lt;h2 id=&quot;so-where-did-we-end-up&quot;&gt;So where did we end up?&lt;a href=&quot;#so-where-did-we-end-up&quot; aria-label=&quot;Link to “So where did we end up?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These little tweaks ended up making quite a difference to our audit metrics.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;bunx&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;afdocs&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;https://danny.is&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;--verbose&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;16&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;passed,&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;warnings,&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;failed,&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;skipped&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (23 &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;total&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;→ bunx afdocs check https://danny.is --verbose...16 passed, 3 warnings, 3 failed, 1 skipped (23 total)&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The remaining warnings &amp;amp; failures are all false positives as far as I’m concerned. The &lt;code&gt;isitagentready&lt;/code&gt; audit jumped from a score of ~20 to 43 overall, with all the things I care about now showing “green” (&lt;a href=&quot;https://isitagentready.com/danny.is&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;check it out&lt;/a&gt;).&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-06-19-isagentready.BlBC-qx__1iIETW.webp&quot; srcset=&quot;/_astro/2026-06-19-isagentready.BlBC-qx__276TpQ.webp 640w, /_astro/2026-06-19-isagentready.BlBC-qx__Z1dffc0.webp 750w, /_astro/2026-06-19-isagentready.BlBC-qx__1HmfwV.webp 828w, /_astro/2026-06-19-isagentready.BlBC-qx__1iIETW.webp 1000w&quot; alt=&quot;isitagentready.com showing danny.is at a score of 43, with the content and discovery checks I care about passing&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1000px) 1000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1000&quot; height=&quot;1011&quot;&gt;
&lt;p&gt;Most of the “failures” here have to do with API, Auth, MCP &amp;amp; Skill Discovery. Not only are these extremely new/immature standards, they’re also not very relevant to content-first websites like this one.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;a href=&quot;#wrapping-up&quot; aria-label=&quot;Link to “Wrapping up”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;None of this was difficult to do, but it did require a few minutes actually thinking about how agents consume content on a site like this. I think my biggest takeaway here is that it doesn’t really matter whether &lt;code&gt;/llms.txt&lt;/code&gt; is adopted as a standard by harness-makers, in the same way it doesn’t really matter about RSS vs Atom feeds. But it &lt;strong&gt;does&lt;/strong&gt; matter that I – as a website owner – do my best to signpost users appropriately so they know where to find stuff.&lt;/p&gt;</content:encoded></item><item><title>Speeding up Astro builds and improving deployment</title><link>https://danny.is/writing/speeding-up-astro-builds/</link><guid isPermaLink="true">https://danny.is/writing/speeding-up-astro-builds/</guid><pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I first started &lt;a href=&quot;/writing/moving-to-astro&quot;&gt;moving this site to Astro&lt;/a&gt; I decided that I wanted a &lt;em&gt;truly statically-generated site&lt;/em&gt;. In other words, I want &lt;code&gt;astro build&lt;/code&gt; to produce a folder of plain old files which can be served by &lt;strong&gt;any&lt;/strong&gt; webserver which can serve files. So while I currently deploy to &lt;a href=&quot;https://vercel.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vercel&lt;/a&gt;, nothing about the build &lt;strong&gt;depends&lt;/strong&gt; on Vercel. This means I can use a &lt;a href=&quot;https://github.com/features/actions&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Action&lt;/a&gt; to build the site and then dump the resultant &lt;code&gt;dist/&lt;/code&gt; on Vercel (or anywhere else I want).&lt;/p&gt;
&lt;p&gt;I’ve been doing a lot of work here recently and noticed two problems:&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;Slow builds&lt;/strong&gt;. Every time I push to main I have to wait for ages. Builds were taking upwards of five minutes from push to deploy, with &lt;code&gt;astro build&lt;/code&gt; alone taking &lt;strong&gt;4m 30s&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rubbish Caching&lt;/strong&gt;. Just dumping a static site meant Vercel was serving everything with &lt;code&gt;cache-control: public, max-age=0, must-revalidate&lt;/code&gt; headers, including content-hashed assets and static files like fonts. The lack of Vercel config was also preventing my custom 404 page from being served and there were a few other little bits which are only possible to fix by configuring how my files are &lt;em&gt;served by Vercel&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before looking at anything else we should see if we can reduce the weight of our build-time dependencies. Individually this is unlikely to make a huge difference to build time but it’s good hygiene and reduces supply-chain risks to boot.&lt;/p&gt;
&lt;p&gt;I ended up removing some old unused packages and moving a bunch to &lt;code&gt;devDependencies&lt;/code&gt; since they’re not needed at build-time. I replaced &lt;a href=&quot;https://www.npmjs.com/package/unfurl.js&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;unfurl.js&lt;/code&gt;&lt;/a&gt; with a small &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/blob/main/src/utils/fetchLinkMetadata.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;utility function&lt;/a&gt;, and noticed that while I was using &lt;a href=&quot;https://playwright.dev&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Playwright&lt;/a&gt; for end-to-end tests &amp;amp; rendering &lt;a href=&quot;https://mermaid.js.org&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mermaid&lt;/a&gt; diagrams, I was &lt;em&gt;also loading &lt;a href=&quot;https://pptr.dev&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;puppeteer&lt;/a&gt;&lt;/em&gt; to grab screenshots in a scraper script. Since puppeteer bundles Chromium that’s not ideal, so now I’m using playwright everywhere.&lt;/p&gt;
&lt;h2 id=&quot;speeding-up-the-astro-build-on-github&quot;&gt;Speeding up the Astro build on GitHub&lt;a href=&quot;#speeding-up-the-astro-build-on-github&quot; aria-label=&quot;Link to “Speeding up the Astro build on GitHub”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Three jobs run per push (all defined in &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/blob/main/.github/workflows/deploy.yml&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;deploy.yml&lt;/code&gt;&lt;/a&gt;): &lt;strong&gt;Check → Build → Deploy&lt;/strong&gt;. Measured on a real run against &lt;code&gt;main&lt;/code&gt; I got:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;The expensive bit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Check&lt;/td&gt;
&lt;td&gt;102s&lt;/td&gt;
&lt;td&gt;Playwright install ~23s, e2e tests ~30s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build&lt;/td&gt;
&lt;td&gt;313s (5m 13s)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;astro build&lt;/code&gt; was 4m 30s of it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy (prod)&lt;/td&gt;
&lt;td&gt;48s&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx vercel&lt;/code&gt; re-downloading the CLI took ~24s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Inside &lt;code&gt;astro build&lt;/code&gt;, two phases were the main culprits and both are cacheable across runs:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OG image generation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;92s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;167 images @ ~553ms average&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Image optimisation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2m 4s&lt;/strong&gt; (124s)&lt;/td&gt;
&lt;td&gt;312 transforms (avif + webp + jpg) from ~100 source images&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Together, these account for about 80% of the actual build time and ~70% of the Build job. On top of that, the &lt;strong&gt;build&lt;/strong&gt; job waited for the &lt;strong&gt;check&lt;/strong&gt; job to finish before starting, which added about 100s to every run.&lt;/p&gt;
&lt;h3 id=&quot;fix-1--cache-node_modulesastro&quot;&gt;Fix 1 — Cache &lt;code&gt;node_modules/.astro&lt;/code&gt;&lt;a href=&quot;#fix-1--cache-node_modulesastro&quot; aria-label=&quot;Link to “Fix 1 — Cache node_modules/.astro”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;GitHub Actions gives every job a clean machine. To carry files between runs we can use &lt;a href=&quot;https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;actions/cache&lt;/code&gt;&lt;/a&gt;, which takes a &lt;code&gt;path&lt;/code&gt; and a &lt;code&gt;key&lt;/code&gt; to store it under. On the next run, if a cache exists for that &lt;code&gt;key&lt;/code&gt; it gets restored to &lt;code&gt;path&lt;/code&gt;. I was already doing this for Playwright’s browsers, which are slow to download:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;/&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Cache Playwright&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;actions/cache@v4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;~/.cache/ms-playwright&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;playwright-${{ runner.os }}-${{ hashFiles(&amp;#39;**/bun.lock&amp;#39;) }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;restore-keys&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;playwright-${{ runner.os }}-&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;- name: Cache Playwright  uses: actions/cache@v4  with:    path: ~/.cache/ms-playwright    key: playwright-${{ runner.os }}-${{ hashFiles(&apos;**/bun.lock&apos;) }}    restore-keys: |      playwright-${{ runner.os }}-&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This is a common pattern: the key is a hash of &lt;code&gt;bun.lock&lt;/code&gt;, so the cache is reused until any of my dependencies change, at which point the key changes and fresh browsers are downloaded and re-cached. The &lt;code&gt;restore-keys&lt;/code&gt; line is a prefix fallback — if there’s no exact match, GitHub restores the most recent cache whose key &lt;em&gt;starts&lt;/em&gt; with &lt;code&gt;playwright-&amp;lt;os&amp;gt;-&lt;/code&gt;, so a dependency bump still starts from a recent cache rather than from nothing.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Astro&lt;/a&gt; keeps its own cache in &lt;code&gt;node_modules/.astro/&lt;/code&gt; and the useful part for us is how it handles images. Every optimised image is written out with a hash of the source file and its transform baked into the filename. So when Astro goes to optimise an image, if a file with that exact hash already exists, the work is already done and it skips it.&lt;/p&gt;
&lt;p&gt;Astro does this on disk but I wasn’t caching this across builds so every image was regenerated on every build. The fix is obvious: persist &lt;code&gt;node_modules/.astro/&lt;/code&gt; between runs with &lt;code&gt;actions/cache&lt;/code&gt;, like we did with Playwright. The only interesting question is &lt;strong&gt;what to key it on&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;It’s tempting to copy the Playwright pattern and key on a hash of the content (&lt;code&gt;hashFiles(&amp;#39;src/content/**&amp;#39;)&lt;/code&gt; or similar), but that throws away the &lt;em&gt;entire&lt;/em&gt; image cache the moment &lt;em&gt;any&lt;/em&gt; content changes, which is most pushes. So instead I’m keying on the commit SHA which is unique to every push. Combined with the &lt;code&gt;restore-keys&lt;/code&gt; prefix fallback, this means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every successful run writes a fresh cache snapshot under its own SHA (remember, keys are write-once).&lt;/li&gt;
&lt;li&gt;Every run restores the &lt;strong&gt;most recent&lt;/strong&gt; snapshot via the prefix, regardless of what changed.&lt;/li&gt;
&lt;li&gt;Astro’s own per-image hashing then re-processes only the images that actually changed.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Cache Astro build output&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;actions/cache@v4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;node_modules/.astro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;astro-cache-${{ runner.os }}-${{ github.sha }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;restore-keys&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;astro-cache-${{ runner.os }}-&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;- name: Cache Astro build output  uses: actions/cache@v4  with:    path: node_modules/.astro    key: astro-cache-${{ runner.os }}-${{ github.sha }}    restore-keys: |      astro-cache-${{ runner.os }}-&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This is the pattern &lt;a href=&quot;https://github.com/withastro/action&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;withastro/action&lt;/code&gt;&lt;/a&gt; uses and it drastically sped up my build times.&lt;/p&gt;
&lt;h3 id=&quot;fix-2--add-a-file-cache-to-og-image-generation&quot;&gt;Fix 2 — Add a file-cache to OG image generation&lt;a href=&quot;#fix-2--add-a-file-cache-to-og-image-generation&quot; aria-label=&quot;Link to “Fix 2 — Add a file-cache to OG image generation”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Astro’s content-addressed caching only applies to its own image optimisation, and my OG images are generated by an Astro &lt;em&gt;endpoint&lt;/em&gt; (a &lt;code&gt;.png&lt;/code&gt; route that renders each post’s social-share image with &lt;a href=&quot;https://github.com/vercel/satori&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;satori&lt;/a&gt; and &lt;a href=&quot;https://github.com/yisibl/resvg-js&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;resvg&lt;/a&gt;). Astro doesn’t cache this kind of thing, so even with &lt;code&gt;node_modules/.astro/&lt;/code&gt; cached on GH Actions, every build re-renders all the OG images from scratch.&lt;/p&gt;
&lt;p&gt;What if we also cached these files to &lt;code&gt;node_modules/.astro/&lt;/code&gt; in a similar way to Astro’s image cache?&lt;/p&gt;
&lt;p&gt;Before rendering, we hash everything that affects the image and then if a PNG already exists for that hash, we return it; otherwise we render it, write the PNG, and return it. Here’s the gist of &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/blob/main/src/utils/og-image-generator.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;og-image-generator.ts&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; CACHE_DIR &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; path.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(process.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;cwd&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;node_modules&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.astro&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;og-cache&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; CACHE_VERSION &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;v5&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// Content-addressed cache key: any change to the data, template, or&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// dimensions produces a different key, so edits regenerate the image.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; cacheKey &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;createHash&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;sha256&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;update&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;({ data, template, width, height, v&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; CACHE_VERSION }))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;digest&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;hex&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; cachePath &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; path.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(CACHE_DIR, &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;cacheKey&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.png`&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// Cache hit — return the previously rendered PNG.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;try&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; fs.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;readFile&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(cachePath);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// Miss (or unreadable) — fall through and generate.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// ... satori + resvg render ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// Cache the successful render&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;try&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; fs.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(CACHE_DIR, { recursive&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; fs.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;writeFile&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(cachePath, pngBuffer);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (cacheError) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;console.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;warn&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Failed to write OG image cache:&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, cacheError);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;const CACHE_DIR = path.join(process.cwd(), &apos;node_modules&apos;, &apos;.astro&apos;, &apos;og-cache&apos;);const CACHE_VERSION = &apos;v5&apos;;// Content-addressed cache key: any change to the data, template, or// dimensions produces a different key, so edits regenerate the image.const cacheKey = createHash(&apos;sha256&apos;)  .update(JSON.stringify({ data, template, width, height, v: CACHE_VERSION }))  .digest(&apos;hex&apos;);const cachePath = path.join(CACHE_DIR, `${cacheKey}.png`);// Cache hit — return the previously rendered PNG.try {  return await fs.readFile(cachePath);} catch {  // Miss (or unreadable) — fall through and generate.}// ... satori + resvg render ...// Cache the successful rendertry {  await fs.mkdir(CACHE_DIR, { recursive: true });  await fs.writeFile(cachePath, pngBuffer);} catch (cacheError) {  console.warn(&apos;Failed to write OG image cache:&apos;, cacheError);}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A few things make this safe to do:&lt;/p&gt;
&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;It’s only safe to content-hash because the render is deterministic.&lt;/strong&gt; I embed static TTF fonts straight into &lt;code&gt;satori({ fonts })&lt;/code&gt; rather than letting it fetch them, so identical inputs always produce a byte-identical PNG. If the fonts came over the network or from the system, the same inputs could render differently and a content hash would be a lie.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The key contains everything that affects the output&lt;/strong&gt; — &lt;code&gt;data&lt;/code&gt; (the post’s title, description, url and type), the template name, and the dimensions. Editing a post’s title means only that image is regenerated.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;CACHE_VERSION&lt;/code&gt; is a manual override.&lt;/strong&gt; Things that are the same for every image (template markup, branding etc) aren’t in the per-image key, so if I change how the OG images &lt;em&gt;look&lt;/em&gt; none of the keys will change and I’d end up serving the old cached versions. Bumping &lt;code&gt;CACHE_VERSION&lt;/code&gt; changes every key at once so I can just bump this to invalidate the whole lot.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Writes are best-effort and the fallback isn’t cached.&lt;/strong&gt; A failed cache write never fails the build, and if satori falls over and we render a simpler &lt;a href=&quot;https://sharp.pixelplumbing.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Sharp&lt;/a&gt;-generated fallback instead, that is deliberately &lt;em&gt;not&lt;/em&gt; cached.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;fix-3--run-check-and-build-in-parallel-and-only-deploy-after-both&quot;&gt;Fix 3 — Run Check and Build in parallel and only deploy after both&lt;a href=&quot;#fix-3--run-check-and-build-in-parallel-and-only-deploy-after-both&quot; aria-label=&quot;Link to “Fix 3 — Run Check and Build in parallel and only deploy after both”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;check&lt;/strong&gt; job just runs &lt;code&gt;bun run check:all&lt;/code&gt; which basically does this and fails if any checks fail:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;tsc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;--noEmit&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;astro&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;prettier&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;--check&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;eslint&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;vitest&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;tests/unit&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;playwright&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;tsc --noEmit &amp;amp;&amp;amp;astro check &amp;amp;&amp;amp;prettier --check . &amp;amp;&amp;amp;eslint . &amp;amp;&amp;amp;vitest run tests/unit &amp;amp;&amp;amp;playwright test&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;strong&gt;build&lt;/strong&gt; job just runs &lt;code&gt;bun run build&lt;/code&gt;, prepares an artifact for Vercel and uploads it using &lt;code&gt;actions/upload-artifact&lt;/code&gt;. The &lt;strong&gt;deploy&lt;/strong&gt; job grabs that artifact and drops it on Vercel.&lt;/p&gt;
&lt;p&gt;There’s no reason at all why &lt;strong&gt;check&lt;/strong&gt; and &lt;strong&gt;build&lt;/strong&gt; shouldn’t run in parallel, so long as &lt;strong&gt;deploy&lt;/strong&gt; waits for &lt;em&gt;both&lt;/em&gt; of them to finish successfully.&lt;/p&gt;
&lt;p&gt;Before: &lt;code&gt;checks → build → deploy&lt;/code&gt; (build waited on checks, ~100s idle). After: Check and Build run &lt;strong&gt;in parallel&lt;/strong&gt;, and Deploy waits on &lt;strong&gt;both&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;checks&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Starts immediately&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;         &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Starts immediately (dropped `needs: checks`)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;deploy-production&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;needs&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;checks&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;]      &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# both must pass before anything ships&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;github.ref == &amp;#39;refs/heads/main&amp;#39;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;jobs:  checks:        # Starts immediately  build:         # Starts immediately (dropped `needs: checks`)  deploy-production:    needs: [checks, build]      # both must pass before anything ships    if: github.ref == &apos;refs/heads/main&apos;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;fix-4--stop-npx-re-downloading-the-vercel-cli&quot;&gt;Fix 4 — Stop &lt;code&gt;npx&lt;/code&gt; re-downloading the Vercel CLI&lt;a href=&quot;#fix-4--stop-npx-re-downloading-the-vercel-cli&quot; aria-label=&quot;Link to “Fix 4 — Stop npx re-downloading the Vercel CLI”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;deploy&lt;/strong&gt; job ran &lt;code&gt;npx vercel&lt;/code&gt; which fetched a fresh copy of the Vercel CLI on every deploy (adding ~24s) because &lt;code&gt;ubuntu-latest&lt;/code&gt; no longer pre-bundles it. Pinning the CLI version in an env var and cacheing npm’s download cache means &lt;code&gt;npx&lt;/code&gt; resolves from that instead of re-downloading.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;VERCEL_CLI_VERSION&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;54.6.1&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# ...in each deploy job:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Cache Vercel CLI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;actions/cache@v4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;~/.npm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;vercel-cli-${{ runner.os }}-${{ env.VERCEL_CLI_VERSION }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Deploy to Vercel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;npx vercel@${{ env.VERCEL_CLI_VERSION }} deploy --prebuilt --prod --yes --token=${{ secrets.VERCEL_TOKEN }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;env:  VERCEL_CLI_VERSION: &apos;54.6.1&apos;# ...in each deploy job:- name: Cache Vercel CLI  uses: actions/cache@v4  with:    path: ~/.npm    key: vercel-cli-${{ runner.os }}-${{ env.VERCEL_CLI_VERSION }}- name: Deploy to Vercel  run: npx vercel@${{ env.VERCEL_CLI_VERSION }} deploy --prebuilt --prod --yes --token=${{ secrets.VERCEL_TOKEN }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;so-how-much-faster-did-things-get&quot;&gt;So how much faster did things get?&lt;a href=&quot;#so-how-much-faster-did-things-get&quot; aria-label=&quot;Link to “So how much faster did things get?”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Measured before &amp;amp; after on the Build job:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Cold (before)&lt;/th&gt;
&lt;th&gt;Warm (after)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Static entrypoints (HTML + OG)&lt;/td&gt;
&lt;td&gt;2m 17s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;48s&lt;/strong&gt; (OG cached)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image optimisation (Sharp)&lt;/td&gt;
&lt;td&gt;2m 4s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~80ms&lt;/strong&gt; (cached)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;astro build&lt;/code&gt; total&lt;/td&gt;
&lt;td&gt;4m 30s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;58.7s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build job wall-clock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4m 56s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1m 54s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;adding-some-light-vercel-config&quot;&gt;Adding some light Vercel config&lt;a href=&quot;#adding-some-light-vercel-config&quot; aria-label=&quot;Link to “Adding some light Vercel config”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because we’re deploying an already-built site, the &lt;strong&gt;deploy&lt;/strong&gt; job uses &lt;code&gt;vercel deploy --prebuilt&lt;/code&gt; to stop Vercel trying to run its own build. This means it won’t read &lt;code&gt;vercel.json&lt;/code&gt; – the only config consulted is &lt;code&gt;.vercel/output/config.json&lt;/code&gt; (the &lt;a href=&quot;https://vercel.com/docs/build-output-api&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Build Output API&lt;/a&gt;). By default this is basically empty, which is why my custom 404 page wasn’t being served and all my routes had &lt;code&gt;cache-control: public, max-age=0, must-revalidate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The solution is to include a lightweight &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/blob/main/vercel.output-config.json&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;vercel.output-config.json&lt;/code&gt;&lt;/a&gt; in the repo and have CI copy it to &lt;code&gt;.vercel/output/config.json&lt;/code&gt; at deploy time. Here’s the whole file, block by block. Each &lt;code&gt;&amp;quot;continue&amp;quot;: true&lt;/code&gt; rule &lt;em&gt;adds headers and keeps matching&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;^/_astro/(.*)$&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;cache-control&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;public, max-age=31536000, immutable&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; }, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;continue&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;^/fonts/(.*)$&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;cache-control&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;public, max-age=31536000, immutable&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; }, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;continue&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{ &amp;quot;src&amp;quot;: &amp;quot;^/_astro/(.*)$&amp;quot;,  &amp;quot;headers&amp;quot;: { &amp;quot;cache-control&amp;quot;: &amp;quot;public, max-age=31536000, immutable&amp;quot; }, &amp;quot;continue&amp;quot;: true },{ &amp;quot;src&amp;quot;: &amp;quot;^/fonts/(.*)$&amp;quot;,  &amp;quot;headers&amp;quot;: { &amp;quot;cache-control&amp;quot;: &amp;quot;public, max-age=31536000, immutable&amp;quot; }, &amp;quot;continue&amp;quot;: true },&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;/_astro/*&lt;/code&gt; contains content-hashed JS, CSS &amp;amp; images, and &lt;code&gt;/fonts/*&lt;/code&gt; contains version-stamped static font files. So our cache control can be &lt;code&gt;immutable&lt;/code&gt; with a 1 year &lt;code&gt;max-age&lt;/code&gt;. The filename changes every time the content does, which invalidates the cache. This alone is a huge performance boost for people viewing my site.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;^/(favicon|avatar|avatar-circle|icon|end-mark|og-default).*$&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;cache-control&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;continue&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{ &amp;quot;src&amp;quot;: &amp;quot;^/(favicon|avatar|avatar-circle|icon|end-mark|og-default).*$&amp;quot;,  &amp;quot;headers&amp;quot;: { &amp;quot;cache-control&amp;quot;: &amp;quot;public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400&amp;quot; },  &amp;quot;continue&amp;quot;: true },&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Unhashed static assets in &lt;code&gt;public/&lt;/code&gt; which get 1h for browsers and 1 day for edge and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#stale-while-revalidate&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SWR&lt;/a&gt; because their URLs don’t change with their content.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;^/.*&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.xml$&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;cache-control&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;public, max-age=600, s-maxage=3600, stale-while-revalidate=86400&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;continue&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{ &amp;quot;src&amp;quot;: &amp;quot;^/.*\\.xml$&amp;quot;,  &amp;quot;headers&amp;quot;: { &amp;quot;cache-control&amp;quot;: &amp;quot;public, max-age=600, s-maxage=3600, stale-while-revalidate=86400&amp;quot; },  &amp;quot;continue&amp;quot;: true },&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;RSS/feeds (&lt;code&gt;*.xml&lt;/code&gt;) get 10 mins for browsers and 1h for edge &amp;amp; SWR.&lt;/p&gt;
&lt;p&gt;If you’re wondering, &lt;code&gt;s-maxage&lt;/code&gt; is consumed by Vercel’s CDN and stripped from the responses sent to clients — so a browser only ever sees &lt;code&gt;public, max-age=…&lt;/code&gt;, while the edge gets the longer cache lifetime.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Redirects &amp;amp; rewrites:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;^/cv&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.pdf$&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;308&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;location&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/cv-danny-smith.pdf&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; } },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;^/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.well-known/(.*)$&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;dest&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/well-known/$1&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{ &amp;quot;src&amp;quot;: &amp;quot;^/cv\\.pdf$&amp;quot;, &amp;quot;status&amp;quot;: 308, &amp;quot;headers&amp;quot;: { &amp;quot;location&amp;quot;: &amp;quot;/cv-danny-smith.pdf&amp;quot; } },{ &amp;quot;src&amp;quot;: &amp;quot;^/\\.well-known/(.*)$&amp;quot;, &amp;quot;dest&amp;quot;: &amp;quot;/well-known/$1&amp;quot; }&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Redirecting &lt;code&gt;/cv.pdf&lt;/code&gt; to &lt;code&gt;/cv-danny-smith.pdf&lt;/code&gt; with a proper &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;308&lt;/a&gt; only matters because some user agents won’t follow other redirects if they point to binary files. We’ll come back to the &lt;code&gt;well-known&lt;/code&gt; line shortly.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;handle&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;filesystem&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/(.*)&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;404&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;dest&lt;/span&gt;&lt;span style=&quot;--0:#C4E4F2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/404.html&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{ &amp;quot;handle&amp;quot;: &amp;quot;filesystem&amp;quot; },{ &amp;quot;src&amp;quot;: &amp;quot;/(.*)&amp;quot;, &amp;quot;status&amp;quot;: 404, &amp;quot;dest&amp;quot;: &amp;quot;/404.html&amp;quot; }&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Adding &lt;code&gt;{ &amp;quot;handle&amp;quot;: &amp;quot;filesystem&amp;quot; }&lt;/code&gt; ensures that &lt;em&gt;any&lt;/em&gt; real file in &lt;code&gt;dist/&lt;/code&gt; is served, which we want with a static site. The last line ensures my custom &lt;code&gt;dist/404.html&lt;/code&gt; is returned with the proper status code for unknown routes.&lt;/p&gt;
&lt;h3 id=&quot;dealing-with-well-known-files&quot;&gt;Dealing with &lt;code&gt;.well-known&lt;/code&gt; files&lt;a href=&quot;#dealing-with-well-known-files&quot; aria-label=&quot;Link to “Dealing with .well-known files”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I want to serve &lt;a href=&quot;https://en.wikipedia.org/wiki/Well-known_URI&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;well-known URIs&lt;/a&gt; from &lt;code&gt;https://danny.is/.well-known/*&lt;/code&gt;. Right now that’s just a static &lt;code&gt;security.txt&lt;/code&gt; and a couple of other files I’ll discuss in future articles.&lt;/p&gt;
&lt;p&gt;It turns out that Vercel doesn’t serve dotfile directories so I can’t just generate these files in &lt;code&gt;dist/.well-known&lt;/code&gt; and be done with it. It also turns out that Astro won’t reliably build routes placed in a dotfile dir under &lt;code&gt;src/pages/&lt;/code&gt;. So I now keep my well-known stuff in two &lt;em&gt;non-dot&lt;/em&gt; directories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public/well-known/&lt;/code&gt; for static files.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/pages/well-known/&lt;/code&gt; for those which need build-time generation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Files from both end up in &lt;code&gt;dist/well-known/&lt;/code&gt; and the Vercel rewrite rule you saw above maps requests for &lt;code&gt;https://danny.is/.well-known/*&lt;/code&gt; onto &lt;code&gt;dist/well-known/*&lt;/code&gt; &lt;strong&gt;on Vercel&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;But even if Vercel won’t serve &lt;code&gt;dist/.well-known/&lt;/code&gt; other webservers will so they should be in the right place in my generated site in case I decide to move off Vercel. We can achieve this with a simple &lt;code&gt;postbuild&lt;/code&gt; script in &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/blob/main/package.json&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/a&gt; which runs&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;rm&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;-rf&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;dist/.well-known&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;cp&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;-R&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;dist/well-known&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;dist/.well-known&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;rm -rf dist/.well-known &amp;amp;&amp;amp; cp -R dist/well-known dist/.well-known&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Since this runs automatically after every &lt;code&gt;astro build&lt;/code&gt;, I always get a proper &lt;code&gt;.well-known/&lt;/code&gt; directory.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;a href=&quot;#wrapping-up&quot; aria-label=&quot;Link to “Wrapping up”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A typical content push now goes from commit to live in around two minutes instead of six, my assets finally cache the way they should, and my 404 page actually shows up. Best of all, none of this cost me the thing I cared about in the first place: &lt;code&gt;astro build&lt;/code&gt; still spits out a plain folder of files that’ll run on any webserver.&lt;/p&gt;</content:encoded></item><item><title>Some Work on this Site</title><link>https://danny.is/writing/2026-05-28-work-on-this-site/</link><guid isPermaLink="true">https://danny.is/writing/2026-05-28-work-on-this-site/</guid><description>A tour of the small, fiddly things I&apos;ve added to this site since moving to Astro — a homemade video player, nicer tables, markdown previews, file trees, a fourth typeface and a CSS rewrite underneath it all.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last autumn I &lt;a href=&quot;/writing/moving-to-astro&quot;&gt;wrote about&lt;/a&gt; moving this site to Astro and have shared a few notes about &lt;a href=&quot;/notes/adding-share-features&quot;&gt;little features&lt;/a&gt; I’ve added since then, but over the last few months I’ve picked away at a lot of little bits and pieces here which I haven’t really shown off. So I’m gonna do that now if you’ll indulge me for a minute.&lt;/p&gt;
&lt;h2 id=&quot;i-made-a-video-platform&quot;&gt;I made a video platform&lt;a href=&quot;#i-made-a-video-platform&quot; aria-label=&quot;Link to “I made a video platform”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve been building my own platform for recording and sharing videos (more on that in another post soon), so I can now add videos like this…&lt;/p&gt;
&lt;figure class=&quot;lcvid&quot; data-lcvid data-astro-cid-73pxir2x&gt;  &lt;div class=&quot;lcvid-player&quot; style=&quot;aspect-ratio: 1920 / 1080;&quot; data-astro-cid-73pxir2x&gt; &lt;button type=&quot;button&quot; class=&quot;lcvid-facade&quot; aria-label=&quot;Play: Videos on this site!&quot; data-astro-cid-73pxir2x&gt; &lt;img class=&quot;lcvid-poster&quot; src=&quot;https://v.danny.is/polite-phones-jam/poster.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; data-astro-cid-73pxir2x&gt; &lt;span class=&quot;lcvid-play&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt; &lt;svg viewBox=&quot;0 0 72 72&quot; data-astro-cid-73pxir2x&gt; &lt;circle cx=&quot;36&quot; cy=&quot;36&quot; r=&quot;35&quot; data-astro-cid-73pxir2x&gt;&lt;/circle&gt; &lt;path d=&quot;M29 23 L29 49 L51 36 Z&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;/svg&gt; &lt;/span&gt; &lt;span class=&quot;lcvid-duration-badge&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt;31s&lt;/span&gt; &lt;/button&gt; &lt;template class=&quot;lcvid-template&quot; data-astro-cid-73pxir2x&gt; &lt;media-player title=&quot;Videos on this site!&quot; poster=&quot;https://v.danny.is/polite-phones-jam/poster.jpg&quot; crossorigin=&quot;anonymous&quot; playsinline=&quot;true&quot; load=&quot;eager&quot; preload=&quot;auto&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt; &lt;media-provider data-astro-cid-73pxir2x=&quot;true&quot;&gt; &lt;source src=&quot;https://v.danny.is/polite-phones-jam/raw/1080p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;1920&quot; data-height=&quot;1080&quot; data-astro-cid-73pxir2x&gt;&lt;source src=&quot;https://v.danny.is/polite-phones-jam/raw/720p.mp4&quot; type=&quot;video/mp4&quot; data-width=&quot;1280&quot; data-height=&quot;720&quot; data-astro-cid-73pxir2x&gt; &lt;track src=&quot;https://v.danny.is/polite-phones-jam/captions.vtt&quot; kind=&quot;subtitles&quot; srclang=&quot;en&quot; label=&quot;English&quot; default data-astro-cid-73pxir2x&gt; &lt;media-poster class=&quot;vds-poster&quot; src=&quot;https://v.danny.is/polite-phones-jam/poster.jpg&quot; alt=&quot;Videos on this site!&quot; data-astro-cid-73pxir2x=&quot;true&quot;&gt;&lt;/media-poster&gt; &lt;/media-provider&gt; &lt;media-video-layout data-astro-cid-73pxir2x=&quot;true&quot;&gt;&lt;/media-video-layout&gt; &lt;/media-player&gt; &lt;/template&gt; &lt;/div&gt; &lt;figcaption class=&quot;lcvid-meta&quot; data-astro-cid-73pxir2x&gt; &lt;span class=&quot;lcvid-detail&quot; data-astro-cid-73pxir2x&gt; &lt;span data-astro-cid-73pxir2x&gt;31s&lt;/span&gt; &lt;span aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt;·&lt;/span&gt; &lt;span data-astro-cid-73pxir2x&gt;27 May 2026&lt;/span&gt; &lt;/span&gt; &lt;a class=&quot;lcvid-open&quot; href=&quot;https://v.danny.is/polite-phones-jam&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-astro-cid-73pxir2x&gt; &lt;span data-astro-cid-73pxir2x&gt;Open&lt;/span&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;12&quot; height=&quot;12&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot; data-astro-cid-73pxir2x&gt; &lt;path d=&quot;M15 3h6v6&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;path d=&quot;M10 14L21 3&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;path d=&quot;M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6&quot; data-astro-cid-73pxir2x&gt;&lt;/path&gt; &lt;/svg&gt; &lt;/a&gt; &lt;/figcaption&gt; &lt;div class=&quot;lcvid-description&quot; data-astro-cid-73pxir2x&gt; &lt;p data-astro-cid-73pxir2x&gt;Quick demo of a blog&amp;#39;s video embedding feature&lt;/p&gt; &lt;/div&gt;  &lt;/figure&gt; &lt;script type=&quot;module&quot; src=&quot;/home/runner/work/dannyis-astro/dannyis-astro/src/components/mdx/LCVid.astro?astro&amp;type=script&amp;index=0&amp;lang.ts&quot;&gt;&lt;/script&gt;
&lt;p&gt;The video platform makes embedding pretty easy&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;true&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; but I wanted videos I share here to feel like first-class citizens of &lt;em&gt;this&lt;/em&gt; site. Instead of rendering the video player from the video platform, my new &lt;code&gt;LCVid.astro&lt;/code&gt; component renders its own Vidstack player after grabbing the info it needs from the video platform via a JSON endpoint. This obviously includes the URLs needed to embed a Vidstack player (&lt;code&gt;1080p.mp4&lt;/code&gt;, &lt;code&gt;720p.mp4&lt;/code&gt;, &lt;code&gt;poster.jpg&lt;/code&gt;, &lt;code&gt;captions.srt&lt;/code&gt;, &lt;code&gt;storyboard.jpg&lt;/code&gt;, &lt;code&gt;storyboard.vtt&lt;/code&gt;), but since we do this at build-time we can also grab the title, description, length, transcript and cover image and use it to generate sensible HTML around the video player. I can use the component like this:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;/&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;md&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;LCVid&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;my-video-slug&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;LCVid src=&amp;quot;my-video-slug&amp;quot; /&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It supports the following props:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prop&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; Full URL, absolute path or bare slug.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override title.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override description.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aspectRatio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override aspect ratio.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;showTitle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render title above player.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;showDescription&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render description below player.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;showMeta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render duration and date.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;showOpenLink&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render “Open” link to the original video page.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;showTranscript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render transcript inside a collapsible &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;code-blocks--tables&quot;&gt;Code Blocks &amp;amp; Tables&lt;a href=&quot;#code-blocks--tables&quot; aria-label=&quot;Link to “Code Blocks &amp;amp; Tables”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As you just saw, I’ve also made some tweaks to how tables and code blocks are rendered. Tables just got a bit of CSS-love: they use tabular numerals, support captions and render various sorts of “header” nicely. Here’s an example:&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; role=&quot;region&quot; tabindex=&quot;0&quot; aria-label=&quot;Scrollable table&quot; data-astro-cid-u3bapfg7&gt; &lt;table class=&quot;astro-u3bapfg7&quot; data-astro-cid-u3bapfg7&gt;&lt;caption&gt;Table 1. The numbers are made up, they’re just here to show off tabular figures.&lt;/caption&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Plan&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Code&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Monthly&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Annual&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Hobby&lt;/td&gt;&lt;td&gt;&lt;code&gt;free&lt;/code&gt;&lt;/td&gt;&lt;td&gt;£0&lt;/td&gt;&lt;td&gt;£0&lt;/td&gt;&lt;td&gt;For tinkering on personal side projects.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Starter&lt;/td&gt;&lt;td&gt;&lt;code&gt;start-25&lt;/code&gt;&lt;/td&gt;&lt;td&gt;£9&lt;/td&gt;&lt;td&gt;£90&lt;/td&gt;&lt;td&gt;Everything a solo freelancer needs to begin.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th colspan=&quot;5&quot;&gt;For bigger teams&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Team&lt;/td&gt;&lt;td&gt;&lt;code&gt;team-99&lt;/code&gt;&lt;/td&gt;&lt;td&gt;£49&lt;/td&gt;&lt;td&gt;£490&lt;/td&gt;&lt;td&gt;Shared workspaces and roles for small teams.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Enterprise&lt;/td&gt;&lt;td&gt;&lt;code&gt;ent-x&lt;/code&gt;&lt;/td&gt;&lt;td&gt;£199&lt;/td&gt;&lt;td&gt;£1,990&lt;/td&gt;&lt;td&gt;Advanced controls, SSO and priority support included.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;/div&gt;
&lt;p&gt;Code blocks are rendered using &lt;a href=&quot;https://expressive-code.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Expressive Code&lt;/a&gt; which makes it easy to add titles and line highlights while still using markdown’s fenced code blocks. It supports a bunch of nice features including file titles and line highlights.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;expressive-code-demo.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;readingTime&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFA344;--0fs:italic&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; words &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; text.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;trim&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FF7B71&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;\s&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;--0:#FF7B71&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;).length;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#ee9ec9&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; Math.&lt;/span&gt;&lt;span style=&quot;--0:#7ec3b6&quot;&gt;ceil&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(words &lt;/span&gt;&lt;span style=&quot;--0:#ee9ec9&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#c5abe8&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;export function readingTime(text: string): number {  const words = text.trim().split(/\s+/).length;  return Math.ceil(words / 200);}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And terminal examples are rendered in a nice-looking frame…&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;bun&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;astro@latest&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;expressive-code-is-cool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;expressive-code-is-cool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;bun&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;bun create astro@latest expressive-code-is-coolcd expressive-code-is-coolbun run dev&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The color palette used for syntax highlighting took a little while to get right (and still isn’t perfect) but I hope it feels consistent &lt;em&gt;enough&lt;/em&gt; with the rest of this site for the time being.&lt;/p&gt;
&lt;h2 id=&quot;inline-styles-footnotes-lists--callouts&quot;&gt;Inline Styles, Footnotes, Lists &amp;amp; Callouts&lt;a href=&quot;#inline-styles-footnotes-lists--callouts&quot; aria-label=&quot;Link to “Inline Styles, Footnotes, Lists &amp;amp; Callouts”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Inline text styles got a little love recently too. Strikethroughs are rendered &lt;del&gt;like this right here&lt;/del&gt;, which is fun. Keyboard elements render as little 3D keys that depress on hover: &lt;kbd&gt;⌘&lt;/kbd&gt; + &lt;kbd&gt;Shift&lt;/kbd&gt; + &lt;kbd&gt;P&lt;/kbd&gt;. External links automatically get a little arrow, so &lt;a href=&quot;https://astro.build&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a link to Astro&lt;/a&gt; looks different from &lt;a href=&quot;/notes/&quot;&gt;a link to my notes&lt;/a&gt;. Abbreviations like &lt;abbr title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/abbr&gt; get a dotted underline and custom cursor. I’ve also made some very subtle tweaks to &lt;code&gt;code&lt;/code&gt;, &lt;em&gt;italic&lt;/em&gt;, &lt;strong&gt;bold&lt;/strong&gt; and &lt;mark&gt;highlight&lt;/mark&gt; styles.&lt;/p&gt;
&lt;p&gt;Footnotes&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;true&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; have also had a little refresh. Clicking a footnote will show it immediately below the current paragraph (try it 👆) and the client-side JS for this is only served if footnotes are present.&lt;/p&gt;
&lt;h3 id=&quot;lists-that-breathe&quot;&gt;Lists that breathe&lt;a href=&quot;#lists-that-breathe&quot; aria-label=&quot;Link to “Lists that breathe”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I use lists pretty heavily in my writing. Sometimes they contain short bullets like this…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Apples 🍎&lt;/li&gt;
&lt;li&gt;Oranges 🍊&lt;/li&gt;
&lt;li&gt;Pears 🍐&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But often each list item is much longer – more like a paragraph…&lt;/p&gt;
&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;Nonsense Item One&lt;/strong&gt;. &lt;em&gt;Ea qui sint ex eiusmod Lorem commodo dolor excepteur. Commodo non magna irure labore excepteur non culpa nulla aute consequat. Excepteur aliquip esse magna fugiat. Occaecat ad est in consectetur. Exercitation ex excepteur sit eiusmod cillum exercitation reprehenderit. Aliquip do enim sunt est amet pariatur.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nonsense Item Two&lt;/strong&gt;. &lt;em&gt;Commodo magna dolor proident pariatur cupidatat non esse proident irure laborum do culpa aliquip dolor. Sit velit culpa occaecat sunt officia. Reprehenderit duis adipisicing in incididunt nulla velit ipsum duis nostrud minim reprehenderit nisi culpa et ad.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nonsense Item Three&lt;/strong&gt;. &lt;em&gt;Tempor dolore do adipisicing minim eu eiusmod eu sint. Adipisicing incididunt elit eiusmod in consequat et eiusmod do. Eu sit sit nostrud eu eu dolor ipsum aliquip ullamco.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These should be treated differently when it comes to vertical rhythm: lists with longer content should be spaced like paragraphs (because they essentially &lt;em&gt;are&lt;/em&gt; paragraphs) whereas lists with short bullet points should have less leading between them. This has bugged me for ages, so I fixed it with a build-time plugin which measures the average length of a list’s items and decides how they should be vertically spaced. You can see this in action in the two lists above.&lt;/p&gt;
&lt;h3 id=&quot;slightly-better-callouts&quot;&gt;Slightly Better Callouts&lt;a href=&quot;#slightly-better-callouts&quot; aria-label=&quot;Link to “Slightly Better Callouts”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I covered callouts in &lt;a href=&quot;/writing/moving-to-astro&quot;&gt;&lt;em&gt;Moving this site to Astro&lt;/em&gt;&lt;/a&gt; but have made a couple of minor tweaks so they render more consistently in different contexts, particularly how icons are rendered.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;green&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;✅&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;This callout uses an emoji as its marker instead of an icon.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;aside aria-label=&quot;Heads up&quot; class=&quot;callout  &quot; data-callout-color=&quot;orange&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;&lt;svg width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 20 20&quot; data-astro-cid-lnlu5god=&quot;true&quot; data-icon=&quot;heroicons:exclamation-triangle-20-solid&quot;&gt;   &lt;symbol id=&quot;ai:heroicons:exclamation-triangle-20-solid&quot;&gt;&lt;path fill=&quot;currentColor&quot; fill-rule=&quot;evenodd&quot; d=&quot;M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625zM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5m0 9a1 1 0 1 0 0-2a1 1 0 0 0 0 2&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;/symbol&gt;&lt;use href=&quot;#ai:heroicons:exclamation-triangle-20-solid&quot;&gt;&lt;/use&gt;  &lt;/svg&gt;&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;h3 class=&quot;title&quot; data-astro-cid-lnlu5god&gt;Heads up&lt;/h3&gt; &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;And this one uses a Heroicon.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;new-fence-based-components&quot;&gt;New Fence-Based Components&lt;a href=&quot;#new-fence-based-components&quot; aria-label=&quot;Link to “New Fence-Based Components”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Historically, the majority of fenced code blocks in my writing have contained either actual code or terminal i/o – both of which are handled perfectly by Expressive Code. But as I work more with AI tools I’m increasingly finding my code blocks contain &lt;strong&gt;markdown&lt;/strong&gt; examples and &lt;strong&gt;file trees&lt;/strong&gt;. While both of these are perfectly readable as plain-text code blocks, I figured I could probably do better than that.&lt;/p&gt;
&lt;h3 id=&quot;markdown-blocks&quot;&gt;Markdown Blocks&lt;a href=&quot;#markdown-blocks&quot; aria-label=&quot;Link to “Markdown Blocks”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’ve added a new &lt;code&gt;MarkdownBlock.astro&lt;/code&gt; component which renders like this:&lt;/p&gt;
&lt;markdown-preview code=&quot;## An example of included markdown

This is an example of the kind of markdown file I often end up including in my content. The preview renders all the usual markdown syntax like **bold**, _italic_, `inline code`, and a
[link to example.com](https://example.com). Also lists, of course:

- one
- two
- three

And blockquotes:

&gt; A short quotation from elsewhere.

And also a load of other features which I really don&apos;t need to show you here.&quot; title=&quot;README.md&quot;&gt;&lt;/markdown-preview&gt;
&lt;p&gt;The component itself takes a markdown string, optional &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;defaultView&lt;/code&gt; and renders both a preview of the markdown &lt;em&gt;and&lt;/em&gt; the usual code block, with a toggle to flip between the two and a button to copy the markdown. A custom remark plugin renders this whenever I add &lt;code&gt;preview&lt;/code&gt; to a markdown-language fenced code block and I can optionally specify a title.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;md&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;```md preview title=&amp;quot;My Preview&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;```md preview title=&amp;quot;My Preview&amp;quot;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I particularly like this approach because these are still fenced code blocks and will continue to render as such on platforms like GitHub.&lt;/p&gt;
&lt;h3 id=&quot;file-trees&quot;&gt;File Trees&lt;a href=&quot;#file-trees&quot; aria-label=&quot;Link to “File Trees”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File trees in my content are likely to look something like this:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;content/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;articles/&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;                              &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# long-form writing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;   &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;2026-05-28-work-on-this-site.mdx&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;   &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# you are here&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;config.ts&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;                              &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# the collection schema&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;content/├── articles/                              # long-form writing│   └── 2026-05-28-work-on-this-site.mdx   # you are here└── config.ts                              # the collection schema&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When I first considered rendering these as &lt;strong&gt;nice HTML&lt;/strong&gt; I looked at Starlight’s &lt;a href=&quot;https://github.com/withastro/starlight/blob/main/packages/starlight/user-components/FileTree.astro&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;FileTree&lt;/code&gt;&lt;/a&gt; component, but it’s designed to expect a markdown ordered list and much like with markdown blocks, it’s important to me that file trees in my actual content are in &lt;code&gt;tree(1)&lt;/code&gt; format above… because that’s how I’m likely to write them, and because they’ll still work wherever else they’re rendered.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;FileTree.astro&lt;/code&gt; component and its associated remark plugin work much the same as the MarkdownBlock one. A fenced code block like this:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;md&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;```tree title=&amp;quot;My Thing&amp;quot; {12}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;content/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;├── assets/                                # Assets, innit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   ├── My Video.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   ├── pic.jpg                            # User&amp;#39;s avatar image&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   ├── song.mp3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   ├── data.json                          # Diagnostic data used by [Hal](https://en.wikipedia.org/wiki/HAL_9000).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   ├── robots.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   └── .config                            # Assets config file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;├── articles/                              # long-form writing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   ├── 2025-10-25-moving-to-astro.mdx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   ├── ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   └── 2026-05-28-work-on-this-site.mdx   # you are here&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;├── notes/                                 # short-form posts and links&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;│   └── adding-share-features.mdx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;└── config.ts                              # the collection schema&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;```&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;```tree title=&amp;quot;My Thing&amp;quot; {12}content/├── assets/                                # Assets, innit│   ├── My Video.mp4│   ├── pic.jpg                            # User&apos;s avatar image│   ├── song.mp3│   ├── data.json                          # Diagnostic data used by [Hal](https://en.wikipedia.org/wiki/HAL_9000).│   ├── robots.txt│   └── .config                            # Assets config file├── articles/                              # long-form writing│   ├── 2025-10-25-moving-to-astro.mdx│   ├── ...│   └── 2026-05-28-work-on-this-site.mdx   # you are here├── notes/                                 # short-form posts and links│   └── adding-share-features.mdx└── config.ts                              # the collection schema```&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;… will render an interactive component like this …&lt;/p&gt;
&lt;file-tree code=&quot;content/
├── assets/                                # Assets, innit
│   ├── My Video.mp4
│   ├── pic.jpg                            # User&apos;s avatar image
│   ├── song.mp3
│   ├── data.json                          # Diagnostic data used by [Hal](https://en.wikipedia.org/wiki/HAL_9000).
│   ├── robots.txt
│   └── .config                            # Assets config file
├── articles/                              # long-form writing
│   ├── 2025-10-25-moving-to-astro.mdx
│   ├── ...
│   └── 2026-05-28-work-on-this-site.mdx   # you are here
├── notes/                                 # short-form posts and links
│   └── adding-share-features.mdx
└── config.ts                              # the collection schema&quot; title=&quot;My Thing&quot; highlight=&quot;12&quot;&gt;&lt;/file-tree&gt;
&lt;p&gt;Some interesting points to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;title=&amp;quot;&amp;quot;&lt;/code&gt; and &lt;code&gt;{12}&lt;/code&gt; both mirror the conventions used by Expressive Code for setting a title and highlighting a line.&lt;/li&gt;
&lt;li&gt;There’s no client-side JS here – folders collapse using plain old &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; HTML elements.&lt;/li&gt;
&lt;li&gt;The parser is glyph-agnostic: &lt;code&gt;├──&lt;/code&gt;, &lt;code&gt;|--&lt;/code&gt;, &lt;code&gt;+--&lt;/code&gt;, tabs or spaces all parse the same way so long as they’re properly structured.&lt;/li&gt;
&lt;li&gt;Icons are coloured by file type and &lt;code&gt;# comments&lt;/code&gt; render as inline markdown (so links work in them).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;a-fourth-typeface&quot;&gt;A Fourth Typeface&lt;a href=&quot;#a-fourth-typeface&quot; aria-label=&quot;Link to “A Fourth Typeface”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I first settled on typefaces for this site I chose &lt;a href=&quot;/writing/website-redesign-v/&quot;&gt;Literata for articles&lt;/a&gt; and &lt;a href=&quot;/writing/website-redesign-xv#deciding-on-a-typeface&quot;&gt;Jost for everything else&lt;/a&gt;. While I’ve always been happy with that choice for bookish prose, my choice of Jost never quite sat right with me even after switching it out for &lt;em&gt;League Spartan&lt;/em&gt; some time ago. Eventually I realised this is because I have four distinct use cases here:&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;Long Form Prose&lt;/strong&gt;. I want my essays &amp;amp; articles set in a distinct “bookish” serif. Literata works perfectly for this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code&lt;/strong&gt;. Code examples and a few other bits should be set in a monospace font which is suited to the job. I’m still happy with Fira Code for this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Display Type&lt;/strong&gt;. The aesthetic of this site is &lt;a href=&quot;/writing/website-redesign-xv&quot;&gt;inspired&lt;/a&gt; by early twentieth century typographic design, so a somewhat-geometric sans-serif display face is important as a &lt;em&gt;design element&lt;/em&gt; for page headings and the like.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Other Copy &amp;amp; UI Stuff&lt;/strong&gt;. Copy which doesn’t fit into any of 👆 includes the (not-long-form) prose in my notes and other pages, as well as UI controls like buttons and menus.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’ve slowly come to realise that (3) and (4) are mutually exclusive. Any typeface that’s bold enough (and &lt;em&gt;Early London Underground Vibes&lt;/em&gt; enough) to work as the kinda display face I want is gonna be a poor face for rendering normal copy or UI controls, and vice versa. Even worse, any typeface which &lt;em&gt;kinda&lt;/em&gt; works for both is inevitably &lt;strong&gt;not good enough at either&lt;/strong&gt;. And so after considering a whole bunch of (mostly lovely) typefaces, I went with this:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Typeface&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Used for&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;In the wild&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th scope=&quot;row&quot; style=&quot;font-family: var(--font-display);&quot;&gt;Geist&lt;/th&gt;&lt;td&gt;Display stuff&lt;/td&gt;&lt;td style=&quot;font-family: var(--font-display); font-size: var(--font-size-md); font-weight: var(--font-weight-bold); letter-spacing: -0.01em;&quot;&gt;DANNY SMITH&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope=&quot;row&quot; style=&quot;font-family: var(--font-prose);&quot;&gt;Literata&lt;/th&gt;&lt;td&gt;Long-form prose&lt;/td&gt;&lt;td style=&quot;font-family: var(--font-prose);&quot;&gt;The article you’re reading rn.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope=&quot;row&quot; style=&quot;font-family: var(--font-ui);&quot;&gt;Figtree&lt;/th&gt;&lt;td&gt;UI and short-form prose&lt;/td&gt;&lt;td style=&quot;font-family: var(--font-ui);&quot;&gt;This is a sans-serif which works great for UI stuff and also for prose.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope=&quot;row&quot; style=&quot;font-family: var(--font-code);&quot;&gt;Fira Code&lt;/th&gt;&lt;td&gt;Monospaced &amp;amp; code&lt;/td&gt;&lt;td&gt;&lt;code&gt;const ok = x !== y;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;This hugely improves the readability of non-bookish body copy (mainly my &lt;a href=&quot;/notes/&quot;&gt;notes&lt;/a&gt;), while making stuff like headings more punchy.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;a href=&quot;#wrapping-up&quot; aria-label=&quot;Link to “Wrapping Up”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;💡&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;If you’re on a desktop machine, you might have noticed the table of contents to the left, which is also a new feature I enable for certain articles.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;Before I started on all this I took the time to rework my CSS to take advantage of all the awesome new stuff and simplify things as far as possible. This involved adopting layers, OKLCH colours, light-dark() theming and a bunch of other stuff which I may get round to writing about sometime. I also put together a proper styleguide-cum-component library to act as a reference for me when working on the design of this site. If you’re curious, the &lt;a href=&quot;/styleguide&quot;&gt;styleguide&lt;/a&gt; is public — though it’s really just for me.&lt;/p&gt;
&lt;p&gt;None of this stuff is at all game-changing for the tiny number of folks who visit this site, but there’s a real joy in doing this kinda work on my own little bit of the internet.&lt;/p&gt;
&lt;section data-footnotes=&quot;true&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a href=&quot;#footnote-label&quot; aria-label=&quot;Link to “Footnotes”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Appending &lt;code&gt;/embed&lt;/code&gt; to a video URL returns HTML specifically tailored for use in an iFrame – see &lt;a href=&quot;https://v.danny.is/polite-phones-jam/embed&quot;&gt;https://v.danny.is/polite-phones-jam/embed&lt;/a&gt; for an example. The site also has an &lt;a href=&quot;https://oembed.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;oEmbed&lt;/a&gt; endpoint and the standard video pages are set up so platforms like Notion can properly render placeholders when they are used for embedding. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;This is the footnote. On a page with JavaScript it expands inline, with a close button; without it, it’s just a regular jump-to-bottom link. &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Introducing Astro Editor</title><link>https://danny.is/writing/introducing-astro-editor/</link><guid isPermaLink="true">https://danny.is/writing/introducing-astro-editor/</guid><pubDate>Thu, 08 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve launched &lt;a href=&quot;https://astroeditor.danny.is/&quot;&gt;Astro Editor&lt;/a&gt;, a desktop app for authoring and editing Markdown and MDX content in local &lt;a href=&quot;https://astro.build/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Astro&lt;/a&gt; Content Collections! I’m actually a bit late with this blog post since I shipped 1.0.0 six weeks ago, but hey – better late than never!&lt;/p&gt;
&lt;h2 id=&quot;what-is-it&quot;&gt;What is it?&lt;a href=&quot;#what-is-it&quot; aria-label=&quot;Link to “What is it?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most Astro &lt;a href=&quot;https://docs.astro.build/en/guides/content-collections/#what-are-content-collections&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;content collections&lt;/a&gt; basically comprise two things: a folder containing markdown files with their &lt;em&gt;metadata&lt;/em&gt; as YAML frontmatter, and a Zod schema describing the structure that frontmatter should have. These content files live alongside your code.&lt;/p&gt;
&lt;p&gt;Astro Editor provides an interface for working with these markdown files which is designed for &lt;strong&gt;writing&lt;/strong&gt; not coding. The editor itself is a distractionless flow-state writing environment – it’s minimalist and gets out of your way. The frontmatter isn’t shown in the document, it appears as suitable UI components in a hideable sidebar. This shows both the frontmatter in the actual file &lt;strong&gt;and the available fields from the content collections schema&lt;/strong&gt;. Here’s a screenshot of me writing this article right now.&lt;/p&gt;
  &lt;img src=&quot;/_astro/2026-01-08-ae-screenshot-1.BWnuBAni_OdPkw.webp&quot; srcset=&quot;/_astro/2026-01-08-ae-screenshot-1.BWnuBAni_2m57ij.webp 640w, /_astro/2026-01-08-ae-screenshot-1.BWnuBAni_Z1wEyYL.webp 750w, /_astro/2026-01-08-ae-screenshot-1.BWnuBAni_1V319n.webp 828w, /_astro/2026-01-08-ae-screenshot-1.BWnuBAni_ZItanX.webp 1080w, /_astro/2026-01-08-ae-screenshot-1.BWnuBAni_2l0nfH.webp 1280w, /_astro/2026-01-08-ae-screenshot-1.BWnuBAni_1eKh3i.webp 1668w, /_astro/2026-01-08-ae-screenshot-1.BWnuBAni_2jbOvK.webp 2048w, /_astro/2026-01-08-ae-screenshot-1.BWnuBAni_Z1NRUqX.webp 2560w, /_astro/2026-01-08-ae-screenshot-1.BWnuBAni_OdPkw.webp 3840w&quot; alt=&quot;The Astro Editor app showing this article being written, with the frontmatter sidebar visible on the right&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 3840px) 3840px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;3840&quot; height=&quot;2160&quot;&gt;
&lt;p&gt;When I dragged that screenshot into the editor it was automatically copied to the right folder in my project and renamed appropriately before the markdown image tag was inserted. I can check it’s the right one by holding &lt;kbd&gt;⌥&lt;/kbd&gt; while hovering over the image tag, which shows a little preview. Astro Editor is full of little features like this, all of which are intended to let you focus on &lt;strong&gt;writing words&lt;/strong&gt; without getting distracted. You can see the main features on the &lt;a href=&quot;https://astroeditor.danny.is/&quot;&gt;website&lt;/a&gt; (more in the &lt;a href=&quot;https://github.com/dannysmith/astro-editor/blob/main/docs/user-guide.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;userguide&lt;/a&gt;). Here’s a full explanation and demo:&lt;/p&gt;
&lt;lite-youtube videoid=&quot;SJkXD59gQ5U&quot; style=&quot;background-image: url(&apos;https://i.ytimg.com/vi/SJkXD59gQ5U/hqdefault.jpg&apos;)&quot;&gt; &lt;a href=&quot;https://youtube.com/watch?v=SJkXD59gQ5U&quot; class=&quot;lyt-playbtn&quot;&gt; &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Play&lt;/span&gt; &lt;/a&gt; &lt;/lite-youtube&gt; &lt;script type=&quot;module&quot; src=&quot;/home/runner/work/dannyis-astro/dannyis-astro/node_modules/@astro-community/astro-embed-youtube/YouTube.astro?astro&amp;type=script&amp;index=0&amp;lang.ts&quot;&gt;&lt;/script&gt;         
&lt;h2 id=&quot;why&quot;&gt;Why?&lt;a href=&quot;#why&quot; aria-label=&quot;Link to “Why?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because I’ve wanted this myself for ages and the closest I ever got was &lt;a href=&quot;https://getdarkmatter.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Darkmatter&lt;/a&gt; – which I paid for and used regularly but never &lt;em&gt;quite&lt;/em&gt; did what I wanted.&lt;/p&gt;
&lt;h3 id=&quot;but-why&quot;&gt;But &lt;em&gt;why&lt;/em&gt;?&lt;a href=&quot;#but-why&quot; aria-label=&quot;Link to “But why?”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Most folks who publish content with Astro work in two distinct &lt;em&gt;modes&lt;/em&gt;. We’re in &lt;strong&gt;coder mode&lt;/strong&gt; when we’re editing Astro components, pages, CSS etc. This is best done in a &lt;em&gt;coding tool&lt;/em&gt; like VSCode. We’re in &lt;strong&gt;writer mode&lt;/strong&gt; when we’re writing or editing prose in markdown. Editors designed for coding are not well suited to this - they have too many distractions and lack the kinds of tools which help with writing and editing prose.&lt;/p&gt;
&lt;p&gt;Because of this, it’s common for people to &lt;strong&gt;write&lt;/strong&gt; in tools like &lt;a href=&quot;https://ia.net/writer&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;iA Writer&lt;/a&gt; or &lt;a href=&quot;https://obsidian.md/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Obsidian&lt;/a&gt; and then switch to VSCode to add frontmatter, build and publish. The workflow often looks something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new draft markdown file &amp;amp; start writing&lt;/li&gt;
&lt;li&gt;Edit and tweak (maybe over a number of sessions)&lt;/li&gt;
&lt;li&gt;Add frontmatter for things like description, tags etc&lt;/li&gt;
&lt;li&gt;Build &amp;amp; run locally to check everything works&lt;/li&gt;
&lt;li&gt;Push to github and deploy/publish&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Steps 1-3 are very much &lt;em&gt;writer mode&lt;/em&gt; tasks, while 4 &amp;amp; 5 are definitely &lt;em&gt;coder mode&lt;/em&gt; tasks. Astro Editor is only concerned with the former which means things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code blocks are not syntax highlighted because writing example code should be done in a code editor.&lt;/li&gt;
&lt;li&gt;You can’t commit or publish because that’s best done in a code editor or terminal.&lt;/li&gt;
&lt;li&gt;You can’t preview your writing because that’s best done in a browser while running your site on localhost.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And because the goal is &lt;strong&gt;simplicity when in writer mode&lt;/strong&gt;, Astro Editor is intentionally opinionated about its UI and limits the user customisation features to &lt;em&gt;“making it work with your Astro project and no more”&lt;/em&gt;. It’s not possible to customise colour schemes, typefaces etc.&lt;/p&gt;
&lt;h2 id=&quot;where-we-at-now&quot;&gt;Where we at now?&lt;a href=&quot;#where-we-at-now&quot; aria-label=&quot;Link to “Where we at now?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At time of writing &lt;a href=&quot;https://github.com/dannysmith/astro-editor/releases/tag/v1.0.5&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;1.0.5&lt;/a&gt; is stable for macOS and Windows and the GitHub repo has &lt;strong&gt;328 stars&lt;/strong&gt;. While I don’t collect analytics, I do get a ping whenever the auto-updater checks for a new version (which only happens automatically on launch). This includes only the currently installed version, the OS (&lt;code&gt;mac&lt;/code&gt; or &lt;code&gt;win&lt;/code&gt;) and a random UUID unique to each install. So I know that as of Thursday 8 January 2026:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are 276 unique installs, 264 of which are on macOS and 12 on Windows.&lt;/li&gt;
&lt;li&gt;Only 20 installs are pre-1.0.0&lt;/li&gt;
&lt;li&gt;14 people opened Astro Editor today (probably, including me).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More importantly though… I’m writing in it basically every day. And I &lt;strong&gt;really fucking enjoy it&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;so-whats-next&quot;&gt;So what’s Next?&lt;a href=&quot;#so-whats-next&quot; aria-label=&quot;Link to “So what’s Next?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Short answer: &lt;strong&gt;Finished Software&lt;/strong&gt;. After years working in VC-funded startups which by definition can never be “finished”, it’s weirdly exciting to look at a 6-month-old Astro Editor and think &lt;em&gt;“yeah I think it might be finished soon”&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I do still have one or two things on the &lt;a href=&quot;https://github.com/users/dannysmith/projects/5&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;roadmap&lt;/a&gt; though…&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;a href=&quot;https://github.com/dannysmith/astro-editor/issues/82&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;complete reworking&lt;/a&gt; of how project config is managed, including some new options to control &lt;a href=&quot;https://github.com/dannysmith/astro-editor/issues/87&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;file naming&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dannysmith/astro-editor/issues/69&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Internationalization&lt;/a&gt; and basic RTL support.&lt;/li&gt;
&lt;li&gt;Better &lt;a href=&quot;https://github.com/dannysmith/astro-editor/issues/10&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;quick search and internal link insertion&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And maybe better Linux support someday.&lt;/p&gt;
&lt;h2 id=&quot;like--subscribe-plz&quot;&gt;Like &amp;amp; Subscribe plz&lt;a href=&quot;#like--subscribe-plz&quot; aria-label=&quot;Link to “Like &amp;amp; Subscribe plz”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have an Astro site, please give Astro Editor a go. It’s only got to where it is now thanks to a bunch of folks trying it out and sharing feedback on &lt;a href=&quot;https://github.com/dannysmith/astro-editor/issues&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And as a free open-source side project, the only marketing it’s gonna get is from people who like it saying so on their socials &amp;amp; websites. So tell your mates. Plz 🙏&lt;/p&gt;
&lt;h2 id=&quot;quite-the-journey&quot;&gt;Quite The Journey&lt;a href=&quot;#quite-the-journey&quot; aria-label=&quot;Link to “Quite The Journey”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I didn’t mean to build anything proper here. In July ’25 I was just starting to &lt;em&gt;properly&lt;/em&gt; experiment with AI coding beyond Copilot &amp;amp; Cursor autocomplete and was looking for a project to use as a guinea pig. My basic requirements were:&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;It can’t be throwaway&lt;/strong&gt; because learning how to hold these new AI tools &lt;em&gt;properly&lt;/em&gt; meant more than blind vibe coding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It can’t be a toy&lt;/strong&gt; for the same reason.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It must be something I’ll use daily if it works&lt;/strong&gt; because otherwise I’d not have been invested in its quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It must have a GUI&lt;/strong&gt; because at the time LLMs were turbo-shit at UI design, which made this a key learning goal.&lt;/li&gt;
&lt;li&gt;Ideally, &lt;strong&gt;it’s designed to run locally&lt;/strong&gt; because any non-toy web app involves a bunch of third-party services to handle auth and emails and file storage and all the rest. Some of which cost money and all of which require me – the human – to manage them.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So I settled on &lt;em&gt;iA Writer but how I want it to write my blog&lt;/em&gt;, which ticked all the boxes. And duly dictated some nonsense requirements into a markdown file and had cursor turn it into what I thought would be a good PRD, which became Astro Editor’s &lt;a href=&quot;https://github.com/dannysmith/astro-editor/commit/69faa816914e415fba43fc07e363ab6bf2f87923&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;first commit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As an AI Coding noob the maybe-kinda-working app Cursor built from this felt like magic, but after a few hours going round in circles I actually read the code and realised it was (obviously) dogshit slop. And so began the very interesting process of working out how to do that better, which I’ll write about in another article.&lt;/p&gt;
&lt;p&gt;Fast forward a few months and I had a completely rewritten Astro Editor with a not-so-insane architecture, which both me and my new friend Claude could reason about well enough to iterate on features quickly and reliably. And on 18 Aug I shipped a feature-complete &lt;a href=&quot;https://github.com/dannysmith/astro-editor/releases/tag/v0.1.20&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;0.1.20&lt;/a&gt; and shared in the Astro Discord! 🎉&lt;/p&gt;
&lt;p&gt;Feature complete my arse.&lt;/p&gt;
&lt;p&gt;Turns out not everyone has the same Astro setup as me. So I spent September-November dealing with that, which involved a completely different approach to schema reading/parsing, lots of testing on weird and wonderful Astro projects and a smattering of new features.&lt;/p&gt;
&lt;p&gt;I &lt;a href=&quot;https://github.com/dannysmith/astro-editor/releases/tag/v1.0.0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;released 1.0.0&lt;/a&gt; on 22 November 2025.&lt;/p&gt;</content:encoded></item><item><title>Moving this site to Astro</title><link>https://danny.is/writing/moving-to-astro/</link><guid isPermaLink="true">https://danny.is/writing/moving-to-astro/</guid><pubDate>Sat, 25 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;intro-paragraph&quot; data-astro-cid-pmf5gjw4&gt; I started on a redesign of this site &lt;a href=&quot;/writing/website-redesign-i&quot;&gt;back in May 2020&lt;/a&gt; after deciding I wanted a simpler, content-first statically generated site. My main goals were to keep the core as simple as possible – writing in markdown and minimal boilerplate code – while allowing me to noodle around with new-fangled CSS &amp;amp; web-native stuff when I want to. &lt;/div&gt; &lt;script type=&quot;module&quot; src=&quot;/home/runner/work/dannyis-astro/dannyis-astro/src/components/mdx/IntroParagraph.astro?astro&amp;type=script&amp;index=0&amp;lang.ts&quot;&gt;&lt;/script&gt;
&lt;p&gt;After about three years running on Jekyll I started to get frustrated at some of the stuff I couldn’t do easily - primarily the difficulty of working with custom components in markdown. I was using some kinda-janky markdown pre-parsers to let me insert things like callouts…&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;/&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#f6f3ea&quot;&gt;{:.callout}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#f6f3ea&quot;&gt;This is a callout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{:.callout}This is a callout&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Which wasn’t exactly easy to work with and more importantly, if I ever decided to switch away from Jekyll and export all my markdown files, wasn’t exactly obvious or intuitive.&lt;/p&gt;
&lt;p&gt;What I really wanted was the ability to use HTML- or React-like components in my markdown. And as with web components or react components, I wanted to define these as self-contained components with scoped CSS and the like.&lt;/p&gt;
&lt;p&gt;So in early 2023 I started playing with &lt;a href=&quot;https://astro.build/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AstroJS&lt;/a&gt;. The most appealing thing about it was the native support for &lt;a href=&quot;https://mdxjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MDX&lt;/a&gt;, its React-like component architecture where &lt;code&gt;.astro&lt;/code&gt; files contain scoped HTML, CSS and JS, and the relatively small amount of boilerplate needed to get a blog up-and-running.&lt;/p&gt;
&lt;p&gt;I quietly ported this site over to Astro, but with the release of Astro 3.0 in August ’23 so much had changed that it made more sense to start again from scratch. So I switched to the &lt;a href=&quot;https://github.com/dannysmith/dannyis-astro/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;current codebase&lt;/a&gt; around the time Astro 4.0 dropped in December ’23.&lt;/p&gt;
&lt;h2 id=&quot;why-astro&quot;&gt;Why Astro?&lt;a href=&quot;#why-astro&quot; aria-label=&quot;Link to “Why Astro?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;modern-simple--content-first&quot;&gt;Modern, Simple &amp;amp; Content-First&lt;a href=&quot;#modern-simple--content-first&quot; aria-label=&quot;Link to “Modern, Simple &amp;amp; Content-First”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Astro is very well designed, and because it’s built with a modern TypeScript-based stack, it comes with all the advantages you’d expect (typesafe javascript among them).&lt;/p&gt;
&lt;p&gt;When used as an SSG, Astro is also very simple. File-based routing, markdown parsing and YAML frontmatter Just Work™ and stuff like image optimisation come out-of-the-box. And while composing pages from astro components &lt;em&gt;feels&lt;/em&gt; very Reactish, it’s “zero JavaScript by default” – the generated site will only include client-side JS when necessary, which for this site is hardly ever.&lt;/p&gt;
&lt;p&gt;Astro has an extremely strong ecosystem of plugins, tools and themes around it.&lt;/p&gt;
&lt;h3 id=&quot;astro-components--mdx&quot;&gt;Astro Components &amp;amp; MDX&lt;a href=&quot;#astro-components--mdx&quot; aria-label=&quot;Link to “Astro Components &amp;amp; MDX”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.astro.build/en/basics/astro-components/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Components&lt;/a&gt; can be anything from a tiny snippet of HTML to a reusable UI component (which takes props and/or content) to a whole page layout. Here’s an &lt;code&gt;example.astro&lt;/code&gt; component:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;const { title } = Astro.props;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;const theNumber = Math.random();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;mything&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;{title}&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;Number: {theNumber}&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;red&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;is:inline&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;alert&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Hello&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;---const { title } = Astro.props;const theNumber = Math.random();---&lt;section class=&amp;quot;mything&amp;quot;&gt;  &lt;h1&gt;{title}&lt;/h1&gt;  &lt;p&gt;Number: {theNumber}&lt;/p&gt;&lt;/section&gt;&lt;style&gt;  h1 {    color: red;  }&lt;/style&gt;&lt;script is:inline&gt;  alert(&apos;Hello&apos;);&lt;/script&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;The TypeScript in the &lt;em&gt;component script&lt;/em&gt; at the top runs only at build time. The ability to co-locate this “server-side” code with the component is useful for keeping things organised.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;component template&lt;/em&gt; (below the last &lt;code&gt;---&lt;/code&gt;) is the HTML which will be rendered when the component is used, except (much like JSX) you can use variables defined in the &lt;em&gt;component script&lt;/em&gt;, or other Astro components imported in it.&lt;/li&gt;
&lt;li&gt;The CSS in the style tag is scoped to this astro component only.&lt;/li&gt;
&lt;li&gt;The script tag at the bottom uses a special astro directive, and will be executed client-side.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Building a site using Astro components is much the same as using React components (or &lt;em&gt;partials&lt;/em&gt; in other SSGs). But the real power is using them &lt;strong&gt;inside Markdown Content&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;mdx-components&quot;&gt;MDX Components&lt;a href=&quot;#mdx-components&quot; aria-label=&quot;Link to “MDX Components”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://mdxjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MDX&lt;/a&gt; lets you use JSX inside markdown files, and is one of the main reasons I chose Astro over other SSGs. In Astro, you can import &lt;code&gt;.astro&lt;/code&gt; components into &lt;code&gt;mdx&lt;/code&gt; files and use them in the same way you can anywhere else. I’ve ended up with quite a lot of &lt;em&gt;really simple&lt;/em&gt; components which just render some HTML and apply some scoped CSS to it. Here’s my &lt;code&gt;highlight.astro&lt;/code&gt; component:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;highlight&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;slot&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B71;--0fs:italic;--0td:underline&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.highlight&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-highlight-bg);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border-radius&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;span class=&amp;quot;highlight&amp;quot;&gt;&lt;slot /&gt;&lt;/span&gt;&lt;style&gt;  .highlight {    background: var(--color-highlight-bg);    padding: 0 8px 4px;    border-radius: 4px;  }&lt;/style&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And now when I’m writing in markdown I can use it like this:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;md&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;Here is some text &amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;highlight&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;that is highlighted&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;highlight&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;Here is some text &lt;highlight&gt;that is highlighted&lt;/highlight&gt;.&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And with more complex components like callouts…&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;md&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;Callout&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;emoji&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;💡&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;blue&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum hic a dolorem ipsa labore cum.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;Callout&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;Callout emoji=&amp;quot;💡&amp;quot; type=&amp;quot;blue&amp;quot;&gt;  Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum hic a dolorem ipsa labore cum.&lt;/Callout&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;…which renders…&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;blue&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;💡&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum hic a dolorem ipsa labore cum.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;The combination of Astro’s component-based architecture and &lt;em&gt;components-in-MDX-files&lt;/em&gt; ticks some important boxes for me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Components make for easy maintenance long term, especially when dealing with CSS.&lt;/li&gt;
&lt;li&gt;Scoped CSS and JavaScript mean I can iterate on styles without worrying about breaking other parts of the site.&lt;/li&gt;
&lt;li&gt;Using components in Markdown makes it extremely easy and quick to write fully featured content.&lt;/li&gt;
&lt;li&gt;If I ever want to switch away from Astro, the structure of Astro components and how they’re used in Markdown files should make it fairly easy to port my content to another system.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;so-where-am-i-now&quot;&gt;So Where Am I Now?&lt;a href=&quot;#so-where-am-i-now&quot; aria-label=&quot;Link to “So Where Am I Now?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Two years into running this site on Astro, I’ve settled into a setup that gives me everything I need without much fuss.&lt;/p&gt;
&lt;h3 id=&quot;content-collections&quot;&gt;Content Collections&lt;a href=&quot;#content-collections&quot; aria-label=&quot;Link to “Content Collections”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The site uses two main collections: &lt;strong&gt;Articles&lt;/strong&gt; and &lt;strong&gt;Notes&lt;/strong&gt;. Articles are long-form pieces like this one – they live in &lt;code&gt;/writing/&lt;/code&gt; and support cover images, tags, and a bunch of other metadata. Notes are shorter, usually links to interesting things I’ve found with a bit of commentary. They’re deliberately minimal: just a title, optional source URL, and publication date. Both use the same filename pattern (&lt;code&gt;YYYY-MM-DD-slug.mdx&lt;/code&gt;) which keeps things organised and makes it dead simple to create new content.&lt;/p&gt;
&lt;h3 id=&quot;plugins&quot;&gt;Plugins&lt;a href=&quot;#plugins&quot; aria-label=&quot;Link to “Plugins”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’m using a handful of plugins that handle the boring stuff so I don’t have to think about it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Expressive Code&lt;/strong&gt; handles syntax highlighting for code blocks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Astro Icon&lt;/strong&gt; lets me use SVG icons without manually importing and managing them. I can just drop &lt;code&gt;&amp;lt;Icon name=&amp;quot;heroicons:home&amp;quot; /&amp;gt;&lt;/code&gt; into any component.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sitemap&lt;/strong&gt; generates a sitemap.xml automatically, which is one of those things that’s trivial but annoying to maintain by hand.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the markdown processing side, I’m using a few rehype plugins:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;rehype-autolink-headings&lt;/strong&gt; adds anchor links to headings (so you can link to specific sections)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;rehype-external-links&lt;/strong&gt; automatically opens external links in new tabs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;rehype-mermaid&lt;/strong&gt; renders mermaid diagrams at build time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ve also got a custom remark plugin that calculates reading time for articles.&lt;/p&gt;
&lt;h3 id=&quot;components&quot;&gt;Components&lt;a href=&quot;#components&quot; aria-label=&quot;Link to “Components”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’ve got about fifteen components in &lt;code&gt;src/components/mdx/&lt;/code&gt; – things like &lt;code&gt;&amp;lt;Callout&amp;gt;&lt;/code&gt; for highlighted notes, &lt;code&gt;&amp;lt;Accordion&amp;gt;&lt;/code&gt; for collapsible sections, &lt;code&gt;&amp;lt;BookmarkCard&amp;gt;&lt;/code&gt; for pretty link cards, and &lt;code&gt;&amp;lt;Notion&amp;gt;&lt;/code&gt; for embedding Notion links. Nothing fancy – just small bits of HTML with scoped styles.&lt;/p&gt;
&lt;p&gt;The page layouts are simple – components like &lt;code&gt;BaseHead&lt;/code&gt;, &lt;code&gt;MainNavigation&lt;/code&gt;, and &lt;code&gt;Footer&lt;/code&gt; get composed into layout templates like &lt;code&gt;Article.astro&lt;/code&gt; and &lt;code&gt;Note.astro&lt;/code&gt;, which wrap the markdown content. The whole design system is based on CSS custom properties defined in &lt;code&gt;global.css&lt;/code&gt;, so switching between light and dark themes (or tweaking colours) is just a matter of changing a few variable values.&lt;/p&gt;
&lt;h2 id=&quot;future-plans&quot;&gt;Future Plans&lt;a href=&quot;#future-plans&quot; aria-label=&quot;Link to “Future Plans”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are some notes &amp;amp; ideas &lt;a href=&quot;https://github.com/users/dannysmith/projects/1&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;on GitHub&lt;/a&gt;, but I’m not in a particular rush to ship any of it. I’d rather spend my time working on &lt;a href=&quot;http://astroeditor.danny.is/&quot;&gt;Astro Editor&lt;/a&gt; or using it to write stuff here.&lt;/p&gt;</content:encoded></item><item><title>AI and ADHD</title><link>https://danny.is/writing/ai-and-adhd/</link><guid isPermaLink="true">https://danny.is/writing/ai-and-adhd/</guid><description>Thoughts on ADHD and Artificial Intelligence</description><pubDate>Thu, 10 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;intro-paragraph&quot; data-astro-cid-pmf5gjw4&gt; My friend Vicki’s “mate have you considered maybe youse got ADHD” was the first time I remember this topic coming up. That was six years back and I promptly forgot about it. Then two years ago – while ADHD was all the rage on TikTok – my friend Janina had a period of revelation about herself and made me do a bunch of tests. Turns out I probably have Adult ADHD. &lt;/div&gt; &lt;script type=&quot;module&quot; src=&quot;/home/runner/work/dannyis-astro/dannyis-astro/src/components/mdx/IntroParagraph.astro?astro&amp;type=script&amp;index=0&amp;lang.ts&quot;&gt;&lt;/script&gt;
&lt;p&gt;I’m writing this because I read a couple of posts this week which got me thinking about ADHD again, and because I’ve been knee-deep in AI-related things for the last month. The article below is a response to DHH’s &lt;a href=&quot;https://web.archive.org/web/20241121071802/https://world.hey.com/dhh/cold-reading-an-adhd-affliction-44163793&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rant&lt;/a&gt; about ADHD, which I remember skimming last year. I only just found Schneems’ response, which had me in stitches more than once…&lt;/p&gt;
&lt;div class=&quot;bookmark-card cq &quot; data-astro-cid-tcads4ih&gt; &lt;a href=&quot;https://www.schneems.com/2024/11/21/my-red-hot-adhd-programming-affliction/&quot; class=&quot;bookmark-link&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-astro-cid-tcads4ih&gt; &lt;div class=&quot;bookmark-content&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-title&quot; data-astro-cid-tcads4ih&gt;My Red Hot ADHD Programming &amp;#39;Affliction&amp;#39;&lt;/span&gt; &lt;p class=&quot;bookmark-description&quot; data-astro-cid-tcads4ih&gt;Sorry, Dave, ADHD is real, and (not acknowledging it) can hurt you. Hi. I’m Richard. I’m a Ruby Core Contributor. I also code in Rust, and enjoy giving talks...&lt;/p&gt; &lt;span class=&quot;bookmark-meta&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-facts&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-fact bookmark-domain&quot; data-astro-cid-tcads4ih&gt;  schneems.com &lt;/span&gt;  &lt;/span&gt;  &lt;/span&gt; &lt;/div&gt; &lt;div class=&quot;bookmark-preview&quot; data-shape=&quot;banner&quot; data-astro-cid-tcads4ih&gt; &lt;img src=&quot;/link-previews/v1-ea640ddabef17dc0.webp&quot; alt width=&quot;800&quot; height=&quot;539&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-cid-tcads4ih&gt; &lt;/div&gt; &lt;/a&gt; &lt;/div&gt;
&lt;p&gt;He makes a lot of good points, but two bits cracked me up &lt;strong&gt;in relation to myself&lt;/strong&gt;…&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My biggest behavioral issue was that I snuck books into class and read them under my desk. If your mental picture of someone with ADHD is an out-of-control “little monster,” then this seems like the opposite. I got through college with a degree, taught myself Ruby on Rails&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I also snuck books into school to read under the desk. One teacher gave me detention for it. Others brought me &lt;em&gt;really interesting&lt;/em&gt; books from home (in one case a giant stack of New Scientist magazines), and occasionally let me get away with reading &lt;em&gt;over&lt;/em&gt; the desk instead of doing whatever I was supposed to be doing. And like Schneems, I also got through university with a degree and taught myself Ruby on Rails.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One day, I was having an argument with my wife. It was about how I never planned dates. It got heated. She had to go to a meeting, and I planned to return to the conversation “Hey, how about we grab lunch after your meeting to talk it over.” I realized that I needed to meet the basic expectations of planning events and trying to compensate for them by taking action then and there. The suggestion might seem trite or pandering, but she thought it was good and agreed.&lt;/p&gt;
&lt;p&gt;An hour later, she walked into my office (garage), and I literally couldn’t remember why she was there. Or rather, I was in the middle of another task, and when she walked in, I had to rack my brain to quickly retrieve the info that would provide context to her: “I’m here.” It took a split second too long, and she noticed. Turns out that I don’t “have a good memory.” What I have is the ability to recall extremely detailed information about a situation, given that I’m primed with the context.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I laughed out loud at this for like five minutes. In the two years since my friend Janina made me do those tests, we’ve laughed a lot at how differently this stuff affects each of us. The venn diagram of our symptoms barely overlaps. What Schneems describes here is not a thing for her at all, but it &lt;strong&gt;very much is for me&lt;/strong&gt;. Janina and I share a house, so she’s seen (and been frustrated by) many versions of this over the years. See also my ex-girlfriends. And colleagues. And friends. Every single person I sent that quote to laughed – especially at the last two sentences.&lt;/p&gt;
&lt;p&gt;Those last sentences are a beautifully concise version of something I’ve always struggled to articulate about myself: &lt;em&gt;“What I have is the ability to recall extremely detailed information about a situation, given that I’m primed with the context.”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You and me both, Schneems. You and me both.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Entirely unrelated, I stumbled upon this wonderfully creative site:&lt;/p&gt;
&lt;div class=&quot;bookmark-card cq &quot; data-astro-cid-tcads4ih&gt; &lt;a href=&quot;https://www.adhdexperience.com/&quot; class=&quot;bookmark-link&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-astro-cid-tcads4ih&gt; &lt;div class=&quot;bookmark-content&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-title&quot; data-astro-cid-tcads4ih&gt;adhdexperience.com&lt;/span&gt;  &lt;span class=&quot;bookmark-meta&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-facts&quot; data-astro-cid-tcads4ih&gt; &lt;span class=&quot;bookmark-fact bookmark-domain&quot; data-astro-cid-tcads4ih&gt;  adhdexperience.com &lt;/span&gt;  &lt;/span&gt; &lt;span class=&quot;bookmark-flag&quot; data-astro-cid-tcads4ih&gt;Link may be dead&lt;/span&gt; &lt;/span&gt; &lt;/div&gt;  &lt;/a&gt; &lt;/div&gt;
&lt;p&gt;Once I’d stopped puzzling over how on earth this was built on Wix, I read the list of symptoms. It’s been two years since I read a list of ADHD symptoms and I properly chuckled at how many apply to me. I’ve circled those which I &lt;strong&gt;definitely&lt;/strong&gt; experience (and I’m sure my friends would recognise) below.&lt;/p&gt;
  &lt;img src=&quot;/_astro/adhd-screenshot.C9e6aqIX_Z21u1g5.webp&quot; srcset=&quot;/_astro/adhd-screenshot.C9e6aqIX_SbSVK.webp 640w, /_astro/adhd-screenshot.C9e6aqIX_ZU8sO0.webp 750w, /_astro/adhd-screenshot.C9e6aqIX_Z2kDVrn.webp 828w, /_astro/adhd-screenshot.C9e6aqIX_Z2x4rOH.webp 1080w, /_astro/adhd-screenshot.C9e6aqIX_Z1oUdjr.webp 1280w, /_astro/adhd-screenshot.C9e6aqIX_2nbSmN.webp 1668w, /_astro/adhd-screenshot.C9e6aqIX_1IcTl0.webp 2048w, /_astro/adhd-screenshot.C9e6aqIX_Z21u1g5.webp 2155w&quot; alt=&quot;Screenshot of ADHD Symptoms&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2155px) 2155px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2155&quot; height=&quot;1577&quot;&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; Over the last 20 years I’ve developed some &lt;strong&gt;extremely good&lt;/strong&gt; mechanisms for dealing with many of these symptoms (without which I’d have circled &lt;em&gt;everything&lt;/em&gt; bar four). I also have fairly good mechanisms for many of those I circled, but not yet to the extent they don’t affect me. &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;me-and-adhd&quot;&gt;Me and ADHD&lt;a href=&quot;#me-and-adhd&quot; aria-label=&quot;Link to “Me and ADHD”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When ADHD was all over TikTok, loads of friends were prodding me about getting a formal diagnosis. I didn’t seek one because knowing whether I &lt;em&gt;Have ADHD&lt;/em&gt; or not wasn’t &lt;strong&gt;useful to me&lt;/strong&gt;, and still isn’t.&lt;/p&gt;
&lt;p&gt;Most people I know – when faced with mountains of evidence that &lt;em&gt;you very obviously have ADHD&lt;/em&gt; – reacted with some version of “OMG this explains soooo much, this is really helpful to know”. And for most of them it’s been &lt;strong&gt;really&lt;/strong&gt; fucking helpful.&lt;/p&gt;
&lt;p&gt;My own reaction was one of mild interest. Because while &lt;em&gt;“you very obviously have ADHD”&lt;/em&gt; was news to me, &lt;strong&gt;not one of the symptoms was&lt;/strong&gt; news. I’ve always held the view that these “symptoms” were just kinda &lt;em&gt;how my brain is&lt;/em&gt;. Sure, they make me super-shit at a bunch of things, but they also make weirdly great at other things. Whether I like it or not, they’re how my brain is. And always has been.&lt;/p&gt;
&lt;p&gt;This muted reaction is partly because I’ve been an accidental stoic from a young age – I’ve always thought it insane to try to control the uncontrollable. Things are how they are and unless we can &lt;strong&gt;actually&lt;/strong&gt; change them it’s better to embrace reality and look for what we &lt;em&gt;can&lt;/em&gt; change. I can no more control the symptoms I circled above than I can my emotions. But I can control how I &lt;strong&gt;respond&lt;/strong&gt; to them.&lt;/p&gt;
&lt;p&gt;I realised years ago that my brain was a bit weird. I also learned that any attempt to fundamentally change it was utterly exhausting, felt turbo-shit, and – most importantly – &lt;strong&gt;didn’t work at all&lt;/strong&gt;. And if I couldn’t change how my brain worked, the only sensible response was to happily accept reality, and focus on useful things I &lt;strong&gt;could change&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Which is why I read &lt;em&gt;Getting Things Done&lt;/em&gt; as a weed-smoking seventeen-year-old: I had some vague notion that I couldn’t rely on my brain to stay organised. All my mates thought this was very weird. And when – like most folks who read it – I failed to stick to the “GTD Way” for more than a minute, I took the bits that &lt;strong&gt;actually helped me&lt;/strong&gt; and left the rest behind. And I’ve kinda been doing that ever since.&lt;/p&gt;
&lt;p&gt;The result is: I’m self-employed, work remotely for multiple clients and intentionally avoid anything requiring a fixed 9-5 or Mon-Fri schedule. I write &lt;strong&gt;everything&lt;/strong&gt; down immediately, lest it’s gone when my mental context changes 10 seconds later. Every time-based thing goes straight in my calendar, which is always with me. I own an Apple Watch specifically to poke me when I have meetings. I’ve internalised a bunch of habits which keep my physical space tidy without any conscious mental effort. I have hard rules to avoid impulse purchases and hasty big decisions (fundamentally: “always sleep on it”). &lt;em&gt;And I could go on and on…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The point is: I’ve been finding out how to work &lt;em&gt;well&lt;/em&gt; with my own brain for decades. Far from being exhausting, the kinda stuff above is &lt;strong&gt;liberating&lt;/strong&gt; when I get it right, because it &lt;strong&gt;feels easy for once&lt;/strong&gt;. Figuring this stuff out, however, is often &lt;em&gt;not&lt;/em&gt; easy. The hard work is always the &lt;strong&gt;figuring out&lt;/strong&gt;, not the sustained doing. Because if sustaining a thing feels hard, I haven’t figured out the hard bit of it yet.&lt;/p&gt;
&lt;p&gt;Whether you have ADHD or not, finding ways to work &lt;strong&gt;with&lt;/strong&gt; your brain rather than &lt;em&gt;against&lt;/em&gt; it will never be a bad thing. And after working heavily with AI for the last few months, I have some embryonic thoughts which I’ll expand on some other time.&lt;/p&gt;
&lt;h2 id=&quot;embryonic-thoughts-on-ai--adhd&quot;&gt;Embryonic Thoughts on AI &amp;amp; ADHD&lt;a href=&quot;#embryonic-thoughts-on-ai--adhd&quot; aria-label=&quot;Link to “Embryonic Thoughts on AI &amp;amp; ADHD”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In no particular order, some embryonic thoughts on AI, me, and possibly ADHD…&lt;/p&gt;
&lt;h3 id=&quot;1️⃣-a-focussed-context-window-for-me-not-the-llm&quot;&gt;1️⃣ A Focussed Context Window (for me, not the LLM)&lt;a href=&quot;#1️⃣-a-focussed-context-window-for-me-not-the-llm&quot; aria-label=&quot;Link to “1️⃣ A Focussed Context Window (for me, not the LLM)”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When most folks get a WhatsApp message asking if they’re free on such-and-such date they’ll open their calendar app and either reply with an alternative date or add an event to their calendar, before closing it and carrying on with whatever they were doing before.&lt;/p&gt;
&lt;p&gt;When I get a message, 50% likely to do exactly this. But in the other 50% I’ll catch sight of some unrelated meeting or event next week and my brain will instantly switch context to thinking about that. Worst case, this means I forget to deal with the message which caused me to open my calendar in the first place. Best case it takes 15 minutes to refocus my brain on whatever I was doing before.&lt;/p&gt;
&lt;p&gt;And this applies to &lt;strong&gt;loads of shit&lt;/strong&gt;. It’s why I check email at designated times and why none of my tech sends me notifications. This problem is easily solvable by deliberatly doing &lt;em&gt;One Thing At A Time&lt;/em&gt; in an isolated context. Right now I’m writing this post in a super-zen minimalist editor precisely so I don’t get distracted.&lt;/p&gt;
&lt;p&gt;But that’s not how most knowledge work works. Most work constantly requires &lt;strong&gt;context and information&lt;/strong&gt; from places other than where we’re currently working – calendar, meeting notes, slack, notion, the web, etc etc etc etc.&lt;/p&gt;
&lt;p&gt;So the real challenge for me here is creating an environment where I always have the &lt;strong&gt;full context I need for what I’m doing right now but nothing more&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;While I can kinda do this manually it takes way more energy than it does for most folks, so I’m particularly excited about the potential for AI tools to help with this. Which is kinda amusing because – like me – LLMs also operate best when their context window contains &lt;em&gt;exactly&lt;/em&gt; the right stuff.&lt;/p&gt;
&lt;h3 id=&quot;2️⃣-meetings-etc&quot;&gt;2️⃣ Meetings Etc&lt;a href=&quot;#2️⃣-meetings-etc&quot; aria-label=&quot;Link to “2️⃣ Meetings Etc”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Meeting recording and transcription tools are a big deal. For most of my life I’ve waged an internal battle between losing myself in the experience of a good meeting and ensuring something useful comes out of it after. Whether I’m stream-of-consciousness jabbering while excitedly hand-waving or listening rapt while others do so, I’m concious that creating &amp;amp; guiding &lt;strong&gt;effective&lt;/strong&gt; conversation – and &lt;strong&gt;capturing the useful bits of it&lt;/strong&gt; – requires skilled facilitation. I often end up in that role, requiring a decision on whether I want to do it well &lt;strong&gt;or&lt;/strong&gt; fully participate in the conversation. Because I absolutely cannot do justice to both at the same time.&lt;/p&gt;
&lt;p&gt;AI cannot &lt;strong&gt;ever&lt;/strong&gt; replace skilled human facilitators. But in the (many) situations where my de facto facilitation role is just preventing unhelpful tangents and ensuring we remember the important bits after… AI &lt;strong&gt;can totally take the latter away from me during the meeting itself&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Paired with &lt;a href=&quot;https://betterat.work/tool/meetings&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;vaguely sensible meeting practices&lt;/a&gt;, AI tools like &lt;a href=&quot;https://www.notion.com/en-gb/product/ai-meeting-notes&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Notion’s meeting recorder&lt;/a&gt; have really helped me to stay present and focussed during calls.&lt;/p&gt;
&lt;h3 id=&quot;3️⃣-faster-side-quests&quot;&gt;3️⃣ Faster Side Quests&lt;a href=&quot;#3️⃣-faster-side-quests&quot; aria-label=&quot;Link to “3️⃣ Faster Side Quests”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’m certain there’s a subreddit dedicated to the partners of ADHDish people sharing funny-but-exasperated stories about this afternoon’s progression from &lt;em&gt;quickly cleaning the garage&lt;/em&gt; to &lt;em&gt;driving forty miles for a custom-machined nozzle for the tool that oils the leaf blower&lt;/em&gt; while the contents of the garage remains 50% organised and labelled in excruciating detail and 50% &lt;strong&gt;definitely not going back in the garage before nightfall&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I’m like this, and I fucking love it. I love that custom-machined nozzle and its metaphorical brothers. I could legitimately argue that my whole career has been a series of semi-random side-quests stacked on top of each other.&lt;/p&gt;
&lt;p&gt;I’m lucky that given enough time I’m pretty good at working back up the chain of side-quests until eventually &lt;strong&gt;the garage ends up clean&lt;/strong&gt;. But that often takes a bit too long, and I run out of time, and the stuff on the drive gets nicked or rained on otherwise ruined. &lt;strong&gt;If only today was longer and I had a floodlight etc&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;LLMs won’t help me with my garage, but they can make a lot of computer-based side-quests &lt;strong&gt;waaaaaay faster&lt;/strong&gt;. More importantly, they significantly reduce the time between me embarking on a side-quest and realising either A) it’s more than a side-quest and I should treat it as such or B) it’s not worth persuing. I’m talking weeks to hours in some cases.&lt;/p&gt;
&lt;p&gt;TL;DR: LLMs have allowed me to follow side-on-side-on-side-quests and come back up the &lt;em&gt;callback chain&lt;/em&gt; in a reasonable timeframe way more than I could before. Which is both fun and useful.&lt;/p&gt;
&lt;h3 id=&quot;4️⃣-context-switching-when-bored&quot;&gt;4️⃣ Context Switching When Bored&lt;a href=&quot;#4️⃣-context-switching-when-bored&quot; aria-label=&quot;Link to “4️⃣ Context Switching When Bored”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I get bored way to easily. But I also hyperfocus easily. I’m lucky enough that I’ve worked out how to blend those things into something which makes me good at learning things and doing things.&lt;/p&gt;
&lt;p&gt;A large part of that is my ability to maintain focus on things in a macro sense over periods of time. But the one thing I &lt;strong&gt;cannot fucking do&lt;/strong&gt; is maintain focus in a micro-sense when it’s gone. In the course of writing this article I’ve walked downstairs six times to smoke a cigarette and twice to make a cup of tea. I’ve folded five T-Shirts, weighed myself and noted the result in my journal and resisted countless urges to open a web browser.&lt;/p&gt;
&lt;p&gt;None of those things took more than 5 minutes and none distracted my macro-brain from this article. But without them I’d have been impotently raging at my keyboard.&lt;/p&gt;
&lt;p&gt;Because while I need that &lt;strong&gt;focused context window&lt;/strong&gt; in a macro-sense, I need regular mini-distractions in the micro sense.&lt;/p&gt;
&lt;p&gt;Surprisingly, I’ve found that doing &lt;strong&gt;at most two things at once&lt;/strong&gt; with agentic AI tools ticks this box. I legit smoke fewer cigarettes when working on two unrelated tasks using AI agents. When I need a break from hard thinking about Thing A, I set an agent off doing something useful on it and context switch to checking in on what AI has done on Thing B. Prodding Thing B’s AI towards the next thing is just enough of a break to bring my brain back to Thing A. 🔄.&lt;/p&gt;
&lt;p&gt;This only seems to work if Things A &amp;amp; B are not the same complexity. AI coding agents (in Aug 2025) don’t have sufficiently nuanced mental models to make non-trivial decisions about software engineering – nuanced mental models is my job as the human overseer. But if I’m doing that for Thing A, I need Thing B to need the same &lt;em&gt;nuanced mental models&lt;/em&gt; I’d give to smoking a cigarette or making a cuppa.&lt;/p&gt;
&lt;p&gt;When this works, context switching between two tasks &lt;strong&gt;improves my ability to focus on both&lt;/strong&gt;. Which is new to me (despite many attempts to kid myself otherwise in the past).&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up…&lt;a href=&quot;#wrapping-up&quot; aria-label=&quot;Link to “Wrapping up…”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Writing this has been weirdly cathartic because it’s the first time I’ve properly ordered &lt;em&gt;any&lt;/em&gt; thoughts about this stuff. But also because whether we’re talking about AI or our own brains my parting message is the same…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is what it is, and pretending otherwise is foolish. Our job is to work out how to use it in a way that’s positive for both ourselves and the wider world&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I don’t think AI will ever be better at 👆 than humans. Solong as we give it the time of day.&lt;/p&gt;</content:encoded></item><item><title>Personal CRMs Aren&apos;t What I Need</title><link>https://danny.is/writing/personal-crms-clay-earth-is-not-what-i-need/</link><guid isPermaLink="true">https://danny.is/writing/personal-crms-clay-earth-is-not-what-i-need/</guid><description>My honest take on why personal CRMs fail for relationship-building. Alternative approaches to managing professional networks by remote work expert Danny Smith.</description><pubDate>Mon, 05 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I paid a 12-month subscription for a Personal CRM called &lt;a href=&quot;https://clay.earth/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clay&lt;/a&gt; because I need help “managing my personal and professional relationships”. It’s not bad software. BUT…&lt;/p&gt;
&lt;p&gt;It turns out what I need is &lt;strong&gt;not Clay&lt;/strong&gt;, or any other “Personal CRM” I’ve found.&lt;/p&gt;
&lt;p&gt;It feels like most of these Personal CRMs are made by folks with very different problems to me.&lt;/p&gt;
&lt;p&gt;I know a lot of founders, execs, freelancers (and salespeople) who are very good at building and maintaining their personal relationships… it’s a &lt;strong&gt;huge&lt;/strong&gt; part of their job after all. Most of them reach out to people, meet new people and get/make a lot of useful intros on a daily basis.&lt;/p&gt;
&lt;p&gt;For them, Clay’s features are probably pretty useful. I can imagine it’s useful knowing it’s Andy’s birthday today, and when you last spoke 9 months Andy was struggling with X, Y and Z and had a baby due in August. And that Andy’s job on LinkedIn changed 3 months ago. Etc. Because all that makes excellent context for a quick email to reopen a conversation.&lt;/p&gt;
&lt;p&gt;If you read that and thought “well duuh what’s a CRM for?” you’re one of the founders/execs/freelancers/salespeople I’m talking about 👆. We probably agree that the job of a personal CRM is keeping information about the people you know organised, current and readily available.&lt;/p&gt;
&lt;p&gt;Which is exactly what all CRMs are for… sales CRMs were originally designed to help salespeople manage exactly this kind of information in a sane way, making it easier to maintain meaningful contextual relationships with customers &amp;amp; leads.&lt;/p&gt;
&lt;p&gt;But that’s not at all what &lt;strong&gt;I need&lt;/strong&gt; from a “Personal CRM”. If it was I’d happily be using a janky setup of Hubspot, Zoho, Salesforce (💸) or similar to manage my personal relationships.&lt;/p&gt;
&lt;p&gt;What I need is a tool that solves a different set of problems. I need a tool that makes maintaining and growing my personal relationships &amp;amp; network easy and fun. I need a tool for people who are not good at this stuff in the first place.&lt;/p&gt;
&lt;p&gt;I need a tool that pings me before I meet someone new and suggests context-aware questions &amp;amp; conversation points. I need a frictionless way to note the important bits down and resurface them appropriately. I need to know when two of my contacts have something non-obvious in common, and an easy way to intro them. I need to be magically reminded that when I last spoke to Anna from Random Startup Ltd she was excited about her family trip to Croatia, and I want that reminder (with context) 3 days after she gets home – or when I happen to end up in Croatia myself.&lt;/p&gt;
&lt;p&gt;When I meet people in real life I invariably have a million great conversations, intro people all over the place and get excited. I am by no means bad at building genuine relationships. I need a tool that helps me feel like that when I’m sat at my desk.&lt;/p&gt;
&lt;p&gt;Most of all, I need a “Personal CRM” which solves an unsolved problem for me…&lt;/p&gt;
&lt;p&gt;I need a tool that makes managing my network of personal relationships feel like the opposite of what salespeople use CRMs for.&lt;/p&gt;
&lt;p&gt;I have a large extended network of people with whom I have potential mutually meaningful relationships. In sales parlance, they’re almost all “very warm”. But for the most part, none of us is trying to sell shit to each other. In common with a lot of other people in that network, I’m crap at maintaining the relationships that constitute it and even crappier at cultivating the second-order relationships that could expand it.&lt;/p&gt;
&lt;p&gt;I need tooling to help me with that.&lt;/p&gt;
&lt;p&gt;Since we’re discussing &lt;strong&gt;Personal CRM&lt;/strong&gt;s, here’s a not-at-all-work-related take…&lt;/p&gt;
&lt;p&gt;My housemate is amazing at maintaining her myriad personal relationships. Nothing to do with her work. She &lt;strong&gt;loves&lt;/strong&gt; buying super-thoughtful presents for people she barely knows. She always remembers to invite randos from the pub to stuff they’d enjoy. She always remembers to message about important stuff in folks’ lives. And very quickly “barely knows” doesn’t apply. This is genuine relationship building done well.&lt;/p&gt;
&lt;p&gt;Meanwhile I’m the opposite (I’d forget my own birthday if said housemate didn’t remind me). Sure, I could learn to behave like her, but it would never be natural and I’d never get off on it the same way she does.&lt;/p&gt;
&lt;p&gt;It feels like every Personal CRM I’ve seen is designed for people like my housemate, or even worse… the I-Am-At-Work equivalent of her. ie. Folks who enjoy and are somewhat good at this stuff.&lt;/p&gt;
&lt;p&gt;Perhaps obviously, it also feels like every Personal CRM I’ve seen &lt;strong&gt;designed by&lt;/strong&gt; this kind of person.&lt;/p&gt;
&lt;p&gt;And it feels like for this kind of person the core value prop of a “Personal CRM” has already been solved by CRMs in general. (Every good salesperson I know maintains a personal CRM of sorts… none use a specific &lt;em&gt;Personal CRM&lt;/em&gt; tool.)&lt;/p&gt;
&lt;p&gt;It feels like at best, a &lt;em&gt;Personal CRM&lt;/em&gt; is slightly tweaked UX presented as a skin over an already-solved problem.&lt;/p&gt;
&lt;p&gt;Meanwhile the problems I have are mostly unsolved by software. I signed up for Clay because I need help “managing my personal and professional relationships”. But what I need is not a tool like Clay. Because everything I said above.&lt;/p&gt;
&lt;p&gt;And I’m convinced I’m not alone here. I know too many people like me who are excellent at building deep, meaningful relationships with a few people, but can’t bridge the gap to doing so more widely.&lt;/p&gt;
&lt;p&gt;I’m equally convinced that &lt;em&gt;none of us&lt;/em&gt; is likely to found a “Personal CRM” startup to address this.&lt;/p&gt;
&lt;p&gt;But my goodness would we throw money at someone who did.&lt;/p&gt;
&lt;p&gt;Does this chime with anyone else? 👆&lt;/p&gt;
&lt;p&gt;PS. I’d love to see Clay’s churn data, segmented by a yes/no to “Do you enjoy buying gifts &amp;amp; organising birthday parties, and when you have amazing convos with people at conferences, do you message them afterwards?” Then label with &lt;em&gt;potential high-value customer&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;EDIT: Practical Example…&lt;/p&gt;
&lt;p&gt;I bumped into Chase Warrington a bit ago. I’ve been a fan of Chase’s work for ages, and very much look up to him in a work context. He’s exactly the kind of person that should see me stuffing everything I know about him into my “Personal CRM” so I can extract the maximum value from our relationship.&lt;/p&gt;
&lt;p&gt;But that’d be mental because neither of us are arseholes, we’ve got nothing to sell each other and &lt;strong&gt;fuck basing any relationship on the extraction of value&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I want a Personal CRM that’s like “Chase just interviewed someone you know on his podcast, you should listen to that one and then maybe WhatsApp them both?” or “Chase is in London right now and he’d definitely like this band you’re going to see tomorrow but FYI he’s here for a funeral”.&lt;/p&gt;
</content:encoded></item><item><title>Building Healthy Remote Organizations - The Silent Enabler of Success</title><link>https://danny.is/writing/organisational-health/</link><guid isPermaLink="true">https://danny.is/writing/organisational-health/</guid><description>How to build resilient, calm organizations that scale successfully. Proven strategies for organizational health and team effectiveness.</description><pubDate>Thu, 10 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For a long time I’ve believed that &lt;strong&gt;organisational health&lt;/strong&gt; is a hugely undervalued ingredient for success – especially for startups that are scaling fast. &lt;strong&gt;It’s the silent enabler of so much else&lt;/strong&gt;. Before I give you my take on it, here’s a succinct definition of Organisational Health…&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An organization’s ability to function effectively, to cope adequately, to change appropriately, and to grow from within.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For me it’s about intentionally creating a resilient, cohesive organisation that can operate with a sustained sense of &lt;strong&gt;calm&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Really healthy organisations tend to feel calm even when they’re ploughing through unknown waters at a thousand miles per hour. They’re able to operate on the edge of chaos without becoming chaotic.&lt;/p&gt;
&lt;p&gt;Despite the obvious benefits of a healthy organisation, it’s often neglected – I think for a few reasons…&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;It seems &lt;strong&gt;kinda boring&lt;/strong&gt; – we’re conditioned to believe the best solutions come from fancy, innovative approaches. Being healthy isn’t complex or innovative… but it does takes sustained &lt;strong&gt;effort&lt;/strong&gt;. And there’s none of the adrenaline which usually comes with startup leadership.&lt;/li&gt;
&lt;li&gt;It’s &lt;strong&gt;very hard to quantify results&lt;/strong&gt;. The best measurements of organisational health are whatever we use to measure general success (growth, revenue etc) but this makes it very hard to measure the effect of work. As always, &lt;em&gt;useful&lt;/em&gt; proxy metrics are hard to find.&lt;/li&gt;
&lt;li&gt;It needs a &lt;strong&gt;holistic approach&lt;/strong&gt; - it cuts across leadership, operations and people functions, as well as every other function in a business. Which makes it hard.&lt;/li&gt;
&lt;li&gt;Founders are often &lt;strong&gt;unskilled in this domain&lt;/strong&gt; – the skills needed to grow a healthy organisation often feel quite at odds with the other skills required to be a good founder. (Some only realise this when the wheels fall off but I’ve met a lot who realised from the start, felt horribly inadequate as leaders and stuck their heads in the sand.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Remote and distributed companies need to be &lt;strong&gt;much healthier&lt;/strong&gt; than office-based firms because co-location allows us to paper over &lt;strong&gt;an awful lot of cracks&lt;/strong&gt;. And systemic health problems have a disproportionately large effect on remote teams, which quickly compounds with scale.&lt;/p&gt;
&lt;h2 id=&quot;so-what-does-a-healthy-organisation-look-like-to-me&quot;&gt;So what does a healthy organisation look like to me?&lt;a href=&quot;#so-what-does-a-healthy-organisation-look-like-to-me&quot; aria-label=&quot;Link to “So what does a healthy organisation look like to me?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These kinds of things…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Happy&lt;/strong&gt;, autonomous and empowered people (obviously).&lt;/li&gt;
&lt;li&gt;A unified, coherent senior leadership &lt;strong&gt;team&lt;/strong&gt; exhibiting all the traits &amp;amp; behaviours we’d expect of &lt;em&gt;any other great team&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Common, well-understood &lt;strong&gt;shared&lt;/strong&gt; norms, habits, behaviours &amp;amp; ways of working. (We could label this “culture”.)&lt;/li&gt;
&lt;li&gt;Common, well-understood &lt;strong&gt;values&lt;/strong&gt; and &lt;strong&gt;principles&lt;/strong&gt; which facilitate aligned independent decision-making at all levels.&lt;/li&gt;
&lt;li&gt;A clear, &lt;em&gt;truthful&lt;/em&gt; and well-understood &lt;strong&gt;structure&lt;/strong&gt; (org design, teams, career levels, salaries etc).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Excellent&lt;/em&gt; communication everywhere.&lt;/li&gt;
&lt;li&gt;A culture that values &lt;strong&gt;clarity&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A culture of &lt;strong&gt;trust&lt;/strong&gt; and psychological &lt;strong&gt;safety&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A culture that cares about &lt;strong&gt;how&lt;/strong&gt; we work, and actively tries to make it better.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Systems&lt;/strong&gt;, processes and &lt;strong&gt;tools&lt;/strong&gt; that make it easy to “operationalise” repetitive work.&lt;/li&gt;
&lt;li&gt;Managers who have the &lt;strong&gt;time&lt;/strong&gt;, &lt;strong&gt;skill&lt;/strong&gt; and &lt;strong&gt;motivation&lt;/strong&gt; to lead and manage well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these things happen by accident.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;💡&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;While it helps and is amazing to be part of, &lt;strong&gt;I don’t think shared passion for the mission is
necessary&lt;/strong&gt; – there are thousands of very healthy companies whose people don’t really give a damn
about the mission. (Potential exception: your senior leadership team.)&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;so-when-should-you-start-thinking-about-organisational-health&quot;&gt;So when should you start thinking about organisational health?&lt;a href=&quot;#so-when-should-you-start-thinking-about-organisational-health&quot; aria-label=&quot;Link to “So when should you start thinking about organisational health?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Short answer: &lt;strong&gt;as soon as you reasonably can.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Small, young companies get disproportionally large gains from the effort, which &lt;strong&gt;compound over time&lt;/strong&gt;. Without the right foundations this all gets exponentially harder as you scale (and as your collective habits and ways of working become entrenched). I’ve seen way too many companies ignore this stuff until they’ve raised a Series B and the wheels start falling off.&lt;/p&gt;
&lt;p&gt;Having the foundations for a &lt;strong&gt;super healthy&lt;/strong&gt; organisation allow you to scale quickly without as much pain, and help things stay calm as the organisation rapidly becomes more complex.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;💡&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;If you’re a tiny seed-stage startup scrambling to find product-market fit, you should probably
focus 💯 on that (solid foundations are useless if they’re the wrong shape for the house, or you
never build it). But &lt;strong&gt;as soon as you reasonably can&lt;/strong&gt;, step back and think about how to build a
healthy organisation.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;so-how-do-we-start-getting-healthier-&quot;&gt;So how do we start getting healthier? 🌱&lt;a href=&quot;#so-how-do-we-start-getting-healthier-&quot; aria-label=&quot;Link to “So how do we start getting healthier? 🌱”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Firstly, we need to believe it’s &lt;em&gt;at least&lt;/em&gt; as important as our other business aims. Being healthy is unlikely to be the number one goal but, as with human health, unless we make a strong commitment to it, it’ll always lose out to other more pressing stuff.&lt;/p&gt;
&lt;p&gt;Secondly, we need at least one person who’s wholly focussed on keeping our organisation healthy. I’ve seen some amazing small startups where this is the &lt;strong&gt;fundamental&lt;/strong&gt; job of the COO. More recently, I’ve seen this owned by a &lt;em&gt;Head of Remote&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In larger firms I’ve seen teams called &lt;em&gt;Workplace Design&lt;/em&gt;, &lt;em&gt;Office of the COO&lt;/em&gt;, &lt;em&gt;Remote Operations&lt;/em&gt;, &lt;em&gt;People Operations&lt;/em&gt;, (plain old) &lt;em&gt;Operations&lt;/em&gt;, and the like. The one thing these have in common is that they are 100% focussed on making the &lt;strong&gt;company itself&lt;/strong&gt; as healthy as possible.&lt;/p&gt;
&lt;p&gt;Ultimately, keeping an organisation healthy will always be a joint effort led by people like…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;COO, VP Ops etc&lt;/li&gt;
&lt;li&gt;CPO, VP People etc&lt;/li&gt;
&lt;li&gt;Chief of Staff, Head of Remote etc&lt;/li&gt;
&lt;li&gt;Leaders specific to the company’s core business (eg CTO for tech companies)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this happens by accident.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;💡&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;I nicked the term &lt;strong&gt;Organisational Health&lt;/strong&gt; from &lt;em&gt;&lt;a href=&quot;https://www.tablegroup.com/books/the-advantage/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Advantage by Patrick
Lencioni&lt;/a&gt;.&lt;/em&gt; The first chapter makes a much fuller
argument for its importance than I have here.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;guiding-principles-&quot;&gt;Guiding Principles 📜&lt;a href=&quot;#guiding-principles-&quot; aria-label=&quot;Link to “Guiding Principles 📜”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here are a few loose principles I’ve found useful for helping folks approach organisational health. (I could – and probably will – write a whole article on each of them.)&lt;/p&gt;
&lt;h3 id=&quot;1️⃣have-an-internal-focus&quot;&gt;1️⃣ Have an internal focus&lt;a href=&quot;#1️⃣have-an-internal-focus&quot; aria-label=&quot;Link to “1️⃣ Have an internal focus”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Most functions in a startup are led by external needs &amp;amp; drivers: Product, Engineering, Sales, Marketing, Strategy, etc… all &lt;em&gt;should&lt;/em&gt; have their eyes set mostly on the outside world.&lt;/p&gt;
&lt;p&gt;We need at least one function which has an internal focus, &lt;strong&gt;treats the company like a product&lt;/strong&gt;, itself as a product team and its employees as users. And this function needs a good product manager.&lt;/p&gt;
&lt;p&gt;Done well, this function is a huge force multiplier for all the others.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“We design how our company works &lt;strong&gt;as thoughtfully as our products&lt;/strong&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;2️⃣beware-organisational-debt&quot;&gt;2️⃣ Beware organisational debt&lt;a href=&quot;#2️⃣beware-organisational-debt&quot; aria-label=&quot;Link to “2️⃣ Beware organisational debt”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Just like a software product accrues technical debt, a company accrues organisational debt. The effects are similar - it gets increasingly harder to get stuff done over time.&lt;/p&gt;
&lt;p&gt;Used strategically, this kind of debt is useful because it lets us move faster, but the tweet below explains why we &lt;strong&gt;must&lt;/strong&gt; be intentional about how we incur it. And how we pay it down.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&lt;p&gt;The opposite of technical debt isn&amp;#39;t zero debt, it&amp;#39;s compounding gains: each new thing
makes it easier and faster to build new things.&lt;/p&gt;&lt;/p&gt;&lt;p&gt;— Matt Wensing 🐙 (@mattwensing) &lt;/p&gt;&lt;a href=&quot;https://twitter.com/mattwensing/status/1270837000069840896?ref_src=twsrc%5Etfw&quot;&gt;&lt;p&gt;June 10, 2020&lt;/p&gt;&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;h3 id=&quot;3️⃣human--system&quot;&gt;3️⃣ Human + System&lt;a href=&quot;#3️⃣human--system&quot; aria-label=&quot;Link to “3️⃣ Human + System”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These are the two fundamental components, and Internal Operations should focus on both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Human&lt;/strong&gt; - Relationships, culture, leadership, human growth, shared mindset, “vibes” etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System&lt;/strong&gt; - Structure, process, policy, tooling, automation etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These need to be viewed and tackled together and the highest leverage stuff addresses both human &amp;amp; system concerns at the same time (eg. shared behaviours &amp;amp; habits).&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;🌱&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;A garden needs plants (humans) that take their own path. These need thoughtful nurturing. It also
needs pots, trellises, paths (systems) to support the plants. These aren’t nurtured, they’re
engineered. If we want an awesome garden we need to engineer the right systems and nurture the
plants right – this will only work if view both together as a whole. 🌱&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h3 id=&quot;4️⃣behaviours--habits-over-processes--tools&quot;&gt;4️⃣ Behaviours &amp;amp; habits &lt;em&gt;over&lt;/em&gt; processes &amp;amp; tools&lt;a href=&quot;#4️⃣behaviours--habits-over-processes--tools&quot; aria-label=&quot;Link to “4️⃣ Behaviours &amp;amp; habits over processes &amp;amp; tools”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While processes and tools are important, the most effective way to improve an organisation’s health is by improving the habits and default behaviours of its people.&lt;/p&gt;
&lt;p&gt;If you’ve ever tried to impose a new system or process without considering behavioural change, you’ve probably seen a lot of folks ignore it and revert to what they know. &lt;strong&gt;People always fall back on their habits and default behaviours. Especially in groups.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On the plus side, given the right habits, behaviours, principles, environment etc, people will likely create effective systems &amp;amp; tools on their own.&lt;/p&gt;
&lt;p&gt;That said… used intentionally, processes and tools &lt;strong&gt;can be very powerful tools&lt;/strong&gt; for affecting behaviour &amp;amp; habits. Especially in groups.&lt;/p&gt;
&lt;h3 id=&quot;5️⃣strive-for-clarity&quot;&gt;5️⃣ Strive for clarity&lt;a href=&quot;#5️⃣strive-for-clarity&quot; aria-label=&quot;Link to “5️⃣ Strive for clarity”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A culture of clarity reduces day-to-day uncertainty and tends plenty of efficiency savings (especially in distributed companies). But its greatest value comes from how it &lt;strong&gt;reduces the cognitive load on people&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;At best, lack of clarity leads to increased cognitive load as folks struggle to make sense of things and make decisions themselves. At worst, it leads to a culture of uncertainty and stress because in the absence of facts people always assume the worst. In either case, it means people are spending a lot of time thinking and talking about unnecessary stuff.&lt;/p&gt;
&lt;p&gt;Creating clarity in a complex and inherently unclear environment is one of the hardest things about leadership, so leaders often think about clarity in a very macro sense: is everyone clear on our mission? Or our Q3 goals?&lt;/p&gt;
&lt;p&gt;Yet most of the uncertainty, inefficiency &amp;amp; stress I’ve seen comes from lack of clarity on tiny things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I’ve found five different versions of this template and have two conflicting answers about which one to use here.&lt;/li&gt;
&lt;li&gt;What’s this meeting about? I don’t really know what’s expected of me and what are these weird notes I’ve been sent all about?&lt;/li&gt;
&lt;li&gt;Is it okay to expense my new headphones? How do I do that? Who do I even ask?&lt;/li&gt;
&lt;li&gt;Who should I talk to on the marketing team about this thing? Misha told me Andy, but which Andy? He’s not on the org chart? Wait, is Marketing even the right team?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Creating clarity &amp;amp; consistency on stuff like this is often much harder than it seems – especially in large, fast-growing or globally distributed companies. But done right, it can have an insanely big effect on organisational health.&lt;/p&gt;
&lt;aside aria-label=&quot;Examples&quot; class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;div class=&quot;icon&quot; data-astro-cid-lnlu5god&gt;💡&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt; &lt;h3 class=&quot;title&quot; data-astro-cid-lnlu5god&gt;Examples&lt;/h3&gt; &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;ul&gt;&lt;li&gt;Vision &amp;amp; values provide clarity on why we’re all here.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Consistent systems and processes create clarity about how things work (and &lt;b&gt;why&lt;/b&gt;).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;Strong principles provide clarity on how to make decisions.&lt;/li&gt;&lt;li&gt;Org structure provides contextualised clarity on who’s responsible for what.&lt;/li&gt;&lt;li&gt;Mission → strategy → plans provide clarity on where we’re going and how.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Clear, consistent internal messaging &lt;b&gt;communicates&lt;/b&gt; clarity re everything &lt;/p&gt;&lt;/li&gt;&lt;p&gt;above.
&lt;li&gt;etc.&lt;/li&gt;&lt;/p&gt;&lt;/ul&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h3 id=&quot;6️⃣practice-simplicity&quot;&gt;6️⃣ Practice simplicity&lt;a href=&quot;#6️⃣practice-simplicity&quot; aria-label=&quot;Link to “6️⃣ Practice simplicity”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Simplicity matters because simple things are easy for people to &lt;em&gt;fully&lt;/em&gt; understand and reason about. When designing systems we have to consider the &lt;strong&gt;usefulness and accuracy of the mental models folks form about them&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It’s much easier to augment folks’ existing &lt;em&gt;correct&lt;/em&gt; mental models than to fix their incorrect ones.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system. — &lt;strong&gt;John Gall&lt;/strong&gt; (1975, p.71)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Designing simple solutions for complicated problems is almost never simple itself, but wherever possible we should aggressively err on the side of simplicity.&lt;/p&gt;
&lt;h3 id=&quot;7️⃣harness-the-power-of-principles&quot;&gt;7️⃣ Harness the power of principles&lt;a href=&quot;#7️⃣harness-the-power-of-principles&quot; aria-label=&quot;Link to “7️⃣ Harness the power of principles”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Principles are often more useful than rigid rules because when they’re fully understood they allow people to confidently make decisions without constant recourse to others. They also help people understand &lt;strong&gt;the why&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Even when you need rigid rules, clearly explaining the principles which underpin them increases transparency &amp;amp; shared understanding, and helps folks to adapt the rules intelligently.&lt;/p&gt;
&lt;p&gt;For any principles to be useful they need to be well-thought-out, easy to understand and &lt;strong&gt;constantly reinforced by everyone&lt;/strong&gt;. Otherwise, they’re just more words in a document somewhere.&lt;/p&gt;
&lt;h3 id=&quot;8️⃣create-frameworks-as-well-as-tools&quot;&gt;8️⃣ Create frameworks (as well as tools)&lt;a href=&quot;#8️⃣create-frameworks-as-well-as-tools&quot; aria-label=&quot;Link to “8️⃣ Create frameworks (as well as tools)”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our inwards-facing function needs to build an environment &amp;amp; toolset that helps other functions do their best work. But also go one step further and create the environment &amp;amp; meta-tools that allow others to design their own.&lt;/p&gt;
&lt;p&gt;We can’t design an entire company from the top. Nor can we design by general consensus on everything. Instead, we need to provide a framework of strong, consistent guide-rails (principles, tools, practices, structures etc) that everyone trusts.&lt;/p&gt;
&lt;p&gt;This is the kernel of our “Operating System”. If everyone &lt;strong&gt;really&lt;/strong&gt; understands this framework and buys into it, (with a little help) they’ll be able to create their own systems of work on top of it. And our kernel helps keep those systems compatible with each other.&lt;/p&gt;
&lt;p&gt;Doing this is the only real way to create a &lt;strong&gt;truly agile organisation&lt;/strong&gt; that can evolve as it scales.&lt;/p&gt;
&lt;p&gt;Of course, keeping the kernel fit for purpose takes ongoing effort. As does guiding and nurturing the people and systems that use it.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;These are just a few of my thoughts on what it takes to keep an organisation healthy, but hopefully they’ve sparked a few thoughts for you.&lt;/p&gt;
&lt;p&gt;If you take one thing away from this, let it be the idea that creating a great company only happens when we set out to do so with intentionally, and when we get it right the sense of calm it can provide is a silent enabler of so much else.&lt;/p&gt;
&lt;p&gt;And with many of us moving towards remote, globally distributed organisations, all this matters &lt;strong&gt;much more&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item><item><title>Website Redesign Part XVI - A Header and Footer for Articles</title><link>https://danny.is/writing/website-redesign-xvi/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-xvi/</guid><pubDate>Sat, 29 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I pretty much finished designing the article pages back in &lt;a href=&quot;/writing/website-redesign-xi&quot;&gt;Part XI&lt;/a&gt;, and after &lt;a href=&quot;/writing/website-redesign-xv&quot;&gt;deciding on a typeface and some general design rules&lt;/a&gt; for the rest of the site it’s time to finish it off by adding a header and footer to the article pages.&lt;/p&gt;
&lt;h2 id=&quot;the-article-header&quot;&gt;The Article Header&lt;a href=&quot;#the-article-header&quot; aria-label=&quot;Link to “The Article Header”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here are a few ideas I played around with for the header.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-08-13-headers1.png&quot; alt=&quot;Header Example 1&quot;&gt;
&lt;img src=&quot;/uploads/2020-08-13-headers2.png&quot; alt=&quot;Header Example 2&quot;&gt;
&lt;img src=&quot;/uploads/2020-08-13-headers3.png&quot; alt=&quot;Header Example 3&quot;&gt;
&lt;img src=&quot;/uploads/2020-08-13-headers4.png&quot; alt=&quot;Header Example 4&quot;&gt;
&lt;p&gt;Although I quite like some of these, it feels like I’m overcomplicating things a bit. It might be better to go with something really simple for now – just a link back to the homepage in the top left corner.&lt;/p&gt;
&lt;h2 id=&quot;the-article-footer&quot;&gt;The Article Footer&lt;a href=&quot;#the-article-footer&quot; aria-label=&quot;Link to “The Article Footer”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The footer needs to include some links, my name and a copyright notice. After a little playing about I ended up with something like this…&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-08-13-footers1.png&quot; alt=&quot;Footer Example&quot;&gt;
&lt;p&gt;While I like this, it needs more thought to make in work well on small screens, and I’ll be a challenge to implement without overcomplicating the code because of how I’ve set up the article CSS. So much like the header, I think it makes more sense to keep things simple and just provide a link to the articles index, rather than listing related/recent articles. I expect I’ll revisit this in the future.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-08-13-footers-final.png&quot; alt=&quot;Final Footer&quot;&gt;
&lt;h2 id=&quot;the-code&quot;&gt;The Code&lt;a href=&quot;#the-code&quot; aria-label=&quot;Link to “The Code”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s what I ended up with for the CSS…&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/dannysmith/e108ee4b0f243623182e98c6d5c1e8d5.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://gist.github.com/dannysmith/904d92a12f16a67cc3a0a0df47d4bc1e.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;There’s definitely some scope to simplify this, and to add some more “features” to the footer, but this feels like it’s enough to call the articles &lt;em&gt;done&lt;/em&gt; and move on to the rest of the site.&lt;/p&gt;
</content:encoded></item><item><title>Auto Generating Jekyll Posts that Redirect to Medium</title><link>https://danny.is/writing/auto-generating-jekyll-posts-from-medium/</link><guid isPermaLink="true">https://danny.is/writing/auto-generating-jekyll-posts-from-medium/</guid><pubDate>Thu, 13 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m making quite heavy use of the &lt;a href=&quot;https://github.com/jekyll/jekyll-redirect-from&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;jekyll-redirect-from&lt;/a&gt; plugin. This allows me to create a normal apge or post file with a special frontmatter item and have the page 301 redirect to the specified URL. I’m already using this to forward URLs like &amp;lt;danny.is/using&amp;gt; to Notion, but I also want to use it for blog posts that I’ve written elsewhere.&lt;/p&gt;
&lt;p&gt;I’ve recently written an article for &lt;a href=&quot;https://www.delocate.co/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Delocate&lt;/a&gt;, and I can quickly add that to this site by creating a file in &lt;code&gt;_posts&lt;/code&gt; called &lt;code&gt;2020-07-31-not-all-comms-needs-a-purpose.md&lt;/code&gt; with the following frontmatter.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Not all communication needs a definite purpose&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;redirectURL&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;https://www.delocate.co/blog/not-all-communication-needs-a-definite-purpose&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;platform&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;delocate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;title: Not all communication needs a definite purposeredirectURL: &amp;#x27;https://www.delocate.co/blog/not-all-communication-needs-a-definite-purpose&amp;#x27;platform: delocate&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The post will behave totally normally as far as Jekyll is concerned, but instead of rendering the empty body, it will 301 redirect to the specified URL.&lt;/p&gt;
&lt;p&gt;Although I’d like to move all my medium posts over to this site in the end, that’s a big job. So for now I want to create a file like this for every medium post I’ve written. Creating 40 of these files by hand isn’t very appealing, so I whipped up a quick ruby script to do it for me.&lt;/p&gt;
&lt;p&gt;I started out trying to parse medium’s RSS feed but it doesn’t include all the posts, so I’m using medium’s private API instead. The script hits the medium API, extracts the data I need into an array and then loops over it creating a correctly formatted file for each post. It lives in &lt;code&gt;scripts&lt;/code&gt; so I can run it whenever I want with &lt;code&gt;ruby ./scriipts/generate_medium_forwards.rb&lt;/code&gt; to pull any new medium articles in.(If the whole &lt;code&gt;Medium&lt;/code&gt; class seems like overkill, it’s because I based this on some old code that was more reusable.) Here’s the code…&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/dannysmith/f984e6fd68583af0a4ae1e2ea708a84f.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;This worked a treat and I only had one hiccup: I spend far too long trying to work out why many of the articles had identical &lt;code&gt;published_at&lt;/code&gt; dates before I eventually realised that I needed the &lt;code&gt;firstPublishedAt&lt;/code&gt; value rather than the &lt;code&gt;publishedAt&lt;/code&gt; one – the latter gets updated every time the post is edited and re-published.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part XV - A typographic system for the rest of this site</title><link>https://danny.is/writing/website-redesign-xv/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-xv/</guid><pubDate>Wed, 12 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I started this redesign I already had a fairly clear idea of the aesthetic I wanted, based on the existing design. These two pages show that off quite well.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-03-17-dasmith1.png&quot; alt=&quot;Screenshot of existing website homepage&quot;&gt;
&lt;img src=&quot;/uploads/2020-03-17-dasmith2.png&quot; alt=&quot;Screenshot of existing website 404 page&quot;&gt;
&lt;p&gt;I’m not sure where I got this aesthetic from, but it’s clearly draws on the work of &lt;a href=&quot;https://en.wikipedia.org/wiki/Jan_Tschichold&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jan Tschichold&lt;/a&gt;. Jan was a typographer and designer who’s famous for a whole load of stuff (go read his Wikipedia page). I particularly like this style, which is closely associated with him.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-08-12-jan1.png&quot; alt=&quot;Jan Tschichold Example 1&quot;&gt;
&lt;img src=&quot;/uploads/2020-08-12-jan2.png&quot; alt=&quot;Jan Tschichold Example 2&quot;&gt;
&lt;img src=&quot;/uploads/2020-08-12-jan3.jpg&quot; alt=&quot;Jan Tschichold Example 3&quot;&gt;
&lt;img src=&quot;/uploads/2020-08-12-jan4.jpg&quot; alt=&quot;Jan Tschichold Example 4&quot;&gt;
&lt;p&gt;I spent quite a while trying to establish some sort of typographic hierarchy for the rest of the site based partly on some thoughts from my &lt;a href=&quot;/writing/website-redesign-xiii&quot;&gt;last article&lt;/a&gt;. Eventually I realised that this is really hard when I’m going for a deliberately “whacky” aesthetic. The kind of style I’m after doesn’t lend itself well to rigid hierarchy. And the fact I’m likely to size some type with viewport units makes this approach even less sensible.&lt;/p&gt;
&lt;p&gt;So I went back to square one and decided on a few guidelines and constraints in place of a hierarchy:&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Use type and layout over imagery and effects.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; The site should always look good in greyscale. While I can use colour, it should never be the main design element.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; One typeface only, but I can use it in any weight or size that feels right.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Always consider visual hierarchy, and be consistent across pages where possible.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; A grid of 20px, but with permission to ignore wherever it makes sense.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also settled on a modular component-based approach where possible, and identified a few core components that will be useful in all sorts of contexts going forward. They’re deliberately simple and extensible in their design.&lt;/p&gt;
&lt;h2 id=&quot;deciding-on-a-typeface&quot;&gt;Deciding on a typeface&lt;a href=&quot;#deciding-on-a-typeface&quot; aria-label=&quot;Link to “Deciding on a typeface”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I spent way too long &lt;a href=&quot;/writing/website-redesign-v&quot;&gt;choosing a typeface for articles&lt;/a&gt;, so I’m keeping the simple. I decided to stick with &lt;strong&gt;Futura PT&lt;/strong&gt;. It works perfectly for the look I’m going for and looks good in all caps, which I’ll probably be using a lot. This choice has some downsides though: the biggest being that it’s not available for free so I’d have to keep my Adobe Fonts license and accept the dependancy on Adobe’s service.&lt;/p&gt;
&lt;p&gt;I’d pretty much decided to suck that up when I discovered &lt;a href=&quot;https://indestructibletype.com/Jost.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jost*&lt;/a&gt;. It’s a pretty close match for Futura, is open source and available as a variable font. It’s slightly larger than Futura but after a bit of trial and error I think it’s a good trade-off.&lt;/p&gt;
&lt;h2 id=&quot;text-styles--components&quot;&gt;Text Styles &amp;amp; Components&lt;a href=&quot;#text-styles--components&quot; aria-label=&quot;Link to “Text Styles &amp;amp; Components”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With that decision made, I threw together a few basic components and text styles in Figma. there’s no real system for this – I started with the components, extracted text styles from them and then did my best to consolidate them a bit.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-08-12-typography.png&quot; alt=&quot;Text Styles in Figma&quot;&gt;
&lt;img src=&quot;/uploads/2020-08-12-components.png&quot; alt=&quot;Components in Figma&quot;&gt;
&lt;p&gt;Having these styles and components is a great starting point for some experimentation. I’m gonna hold off coding them up until I’ve done a few iterations in Figma – I’m sure they’ll change a little bit as I play with them. The only technical work I’ve done is to build Jost’s variable version from source and bring it into my site.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part XIII - Taking Stock of Content</title><link>https://danny.is/writing/website-redesign-xiii/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-xiii/</guid><pubDate>Sun, 12 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m now pretty happy with how the essays on here look, so it’s time to turn my attention to the rest of the site. Although I have an idea of the aesthetic I’m after, every time I’ve opened Figma to have a play I’ve found myself staring at a blank page wrought with indecision – the dreaded writers block. Or rather, &lt;em&gt;designers block&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I didn’t feel this at all when designing the essay pages because I had a clear idea of what I was trying to do. I already had content to work with and my only job was to make it as readable and beautiful as possible while keeping things simple. Clear goal. Clear starting point.&lt;/p&gt;
&lt;p&gt;But with the rest of the site it’s not so clear. In Part IV I identified a list of pages I need to design, and I did some high-level pondering on goals and user needs in Part II. But beyond that, I’ve basically got no constrains and no starting point. And it’s paralysing me.&lt;/p&gt;
&lt;p&gt;Back when I made websites for a living, the first thing I’d look at after user goals was a content audit. If the client didn’t have any existing content to audit, we’d focus on the content they &lt;em&gt;thought they were going to have&lt;/em&gt;. So that’s just what I’m going to do now.&lt;/p&gt;
&lt;h2 id=&quot;a-content-audit-for-this-site&quot;&gt;A Content audit for this site&lt;a href=&quot;#a-content-audit-for-this-site&quot; aria-label=&quot;Link to “A Content audit for this site”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;img src=&quot;/uploads/2020-07-11-postit-content-unorganised.png&quot; alt=&quot;Post-it notes of content audit - unorganised&quot;&gt;
&lt;p&gt;I can think of a hundred other things I might want to put here &lt;em&gt;someday&lt;/em&gt;, but that’s all the stuff I’m thinking about putting here &lt;strong&gt;right now&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;organising-the-post-its&quot;&gt;Organising the post-its&lt;a href=&quot;#organising-the-post-its&quot; aria-label=&quot;Link to “Organising the post-its”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let’s take a stab at organising these post-its according to a few different taxonomies. This might seem like overkill for such a small set of content but this exercise often throws up unexpected and creative ideas for organising and grouping. Even if we don’t use them, they can help seed concepts we use elsewhere.&lt;/p&gt;
&lt;h4 id=&quot;by-type&quot;&gt;By type&lt;a href=&quot;#by-type&quot; aria-label=&quot;Link to “By type”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Let’s start with the most obvious, grouping by content type – the first word of each post-it is a good starting point. We could also group by whether the content is &lt;strong&gt;mostly writing&lt;/strong&gt; (yellow) or &lt;strong&gt;mostly something else&lt;/strong&gt; (orange).&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-11-postit-content-type.png&quot; alt=&quot;Post-it notes of content audit - by type&quot;&gt;
&lt;h4 id=&quot;by-author&quot;&gt;By author&lt;a href=&quot;#by-author&quot; aria-label=&quot;Link to “By author”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I’ve been thinking a lot about the creation and consumption of information lately, so it’s not surprising this springs to mind as a possible grouping: did I create it, or did I consume it? Some stuff is hard to categorise here – a book recommendation is clearly consumption, but what if I add my own review?&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-11-postit-create-consume.png&quot; alt=&quot;Post-it notes of content audit - by author&quot;&gt;
&lt;h4 id=&quot;by-professional-or-personal&quot;&gt;By professional or personal&lt;a href=&quot;#by-professional-or-personal&quot; aria-label=&quot;Link to “By professional or personal”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Since this is a personal site, we could consider grouping the content by whether or not it’s related to my work.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-11-postit-content-personalprofessional.png&quot; alt=&quot;Post-it notes of content audit - by professional or personal&quot;&gt;
&lt;p&gt;It’s pretty obvious that most of the content types here are focussed on my work. Looking at the &lt;em&gt;both&lt;/em&gt; pile, the vast majority of content represented by each post-it is work-oriented. (While I do read some non-work articles via pocket, they’re in the minority. Likewise for articles on this site. Etc.)&lt;/p&gt;
&lt;p&gt;So this grouping doesn’t feel very useful. Moreover, it doesn’t feel &lt;strong&gt;authentic&lt;/strong&gt; – I’ve always mixed work and play and I’m not really sure where the line between them is. That solitary orange sticky note makes me question whether my music videos belong on this site, though.&lt;/p&gt;
&lt;h4 id=&quot;by-importance&quot;&gt;By Importance&lt;a href=&quot;#by-importance&quot; aria-label=&quot;Link to “By Importance”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Some of these post-its are more important to me than others. My writing – whether on this site or elsewhere – is pretty core. As are links to my series’ on Notion. But the books I’m reading are much less important – I could live without having them on this site at all.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-11-postit-content-importance.png&quot; alt=&quot;Post-it notes of content audit - by importance&quot;&gt;
&lt;p&gt;Perhaps the design should emphasise the important and de-emphasise the less-important? Or perhaps the less-important shouldn’t live here at all? My view of what’s important is almost certain to change over time, so if I do emphasise some content based on this it should be easy to change.&lt;/p&gt;
&lt;h4 id=&quot;by-permanence&quot;&gt;By Permanence&lt;a href=&quot;#by-permanence&quot; aria-label=&quot;Link to “By Permanence”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One of the main reasons I’m moving my writing to my own blog is permanence. I don’t like the fact that Medium controls my content and URLs. I want a more permanent place to put my words. Somewhere they can live forever – &lt;strong&gt;etched in stone&lt;/strong&gt;, as it were.&lt;/p&gt;
&lt;p&gt;The same is not true for other types of content. My tweets are ephemeral, for example: I wouldn’t be overly bothered if they all disappeared one day. Perhaps we could try grouping along these lines – stuff that I care about keeping vs stuff that I don’t.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-11-postit-content-permenance.png&quot; alt=&quot;Post-it notes of content audit - by permanence&quot;&gt;
&lt;h3 id=&quot;but-what-about-the-actual-content&quot;&gt;But what about the actual content?&lt;a href=&quot;#but-what-about-the-actual-content&quot; aria-label=&quot;Link to “But what about the actual content?”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you’ve ever done any information architecture you’re probably shouting at your screen right now…&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;BUT YOU HAVEN’T CONSIDERED ANY &lt;strong&gt;ACTUAL CONTENT&lt;/strong&gt;! It doesn’t make sense to group based solely on &lt;em&gt;medium&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You’ve got a point.&lt;/p&gt;
&lt;p&gt;It’s kind of ridiculous to go through this exercise based only on the medium in which the content exists. We should also be considering things like subject area, target audience and purpose. For large sets of information these things will probably suggest much more useful taxonomies than content type.&lt;/p&gt;
&lt;p&gt;I’ve ignored these so far because I don’t want to base the structure or conceptual design of this site on the topics I’m currently engaging with. I know too well how quickly they can change, and it’s important this site can adapt to those changes without much effort on my part.&lt;/p&gt;
&lt;p&gt;Having said that, I’d be foolish not to at least consider the kind of content I’m producing right now. For the past few years, my interests – and especially the stuff I write about –have fallen in one of four loose categories.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-11-postit-content-themes.png&quot; alt=&quot;Content Categories&quot;&gt;
&lt;p&gt;I’ve started to loosely group these into two super-categories: &lt;strong&gt;Human&lt;/strong&gt; and &lt;strong&gt;Systems&lt;/strong&gt;. The first includes everything where an actual human is the focus – think coaching, mentoring, nurturing etc. The second is focussed on the environment in which humans operate and how it can be engineered to provide the most useful support. I’ve been doing a lot of gardening during lockdown, so if you’ll excuse the metaphor…&lt;/p&gt;
&lt;p&gt;Caring for a garden requires system design and engineering. Plants need the right soil, pots and sunlight. Some need the support of a rigid framework like a trellis. Growing from seed often means adherence to a thoroughly-researched process of feeding and repotting. But on its own, this stuff can’t possibly create a beautiful, thriving garden. Plants need to be nurtured and guided, and they won’t always do what we expect. As gardeners, we must observe individual plants and do what’s best for them, using our intuition and experience to guide us.&lt;/p&gt;
&lt;p&gt;A creeper might grow without a trellis, but it will be in a random direction and will have to work much harder to get up the wall. But a trellis is useless without daily watering and occasionally guidance. And sometimes a creeper will ignore the trellis and decide to weave beautifully around a fence instead. It’s up to us to notice where it’s heading and either correct or encourage it. And perhaps remove the trellis, if it’s not proving useful.&lt;/p&gt;
&lt;p&gt;Like creepers, people need trellis’. They’re the systems, habits, processes and frameworks that support us and make our lives easier.&lt;/p&gt;
&lt;p&gt;We also need nurturing and the freedom to explore our own path. If we have good gardeners looking after us, we can rely on them to guide and prune as needed, and to move or replace the trellis when it isn’t helping.&lt;/p&gt;
&lt;p&gt;The more I reflect, the more sense these two categories make to me. Both &lt;strong&gt;Human&lt;/strong&gt; and &lt;strong&gt;Systems&lt;/strong&gt; work need to be in balance, and my interests can be sorted fairly well into those two buckets. (It’s interesting that the least happy bits of my career have occurred when I’ve been &lt;em&gt;too&lt;/em&gt; focussed on one or the other)&lt;/p&gt;
&lt;p&gt;I’ll explore this idea further outside the context of this redesign, but it provides a reasonable starting point for a top-level grouping of my content.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-11-postit-content-themes-super.png&quot; alt=&quot;Content Super Categories&quot;&gt;
&lt;h3 id=&quot;how-is-all-this-useful&quot;&gt;How is all this useful?&lt;a href=&quot;#how-is-all-this-useful&quot; aria-label=&quot;Link to “How is all this useful?”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What can we take from all this, and does any of it help with my &lt;em&gt;designers block&lt;/em&gt;? This process has certainly sparked a few interesting thoughts which I’ll definitely come back to in the future. It’s also sparked a few that are useful now.&lt;/p&gt;
&lt;p&gt;Grouping by &lt;strong&gt;type&lt;/strong&gt; provides a good indication of the components I should think about (Link, Video, Book, Page, Article, Other), but is not useful as a way of actually organising content.&lt;/p&gt;
&lt;p&gt;Grouping by &lt;strong&gt;author&lt;/strong&gt; raises the possibility of subtly differentiating content I’ve created from content I’ve consumed/curated. I’m not sure how useful this distinction will be for readers, but it still appeals to me as something to consider.&lt;/p&gt;
&lt;p&gt;Grouping by &lt;strong&gt;professional/personal&lt;/strong&gt; isn’t really useful, though perhaps my music doesn’t belong on this site and I should just link to youtube.&lt;/p&gt;
&lt;p&gt;Grouping by &lt;strong&gt;importance&lt;/strong&gt; raises the need for flexibility. I need way of focusing attention on stuff that’s important to me right now, knowing it might change over time. This might be as simple as moving stuff that’s currently important to the top of some list, but this has design implications. It should be easy for me to promote/highlight different types of content as my needs change.&lt;/p&gt;
&lt;p&gt;Grouping by &lt;strong&gt;permanence&lt;/strong&gt; doesn’t feel very useful at all.&lt;/p&gt;
&lt;p&gt;Grouping by &lt;strong&gt;Human/System&lt;/strong&gt;, and by the four categories of content feels like a good starting point for some labelling system which I should explore further.&lt;/p&gt;
&lt;h2 id=&quot;keeping-it-simple&quot;&gt;Keeping it simple&lt;a href=&quot;#keeping-it-simple&quot; aria-label=&quot;Link to “Keeping it simple”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Simplicity is a the core value of this redesign and working through this process has helped me reach a few decisions about how to simplify things.&lt;/p&gt;
&lt;ul class=&quot;contains-task-list long-list-items&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; I’ve no need for a &lt;strong&gt;coaching&lt;/strong&gt; page right now. I can add this in the future.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; I still want a &lt;strong&gt;hire me&lt;/strong&gt; page but it makes much more sense for this to live in notion. I’ll forward &lt;code&gt;https://danny.is/lookingforwork&lt;/code&gt; and &lt;code&gt;https://danny.is/hireme&lt;/code&gt; to a public Notion page and be done with it.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; I won’t show any actual videos on my site. Instead, I’ll present any &lt;strong&gt;video series’&lt;/strong&gt; as links to notion pages or YouTube playlists. I already have these set up.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; I won’t have a page for my &lt;strong&gt;music&lt;/strong&gt;. I’ll just forward &lt;code&gt;https://danny.is/singing&lt;/code&gt; to my dormant youtube channel. I will show this link on the homepage, though – singing is a big part of who I am.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; I won’t worry about &lt;strong&gt;notes&lt;/strong&gt; for the moment&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. They’re an important part of how I’d like to share content but aren’t a priority and Notion might well be the best place for them.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; I don’t care about books at all right now. If I really want to write a book review I can just write an article.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; &lt;strong&gt;Core design concepts should be expandable to accommodate the unknown&lt;/strong&gt;. For example, these would be bad ideas:
&lt;ul&gt;
&lt;li&gt;Deciding I have three main types of content and basing my entire design concept on “threes”.&lt;/li&gt;
&lt;li&gt;Having all my videos on youtube and Loom, and using their brand colours as core design elements.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;a href=&quot;#wrapping-up&quot; aria-label=&quot;Link to “Wrapping up”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I started this article as a way to get over writer’s block, hoping that a content audit would help provide a few constraints and possible design directions. It’s been an interesting little exercise and has definitely helped unblock me, but when I think about it all I’ve really done is intentionally focus my attention the same problem I had in the first place: &lt;em&gt;what should I have on this site and how should I organise it?&lt;/em&gt;. So while this “content audit” was interesting, its real value came from &lt;strong&gt;forcing&lt;/strong&gt; me to consider this problem in a structured, systematic way.&lt;/p&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a href=&quot;#footnote-label&quot; aria-label=&quot;Link to “Footnotes”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;If you’re wondering what I mean by &lt;em&gt;notes&lt;/em&gt;, &lt;a href=&quot;https://notes.danny.is/&quot;&gt;I used to use tumblr for this sort of thing&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item><item><title>Website Redesign Part XIV - Stepping Into Figma</title><link>https://danny.is/writing/website-redesign-xiv/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-xiv/</guid><pubDate>Sun, 12 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Figma is one of my new favourite tools. I’ve been an avid fan of Sketch since I first picked it up in 2011. Compared to the other tools available at the time, it was a breath of fresh air. I still remember how excited I felt after I first used it for some actual work.&lt;/p&gt;
&lt;p&gt;The first time I used Figma I had a similar feeling. Everything was just &lt;strong&gt;easy&lt;/strong&gt;. Coming from Sketch, Figma felt comfortable and a lot of the keyboard shortcuts are the same so no need to reconfigure my finger-memory. Figma has some major advantages over Sketch. It’s &lt;strong&gt;web-based&lt;/strong&gt; so it runs on any machine with a browser and my files live in the cloud by default. Anything I can do to simplify my laptop’s setup is a win for me. It also has great multiplayer – working collaboratively is an absolute joy. And it feels simpler than Sketch. The interface feels more intuitive and the way it handles components feels way more natural. So I’m now using Figma for pretty much all my design work.&lt;/p&gt;
&lt;p&gt;Until now, I’ve been designing this site in the browser and haven’t felt the need for a design tool. But now I’m getting to the rest of the site, I know I’ll want to experiment with layout and colour faster than CSS allows. I also want to explore Figma’s component and shared styles features and this feels like a good excuse.&lt;/p&gt;
&lt;h2 id=&quot;text-styles&quot;&gt;Text Styles&lt;a href=&quot;#text-styles&quot; aria-label=&quot;Link to “Text Styles”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We can recreate the styles for these essays pretty easily. If we assume 16px as our base font size, it’s just a matter of doing some maths based on the &lt;code&gt;em&lt;/code&gt; values in our CSS to get the size and line-height of all our text styles. Figma also lets us control some of the more advanced font features we’ve been using in our CSS.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-12-figma-fontfeatures.png&quot; alt=&quot;Figma Font Feature Settings&quot;&gt;
&lt;p&gt;With very little work, we can create a styleguide and create corresponding text styles. Here’s what I ended up with.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-12-figma-sypestyles.png&quot; alt=&quot;Text Styles in Figma&quot;&gt;
&lt;h2 id=&quot;colour-styles&quot;&gt;Colour Styles&lt;a href=&quot;#colour-styles&quot; aria-label=&quot;Link to “Colour Styles”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While I haven’t given much thought to colour there are a few colours that I’m using in the CSS, so it makes sense to create some named colour styles and a palette in Figma too.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-12-figma-colours.png&quot; alt=&quot;Colour Palette&quot;&gt;
&lt;h2 id=&quot;grids&quot;&gt;Grids&lt;a href=&quot;#grids&quot; aria-label=&quot;Link to “Grids”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since the &lt;code&gt;line-height&lt;/code&gt; for my essays is 1.5, the baseline grid is 24. I use this measurement quite a lot in the CSS already, both for horizontal and vertical spacing. Figma allows us to create grid styles which we can apply to frames. Let’s create a simple grid style for 24px, and use it to position some content on the grid.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-12-figma-baselinegrid.png&quot; alt=&quot;Baseline Grid at 24px&quot;&gt;
&lt;p&gt;We can also create a grid that represents one line-height’s worth of padding around a frame, like this.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-12-figma-paddinggrid.png&quot; alt=&quot;Padding Grid&quot;&gt;
&lt;p&gt;The trick is to create two grids, one &lt;strong&gt;Rows&lt;/strong&gt; and one &lt;strong&gt;Columns&lt;/strong&gt;, both with the same settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Count: &lt;strong&gt;1&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Type: &lt;strong&gt;Stretch&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Margin: &lt;strong&gt;24&lt;/strong&gt; (your desired padding)&lt;/li&gt;
&lt;li&gt;Gutter: &lt;strong&gt;0&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then we can combine both grids by creating a Grid Style that includes them both. And voila, we have a grid that we can apply to any frame to give us the correct guides for padding.&lt;/p&gt;
&lt;h2 id=&quot;components&quot;&gt;Components&lt;a href=&quot;#components&quot; aria-label=&quot;Link to “Components”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we can call this minimalist design system done, we need to account for a couple of components that we can’t create using text styles alone: code blocks, blockquotes, lists and horizontal rules. We’ll create these as components, using the text and colour styles we’ve already defined. By using constraints we can ensure the content adapts properly as component instances are resized. Here’s what I ended up with.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-12-figma-essay-components.png&quot; alt=&quot;Figma Components&quot;&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;a href=&quot;#summary&quot; aria-label=&quot;Link to “Summary”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;None of this is especially useful right now, but it’s provided a starting point from which to design the rest of the site and taught me a few things I didn’t know about Figma.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part XII - Responsive Images</title><link>https://danny.is/writing/website-redesign-xii/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-xii/</guid><pubDate>Wed, 01 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The last time I built a content-heavy website, responsive images weren’t really a thing. In fact they were a definite problem for most developers. We could control the visual size of an image with percentage widths and media queries, but we had no easy way of controlling the actual image used by the browser. The easiest thing to do was just supply a very big high-res image that would look good on wide viewports and accept that smaller viewport devices would end up downloading a file with more detail than they could use.&lt;/p&gt;
&lt;p&gt;The last time I gave any real attention to this problem I was working on a site with a lot of high-res background images. I dealt with it in three ways. Firstly, I used a jpeg that was too big for even the largest viewport, but with JPEG compression turned all the way up. This was then scaled down with CSS. This actually &lt;em&gt;reduced&lt;/em&gt; the file size, and generally looked pretty good – even on new-fangled retina screens&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Secondly, I spent ages blurring parts of the image in photoshop. The idea was that the fewer individual colours there were in the JPEG, the smaller the filesize. Take a look at this photo:&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-07-02-hot-air-baloon.jpg&quot; alt=&quot;Hot air ballon in the sky&quot;&gt;
&lt;p&gt;There are probably hundreds of different shades of blue and grey in the sky, each one taking up bytes in the JPEG. By blurring the sky as much as possible, we end up with far fewer shades of blue and so reduce the filesize. Doing this was a pain in the arse.&lt;/p&gt;
&lt;p&gt;All the other stuff involved swapping out images with javascript.&lt;/p&gt;
&lt;p&gt;Thankfully that was ages ago, and we now have some much better ways of handling responsive images. Both &lt;code&gt;secret&lt;/code&gt; and the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element were being talked about when I stopped making websites for a living, but both are now well-supported by modern browsers.&lt;/p&gt;
&lt;h2 id=&quot;srcset&quot;&gt;SRCSET&lt;a href=&quot;#srcset&quot; aria-label=&quot;Link to “SRCSET”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;srcset&lt;/code&gt; attribute allows us to specify different image files to use at different resolutions and viewport widths. It looks like this.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;small.png&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;alt&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;srcset&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;medium.png 1000w large.png 2000w&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;img src=&amp;quot;small.png&amp;quot; alt=&amp;quot;...&amp;quot; srcset=&amp;quot;medium.png 1000w large.png 2000w&amp;quot; /&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This uses the small version by default, but on larger screens the browser will download and use either the medium or large version of the image. All we have to do is make the three versions and bung them in our &lt;code&gt;/images&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;The general rule here is to use an image of the same dimensions as the original and just scale it down to make the smaller versions. As &lt;a href=&quot;https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-srcset/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Chris Coiyer says&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most responsive images uses will be: “I have multiple versions of this graphic. Here they are, use the right one.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;the-picture-element&quot;&gt;The picture element&lt;a href=&quot;#the-picture-element&quot; aria-label=&quot;Link to “The picture element”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The picture element is far more powerful, but also more complex. It allows us to do a bit more at direction as our image size changes – we can use images of different dimensions and control how they are displayed using media query-like rules. A quick google will turn up umpteen articles on how to do this.&lt;/p&gt;
&lt;h2 id=&quot;images-on-this-website&quot;&gt;Images on this website&lt;a href=&quot;#images-on-this-website&quot; aria-label=&quot;Link to “Images on this website”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Right now, most images on this site are included via standard markdown image tags, and the generated HTML is pretty simple:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/uploads/an-image.png&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;alt&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;An image&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;img src=&amp;quot;/uploads/an-image.png&amp;quot; alt=&amp;quot;An image&amp;quot; /&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;By default, these images would display at their natural size, so we’re using some CSS to constrain them to 100% of their container.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;block&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;max-width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;img {  display: block;  max-width: 100%;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Inside essays, this means that smallish images display at their natural size and will not be stretched (we’re using &lt;code&gt;max-width&lt;/code&gt; not &lt;code&gt;width&lt;/code&gt;), but very large images will be scaled down to fit in the essay container.&lt;/p&gt;
&lt;p&gt;The problem with this is that I often include quite large high-res images and set them to full-bleed (more on that later). This is fine for large viewports, but smaller devices will be forced to download the massive high-res file.&lt;/p&gt;
&lt;p&gt;The obvious solution is to generate smaller versions of these high-res images and then use &lt;code&gt;srcset&lt;/code&gt; to let the browser decide which one to show. Except that &lt;strong&gt;this is a blog&lt;/strong&gt;. And if I have to create multiple versions and write a long chunk of HTML every time I want to include an image in my markdown I’ll just stop writing altogether. Clearly, we need a way to automate this.&lt;/p&gt;
&lt;p&gt;While we’re at it, there are a few other things we should consider. Firstly, it’s a good idea to explicitly declare the dimensions of an image in the HTML using the &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;width&lt;/code&gt; attributes. Assuming we don’t override it with CSS, this prevents reflow by telling the browser how much room the image will take up so it can draw the right sized box &lt;em&gt;before&lt;/em&gt; the image has downloaded. So while we’re automating image things we should probably add hight and width to image elements.&lt;/p&gt;
&lt;p&gt;What’s more, modern browsers will soon be able to infer the aspect ratio of an image based on these attributes, and use that to draw a correctly-sized content box &lt;em&gt;even if we’ve changed the width with CSS&lt;/em&gt;. Cool.&lt;/p&gt;
&lt;p&gt;Secondly, HTML &lt;a href=&quot;https://caniuse.com/#feat=loading-lazy-attr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;now supports&lt;/a&gt; native lazy loading via the &lt;code&gt;loading&lt;/code&gt; attribute. This is obviously good for performance and is as easy as adding &lt;code&gt;loading=&quot;lazy&quot;&lt;/code&gt; to our image tags. Addy Osmani has a &lt;a href=&quot;https://addyosmani.com/blog/lazy-loading/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;much more detailed explanation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This ten-minute video from Jen Simmons foes a great job of explaining how all of this works.&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/4-d_SoCHeWE&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;how-do-we-automate-this-stuff&quot;&gt;How do we automate this stuff?&lt;a href=&quot;#how-do-we-automate-this-stuff&quot; aria-label=&quot;Link to “How do we automate this stuff?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Good question. Back when I started this redesign, I made the decision to keep the tech super simple. Here are the principles I defined:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;As few dependencies as possible (both build-time and run-time dependancies).&lt;/li&gt;
&lt;li&gt;Use defaults wherever possible.&lt;/li&gt;
&lt;li&gt;Write as little code as possible.&lt;/li&gt;
&lt;li&gt;Do not over-abstract. Avoid fallbacks, polyfills and hacks.&lt;/li&gt;
&lt;li&gt;Bake accessibility in.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I &lt;em&gt;really&lt;/em&gt; don’t want to add unnecessary complexity to this site. But it feels like this is important enough to add a just little more complexity. If there’s a jekyll plugin that can read my markdown image tags, generate smaller versions of my images and insert appropriate &lt;code&gt;srcset&lt;/code&gt; attributes, I’ll take it. Bonus points if it can add &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;loading&lt;/code&gt; attributes too.&lt;/p&gt;
&lt;p&gt;My initial search actually led me to some awesome plugins for &lt;a href=&quot;https://www.11ty.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Eleventy&lt;/a&gt;. &lt;a href=&quot;https://github.com/liamfiddler/eleventy-plugin-lazyimages#readme&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;This one&lt;/a&gt; does some cool lazy-loading with javascript and data URLs, &lt;a href=&quot;https://github.com/nhoizey/eleventy-plugin-images-responsiver&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this one&lt;/a&gt; creates picture elements, &lt;a href=&quot;https://github.com/eeeps/eleventy-respimg#readme&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this one&lt;/a&gt; does just what I want for cloud-hosted images and &lt;a href=&quot;https://github.com/chromeos/static-site-scaffold-modules/tree/master/modules/eleventy-plugin-local-respimg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this one&lt;/a&gt; basically does what I want but without the bonus points.&lt;/p&gt;
&lt;p&gt;Reading though the source code of these was super interesting, and had nudged me to look further at Eleventy (there are some strong arguments for moving this site to it). But since we’re on Jekyll here, none of these are much use. What we need is the &lt;a href=&quot;https://github.com/wildlyinaccurate/jekyll-responsive-image&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jekyll Responsive Image&lt;/a&gt; plugin.&lt;/p&gt;
&lt;p&gt;This looks like it solves half the problem. It lets us specify &lt;a href=&quot;https://github.com/wildlyinaccurate/jekyll-responsive-image/tree/master/sample-templates&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;our own template&lt;/a&gt; for the generated image HTML which means we can add width, height and lazy loading ourselves. The only downside is that we have to use a liquid tag rather than standard markdown to insert images. No thanks.&lt;/p&gt;
&lt;p&gt;And then jekyll hooks came to the rescue with &lt;a href=&quot;https://medium.com/@ivo.v.valchev/jekyll-responsive-images-with-srcset-5da131415d0f&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this tutorial from Ivo Valchev&lt;/a&gt;. Let’s walk through the process…&lt;/p&gt;
&lt;h3 id=&quot;install-the-stuff&quot;&gt;Install the stuff&lt;a href=&quot;#install-the-stuff&quot; aria-label=&quot;Link to “Install the stuff”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The plugin uses Rmagick which is a wrapper around imagemagick which is – and has always been – a nightmare. So first step is to update or install it and then try installing the gem manually.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;install&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;imagemagick&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;gem&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;install&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;jekyll-responsive-image&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;brew install imagemagickgem install jekyll-responsive-image&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now we can add it to our Gemfile and Jekyll config as per the README. There are a lot of config options available but I started with these: a path to the template, the sizes to generate and an instruction to only save the generated files in the build folder.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;responsive_image&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;template&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;_includes/responsive-image.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;sizes&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;480&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;800&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1400&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;save_to_source&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;responsive_image:  template: _includes/responsive-image.html  sizes:    - width: 480    - width: 800    - width: 1400  save_to_source: false&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;How we need to create our template. I stole most of this straignt from the docs, but I’ve added lazy loading and width/height attributes.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{% raw %} {% capture srcset %} {% for i in resized %} /{{ i.path }} {{ i.width }}w, {% endfor %} {%&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;endcapture %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;img&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/{{ path }}&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;alt&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;{{ alt }}&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;loading&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;lazy&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;srcset&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;{{ srcset | strip_newlines }}&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;{{ original.width }}&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;{{ original.height }}&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{% endraw %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{% raw %} {% capture srcset %} {% for i in resized %} /{{ i.path }} {{ i.width }}w, {% endfor %} {%endcapture %}&lt;img  src=&amp;quot;/{{ path }}&amp;quot;  alt=&amp;quot;{{ alt }}&amp;quot;  loading=&amp;quot;lazy&amp;quot;  srcset=&amp;quot;{{ srcset | strip_newlines }}&amp;quot;  width=&amp;quot;{{ original.width }}&amp;quot;  height=&amp;quot;{{ original.height }}&amp;quot;/&gt;{% endraw %}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;So far so good. We can now insert responsive images with&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{% raw %} {% responsive_image path: image.jpg alt: &quot;My Image&quot; %} {% endraw %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{% raw %} {% responsive_image path: image.jpg alt: &amp;quot;My Image&amp;quot; %} {% endraw %}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To get this working with standard markdown, we need to register a hook that replaces markdown image syntax with this new tag at build-time. We can create &lt;code&gt;_plugins/img_tag_transform.rb&lt;/code&gt; and add the following.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;Jekyll&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;Hooks&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;register&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;:posts&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;:pre_render&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;do&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; |post, _payload|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;docExt &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; post.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;extname&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;tr&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;post.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; post.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;gsub&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/!&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\[&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;(.*)&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\]\(&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;([^&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\)&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;]+)&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\)&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;(?:{:([^}]+)})*/&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;{% raw %}{% responsive_image path: \2 \3 %}{% endraw %}&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;post.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; post.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;gsub&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;path: /&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;path: &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;Jekyll::Hooks.register :posts, :pre_render do |post, _payload|  docExt = post.extname.tr(&amp;#x27;.&amp;#x27;, &amp;#x27;&amp;#x27;)  post.content = post.content.gsub(/!\[(.*)\]\(([^\)]+)\)(?:{:([^}]+)})*/, &amp;#x27;{% raw %}{% responsive_image path: \2 \3 %}{% endraw %}&amp;#x27;)  post.content = post.content.gsub &amp;#x27;path: /&amp;#x27;, &amp;#x27;path: &amp;#x27;end&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It’s a bit hacky, but it works.&lt;/p&gt;
&lt;h2 id=&quot;is-it-worth-it&quot;&gt;Is it worth it?&lt;a href=&quot;#is-it-worth-it&quot; aria-label=&quot;Link to “Is it worth it?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My biggest concern with this approach is the added complexity. Not only have I added Imagemagick as a dependancy, I’ve also added a new jekyll plugin and a slightly hacky custom plugin.&lt;/p&gt;
&lt;p&gt;For now, this feels like an acceptable trade-off, but if I find myself adding more similar stuff in the future I’ll have to revisit this and reconsider the value/complexity ratio.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;full-bleed-images&quot;&gt;Full-bleed Images&lt;a href=&quot;#full-bleed-images&quot; aria-label=&quot;Link to “Full-bleed Images”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While we’re on the subject of images, I’ve also added a few utility classes to make my images a little more interesting. Normal images are constrained to the width of the element they appear in, but sometimes I want full-bleed images that extend out to the edges of the viewport, like this:&lt;/p&gt;
&lt;p&gt;We can do this with a utility class and some &lt;code&gt;calc()&lt;/code&gt; magic.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.full-bleed {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;vw&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;vw&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;// For images that are not wide enough to &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;fill&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; the &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;gap&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;justify-content&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;align-items&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex-start&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay {  .full-bleed {    width: 100vw;    margin-left: calc(50% - 50vw);    // For images that are not wide enough to fill the gap    display: flex;    justify-content: center;    align-items: flex-start;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;These styles are applied to the &lt;em&gt;container&lt;/em&gt; rather than the image itself so that images smaller than the container are never scaled up. We can apply this style in markdown like this&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{% raw %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{:.full-bleed}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;![&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;Image&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;](&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0td:underline&quot;&gt;/uploads/2020-07-02-hot-air-baloon.jpg&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;{% endraw %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{% raw %}{:.full-bleed}![Image](/uploads/2020-07-02-hot-air-baloon.jpg){% endraw %}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Which will produce the following HTML&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;full-bleed&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/uploads/images.jpg&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;alt&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Image&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;p class=&amp;quot;full-bleed&amp;quot;&gt;  &lt;img src=&amp;quot;/uploads/images.jpg&amp;quot; alt=&amp;quot;Image&amp;quot; ... /&gt;&lt;/p&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As well as full bleed, I’ve also added classes for left and right bleed&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.left-bleed&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;vw&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;justify-content&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex-end&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;align-items&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex-start&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.right-bleed&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-right&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;vw&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;justify-content&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex-start&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;align-items&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex-start&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay {  .left-bleed {    margin-left: calc(50% - 50vw);    display: flex;    justify-content: flex-end;    align-items: flex-start;  }  .right-bleed {    margin-right: calc(50% - 50vw);    display: flex;    justify-content: flex-start;    align-items: flex-start;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;summing-up&quot;&gt;Summing up&lt;a href=&quot;#summing-up&quot; aria-label=&quot;Link to “Summing up”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although it’s ben years since I’ve built a proper website like this, I’ve kept myself fairly up-to-speed with developments in CSS. Digging into responsive images has been a nice reminder that there is a whole raft of standards that I’ve ignored and know very little about. I’m now itching to explore some of the other stuff that I remember being discussed and has since matured. I expect I’ll have a play with with SVG (a lot has changed), web components and browser APIs for audio, motion and the like sometime.&lt;/p&gt;
&lt;p&gt;That said, it wasn’t really my intention to go this deep into any performance-related stuff at this stage of the redesign, so while I’m tempted to follow up on this with a load more optimisation I’m gong to force myself back into design mode for a little while and focus on getting the non-blog bits of the site looking nice. Until next time.&lt;/p&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a href=&quot;#footnote-label&quot; aria-label=&quot;Link to “Footnotes”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;There was an excellent article on this at the time, but I can’t seem to find it. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item><item><title>Website Redesign Part XI - Code Blocks and Section Rules</title><link>https://danny.is/writing/website-redesign-xi/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-xi/</guid><pubDate>Sun, 28 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I spent a long time [deciding on a typeface]({% post_url 2020-06-20-website-redesign-v %}) for these essays and I don’t want to spend nearly as long on code snippets. Although this series involves a lot of code snippets, I generally write about code a lot less than I used to, so it doesn’t make sense to spend ages on this. I use the wonderful &lt;a href=&quot;https://www.typography.com/blog/introducing-operator&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Operator Mono&lt;/a&gt; in my text editor and I briefly considered paying for a web license so I could use it here too. But while &lt;em&gt;I&lt;/em&gt; love it, it’s quite unusual and is definitely not suited to everyone’s taste. Both the typeface itself and the act of buying a web license are the opposite of simple, one of my stated values for this site. So instead, I’ll stick with the same requirements I used before: open source, ideally with variable and static versions, with nice ligatures and a range of weights.&lt;/p&gt;
&lt;p&gt;The first thing that came to mind was Fira Code, an extension of Mozilla’s Fira Mono which was designed specifically for legibility. Fira Code adds a bunch of ligatures for character combinations commonly used in programming, as well as making a few adjustments to make code more readable.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-28-ligatures.png&quot; alt=&quot;Fira Code Ligature Sample&quot;&gt;
&lt;img src=&quot;/uploads/2020-06-28-typographics.png&quot; alt=&quot;Fira Code Typography Sample&quot;&gt;
&lt;h2 id=&quot;the-css&quot;&gt;The CSS&lt;a href=&quot;#the-css&quot; aria-label=&quot;Link to “The CSS”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although Fira Code is available via google Fonts it doesn’t support all the opentype features I want, so I ended up cloning the repo and moving TFF, WOFF and WOFF2 versions into the the assets folder like I did for Literata. They’re included in &lt;code&gt;webfonts.scss&lt;/code&gt; and have a custom property set just like the other fonts too.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;root&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;--font-code&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Fira Code&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Inconsolata&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;monospace&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;@supports (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;--font-code&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Fira Code Variable&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Inconsolata&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;monospace&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;:root {  --font-code: &amp;#x27;Fira Code&amp;#x27;, &amp;#x27;Inconsolata&amp;#x27;, monospace;  @supports (font-variation-settings: normal) {    --font-code: &amp;#x27;Fira Code Variable&amp;#x27;, &amp;#x27;Inconsolata&amp;#x27;, monospace;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Since we want to use Fira code &lt;em&gt;everywhere&lt;/em&gt; there’s code, we can set this as the default font for all &lt;code&gt;code&lt;/code&gt; elements, whether inside essays or not. We can also use some of the advanced stylistic settings to turn on a sans-serif lowercase ‘r’ and put gaps in double equals signs. I think this makes code a little more readable.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-family&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--font-code);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-feature-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;ss01&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;ss08&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// sso1 = sans-serif lowercase &quot;r&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// ss08 = Gaps in double/tripple equals&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// See https://github.com/tonsky/FiraCode/wiki/How-to-enable-stylistic-sets&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;code {  font-family: var(--font-code);  font-feature-settings: &amp;#x27;ss01&amp;#x27;, &amp;#x27;ss08&amp;#x27;;  // sso1 = sans-serif lowercase &amp;quot;r&amp;quot;  // ss08 = Gaps in double/tripple equals  // See https://github.com/tonsky/FiraCode/wiki/How-to-enable-stylistic-sets}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Fira code has a bunch of other features like this&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-28-stylistic-sets.png&quot; alt=&quot;Fira Code Stylistic Settings&quot;&gt;
&lt;h3 id=&quot;styling-inline-code&quot;&gt;Styling inline code&lt;a href=&quot;#styling-inline-code&quot; aria-label=&quot;Link to “Styling inline code”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We’ll set the font size to the same as the samm-caps we’re using for abbreviations, which is about equal to the x-height of the body copy. Adding a border, background and a little padding to the left and right is the simplest thing I can think of to differentiate inline code snippets. We’ve also prevented breaking spaces with &lt;code&gt;white-space: pre&lt;/code&gt;, though I might need to revisit this if I end up writing some very long inline code snippets.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; code&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.85&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.5&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;ch&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;background-color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rgba&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.05&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;solid&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rgba&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border-radius&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;white-space&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;pre&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay code {  font-size: 0.85em;  padding: 0 0.5ch;  background-color: rgba(0, 0, 0, 0.05);  border: 1px solid rgba(0, 0, 0, 0.1);  border-radius: 1px;  white-space: pre;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;styling-code-blocks&quot;&gt;Styling code blocks&lt;a href=&quot;#styling-code-blocks&quot; aria-label=&quot;Link to “Styling code blocks”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We already set up some basic styles for &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; elements, so all we need to do is remove the styles we just set for any &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; elements that appear within them.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;pre&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;solid&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rgba&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--base-lh)) &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rgba&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.05&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;overflow-x&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;scroll&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;background-color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;transparent&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;pre {  border: 1px solid rgba(0, 0, 0, 0.2);  padding: calc((1em * var(--base-lh)) - 2px);  background: rgba(0, 0, 0, 0.05);  overflow-x: scroll;  code {    padding: 0;    background-color: transparent;    border: none;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;img src=&quot;/uploads/2020-06-28-codeblocks-screenshot.png&quot; alt=&quot;Screenshot of code block&quot;&gt;
&lt;p&gt;The final thing to do is add syntax highlighting. Since we’re using Jekyll, any fenced code blocks that have a language defined on them will use &lt;a href=&quot;http://rouge.jneen.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Rouge&lt;/a&gt; to apply syntax-highlighing classes in the same way &lt;a href=&quot;https://pygments.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pygments&lt;/a&gt; does. This means we can take an existing theme stylesheet and include it in our bundle. I used &lt;a href=&quot;https://github.com/jwarby/jekyll-pygments-themes/blob/master/colorful.css&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this one&lt;/a&gt; and tweaked a few of the colours slightly. I’ll come back to this once I have a colour palette for the site.&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/dannysmith/72a142f2d84f8c6768c6dff33277b5db.js&quot;&gt;&lt;/script&gt;
&lt;h2 id=&quot;horizontal-rules&quot;&gt;Horizontal Rules&lt;a href=&quot;#horizontal-rules&quot; aria-label=&quot;Link to “Horizontal Rules”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve always liked the section mark (§) and thought it might be nice to superimpose this on horizontal rules. The code for this is pretty simple – we’ll add a psuedo-element containing the mark and use flexbox to position the hr and psuedo element on top of each other. A bit of padding and a background on the psuedo element creates a gap in the line, and a little nudging with &lt;code&gt;translateY&lt;/code&gt; vertically centres the mark.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-28-hr.png&quot; alt=&quot;Horizontal Rule&quot;&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;hr&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;align-items&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;justify-content&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border-bottom&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;solid&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rgba&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;before&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;content:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;§&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;transform&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;translateY&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;-0.15&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-white);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.5&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay hr {  display: flex;  align-items: center;  justify-content: center;  border: 0;  border-bottom: 1px solid rgba(0, 0, 0, 0.2);  &amp;amp;:before {    content: &amp;#x27;§&amp;#x27;;    transform: translateY(-0.15em);    background: var(--color-white);    padding: 0 0.5em;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It would also be nice to add a little more margin than usual either side of the rule. Since we’re using &lt;strong&gt;only&lt;/strong&gt; top-margin to maintain vertical rhythm, we need to use the sibling selector to add margin to the element immediatly following the &lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt; as well as the rule itself.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; hr&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; hr + *&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-top&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--base-lh));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay hr,.essay hr + * {  margin-top: calc(2em * var(--base-lh));}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
</content:encoded></item><item><title>Website Redesign Part IX - Lists</title><link>https://danny.is/writing/website-redesign-ix/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-ix/</guid><pubDate>Thu, 25 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Lists are incredibly useful for certain sorts of article. I tend to make quite heavy use of lists in some of my articles, so it’s important that I give some thought to how they’re styled. Since I’m going for a “bookish” vibe, I’d rather keep the vertical rhythm the same as for all the other text on the page, so I’ll be sticking with the standard top-margin of 1 × &lt;em&gt;line height&lt;/em&gt; and leaving the margin and line height of the list items as they are. With that decision made, we only need to worry about styling the list markers: bullets, numbers and checkboxes.&lt;/p&gt;
&lt;p&gt;After a bit of playing around, I think it’s a bit too jarring to hand the markers in the margin and flush-align the text. If this was for print it might look less wierd, but we’re so used to seing indented lists on the web that it just feels wrong for this medium. So we’ll aim to flish-align the markers and set 1em of left-padding on all list items. As long as we keep the markers set to &lt;code&gt;outside&lt;/code&gt; we’ll end up with the start of out text neatly aligned at 1em from the left margin.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay li,.essay li {  padding-left: 1em;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;bulleted-lists&quot;&gt;Bulleted Lists&lt;a href=&quot;#bulleted-lists&quot; aria-label=&quot;Link to “Bulleted Lists”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After a bit of playing around, I’ve decided to keep the bullets super sumple. We’ll just use a mid-sized bullet and ad a little left padding to pull them inline with the margin.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; ul&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.4&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;list-style-type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;•&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay ul {  padding-left: 0.4em;  list-style-type: &amp;#x27;•&amp;#x27;;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Annoyingly, iOS Safari and Chrome don’t yet support string values for &lt;code&gt;list-style-type&lt;/code&gt;, but since they both support &lt;code&gt;@supports&lt;/code&gt;, we can add a fallback.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@supports&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;list-style-type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;•&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.task-list&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// Begin fix for browsers that don&apos;t support list-style-type: &amp;lt;string&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;list-style-type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.9&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.5&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;position&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;relative&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;before&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;content:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;•&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;position&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;absolute&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;-1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// End fix&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;@supports (not (list-style-type: &amp;#x27;•&amp;#x27;)) {  &amp;amp;:not(.task-list) {    // Begin fix for browsers that don&amp;#x27;t support list-style-type: &lt;string&gt;    list-style-type: none;    padding-left: 0.9em;    li {      padding-left: 0.5em;      position: relative;      &amp;amp;:before {        content: &amp;#x27;•&amp;#x27;;        position: absolute;        left: -1em;      }    }    // End fix  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;numbered-lists&quot;&gt;Numbered Lists&lt;a href=&quot;#numbered-lists&quot; aria-label=&quot;Link to “Numbered Lists”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’re decreasing both the size and optical size for numbers to the same as we did for for &lt;code&gt;abbr&lt;/code&gt; elements, and falling back to the &lt;em&gt;Literata Caption&lt;/em&gt; if variable fonts aren’t supported. We’re also making them slightly bolder than abbreviation since, as markers, it’s important they stand out a little more. We’re then adding some left padding to bring the first character inline with the left margin: single digit numbers will be flush with the margin while extra digits will extend outwards to the left.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit.&lt;/li&gt;
&lt;li&gt;Aliquam tincidunt mauris eu risus.&lt;/li&gt;
&lt;li&gt;Vestibulum auctor dapibus neque.&lt;/li&gt;
&lt;li&gt;Nunc dignissim risus id metus.&lt;/li&gt;
&lt;li&gt;Cras ornare tristique elit.&lt;/li&gt;
&lt;li&gt;Vivamus vestibulum ntulla nec ante.&lt;/li&gt;
&lt;li&gt;Praesent placerat risus quis eros.&lt;/li&gt;
&lt;li&gt;Fusce pellentesque suscipit nibh.&lt;/li&gt;
&lt;li&gt;Integer vitae libero ac risus egestas placerat.&lt;/li&gt;
&lt;li&gt;Vestibulum commodo felis quis tortor.&lt;/li&gt;
&lt;li&gt;Ut aliquam sollicitudin leo.&lt;/li&gt;
&lt;li&gt;Cras iaculis ultricies nulla.&lt;/li&gt;
&lt;li&gt;Donec quis dui at dolor tempor interdum.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;ol&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.8&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;marker&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.85&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-family&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Literata Caption&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;Georgia&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Times New Roman&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;serif&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;600&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@supports&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-family&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Literata Variable&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;Georgia&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Times New Roman&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;serif&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;opsz&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;610&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay ol {  padding-left: 0.8em;  ::marker {    font-size: 0.85em;    font-family: &amp;#x27;Literata Caption&amp;#x27;, Georgia, &amp;#x27;Times New Roman&amp;#x27;, serif;    font-weight: 600;    @supports (font-variation-settings: normal) {      font-family: &amp;#x27;Literata Variable&amp;#x27;, Georgia, &amp;#x27;Times New Roman&amp;#x27;, serif;      font-variation-settings: &amp;#x27;opsz&amp;#x27; 10;      font-weight: 610;    }  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;checklists&quot;&gt;Checklists&lt;a href=&quot;#checklists&quot; aria-label=&quot;Link to “Checklists”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Jekyll allows us to use &lt;a href=&quot;https://github.github.com/gfm/#task-list-items-extension-&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github Flavoured Markdown task lists&lt;/a&gt;, and I occasionally make use of this in my articles. They work like this&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;- [ ] Item 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;- [&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;] Item 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;- [ ] Item 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;- [ ] Item 1- [x] Item 2- [ ] Item 3&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will produce the following output and HTML&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Item 1&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Item 2&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Item 3&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list-item&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;checkbox&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list-item-checkbox&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; /&amp;gt;Item 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list-item&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;input&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;checkbox&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list-item-checkbox&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;checked&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;checked&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;/&amp;gt;Callout inline styles&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list-item&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;checkbox&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list-item-checkbox&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; /&amp;gt;Item 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list-item&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;checkbox&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;task-list-item-checkbox&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; /&amp;gt;Item 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&lt;ul class=&amp;quot;task-list&amp;quot;&gt;  &lt;li class=&amp;quot;task-list-item&amp;quot;&gt;    &lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;task-list-item-checkbox&amp;quot; disabled=&amp;quot;disabled&amp;quot; /&gt;Item 1  &lt;/li&gt;  &lt;li class=&amp;quot;task-list-item&amp;quot;&gt;    &lt;input      type=&amp;quot;checkbox&amp;quot;      class=&amp;quot;task-list-item-checkbox&amp;quot;      disabled=&amp;quot;disabled&amp;quot;      checked=&amp;quot;checked&amp;quot;    /&gt;Callout inline styles  &lt;/li&gt;  &lt;li class=&amp;quot;task-list-item&amp;quot;&gt;    &lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;task-list-item-checkbox&amp;quot; disabled=&amp;quot;disabled&amp;quot; /&gt;Item 2  &lt;/li&gt;  &lt;li class=&amp;quot;task-list-item&amp;quot;&gt;    &lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;task-list-item-checkbox&amp;quot; disabled=&amp;quot;disabled&amp;quot; /&gt;Item 3  &lt;/li&gt;&lt;/ul&gt;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We can use these generated classes to style the checkboxes. We’ll remove the default list styles and padding and set up each &lt;code&gt;li&lt;/code&gt; as a flex container. Each have two flex items – the checkbox and the text. This ensures that the text doesn’t wrap dowin under the checkbox for multi-line items.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.task-list&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;list-style-type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.task-list-item&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;position&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;relative&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;align-content&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.task-list {  list-style-type: none;  padding-left: 0;}.task-list-item {  position: relative;  display: flex;  align-content: center;  padding-left: 0;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next, we’ll remove the default appearance, set a hight and width and nudge the box with some margin so it’s about the same height as lowercase characters and is aligned a tiny bit above the baseline. We’ll also add a border of our own.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.task-list-item-checkbox&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.5&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.7&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.7&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;min-width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.7&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;/* Prevent flex-shrinking */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;-webkit-appearance&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;-moz-appearance&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;-o-appearance&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;appearance&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;outline&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;solid&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rgba&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.task-list-item-checkbox {  margin: 0.5em 1em 0 0;  height: 0.7em;  width: 0.7em;  min-width: 0.7em; /* Prevent flex-shrinking */  -webkit-appearance: none;  -moz-appearance: none;  -o-appearance: none;  appearance: none;  outline: none;  border: 1px solid rgba(0, 0, 0, 0.3);}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, we’ll add a tick (✓) to any that are checked, style it and position it properly within the box.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.task-list-item-checkbox&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;checked&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;before&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\2713&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;position&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;absolute&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;top&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.645&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.6&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;600&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.task-list-item-checkbox:checked:before {  content: &amp;#x27;\2713&amp;#x27;;  position: absolute;  top: 0.645em;  font-size: 0.6em;  font-weight: 600;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To make this work properly on iOS, we also need to explicitly set the background and border-radius, and bump the opacity of disabled checkboxes up to 1.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.task-list-item-checkbox&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border-radius&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;transparent&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;//...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.task-list-item-checkbox&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;] {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;opacity&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.task-list-item-checkbox {  // ...  border-radius: 0px;  background: transparent;  //...}.task-list-item-checkbox[disabled] {  opacity: 1;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And that just about wraps up styling for lists. I’m sure I’ll revisit some of this when I start to add colour to the site, but for now this is a definite improvement over the defaults.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part X - Links</title><link>https://danny.is/writing/website-redesign-x/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-x/</guid><pubDate>Thu, 25 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hyperlinks are what makes the web special. They allow us move seamlessley from idea to idea at our own pace. While we tend to read books sequentially, websites are more like choose your own adventure books – hyperlinks let us choose how we experience them. And it’s that way by &lt;strong&gt;by default&lt;/strong&gt;. So links should be pretty prominent on this site. But at the same time, an article peppered with bright blue links is not easy on the eye. Where links are incidental to the text (as many are), they interrupt the flow of reading. I’ve spent a fair bit of time thinking about how I want links to work and I basically have two requirements.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They must get out of the way when reading and not be a distraction.&lt;/li&gt;
&lt;li&gt;They should obviously be links for users who are looking for them. This includes differentiating between unvisited and visited links.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s start off by removing styles from hyperlinks that have a class. This gives us a good starting point for styling UI links and also deals niceley with the footnote links that jekyll creates.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;] {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;inherit&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;inherit&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;a[class] {  text-decoration: inherit;  color: inherit;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The default clours in most browsers are blue for unvisited links and purple for visited links, so I like the idea of sticking with that since it’s alrreasy well-understood by most users. Rather than colouring the whole word, we might be able to get away with colouring just the underline. This makes the links far less intrusive. we can also pick much less saturated colours than the default.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-25-links-desktop.png&quot; alt=&quot;Links with coloured underlines&quot;&gt;
&lt;img src=&quot;/uploads/2020-06-25-link-visited-desktop.png&quot; alt=&quot;Visited link with coloured underline&quot;&gt;
&lt;p&gt;The hover styles can afford to be a lot bolder and brighter. Something like…&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-25-link-activehover.png&quot; alt=&quot;Active and Hover style for links&quot;&gt;
&lt;p&gt;We can achieve this by setting links to inherit their text colour from their parent and styling the underline with &lt;code&gt;text-decoration-color&lt;/code&gt;. We’ll also add a subtle transition on hover and force the underline width to 1px on browser that support it.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;--color-link-underline: rgba(111, 131, 222, 0&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.5&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;--color-visited-link-underline: rgba(188, 127, 219, 0&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.5&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;--color-active-link-underline: rgb(255, 30, 28);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;]) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;inherit&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration-color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-link-underline);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration-thickness&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;transition&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.25&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;visited&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration-color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-visited-link-underline);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;hover&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;active&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration-color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-active-link-underline);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-active-link-text);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;--color-link-underline: rgba(111, 131, 222, 0.5);--color-visited-link-underline: rgba(188, 127, 219, 0.5);--color-active-link-underline: rgb(255, 30, 28);a:not([class]) {  color: inherit;  text-decoration-color: var(--color-link-underline);  text-decoration-thickness: 1px;  transition: all 0.25s;  &amp;amp;:visited {    text-decoration-color: var(--color-visited-link-underline);  }  &amp;amp;:hover,  &amp;amp;:active {    text-decoration-color: var(--color-active-link-underline);    color: var(--color-active-link-text);  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This feels okay on devices with a pointer. Although the underlines are subtle, if the user isn’t sure it’s a link, they can mouse over and the transation will make it pretty clear. But on touch devices this is impossible, so it feels sensible to make the links a little more ovbious somehow. On mobile devces the text also has a much smaller measure and therefore more multi-line links, whihc looks kinda ugly.&lt;/p&gt;
&lt;p&gt;We can alter our code to cremove the underline on touch devices and instead change the text-colour. I’m using a similar blue and purple here, but it’s nececarrily much darker and without an alpha channel. I tried to use a colour that didn’t affect the typographic colour of the text. If you try squinting and looking at the mobile version the links shouldn’t feel lighter or heavier than the rest of the text.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-25-links-mobile.png&quot; alt=&quot;2020-06-25-links-mobile&quot;&gt;
&lt;img src=&quot;/uploads/2020-06-25-squint.png&quot; alt=&quot;Squint Test&quot;&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;]) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;//...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@media&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (pointer&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; coarse) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-link-text);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;visited&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration-color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-visited-link-underline);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@media&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (pointer&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; coarse) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-visited-link-text);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;//...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;a:not([class]) {  //...  @media (pointer: coarse) {    text-decoration: none;    color: var(--color-link-text);  }  &amp;amp;:visited {    text-decoration-color: var(--color-visited-link-underline);    @media (pointer: coarse) {      color: var(--color-visited-link-text);    }  }  //...}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, I want to do something about the outline browsers apply when a link is focussed by keyboard. I don’t want to do too much with this for accessibility reasosns, so we’ll just change it from blue to our brand red using the &lt;a href=&quot;https://css-tricks.com/keyboard-only-focus-styles/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;focus visible psutedo-selector&lt;/a&gt;. I’ll revisit keyboard navigation once I have a color palette for the site.&lt;/p&gt;
&lt;img src=&quot;/uploads/iso-link-focus.png&quot; alt=&quot;Outline Example&quot;&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;focus-visible&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;outline&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;solid&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--color-red-trans50);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;a:focus-visible {  outline: 3px solid var(--color-red-trans50);}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
</content:encoded></item><item><title>Website Redesign Part VII - Responsive Typography</title><link>https://danny.is/writing/website-redesign-vii/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-vii/</guid><pubDate>Mon, 22 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Having settled on a typeface for my essays, it’s time to set up some basic typography. In the past, I’ve spent a lot of time building maths-based ype systems with myriad variables and functions and it’s so tempting to go down that route this time and try to create a system that caters to every eventuality. But that would be at odds with my &lt;strong&gt;simplicity&lt;/strong&gt; value, so for the time being I’ll stick to the basics. We’re going to set up a base font size and line height and scale that as the viewport grows. We’ll also add just enough whitespace to create a nice vertical rhythm and create some visual hierachy by styling the headings. Everything else can wait.&lt;/p&gt;
&lt;h2 id=&quot;base-font-size&quot;&gt;Base font-size&lt;a href=&quot;#base-font-size&quot; aria-label=&quot;Link to “Base font-size”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’ll start off by setting the font-size of the HTML element to 100%. This makes sure that &lt;code&gt;1rem&lt;/code&gt; will always represent the default font-size of the device or browser - usually 16px. We’ll use rems and ems to define pretty much every other size in the site, so we’ll also set the body &lt;code&gt;font-size&lt;/code&gt; in rems so it’s inherited by other elements.&lt;/p&gt;
&lt;p&gt;We also want to reset the heading sizes to &lt;code&gt;1em&lt;/code&gt;. We’ll probably redefine some of these later but I always think it’s a good idea to start with one size for &lt;em&gt;all elements&lt;/em&gt;, since it often makes sense to differentiate headings with weight, style or color rather than size. We’re using &lt;code&gt;ems&lt;/code&gt; rather than &lt;code&gt;rems&lt;/code&gt; here because we want them to scale if we change the font-size on their container.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;rem&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h5&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;html {  font-size: 100%;}body {  font-size: 1rem;}h1,h2,h3,h4,h5 {  font-size: 1em;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;scaling-type-size&quot;&gt;Scaling Type Size&lt;a href=&quot;#scaling-type-size&quot; aria-label=&quot;Link to “Scaling Type Size”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We can use the new CSS &lt;code&gt;clamp()&lt;/code&gt; function to enable responsive type without the need for a million media queries. This will scale the type from &lt;code&gt;1rem&lt;/code&gt; (16px) to &lt;code&gt;1.375rems&lt;/code&gt; (22px). The middle value is calculated using both viewport units and rems: the &lt;code&gt;vw&lt;/code&gt; causes the type to scale with the viewport width and the rems ensure that user-initiated text resizing still works. I arrived at &lt;code&gt;0.6rem&lt;/code&gt; after a bit of experimentation.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;clamp&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;rem&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.6&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;rem&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;vw&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1.375&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;rem&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;transition&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; font-size &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay {  font-size: clamp(1rem, calc(0.6rem + 1vw), 1.375rem);  transition: font-size 0.1s;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;hyphens&quot;&gt;Hyphens&lt;a href=&quot;#hyphens&quot; aria-label=&quot;Link to “Hyphens”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Enabling hyphenation can make a big difference to readability on small screens, where an uneven right rag can cause very short lines. We can set up a SCSS variable for breakpoints and then use a media query to turn hyphenation off for larger viewports.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;$breakpoint-m&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;-webkit-hyphens&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;auto&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;hyphens&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;auto&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@media&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;min-width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; $breakpoint-m) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;-webkit-hyphens&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;hyphens&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;$breakpoint-m: 42em;.essay &gt; p {  -webkit-hyphens: auto;  hyphens: auto;  @media (min-width: $breakpoint-m) {    -webkit-hyphens: none;    hyphens: none;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;vertical-rhythm&quot;&gt;Vertical rhythm&lt;a href=&quot;#vertical-rhythm&quot; aria-label=&quot;Link to “Vertical rhythm”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The main purpose of a baseline grid is, unsusprisingly, &lt;strong&gt;aligning the baselines of text&lt;/strong&gt;. This makes a huge difference when we have multiple columns of text with differing fonts, sizes and the like.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-21-baseline1.png&quot; alt=&quot;Example 1: aligning text to a baseline &quot;&gt;
&lt;img src=&quot;/uploads/2020-06-21-baseline2.png&quot; alt=&quot;Example 2: aligning text to a baseline &quot;&gt;
&lt;p&gt;While this makes a huge difference to the feel of a page, the fluid nature of web pages (and the way line-height works) makes this quite hard to do consistently. We can avoid this problem entirely by only having one column.&lt;/p&gt;
&lt;p&gt;But baseline grids can also provide a sense of vertical rhythm – something we can do on the web without too much effort. We just need to work out how high each of our lines is and use multiples of that measurement to space things out vertically. (It can also make a good measurement for horizontal spacing.)&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-21-baseline-margins.png&quot; alt=&quot;Example: spacing with a baseline grid&quot;&gt;
&lt;p&gt;For the time being, we’ll keep our vertical rhythm super simple. First, we’ll remove all top and bottom margins - I’ve chosen to do this in my base stylesheet rather than just for the essays. Then we’ll set up a custom property to hold our base line-height and use that to set the line-height for all the elements in the essay container.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;before&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;after&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-top&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-bottom&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;root&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;--base-lh&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1.5&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; *&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;line-height&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--base-lh);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;*,*::before,*::after {  margin-top: 0;  margin-bottom: 0;}:root {  --base-lh: 1.5;}.essay &gt; * {  line-height: var(--base-lh);}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We can now use this base line-height to set margins on our headings, paragraphs and the like. We can use the &lt;a href=&quot;https://alistapart.com/article/axiomatic-css-and-lobotomized-owls/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;lobotomized owl selector&lt;/a&gt; to target all elements that have something above them and add a margin equal to our line-height. For now, we’re using ems so the margins will scale with the font-size.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;* + *&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-top&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--base-lh));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&gt; * + * {  margin-top: calc(1em * var(--base-lh));}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We can add top-margin in the same way to other elements. By sticking with top-margin and never using &lt;code&gt;margin-bottom&lt;/code&gt;, we have a simpler mental model for how spaces between elements are calculated: we never have to worry about margin collapsing or make a decision on whether to use top or bottom margin.&lt;/p&gt;
&lt;p&gt;While we’re here, I also used this tto set some horizintal padding on blockquotes&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;blockquote&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;solid&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rgba&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--base-lh)) - &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-style&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;italic&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-left&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;blockquote {  border-left: 2px solid rgba(0, 0, 0, 0.2);  padding-left: calc((1em * var(--base-lh)) - 2px);  font-style: italic;  margin-left: 0;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;styling-headings&quot;&gt;Styling Headings&lt;a href=&quot;#styling-headings&quot; aria-label=&quot;Link to “Styling Headings”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Looking through my articles I dont’t tend to use H1s very often at all, apart from as the article title. This makes sense from a semantic point of view so we can assume that all H1s inside &lt;code&gt;.essay&lt;/code&gt; are an article title. I tend to use H2 and H3 fairly often to break my articles into sections, and H4s often appear in logner articles. I rarely, if ever use H5-6.&lt;/p&gt;
&lt;p&gt;With this in mind, it makes sense to keep things super-simple. Here’s what I initially settled on…&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-22-heading-type.png&quot; alt=&quot;Screenshot of headings&quot;&gt;
&lt;p&gt;Aside from H1s, all are set at the same size as the body copy. Five and Six are styled identically in italic with the same weight and margins as paragraphs. Four is set in small-caps with a semibold weight, again with the same margin as paragraphs. Three and Two are both set in bold with a bigger top margin, two being differentiated by being uppercase.&lt;/p&gt;
&lt;p&gt;The article title is bigger, and increases is size on larger viewports.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; h1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;rem&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;@media (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;min-width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; $breakpoint-m) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;rem&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;font-weight: 500;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;font-variation-settings: &apos;opsz&apos; 50;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;line-height: 1.3;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; h2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;bold&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-transform&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;uppercase&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;letter-spacing&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.15&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;ch&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-top&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--base-lh));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; h3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;bold&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin-top&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--base-lh));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; h4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;600&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-transform&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;lowercase&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variant&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;small-caps&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;letter-spacing&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.05&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;ch&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; h5&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; h6&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-style&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;italic&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay &gt; h1 {  font-size: 2rem;  @media (min-width: $breakpoint-m) {    font-size: 3rem;  }  font-weight: 500;  font-variation-settings: &amp;#x27;opsz&amp;#x27; 50;  line-height: 1.3;}.essay &gt; h2 {  font-weight: bold;  text-transform: uppercase;  letter-spacing: 0.15ch;  margin-top: calc(2em * var(--base-lh));}.essay &gt; h3 {  font-weight: bold;  margin-top: calc(2em * var(--base-lh));}.essay &gt; h4 {  font-weight: 600;  text-transform: lowercase;  font-variant: small-caps;  letter-spacing: 0.05ch;}.essay &gt; h5,.essay &gt; h6 {  font-weight: normal;  font-style: italic;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I’ve also turned off styling for any links inside headings by having them inherit their styles from the heading elements.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h5&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;visited&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;hover&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;active&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;inherit&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;inherit&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;h1,h2,h3,h4,h5 {  a,  a:visited,  a:hover,  a:active {    color: inherit;    text-decoration: inherit;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I’m generally pretty happy with this, except for one thing. I use level two headings more than any other and because I’ve set them in uppercase I’m missing out on the loveley discretionary ligatures that Literata includes. So I might want to revisit this soon.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part VIII - Adding some style</title><link>https://danny.is/writing/website-redesign-viii/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-viii/</guid><pubDate>Mon, 22 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The last part focussed on setting up a basic typographic system using margins and heading styles. In this part, I’m going to make a bunch of little tweaks to the typography of these essays. Over the next few months I’ll probably make a whole host of little incremental changes like this.&lt;/p&gt;
&lt;h2 id=&quot;old-style-figures--fractions&quot;&gt;Old-style Figures &amp;amp; Fractions&lt;a href=&quot;#old-style-figures--fractions&quot; aria-label=&quot;Link to “Old-style Figures &amp;amp; Fractions”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most good typefaces contain two sorts of figures. Lining figures are designed to sit on the baseline and are generally all the same height. They’re great for headings, tables and UI. Old-style figures overhang the baseline and often vary in height. These are great for setting numbers in body copy.&lt;/p&gt;
&lt;img src=&quot;/uploads/A_Figures.png&quot; alt=&quot;A_Figures&quot;&gt;
&lt;p&gt;I’m using the default lining figures in most places, but I want to use old-style figures in paragraphs, lists and blockquotes (eg. 1957). I also want to use proper fractions like this → 3/4.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; ol&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; ul&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt; &amp;gt; blockquote&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variant-numeric&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;oldstyle-nums&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;diagonal-fractions&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay &gt; p,.essay &gt; ol,.essay &gt; ul,.essay &gt; blockquote {  font-variant-numeric: oldstyle-nums diagonal-fractions;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;bold&quot;&gt;Bold&lt;a href=&quot;#bold&quot; aria-label=&quot;Link to “Bold”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Where variable fonts are supported, I’m making strong tags a little bolder than normal.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;strong&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;600&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@supports&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;625&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;strong {  font-weight: 600;  @supports (font-variation-settings: normal) {    font-weight: 625;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;abbreviaions&quot;&gt;Abbreviaions&lt;a href=&quot;#abbreviaions&quot; aria-label=&quot;Link to “Abbreviaions”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Abbreviations like &lt;abbr title=&quot;Hyper Text Markup Language&quot;&gt;HTML&lt;/abbr&gt; nearly always look best rendered in small caps with a little space between the letters, so I’m doing that for any &lt;code&gt;&amp;lt;abbr&amp;gt;&lt;/code&gt; elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use small caps and transform the text to lowercase to we don’t get any &lt;em&gt;proper&lt;/em&gt; caps mixed in.&lt;/li&gt;
&lt;li&gt;Space out the letters a bit (I’m using &lt;code&gt;ch&lt;/code&gt; here, which is based on the width of the “0” character).&lt;/li&gt;
&lt;li&gt;Use a low optical size and fall back to &lt;em&gt;Literata Caption&lt;/em&gt; where variable fonts aren’t supported.&lt;/li&gt;
&lt;li&gt;Use a slightly heavier weight than normal - roughly equivilent to Semibold.&lt;/li&gt;
&lt;li&gt;Set the cursor to &lt;em&gt;help&lt;/em&gt; is it has a title attribute.&lt;/li&gt;
&lt;li&gt;Make the dotted underline almost invisible, and position it under the baseline rather than on the baseline.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;abbr&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-transform&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;lowercase&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variant&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;small-caps&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;letter-spacing&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.05&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;ch&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-family&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Literata Caption&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;Georgia&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Times New Roman&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;serif&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;400&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.85&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@supports&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-family&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Literata Variable&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;Georgia&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Times New Roman&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;serif&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;opsz&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;410&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-decoration-color&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;rgba&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.15&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-underline-position&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;under&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;] {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;cursor:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;help&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay abbr {  text-transform: lowercase;  font-variant: small-caps;  letter-spacing: 0.05ch;  font-family: &amp;#x27;Literata Caption&amp;#x27;, Georgia, &amp;#x27;Times New Roman&amp;#x27;, serif;  font-weight: 400;  font-size: 0.85em;  @supports (font-variation-settings: normal) {    font-family: &amp;#x27;Literata Variable&amp;#x27;, Georgia, &amp;#x27;Times New Roman&amp;#x27;, serif;    font-variation-settings: &amp;#x27;opsz&amp;#x27; 10;    font-weight: 410;  }  text-decoration-color: rgba(0, 0, 0, 0.15);  text-underline-position: under;  &amp;amp;[title] {    cursor: help;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;first-line--drop-caps&quot;&gt;First line &amp;amp; Drop Caps&lt;a href=&quot;#first-line--drop-caps&quot; aria-label=&quot;Link to “First line &amp;amp; Drop Caps”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’m experimenting with adding drop-cap and small caps for the first line. I’m not yet sure whether I really like this, so will probably revisit later on. I’ve also added &lt;a href=&quot;https://css-tricks.com/almanac/properties/h/hanging-punctuation/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;hanging punctuation&lt;/a&gt; for paragraphs (only supported by Safari at the moment)&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-22-drop-cap.png&quot; alt=&quot;Example: Drop Caps&quot;&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &amp;gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;first-of-type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;first-line&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-transform&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;lowercase&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variant-caps&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;small-caps&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;500&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@supports&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;525&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;first-of-type&lt;/span&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;first-letter&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;text-transform&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;uppercase&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;left&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;line-height&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;padding-right&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0.5&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;ch&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay &gt; p {  &amp;amp;:first-of-type::first-line {    text-transform: lowercase;    font-variant-caps: small-caps;    font-weight: 500;    @supports (font-variation-settings: normal) {      font-weight: 525;    }  }  &amp;amp;:first-of-type::first-letter {    text-transform: uppercase;    float: left;    font-size: 4em;    line-height: 1;    padding-right: 0.5ch;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s it for now. I’ll cover lists, code blocks and links in future articles.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part VI - Adding a Typeface for Essays</title><link>https://danny.is/writing/website-redesign-vi/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-vi/</guid><pubDate>Sun, 21 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After much messing about with typefaces, I think I’ve narrowed the choice for essay copy down to two contenders: Literata and Lora. Both feel “bookish” and are designed for long-form reading on screens. Both are open source and available as variable fonts as well as static. Version 3 of Literata not only allows us to vary the weight, but also the optical size. The static version has four optical sizes – each in various weights and italic versions.&lt;/p&gt;
&lt;img src=&quot;/uploads/literata-optical-sizes.png&quot; alt=&quot;Optical sizes&quot;&gt;
&lt;p&gt;The caption variant is designed for small sizes (around 7pt), the regular variant for body copy and the deck and display variants are meant for headings. The variable version gives me pretty fine-grained control over this.&lt;/p&gt;
&lt;p&gt;Since I’m not sure which of these two typefaces I prefer, I’ve decided to bring both of them into the website and switch them out regularly as I’m working to see if one starts to grow on me.&lt;/p&gt;
&lt;p&gt;Although I initially wanted to serve fonts via Google Fonts, I’ve changed my mind. One of my technical principles is &lt;em&gt;as few dependancies as possible&lt;/em&gt;, and these are both open source I can host the myself. I was finally tipped into this decision because Google Fonts doesn’t have the latest version of literate available.&lt;/p&gt;
&lt;h2 id=&quot;building-the-fonts&quot;&gt;Building the fonts&lt;a href=&quot;#building-the-fonts&quot; aria-label=&quot;Link to “Building the fonts”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve never had to build fonts from source before, but after much &lt;a href=&quot;https://en.wiktionary.org/wiki/yak_shaving&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;yak shaving&lt;/a&gt; to update my python install and various home-brew packages I was able to run the three build scripts from the Literata repo. It toook a while to work out what they do, but essentially:&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/googlefonts/literata/blob/master/sources/build-static.sh&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;build-static.sh&lt;/code&gt;&lt;/a&gt; builds a bunch of TTF font files from source using the &lt;code&gt;fontmake&lt;/code&gt; utility. This produces a &lt;strong&gt;lot of files&lt;/strong&gt; – one for each weight/style/optical-size combination.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/googlefonts/literata/blob/master/sources/build-var.sh&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;build-var.sh&lt;/code&gt;&lt;/a&gt; does the same, but for the variable fonts. It only produces two TTFs: roman and italic.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/googlefonts/literata/blob/master/sources/build-woff.sh&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;build-woff.sh&lt;/code&gt;&lt;/a&gt; takes the TTFs we just built and makes woff and woff2 versions using the &lt;code&gt;sfnt2woff&lt;/code&gt; and &lt;code&gt;woff2_compress&lt;/code&gt; utilities.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since the Lora repo already includes TFF files, I just tweaked &lt;code&gt;build-woff.sh&lt;/code&gt; a bit and used it to make corresponding woff/woff2 files with the same folder structure as Literata.&lt;/p&gt;
&lt;p&gt;After renaming the wolf and woff2 versions of the variable fonts to remove the brackets, I ended up with this in the &lt;code&gt;fonts&lt;/code&gt; directory of this site…&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-18-font-tree.png&quot; alt=&quot;Screenshot of file tree in fonts directory&quot;&gt;
&lt;p&gt;The whole thing comes in at a whopping 28MB, but that’s okay because I’m not actually gonna use most of these in production. I just want them to play with for the moment.&lt;/p&gt;
&lt;h2 id=&quot;font-face-declarations&quot;&gt;@font-face Declarations&lt;a href=&quot;#font-face-declarations&quot; aria-label=&quot;Link to “@font-face Declarations”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I &lt;a href=&quot;https://github.com/jpamental/moby-dick/blob/master/assets/scss/partials/_00_webfonts.scss&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;borrowed the Literata font-face declarations&lt;/a&gt; from Jason Pamental’s amazing &lt;a href=&quot;https://mobydick.wales/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Moby Dick project&lt;/a&gt; and added some similar ones for Lora. I’m pretty sure I’m gonna borrow a lot more from that project over the next few months. They generally follow this format:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;@font-face&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-family&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Literata Caption&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-style&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;italic&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-weight&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;600&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;../fonts/literata/woff2/Literata-7ptSemiBoldItalic.woff2&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;woff2&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;../fonts/literata/woff/Literata-7ptSemiBoldItalic.woff&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;woff&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-display&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;swap&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;@font-face {  font-family: &amp;#x27;Literata Caption&amp;#x27;;  font-style: italic;  font-weight: 600;  src:    url(&amp;#x27;../fonts/literata/woff2/Literata-7ptSemiBoldItalic.woff2&amp;#x27;) format(&amp;#x27;woff2&amp;#x27;),    url(&amp;#x27;../fonts/literata/woff/Literata-7ptSemiBoldItalic.woff&amp;#x27;) format(&amp;#x27;woff&amp;#x27;);  font-display: swap;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It’s been a long time since I actually looked at a @font-face declaration, so if you need a reminder too:&lt;/p&gt;
&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;We’re writing a declaration for &lt;code&gt;Literata-7ptSemiBoldItalic&lt;/code&gt;. This is the &lt;strong&gt;caption&lt;/strong&gt;-sized version, in Semibold italic.&lt;/li&gt;
&lt;li&gt;The first three lines specify when this font file should be used. In this case when we set &lt;code&gt;font-family: &apos;Literata Caption&apos;&lt;/code&gt; with a weight of 600 and an italic &lt;code&gt;font-style&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;src&lt;/code&gt; attribute specifies the path to the woff2 version first and falls back to the woff version if that’s not supported (woff 2 offers much better compression than woff 1). Since woff is widely supported we’re not including any other fallbacks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-display&lt;/code&gt; tells the browser how to handle font loading. In this case, the browser will render the text in a fallback and then our font in once it’s loaded.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The variable font declarations look much the same, but include two values for &lt;code&gt;font-weight&lt;/code&gt;: the minimum and maximum at which it should be used.&lt;/p&gt;
&lt;h2 id=&quot;using-the-fonts&quot;&gt;Using the fonts&lt;a href=&quot;#using-the-fonts&quot; aria-label=&quot;Link to “Using the fonts”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our current CSS sets the font-family for essays using a custom property.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;.essay&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-family&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(--font-essay);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.essay {  font-family: var(--font-essay);}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As the design evolves, we’ll definitely end up with a more complex system for this but for now we can just update the variable to use Literata. We can also tell browsers that support variable fonts to load that instead of the static version.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9;--0fs:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;root&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;--font-essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Literata&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;Georgia&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Times New Roman&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;serif&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;@supports (&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;--font-essay&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Literata Variable&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;Georgia&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;Times New Roman&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;serif&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;:root {  --font-essay: &amp;#x27;Literata&amp;#x27;, Georgia, &amp;#x27;Times New Roman&amp;#x27;, serif;  @supports (font-variation-settings: normal) {    --font-essay: &amp;#x27;Literata Variable&amp;#x27;, Georgia, &amp;#x27;Times New Roman&amp;#x27;, serif;  }}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I’d like to provide a more robust fallback stack (see &lt;a href=&quot;https://github.com/dannysmith/dannyis-jekyll/issues/28&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this issue&lt;/a&gt;) but for now, we’ll just go to Georgia and TNR.&lt;/p&gt;
&lt;h2 id=&quot;some-basic-open-type-features&quot;&gt;Some basic Open Type Features&lt;a href=&quot;#some-basic-open-type-features&quot; aria-label=&quot;Link to “Some basic Open Type Features”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Literata includes some lovely discretionary ligatures, but we don’t want them in the body copy. I’ve already set &lt;code&gt;font-variant-ligatures: common-ligatures;&lt;/code&gt; on the &lt;code&gt;body&lt;/code&gt; in my base stylesheet, but we’ll turn on discretionary ligatures for headings across the whole site:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;h5&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;font-variant-ligatures&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;common-ligatures&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;discretionary-ligatures&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;h1,h2,h3,h4,h5 {  font-variant-ligatures: common-ligatures discretionary-ligatures;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;img src=&quot;/uploads/2020-06-19-heading-ligs.png&quot; alt=&quot;Screenshot of discretionary ligatures in heading&quot;&gt;
&lt;h2 id=&quot;so-where-are-we-at&quot;&gt;So where are we at?&lt;a href=&quot;#so-where-are-we-at&quot; aria-label=&quot;Link to “So where are we at?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;img src=&quot;/uploads/2020-06-19-final-screenshots.png&quot; alt=&quot;Comparison of Literata and Lora applied to an essay&quot;&gt;
&lt;p&gt;I’ll see which of these two feels best as I work on the site over the next few weeks.&lt;/p&gt;
&lt;h3 id=&quot;reflection-on-learning&quot;&gt;Reflection on Learning&lt;a href=&quot;#reflection-on-learning&quot; aria-label=&quot;Link to “Reflection on Learning”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of my goals for this project was my own learning, and after some reflection on that I’ve learned a fair bit during this wanter into web typography…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I finally read &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Elements_of_Typographic_Style&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Elements of Typographic Style&lt;/a&gt; cover-to-cover.&lt;/li&gt;
&lt;li&gt;I’ve learned a lot about type just by &lt;strong&gt;really looking&lt;/strong&gt; at a bunch of different faces.&lt;/li&gt;
&lt;li&gt;I’ve learned a bit about how digital fonts are made and the tools used to build them.&lt;/li&gt;
&lt;li&gt;I’ve started to get excited about opentype features in CSS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ve also re-learned that it’s easy to get blocked by indecision on a personal project like this. I could have wasted months trying to find the perfect article typeface and still not been happy with it. As with most things, &lt;strong&gt;any decision is better than no decision&lt;/strong&gt;. Especially when it’s reversible.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part V - Choosing a Typeface for Essays</title><link>https://danny.is/writing/website-redesign-v/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-v/</guid><pubDate>Sat, 20 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I love typography, but I’m not that good at it. One day I’ll take a typography course at a a design school, but in the meantime I need to choose some typefaces for this website. I’m fairly confident I’ll need two rather different typefaces in the end. A &lt;strong&gt;essay face&lt;/strong&gt; for setting longer-form writing and a &lt;strong&gt;UI face&lt;/strong&gt; for everything else. I like the idea of using a very modernist face for the UI and something more old-fasioned and bookish for the essays. It might be hard to make this work but it seems to chime will with my personality – I’ve always had a tendancy to mix super-modern with traditional oldy-worldy. Trainers and Tweeds, if you will.&lt;/p&gt;
&lt;p&gt;I’ll explore this in more detail in a future article, but for the time-being let’s revisit the four core values I decided on in Part I: &lt;em&gt;Simple&lt;/em&gt;, &lt;em&gt;Beautiful&lt;/em&gt;, &lt;em&gt;Authentic&lt;/em&gt; and &lt;em&gt;Content-first&lt;/em&gt;. Choosing and pairing typefaces is hard, so to keep things simple we’ll start by looking for one. And that last value makes a strong case for starting with the content.&lt;/p&gt;
&lt;h2 id=&quot;what-do-i-need-from-an-essay-typeface&quot;&gt;What do I need from an Essay Typeface&lt;a href=&quot;#what-do-i-need-from-an-essay-typeface&quot; aria-label=&quot;Link to “What do I need from an Essay Typeface”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The most obious requirement is that it’s very readable. And in particular, &lt;strong&gt;on screens&lt;/strong&gt;. It needs to get out of the way and put the content first. It also needs to work well in dark mode, which probably rules out faces with very high stroke contrast (and therefore thin stokes). And to keep things simple, it should also work for headings, quotes, captions and the like –I’d rather not use a third face for this.&lt;/p&gt;
&lt;p&gt;As far as feel goes, I’m going for &lt;strong&gt;bookish but definitely of the digital world&lt;/strong&gt;. This probably means a serif, but maybe not a really traditional one.&lt;/p&gt;
&lt;p&gt;So I’m going to start by looking for faces that are digital natives (ie not redrawings of print faces), which have been designed specifically for readability on the screen. There are a few technical constraints to think about first, though.&lt;/p&gt;
&lt;h2 id=&quot;technical-constrains--features&quot;&gt;Technical Constrains &amp;amp; Features&lt;a href=&quot;#technical-constrains--features&quot; aria-label=&quot;Link to “Technical Constrains &amp;amp; Features”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It must be open source. While I have a subscription to Adobe Fonts I don’t want this project to depend on a paid service. If I can find something that’s hosted on Google Fonts so much the better. Because &lt;strong&gt;simplicity&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I’d &lt;em&gt;prefer&lt;/em&gt; a variable font. Mostly because they’re new and shiny and fun to play with, but also because it’s likely to reduce total page weight. If I go with a variable font, it also needs to have a static version – not all browsers properly support variable fonts yet.&lt;/p&gt;
&lt;p&gt;I’ll need a good selection of weights in both roman and italic. Probably 200 (light), 400 (normal), 700 (bold) as a minimum. I’ll also want support for the following opentype features&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Proper small caps&lt;/li&gt;
&lt;li&gt;Both Old-style and and Lining figures&lt;/li&gt;
&lt;li&gt;Nice default and discretionary ligatures&lt;/li&gt;
&lt;li&gt;Fractions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It would be nice to have a proper superscript for footnotes, but this is hardly crucial.&lt;/p&gt;
&lt;h2 id=&quot;checklists&quot;&gt;Checklists&lt;a href=&quot;#checklists&quot; aria-label=&quot;Link to “Checklists”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve bubbled all of that down into a couple of checklists to help me evaluate fonts. On reflection, none of the items are really mandatory – it’s important I keep an open mind while I’m exploring. But they’re good starter to help narrow down the field.&lt;/p&gt;
&lt;h3 id=&quot;technical-features&quot;&gt;Technical Features&lt;a href=&quot;#technical-features&quot; aria-label=&quot;Link to “Technical Features”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Open source?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Available on Google Fonts?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Variable Font? (with static version as fallback?)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Wights at least: light (200), normal (400) and bold (700) in italic and bold.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Proper Small Caps&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Old-style and lining figures&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Nice ligatures (including discretionary)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Proper Fractions&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Other OpenType features that might be fun/useful?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;style&quot;&gt;Style&lt;a href=&quot;#style&quot; aria-label=&quot;Link to “Style”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Was it made specifically for the screen?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Does it feel bookish?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Does it still feel kinda modern?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Is it easy to read at very small and very large text-sizes?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Also in light and dark modes?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Does it work on low-res screens?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Is it suitable for both dense paragraph-heavy copy and more sparse copy like lists?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Could it work for headings, callouts, quotes and captions too?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Does it get out the way?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Do I think it’s beautiful?&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Does it feel like me?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-shortlist&quot;&gt;The Shortlist&lt;a href=&quot;#the-shortlist&quot; aria-label=&quot;Link to “The Shortlist”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After a lot of exploration, I’ve settled on five candidates. None hit all the criteria in the checklists, but a few get pretty close. And I like them all.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;spectral&quot;&gt;Spectral&lt;a href=&quot;#spectral&quot; aria-label=&quot;Link to “Spectral”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;(&lt;a href=&quot;https://fonts.google.com/specimen/Spectral&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Google font page&lt;/a&gt;)&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-19-Spectral.png&quot; alt=&quot;Type Specimen - Spectral&quot;&gt;
&lt;p&gt;Specral was designed in 2017 as a screen-first typeface. &lt;a href=&quot;https://design.google/library/spectral-new-screen-first-typeface/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;This article&lt;/a&gt; goes into a lot more detail.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Spectral is a new and versatile serif face available in seven weights of roman and italic, with small caps. Intended primarily for text-rich, screen-first environments and long-form reading&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Over the past decade, the amount of attention given to readability studies by typeface designers has yielded either bland and conservative results, or overwrought “solutions.” This is more pronounced in screen typefaces than those made for print media, where common pitfalls include overly simplified shapes, “friendly” letters, and facile tricks like unnecessarily upright italics.&lt;/p&gt;
&lt;p&gt;In response, Spectral takes an unambiguous stance: Screen typefaces should not look like caricatures of themselves. They should instead be functional text designs that stay interesting enough to convey the many voices that contemporary users need.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At first glance, this seems like it might be a great choice, but after some experimentation the very high stroke contrast makes it feel a bit &lt;em&gt;too bookish&lt;/em&gt;. It’s a bit too spindly, somehow. It also doesn’t work that well on dark backgrounds at small sizes.&lt;/p&gt;
&lt;p&gt;If I was setting a book on the web, I’d definitely consider this though.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;lora&quot;&gt;Lora&lt;a href=&quot;#lora&quot; aria-label=&quot;Link to “Lora”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;(&lt;a href=&quot;https://fonts.google.com/specimen/Lora&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Google font page&lt;/a&gt;)&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-19-Lora.png&quot; alt=&quot;Type Specimen - Lora&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;Lora is a well-balanced contemporary serif with roots in calligraphy. It is a text typeface with moderate contrast well suited for body text.&lt;/p&gt;
&lt;p&gt;A paragraph set in Lora will make a memorable appearance because of its brushed curves in contrast with driving serifs. The overall typographic voice of Lora perfectly conveys the mood of a modern-day story, or an art essay.&lt;/p&gt;
&lt;p&gt;Technically Lora is optimised for screen appearance, and works equally well in print.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lora was designed by Olga Karpushina in 2011. It’s definitely the least unusual of the five, though the fairly upright italic is beuatiful. This is definitely one that would get out of the way and put the content first.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-21-lora-promo.png&quot; alt=&quot;Type Promo - Lora&quot;&gt;
&lt;hr&gt;
&lt;h3 id=&quot;alegreya&quot;&gt;Alegreya&lt;a href=&quot;#alegreya&quot; aria-label=&quot;Link to “Alegreya”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;(&lt;a href=&quot;https://fonts.google.com/specimen/Alegreya&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Google font page&lt;/a&gt;)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alegreya is a typeface originally intended for literature. Among its crowning characteristics, it conveys a dynamic and varied rhythm which facilitates the reading of long texts. Also, it provides freshness to the page while referring to the calligraphic letter, not as a literal interpretation, but rather in a contemporary typographic language.&lt;/p&gt;
&lt;p&gt;The italic has just as much care and attention to detail in the design as the roman. The bold weights are strong, and the Black weights are really experimental for the genre. There is also a Small Caps sister family.&lt;/p&gt;
&lt;p&gt;Not only does Alegreya provide great performance, but also achieves a strong and harmonious text by means of elements designed in an atmosphere of diversity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;img src=&quot;/uploads/2020-06-19-Alagreya.png&quot; alt=&quot;Type Specimen - Alegreya&quot;&gt;
&lt;p&gt;I love the fact that Alegreya has a small caps variant and is part of a super family that includes a &lt;a href=&quot;https://fonts.google.com/specimen/Alegreya+Sans&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;sans-serif version&lt;/a&gt; - I was wondering if I could use the sans version for UI copy. But much like Spectral, it almost feels too caligraphic and oldy-worldy-bookish.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-21-alegreya-ligs.png&quot; alt=&quot;Ligratures in Alegreya&quot;&gt;
&lt;hr&gt;
&lt;h3 id=&quot;literata&quot;&gt;Literata&lt;a href=&quot;#literata&quot; aria-label=&quot;Link to “Literata”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;(&lt;a href=&quot;https://fonts.google.com/specimen/Literata&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Google font page&lt;/a&gt;)&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-19-Literata.png&quot; alt=&quot;Type Specimen - Literata&quot;&gt;
&lt;p&gt;Literata is probably one of my favourites. It feels right for the job, somehow. It’s got a huge array of varients plus a variable version and some lovely ligatures. It was commissioned by Google and designed by TypeTogether in 2015 and is the default font family in Google Play Books. I’m not sure I like the idea of using a face that so many folks will associate with Google, but as far as versitility and reading experience goes, this is definitely on the shortlist.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;bitter&quot;&gt;Bitter&lt;a href=&quot;#bitter&quot; aria-label=&quot;Link to “Bitter”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;(&lt;a href=&quot;https://fonts.google.com/specimen/Bitter&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Google font page&lt;/a&gt;)&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-06-19-Bitter.png&quot; alt=&quot;Type Specimen - Bitter&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;Motivated by my love for the pixel I designed Bitter. A “contemporary” slab serif typeface for text, it is specially designed for comfortably reading on any computer or device. The robust design started from the austerity of the pixel grid, based on rational rather than emotional principles. It combines the large x-heights and legibility of the humanistic tradition with subtle characteristics in the characters that inject a certain rhythm to flowing texts.&lt;/p&gt;
&lt;p&gt;Bitter has little variation in stroke weight and the Regular is thicker than a normal ‘Regular’ style for print design. This generates an intense color in paragraphs, accentuated by the serifs that are as thick as strokes with square terminals.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This also scores well when it comes to readability, but the fact it’s slab serifs and very dark color make it feel a bit too unrefined for use here. It’s still on the shortlist though.&lt;/p&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a href=&quot;#footnote-label&quot; aria-label=&quot;Link to “Footnotes”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://sparanoid.com/lab/opentype-features/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Here’s&lt;/a&gt; a good overview of CSS opentype features. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item><item><title>Website Redesign Part III - A Base to Start From</title><link>https://danny.is/writing/website-redesign-iii/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-iii/</guid><description>Building a solid foundation for a personal website redesign. Technical setup, tooling choices, and development workflow for modern web projects.</description><pubDate>Tue, 07 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m trying to keep the CSS as minimal as possible here, but I do want a decent blank slate to work from. So I’ve normalised browser styles with &lt;a href=&quot;https://necolas.github.io/normalize.css/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;normalize.css&lt;/a&gt; and set up some of my own base styles.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reset some basic styles to the way I like them.&lt;/li&gt;
&lt;li&gt;Set up some variables and use them for base colours and typography.&lt;/li&gt;
&lt;li&gt;Remove styles from all lists that have a class - they’re probably part of the UI, not the content.&lt;/li&gt;
&lt;li&gt;Remove styles from links that have a class - same reason.&lt;/li&gt;
&lt;li&gt;Set default link styles for body copy.&lt;/li&gt;
&lt;li&gt;Remove all animations and transitions for people that prefer not to see them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m sure these will change over time, but here they are for now…&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/dannysmith/dc8930ee00a9c47588b096b74870f27b.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://gist.github.com/dannysmith/3e4d15914c9b809b447bb7e7538ae545.js&quot;&gt;&lt;/script&gt;
&lt;h2 id=&quot;a-starting-point-for-article-typography&quot;&gt;A starting point for article typography&lt;a href=&quot;#a-starting-point-for-article-typography&quot; aria-label=&quot;Link to “A starting point for article typography”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since I’m writing on this site now, there’ll be a lot more written content. My &lt;strong&gt;Content-first&lt;/strong&gt; value means I should probably start with that, while my &lt;strong&gt;Simplicity&lt;/strong&gt; value means I should probably ignore layout and start by making the article copy as beautiful and readable as I can.&lt;/p&gt;
&lt;p&gt;So taking a &lt;a href=&quot;https://frankchimero.com/blog/2020/redesign-this-design/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;leaf out of Frank Chimero’s book&lt;/a&gt;, I’m starting with a very very simple set of styles for my articles.&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/dannysmith/0cdf7bde3a20a9453d47c20155ba1d82.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;I’ll focus in on this soon but until I choose a better typeface, &lt;code&gt;--font-copy&lt;/code&gt; is set to Georgia. Like Frank, I still like it.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part IV - Pages and URLs</title><link>https://danny.is/writing/website-redesign-iv/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-iv/</guid><pubDate>Tue, 07 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I bought this domain specifically so I could have cool URLs like &lt;code&gt;danny.is/doingsomething&lt;/code&gt;, so definitely want to use certain URLs, regardless of content. These provide a good starting point for deciding what pages I might want.&lt;/p&gt;
&lt;h4 id=&quot;dannyis&quot;&gt;danny.is&lt;a href=&quot;#dannyis&quot; aria-label=&quot;Link to “danny.is”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The homepage, obviously. Since the last redesign this has just shown a list of links to other pages and external services, but I’ll probably change that a bit with this redesign.&lt;/p&gt;
&lt;h4 id=&quot;dannyisabout&quot;&gt;danny.is/about&lt;a href=&quot;#dannyisabout&quot; aria-label=&quot;Link to “danny.is/about”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A few paragraphs about me. Perhaps some other stuff.&lt;/p&gt;
&lt;h4 id=&quot;dannyisnow&quot;&gt;danny.is/now&lt;a href=&quot;#dannyisnow&quot; aria-label=&quot;Link to “danny.is/now”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A very simple description of what I’m doing at the moment. Inspired &lt;a href=&quot;https://sivers.org/nowff&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;by this&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;dannyiswriting&quot;&gt;danny.is/writing&lt;a href=&quot;#dannyiswriting&quot; aria-label=&quot;Link to “danny.is/writing”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An index of my writing. This will definitely include my writing on this site, but I might also include stuff I write elsewhere.&lt;/p&gt;
&lt;h4 id=&quot;dannyisreading&quot;&gt;danny.is/reading&lt;a href=&quot;#dannyisreading&quot; aria-label=&quot;Link to “danny.is/reading”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Stuff I’m reading. Historically, this has been a big list of links pulled from Pocket and Medium. I want to keep that, but might also include books I’m reading, or recommend.&lt;/p&gt;
&lt;h4 id=&quot;dannyiscv&quot;&gt;danny.is/cv&lt;a href=&quot;#dannyiscv&quot; aria-label=&quot;Link to “danny.is/cv”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A PDF of my CV.&lt;/p&gt;
&lt;h4 id=&quot;dannyisusing&quot;&gt;danny.is/using&lt;a href=&quot;#dannyisusing&quot; aria-label=&quot;Link to “danny.is/using”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Forwards to a &lt;a href=&quot;https://www.notion.so/dannysmith/Danny-Uses-72544bdecd144ca5ab3864d92dcd119b&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Notion page detailing the software and hardware I use&lt;/a&gt;. I’m sticking with Notion for the moment because I already have much of this written.&lt;/p&gt;
&lt;h4 id=&quot;dannyismeeting&quot;&gt;danny.is/meeting&lt;a href=&quot;#dannyismeeting&quot; aria-label=&quot;Link to “danny.is/meeting”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Forwards to a &lt;a href=&quot;https://www.notion.so/Book-a-Meeting-with-Danny-e39fc8def5514b67b559b2e5a51934ae&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;notion page where you can book a meeting with me&lt;/a&gt;. I’ve been sharing the full notion link for about a year now, and it’s just occurred to me that setting up this forward makes a lot of sense. Again, I’m sticking with notion because I already have it set up.&lt;/p&gt;
&lt;h2 id=&quot;other-urls&quot;&gt;Other URLs&lt;a href=&quot;#other-urls&quot; aria-label=&quot;Link to “Other URLs”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve also set up forwards for LinkedIn and youtube, since I find it hard to remember their URLs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;danny.is/linkedin → My LinkedIn profile&lt;/li&gt;
&lt;li&gt;danny.is/youtube → My Youtube channel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It seems likely that I’ll also want two new pages…&lt;/p&gt;
&lt;h4 id=&quot;dannyislookingforwork&quot;&gt;danny.is/lookingforwork&lt;a href=&quot;#dannyislookingforwork&quot; aria-label=&quot;Link to “danny.is/lookingforwork”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A “Hire me” page which is only live when I’m actively looking for work.&lt;/p&gt;
&lt;h4 id=&quot;dannyiscoaching&quot;&gt;danny.is/coaching&lt;a href=&quot;#dannyiscoaching&quot; aria-label=&quot;Link to “danny.is/coaching”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A page detailing my coaching work. I expect this will be the only page where I care about “marketing”.&lt;/p&gt;
&lt;h2 id=&quot;pages-i-need-to-design&quot;&gt;Pages I need to design&lt;a href=&quot;#pages-i-need-to-design&quot; aria-label=&quot;Link to “Pages I need to design”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ignoring the forwards and the &lt;em&gt;now&lt;/em&gt; page, that’s &lt;strong&gt;eight&lt;/strong&gt; pages I need to consider when it comes to design.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Homepage&lt;/li&gt;
&lt;li&gt;About&lt;/li&gt;
&lt;li&gt;Writing (index)&lt;/li&gt;
&lt;li&gt;Writing (article)&lt;/li&gt;
&lt;li&gt;Reading&lt;/li&gt;
&lt;li&gt;Looking for Work&lt;/li&gt;
&lt;li&gt;Coaching&lt;/li&gt;
&lt;li&gt;404&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There’s a part of me that wants to dive into the design of these pages right away, but I’m resisting the urge. Making the writing that’s already here useable is far more important. Keep it simple, Danny.&lt;/p&gt;
</content:encoded></item><item><title>Website Redesign Part II - Principles, Goals and Stuff to Keep</title><link>https://danny.is/writing/website-redesign-ii/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-ii/</guid><description>Design principles and goals for a modern personal website. Balancing simplicity, performance, and content-first architecture in web design.</description><pubDate>Tue, 17 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Until now, every time I’ve redesigned this website it’s been because I didn’t like how it looked. Every few years, I’ve looked at it and felt like it doesn’t adequately represent me.&lt;/p&gt;
&lt;p&gt;When I started &lt;em&gt;this&lt;/em&gt; redesign process, I fully expected to feel the same way. But having spent a bunch of time staring at it, I think I still like a lot of what I have.&lt;/p&gt;
&lt;p&gt;More accurately, I still &lt;strong&gt;really&lt;/strong&gt; like these two pages…&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-03-17-dasmith1.png&quot; alt/&gt;
&lt;img src=&quot;/uploads/2020-03-17-dasmith2.png&quot; alt/&gt;
&lt;p&gt;When I designed them back in 2016 I was going for a modern “Swiss” influenced kinda vibe. I think I want to keep that.&lt;/p&gt;
&lt;p&gt;But let’s take a step back for a second and think about my goals.&lt;/p&gt;
&lt;h2 id=&quot;goals-of-this-site&quot;&gt;Goals of this site&lt;a href=&quot;#goals-of-this-site&quot; aria-label=&quot;Link to “Goals of this site”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First and foremost, it’s a personal expression of me. It’s a place for me to &lt;strong&gt;have fun&lt;/strong&gt;: both with my writing and with design &amp;amp; CSS. So it’s okay if it’s a bit goofy or stuff is a bit broken sometimes.&lt;/p&gt;
&lt;p&gt;But it also represents me professionally.&lt;/p&gt;
&lt;p&gt;Thankfully, in both cases the primary goal remains the same. &lt;strong&gt;To help the reader understand me – Danny.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If I put my UX hat on for a minute, there is a &lt;a href=&quot;https://jtbd.info/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Job To Be Done&lt;/a&gt; that I should consider. A subset of visitors to this site are trying to find out if I’m the right person to help them with something.&lt;/p&gt;
&lt;p&gt;Similarly – with my marketing and I-need-money-to-eat hat on – there is another subset of visitors that need my help; but neither of us know it yet.&lt;/p&gt;
&lt;p&gt;The last thing I want is for my personal site to be some sort of super-optimised landing page for &lt;strong&gt;Danny The Professional®&lt;/strong&gt;. Because that’s no fun. And it’s not at all an expression of me. But I do need to consider these special use cases &lt;em&gt;a bit&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Of course, there are also the random folk who come here to read the stuff I write. They probably just wanna read in peace.&lt;/p&gt;
&lt;p&gt;So if I think in terms of actors and their goals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Danny&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Express himself freely and without constraint.&lt;/li&gt;
&lt;li&gt;Have fun. With writing, design and code.&lt;/li&gt;
&lt;li&gt;Connect with people he can help, or who can help him.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interested reader&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Read Danny’s words in peace.&lt;/li&gt;
&lt;li&gt;(Perhaps) be notified when Danny writes some new words.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person checking Danny out&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Find out if Danny is the right person to help them.&lt;/li&gt;
&lt;li&gt;Contact Danny.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This feels like a good set of goals to start with. But they come in a strict order of priority: Danny → Interested Reader → Person Checking Danny Out. This is my personal website, after all.&lt;/p&gt;
&lt;p&gt;So here’s a rule I’m gonna try to stick to:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given a choice between what I want to do and what I feel I should do because [reasons], always choose the former.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;goals-of-this-redesign&quot;&gt;Goals of this redesign&lt;a href=&quot;#goals-of-this-redesign&quot; aria-label=&quot;Link to “Goals of this redesign”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I said in Part I, I’m doing this primarily for two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I want a simpler, more technology-agnostic website.&lt;/li&gt;
&lt;li&gt;I want to write here, not on Medium.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Combined with the website goals up there ☝️, I think I’m trying to achieve the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Represent myself more authentically.&lt;/li&gt;
&lt;li&gt;Simplify my own mental model for who I am and what I do, as expressed through this site.&lt;/li&gt;
&lt;li&gt;Make things more obvious and useful to readers.&lt;/li&gt;
&lt;li&gt;Put my writing at the front, and make it beautiful.&lt;/li&gt;
&lt;li&gt;Provide somewhere for folks to find out about my work, and how I might be able help them.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I suspect (2) and (3) mean better content organisation and hierarchy, along with some self-imposed constraints. (4) probably means better typography and more focus on the tiny details. As for (5) – that’s probably just a page of its own.&lt;/p&gt;
&lt;p&gt;The first one is interesting though. I said earlier that I like the current design. For the first time ever it still feels authentic now that &lt;em&gt;Redesign Time&lt;/em&gt; has rolled up. And so it does. At least what there is of it.&lt;/p&gt;
&lt;p&gt;And perhaps that’s the problem. My current site is too sparse. Too sanitised. Sure, it has some wacky angles and stuff. But it has no &lt;em&gt;substance&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It also feels a bit cold and unloved. Like a well-machined tool that rarely gets used.&lt;/p&gt;
&lt;p&gt;I’d much rather it felt like a hand-made tool that’s used and loved every day. It’ll probably have more dents and scratches, but it will feel much more like &lt;em&gt;home&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;representing-danny-more-authentically&quot;&gt;Representing Danny more authentically&lt;a href=&quot;#representing-danny-more-authentically&quot; aria-label=&quot;Link to “Representing Danny more authentically”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I recently asked some friends to describe me using as many single words/short phrases as they wanted. The list so far has been a great prompt for reflection and has led me to a fair few &lt;a href=&quot;https://www.habitsforwellbeing.com/whats-your-blindspot/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;blind spots&lt;/a&gt;, but I’m still waiting for a lot of folks to come back to me.&lt;/p&gt;
&lt;p&gt;I’m hoping that this will help identify some words that I feel authentically represent me.&lt;/p&gt;
&lt;p&gt;More on this later.&lt;/p&gt;
&lt;h2 id=&quot;so-where-are-we-at&quot;&gt;So where are we at?&lt;a href=&quot;#so-where-are-we-at&quot; aria-label=&quot;Link to “So where are we at?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Besides the fact that I like my current website, we’ve identified three main actors and their goals. We’ve also established five initial goals for this redesign, and expanded on the most important of them.&lt;/p&gt;
&lt;p&gt;Back in Part I I listed four values that felt right as guides for this project.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple&lt;/li&gt;
&lt;li&gt;Beautiful&lt;/li&gt;
&lt;li&gt;Authentic&lt;/li&gt;
&lt;li&gt;Content-first&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They still feel right to me. And they now have bit more flesh on them.&lt;/p&gt;
&lt;aside class=&quot;callout  &quot; data-callout-color=&quot;default&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content-wrapper&quot; data-astro-cid-lnlu5god&gt;  &lt;div class=&quot;content content-trim&quot; data-astro-cid-lnlu5god&gt; &lt;p&gt;This felt like a good time to define some design principles as guides for my thinking. But after
half an hour with pen and paper nothing seemed solid enough to be considered a principle. So I’ll
revisit this later.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;what-about-my-current-website&quot;&gt;What about my current website?&lt;a href=&quot;#what-about-my-current-website&quot; aria-label=&quot;Link to “What about my current website?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before wrapping this up, I want to take a look at my current website with all this thinking in the back of my mind. I particularly want to look at the two pages I like – the homepage and the 404. Here are some unedited thoughts on them:&lt;/p&gt;
&lt;ol class=&quot;long-list-items&quot;&gt;
&lt;li&gt;They &lt;em&gt;are&lt;/em&gt; simple. ✅&lt;/li&gt;
&lt;li&gt;They put the content first – even though there is hardly any of it. ✅&lt;/li&gt;
&lt;li&gt;There should be more content.&lt;/li&gt;
&lt;li&gt;I think the 404 page is beautiful. I love how the colour, type and layout interact. ✅&lt;/li&gt;
&lt;li&gt;The homepage is less beautiful. Futura looks pretty bad at very large sizes and the whole thing feels slightly unpolished somehow. It lacks attention to the little details. Inauthentic.&lt;/li&gt;
&lt;li&gt;Futura is a lazy choice of typeface - I use it everywhere.&lt;/li&gt;
&lt;li&gt;The 404 page feels like an authentic representation of me&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;true&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. The homepage less so. This might have more to do with the content than the deign. I talk &lt;strong&gt;a lot&lt;/strong&gt; and am generally friendly, and it feels a bit too minimalist and impersonal. It’s somehow not goofy enough, where the 404 page is. At least the palette feels warm ad cosy.&lt;/li&gt;
&lt;li&gt;I’m going for a kinda Bauhaus/Swiss vibe but there’s no grid. I’m a massive nerd for orderly systems and grids, so this feels both inauthentic and like a missed opportunity to create some beauty through mathematics.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Until next time.&lt;/p&gt;
&lt;section data-footnotes=&quot;true&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a href=&quot;#footnote-label&quot; aria-label=&quot;Link to “Footnotes”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Yes I know, “Not Found”. 🙃 &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Website Redesign Part I - The Tech</title><link>https://danny.is/writing/website-redesign-i/</link><guid isPermaLink="true">https://danny.is/writing/website-redesign-i/</guid><description>Technical decisions and architecture choices for rebuilding danny.is. Learn about static site generators, JAMstack, and modern web development approaches.</description><pubDate>Sat, 14 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the years, this website has taken many forms. Initially, it was a Wordpress blog. Then is was a hand-rolled Sinatra app that rendered markdown pages. More recently I’ve blogged on Medium and at the time of writing, my website is a much simpler Sinatra app.&lt;/p&gt;
&lt;p&gt;A few months ago I had an ongoing problem with medium which lasted about three months. I got a new Monzo card and medium couldn’t charge it. Although none of my articles were behind the paywall, it did mean that I couldn’t read other people’s paywalled articles. And even though I was happy to pay, I &lt;strong&gt;couldn’t&lt;/strong&gt;. Eventually, after many many emails between me, Monzo and Medium, I involved Medium’s payment processor Stipe. Turns out that the problem was some bad data on their end. We resolved it, but only because I knew about stripe.&lt;/p&gt;
&lt;p&gt;This whole experience left a bad taste in my mouth, and like a lot of other people it feels like time to move the canonical version of my writing onto a platform that I own… my own website.&lt;/p&gt;
&lt;p&gt;At the same time, I’ve also become frustrated with my website. Considering what it does, it’s most definitely over-engineered. It’s not fun to play with any more, and I seem to spend most of my time on it updating dependancies. I also have to pay Heroku to keep it running.&lt;/p&gt;
&lt;p&gt;Time to simplify things, then. I need a static site.&lt;/p&gt;
&lt;p&gt;Why a static site?&lt;/p&gt;
&lt;p&gt;If my site is just HTML and CSS then it’s easy to change, even if I’ve not touched it in years. There are no complex mental models to remember, no dependancies to manage, no security vulnerabilities to patch. I can also deploy it anywhere. I’m not reliant on any specific third parties. Static sites are also &lt;a href=&quot;https://www.youtube.com/watch?v=b2F-DItXtZs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;web scale&lt;/a&gt; by default when hosted in the right place.&lt;/p&gt;
&lt;p&gt;So that’s my starting point… plain old HTML and CSS, deployed to Github Pages. When I want to write a new article I just make an HTML page. Simple.&lt;/p&gt;
&lt;p&gt;But I also want to enjoy writing articles, which probably means writing in markdown.&lt;/p&gt;
&lt;p&gt;So a static site generator is the obvious choice. All the cool kids are using Gatsby and Hugo, but after spending some time with both I think Jekyll is the best option for me. It’s simple, is battle-tested and is very very stable.&lt;/p&gt;
&lt;p&gt;Before going any further with the tech, I need some values and principles to guide my decisions.&lt;/p&gt;
&lt;h2 id=&quot;values&quot;&gt;Values&lt;a href=&quot;#values&quot; aria-label=&quot;Link to “Values”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After some time with a pen and paper, these seem appropriate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple&lt;/li&gt;
&lt;li&gt;Beautiful&lt;/li&gt;
&lt;li&gt;Authentic&lt;/li&gt;
&lt;li&gt;Content-first&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;engineering-principles&quot;&gt;Engineering Principles&lt;a href=&quot;#engineering-principles&quot; aria-label=&quot;Link to “Engineering Principles”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Based on these values, I’ve pulled out some principles to guide my decisions on tech stack:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;As few dependencies as possible (both build-time and run-time dependancies).&lt;/li&gt;
&lt;li&gt;Use defaults wherever possible.&lt;/li&gt;
&lt;li&gt;Write as little code as possible.&lt;/li&gt;
&lt;li&gt;Do not over-abstract. Avoid fallbacks, polyfills and hacks.&lt;/li&gt;
&lt;li&gt;Bake accessibility in.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’ll come back to these regularly.&lt;/p&gt;
&lt;h2 id=&quot;jekyll-plugins&quot;&gt;Jekyll Plugins&lt;a href=&quot;#jekyll-plugins&quot; aria-label=&quot;Link to “Jekyll Plugins”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s tempting to install a whole bunch of plugins – there are some great ones out there. But every plugin is a build-time dependancy, so I’ve settled on just three.&lt;/p&gt;
&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/benbalter/jekyll-optional-front-matter&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Optional Front Matter&lt;/a&gt;&lt;/strong&gt;. This makes the front-matter optional, obviously. I want it so that my HTML pages don’t need any front matter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/jekyll/jekyll-seo-tag&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SEO Tag&lt;/a&gt;&lt;/strong&gt;. This adds a bunch of SEO-related meta tags in a pretty intelligent way.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/jekyll/jekyll-redirect-from&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Redirect From&lt;/a&gt;&lt;/strong&gt;. This lets me set up redirects in a super-intuitive way. I can crate an empty page or post, specify the redirect in the front matter and be done with it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;but-what-about-dynamic-stuff&quot;&gt;But what about dynamic stuff?&lt;a href=&quot;#but-what-about-dynamic-stuff&quot; aria-label=&quot;Link to “But what about dynamic stuff?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My current website has some dynamic stuff. The only bits I still care about are my reading list and highlights. My medium highlights are pulled in &lt;a href=&quot;https://github.com/dannysmith/dannyis/blob/master/lib/medium_request.rb#L74&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;via their private API&lt;/a&gt; and cached in Redis.&lt;/p&gt;
&lt;p&gt;My pocket items and medium recommendations work a bit differently. When I clap for an article on medium or archive one in pocket IFTTT sends a request to an endpoint which stores the details in a Postgres database which the Sinatra app can access.&lt;/p&gt;
&lt;p&gt;On reflection, I don’t care about the highlights right now.&lt;/p&gt;
&lt;p&gt;But I definitely want to retain the likes from Pocket and Medium. I don’t want to maintain a Postgres database though.&lt;/p&gt;
&lt;p&gt;Enter Airtable. I can have IFTTT create a record in Airtable whenever I clap for a medium article or archive a pocket item.&lt;/p&gt;
&lt;img src=&quot;/uploads/2020-03-17-airtable1.png&quot; alt=&quot;Airtable Screenshot&quot;&gt;
&lt;img src=&quot;/uploads/2020-03-17-airtable2.png&quot; alt=&quot;Airtable Screenshot&quot;&gt;
&lt;p&gt;Although this means I’m dependant on Airtable for this part of my website, I’m okay with that because it’s not a core part of my site: if airtable goes down or kill my account then 🤷‍♂️. The advantages well outweigh the overhead of managing my own server and database. Also…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It’s easy to pipe data into Airtable from all sorts of places.&lt;/li&gt;
&lt;li&gt;It’s easy to edit data in airtable, especially because I use it a bunch anyway.&lt;/li&gt;
&lt;li&gt;It has a very nice API.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is one problem, though. For my javascript to access this data it needs my Airtable API key. I can’t just bung that in my code because then everyone would have it. This is something Netlify’s &lt;a href=&quot;https://www.netlify.com/products/functions/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;lambda functions&lt;/a&gt; are great at: they can act as an intermediate layer between my client-side javascript and the Airtable API.&lt;/p&gt;
&lt;p&gt;Netlify also makes a lot of sense for hosting: it has some bells and whistles Github Pages lacks.&lt;/p&gt;
&lt;p&gt;So here’s where we’re at so far…&lt;/p&gt;
&lt;h4 id=&quot;core-stuff&quot;&gt;Core Stuff&lt;a href=&quot;#core-stuff&quot; aria-label=&quot;Link to “Core Stuff”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Just HTML, CSS and Markdown.&lt;/li&gt;
&lt;li&gt;Jekyll (with a couple of plugins).&lt;/li&gt;
&lt;li&gt;Netlify for hosting.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;reading-list-stuff&quot;&gt;Reading List Stuff&lt;a href=&quot;#reading-list-stuff&quot; aria-label=&quot;Link to “Reading List Stuff”&quot;&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Airtable - data store.&lt;/li&gt;
&lt;li&gt;Netlify Functions - accessing Airtable.&lt;/li&gt;
&lt;li&gt;IFTTT - piping stuff into Airtable from elsewhere.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-about-the-writing-experience&quot;&gt;What about the writing experience?&lt;a href=&quot;#what-about-the-writing-experience&quot; aria-label=&quot;Link to “What about the writing experience?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For the most part, I’m happy to write locally in &lt;a href=&quot;https://ia.net/writer&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;iA Writer&lt;/a&gt; or VSCode. For writing on-the-go, &lt;a href=&quot;https://forestry.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Forestry&lt;/a&gt; seems like the best option. I’d dearly love to find a git-based-jekyll-compatible editor that works like Ghost or Medium, but that can wait.&lt;/p&gt;
&lt;p&gt;With these decisions made, it took a surprisingly small amount of time to get all this wired up. Less than two hours. That’s the value of simplicity, I suppose.&lt;/p&gt;
</content:encoded></item><item><title>Business Process &amp; Tooling</title><link>https://danny.is/writing/2020-03-05-business-process-tooling/</link><guid isPermaLink="true">https://danny.is/writing/2020-03-05-business-process-tooling/</guid><pubDate>Thu, 05 Mar 2020 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Little Thing — Mocking External Services on Ruby</title><link>https://danny.is/writing/2020-03-04-little-thing-mocking-external-services-on-ruby/</link><guid isPermaLink="true">https://danny.is/writing/2020-03-04-little-thing-mocking-external-services-on-ruby/</guid><pubDate>Wed, 04 Mar 2020 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Breaking Down Problems: It’s hard when you’re learning to code</title><link>https://danny.is/writing/2018-11-14-breaking-down-problems-its-hard-when-you-re-learning-to-code/</link><guid isPermaLink="true">https://danny.is/writing/2018-11-14-breaking-down-problems-its-hard-when-you-re-learning-to-code/</guid><pubDate>Wed, 14 Nov 2018 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Little Thing — Value Objects in Ruby</title><link>https://danny.is/writing/2018-11-05-little-thing-value-objects-in-ruby/</link><guid isPermaLink="true">https://danny.is/writing/2018-11-05-little-thing-value-objects-in-ruby/</guid><pubDate>Mon, 05 Nov 2018 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Little Thing #2 — Speeding up ZSH</title><link>https://danny.is/writing/2018-08-09-little-thing-2-speeding-up-zsh/</link><guid isPermaLink="true">https://danny.is/writing/2018-08-09-little-thing-2-speeding-up-zsh/</guid><pubDate>Thu, 09 Aug 2018 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Little Thing #1 — How do RSpec Matchers Work?</title><link>https://danny.is/writing/2018-04-12-little-thing-1-how-do-rspec-matchers-work/</link><guid isPermaLink="true">https://danny.is/writing/2018-04-12-little-thing-1-how-do-rspec-matchers-work/</guid><pubDate>Thu, 12 Apr 2018 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Little Things</title><link>https://danny.is/writing/2018-04-02-little-things/</link><guid isPermaLink="true">https://danny.is/writing/2018-04-02-little-things/</guid><pubDate>Mon, 02 Apr 2018 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Synchronising Git Merges with Slack</title><link>https://danny.is/writing/2017-12-19-synchronising-git-merges-with-slack/</link><guid isPermaLink="true">https://danny.is/writing/2017-12-19-synchronising-git-merges-with-slack/</guid><pubDate>Tue, 19 Dec 2017 00:00:00 GMT</pubDate><content:encoded/></item><item><title>The Definition of Done: What does “done” actually mean?</title><link>https://danny.is/writing/2017-12-19-the-definition-of-done-what-does-done-actually-mean/</link><guid isPermaLink="true">https://danny.is/writing/2017-12-19-the-definition-of-done-what-does-done-actually-mean/</guid><pubDate>Tue, 19 Dec 2017 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Add Starred Slack Messages to Things 3</title><link>https://danny.is/writing/2017-11-26-add-starred-slack-messages-to-things-3/</link><guid isPermaLink="true">https://danny.is/writing/2017-11-26-add-starred-slack-messages-to-things-3/</guid><pubDate>Sun, 26 Nov 2017 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Where are the good Mobile Networks?</title><link>https://danny.is/writing/2017-06-19-where-are-the-good-mobile-networks/</link><guid isPermaLink="true">https://danny.is/writing/2017-06-19-where-are-the-good-mobile-networks/</guid><pubDate>Mon, 19 Jun 2017 00:00:00 GMT</pubDate><content:encoded/></item><item><title>The Definition of Done: What does &quot;done&quot; actually mean?</title><link>https://danny.is/writing/definition-of-done/</link><guid isPermaLink="true">https://danny.is/writing/definition-of-done/</guid><pubDate>Mon, 19 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Scrum defines the &lt;strong&gt;definition of done&lt;/strong&gt; in pretty simple terms: it’s the acceptance criteria that are common to every single user story. For scrum teams, it’s really important to have a solid definition of what “done” means. They work in sprints, and need some way of deciding whether a user story is actually finished. It’s no good ending a sprint with a user story that meets all its acceptance criteria, but had no code review, hasn’t been tested and isn’t deployable. Such a story is clearly not done. Scrum teams solve this by strictly defining “done”. In this case, in addition to meeting the acceptance criteria, the story must also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have had a code review.&lt;/li&gt;
&lt;li&gt;Have been tested.&lt;/li&gt;
&lt;li&gt;Be immediately deployable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If this isn’t the case at the end of the sprint then it isn’t done - we can’t count it’s story points towards the sprint velocity and it needs to go on the backlog for the next sprint planning session.&lt;/p&gt;
&lt;h2 id=&quot;what-does-done-actually-meanthough&quot;&gt;What does “done” actually mean, though?&lt;a href=&quot;#what-does-done-actually-meanthough&quot; aria-label=&quot;Link to “What does “done” actually mean, though?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Is a new feature “done” when it’s in production and being used by customers? It might be. Equally, it might not. Have we published the changelog? Have we emailed our customers to tell them about the feature?&lt;/p&gt;
&lt;p&gt;When is a technical improvement or refactoring “done”? We certainly don’t want to email our customers about it, and we probably don’t want to add it to the changelog.&lt;/p&gt;
&lt;p&gt;The definition of “done” depends on the task.&lt;/p&gt;
&lt;p&gt;Some teams get around this problem by having multiple definitions of “done” - one for bugfixes, another for features, another for technical tasks, and so on.&lt;/p&gt;
&lt;p&gt;At &lt;a href=&quot;http://charliehr.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CharlieHR&lt;/a&gt;, this seemed overly complex. We don’t use scrum, and we don’t work in iterations. But we still need a way to remind ourselves of the stuff that should be done before a task is considered finished. So we invented a checklist.&lt;/p&gt;
&lt;h2 id=&quot;our-dod-atcharlie&quot;&gt;Our DoD at Charlie&lt;a href=&quot;#our-dod-atcharlie&quot; aria-label=&quot;Link to “Our DoD at Charlie”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We use two definitions. The first is &lt;strong&gt;ready to merge&lt;/strong&gt;. Everything in this checklist needs to be ticked off before the pull request is merged to the &lt;code&gt;master&lt;/code&gt; branch. The second is &lt;strong&gt;actually done&lt;/strong&gt;. Everything in this checklist must be ticked off before the card is moved to the Done column on our task boards.&lt;/p&gt;
&lt;p&gt;It’s worth noting that these rules apply to &lt;em&gt;every single work item&lt;/em&gt; that goes through our task boards, so long as it involves code. Whether it’s a large user story with multiple dependencies or a tiny bugfix, the person doing the work is expected to run through these checklists.&lt;/p&gt;
&lt;p&gt;That doesn’t mean that everything on the checklists has to be ticked off for every work item, though - a tiny technical improvement is unlikely to need a marketing email written about it, for example. It does mean that everything in the checklist must be &lt;strong&gt;considered&lt;/strong&gt; for every work item. We trust our engineers to use their judgement.&lt;/p&gt;
&lt;p&gt;While you’re reading the DoD below, bear in mind…&lt;/p&gt;
&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;We use Trello for our task boards. Most teams have columns for &lt;strong&gt;backlog&lt;/strong&gt;, &lt;strong&gt;next up&lt;/strong&gt;, &lt;strong&gt;in progress&lt;/strong&gt; and &lt;strong&gt;done&lt;/strong&gt;. New features and user stories are represented by trello cards on a team backlog.&lt;/li&gt;
&lt;li&gt;Technical improvements and exceptions begin life as GitHub issues. These get a trello card on the backlog when they are assigned to or picked up by a team. Bugs live in &lt;a href=&quot;http://sifterapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Sifter&lt;/a&gt;, and get a trello card when they’re assigned to a team.&lt;/li&gt;
&lt;li&gt;The order in which the items below are considered is irrelevant, so long as they have all been considered.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ve made notes in italics, since some of the items need a bit of explanation.&lt;/p&gt;
&lt;h2 id=&quot;checklist-1-ready-tomerge&quot;&gt;Checklist 1: Ready to Merge&lt;a href=&quot;#checklist-1-ready-tomerge&quot; aria-label=&quot;Link to “Checklist 1: Ready to Merge”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;Kickoff done and requirements clear. Kickoff Document stored on Drive.&lt;/strong&gt; This is where we define our acceptance criteria and the scope of a new feature. Most non-trivial user stories require this, and the kickoff for an epic may spawn multiple, smaller, inter-dependant user stories. Bugfixes don’t normally need a kickoff: it’s down to the product manager and engineers to decide whether a work item needs one. The kickoff document is just the record of the discussion, and contains sufficient acceptance criteria to get the thing out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implementation Plan written and reviewed.&lt;/strong&gt; This is something we’re trying out. For non-trivial work items, engineers discuss and plan the technical solution. This usually involves whiteboarding and scribbling on bits of paper. The implementation plan is a summary of that thinking and is available for engineers from other teams to review. We’re hoping this will help catch architectural and system design problems early. For technical improvements, this discussion has usually happened on the associated github issue already.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DPIA Form Completed.&lt;/strong&gt; Do we need to do a Data Protection Impact Assessment?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Card on Trello, properly labelled, sized and PR/Issue linked (mandatory).&lt;/strong&gt; Before a trello card is picked up it must be labeled, have a T-Shirt size and have any associated stuff (kickoff doc, implementation plan, github issue, sifter issue etc) attached to it. Once a pull request is open, this (or these) should be attached too.&lt;/li&gt;
&lt;li&gt;P&lt;strong&gt;ull Request open and properly labelled (mandatory).&lt;/strong&gt; We open a PR after the first commit to a branch. Early comments on the PR save time and help share knowledge between teams.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acceptance criteria met. Obviously.&lt;/strong&gt; If changes to the requirements have been agreed with the product manager, have they been noted on the kickoff document or trello card?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unit tests written.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Functional specs written.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation folder reviewed for any changes/additions.&lt;/strong&gt; We have a directory containing markdown files that describe the design patterns and conventions we use.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment variables documented, added to secrets manager and present in staging &amp;amp; production environments.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Analytics considered.&lt;/strong&gt; Do we need to track user behaviour, and how should that be reported to our analytics tools. For larger work items, this is usually specified in the the kickoff doc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“What’s New“ section updated.&lt;/strong&gt; If this is a new feature, we may want to tell our users about it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;l18n reviewed.&lt;/strong&gt; All our copy (including microcopy) should live in &lt;a href=&quot;https://guides.rubyonrails.org/i18n.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;locale files&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security &amp;amp; Data Protection Review (mandatory).&lt;/strong&gt; We take security seriously. Have we made a deliberate and methodical check that this doesn’t introduce any obvious security or data protection issues. This is particularly important for small bugfixes, since they may not have had detailed planning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code Review from at least one other (mandatory).&lt;/strong&gt; We strictly enforce this with our GitHub settings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://codeclimate.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CodeClimate&lt;/a&gt; clear of errors.&lt;/strong&gt; As well as tracking our test coverage, we run static analysis tools like rubocop and ESLint against out changes. We’re still tweaking the rules for these tools to suit our in-house style.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature Branch green on CI (mandatory).&lt;/strong&gt; We also enforce this. You cannot merge to master with a red build on your feature branch.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;checklist-2-actuallydone&quot;&gt;Checklist 2: Actually Done&lt;a href=&quot;#checklist-2-actuallydone&quot; aria-label=&quot;Link to “Checklist 2: Actually Done”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul class=&quot;long-list-items&quot;&gt;
&lt;li&gt;&lt;strong&gt;Merged to master and feature branch deleted (mandatory).&lt;/strong&gt; The master branch is automatically deployed to our staging environment. &lt;a href=&quot;https://medium.com/@dannysmith/synchronising-git-merges-with-slack-d905f7cbd55c&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;I wrote about our merge process earlier&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Related GitHub Issues closed or updated.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sifter, Appsignal, and #tech-support issues closed 🏁.&lt;/strong&gt; We have a slack channel where people can raise technical issues. If a work item resolves or relates to one of these, it should be marked. This mostly applies to tiny bugfixes. Resolved bugs and exceptions should be closed in Sifter and Appsignal, respectively.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manually tested on staging environment (mandatory).&lt;/strong&gt; We don’t have dedicated QA people, so engineers and product managers conduct acceptance and exploratory tests. For work items that carry significant business or technical risk, we’ll draft in people from across the company to help.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployed to production with no obvious errors/exceptions (mandatory).&lt;/strong&gt; Once a PR is deployed, we expect our engineers to keep an eye on the slack channel that shows our exceptions. If the deploy causes a flurry of them, it should either be rolled back and fixed or they should be addressed in a new PR before the work item can be considered done.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Release email sent.&lt;/strong&gt; We send a company-wide email for all non-trivial releases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Marketing document updated.&lt;/strong&gt; If the work item is a new feature or important bugfix, it’s recorded in a spreadsheet. The marketing team refer to this when sending updates to our users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Usage metrics checked/set up.&lt;/strong&gt; Have we checked that data is coming through correctly? Do we need to update any formulas or one of our &lt;a href=&quot;https://www.geckoboard.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Geckoboard&lt;/a&gt; dashboards?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Help pages updated.&lt;/strong&gt; If we’ve added a feature, changed the way something works or updated the UI, we might need to update our user guides, help documentation and canned responses in intercom.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FINALLY, card moved to “done” on trello (mandatory).&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;mandatory-items&quot;&gt;Mandatory Items&lt;a href=&quot;#mandatory-items&quot; aria-label=&quot;Link to “Mandatory Items”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You’ll notice that some of the items are marked &lt;em&gt;mandatory&lt;/em&gt;. &lt;strong&gt;Those must be done for every single work item. No exceptions.&lt;/strong&gt; For a scrum purist, these are the only things that should appear in a Definition of Done. For us (right now), they are probably the least useful items on the list - they’re all important enough that we’d remember to do them even if we didn’t have a checklist!&lt;/p&gt;
&lt;p&gt;At the moment, we’re deliberately keeping our mandatory items to a minimum, though we have discussed making more stuff mandatory. As we continue to evolve our process, I expect we’ll add items to support our current engineering goals. If we were focussed on improving our unit test coverage, we might include an item that says &lt;em&gt;“all new models and &lt;abbr title=&quot;Plain Old Ruby Object&quot;&gt;PORO&lt;/abbr&gt;s must have an associated unit test”&lt;/em&gt;. If we were focussed on improving the quality of our codebase, we might insist that “every pull request must improve the CodeClimate quality rating”. We might even decide to &lt;a href=&quot;https://medium.com/rootpath/delivering-business-value-with-kanban-and-validated-learning-55749daffecc&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;make proof of business value a prerequisite&lt;/a&gt; for “done”.&lt;/p&gt;
&lt;p&gt;Currently though, we are focussed on building an awesome product, and while most pull requests include unit tests and quality improvements, &lt;strong&gt;it doesn’t make sense to &lt;em&gt;mandate&lt;/em&gt; that just now.&lt;/strong&gt; Our “Definition of Done” is designed to help us work better and faster, and will change and evolve alongside our business goals.&lt;/p&gt;
</content:encoded></item><item><title>Six awesome services I discovered last year</title><link>https://danny.is/writing/2017-05-18-six-awesome-services-i-discovered-last-year/</link><guid isPermaLink="true">https://danny.is/writing/2017-05-18-six-awesome-services-i-discovered-last-year/</guid><pubDate>Thu, 18 May 2017 00:00:00 GMT</pubDate><content:encoded/></item><item><title>DigitalOcean, DNSimple and Terraform: A very short introduction.</title><link>https://danny.is/writing/2016-10-24-digitalocean-dnsimple-and-terraform-a-very-short-introduction/</link><guid isPermaLink="true">https://danny.is/writing/2016-10-24-digitalocean-dnsimple-and-terraform-a-very-short-introduction/</guid><pubDate>Mon, 24 Oct 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Agile Adoption Patterns</title><link>https://danny.is/writing/2016-06-30-agile-adoption-patterns/</link><guid isPermaLink="true">https://danny.is/writing/2016-06-30-agile-adoption-patterns/</guid><pubDate>Thu, 30 Jun 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Good Slide Design</title><link>https://danny.is/writing/2016-04-23-good-slide-design/</link><guid isPermaLink="true">https://danny.is/writing/2016-04-23-good-slide-design/</guid><pubDate>Sat, 23 Apr 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Slack for Enterprise</title><link>https://danny.is/writing/2016-03-10-slack-for-enterprise/</link><guid isPermaLink="true">https://danny.is/writing/2016-03-10-slack-for-enterprise/</guid><pubDate>Thu, 10 Mar 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>A Standard Software Toolset for Business?</title><link>https://danny.is/writing/2016-03-06-a-standard-software-toolset-for-business/</link><guid isPermaLink="true">https://danny.is/writing/2016-03-06-a-standard-software-toolset-for-business/</guid><pubDate>Sun, 06 Mar 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Finding a Good Colour Palette through Exploration</title><link>https://danny.is/writing/2016-03-02-finding-a-good-colour-palette-through-exploration/</link><guid isPermaLink="true">https://danny.is/writing/2016-03-02-finding-a-good-colour-palette-through-exploration/</guid><pubDate>Wed, 02 Mar 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Why do I teach?</title><link>https://danny.is/writing/2016-02-25-why-do-i-teach/</link><guid isPermaLink="true">https://danny.is/writing/2016-02-25-why-do-i-teach/</guid><pubDate>Thu, 25 Feb 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Automating and Outsourcing</title><link>https://danny.is/writing/2016-02-20-automating-and-outsourcing/</link><guid isPermaLink="true">https://danny.is/writing/2016-02-20-automating-and-outsourcing/</guid><pubDate>Sat, 20 Feb 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Collaborating on a Project</title><link>https://danny.is/writing/2016-01-04-collaborating-on-a-project/</link><guid isPermaLink="true">https://danny.is/writing/2016-01-04-collaborating-on-a-project/</guid><pubDate>Mon, 04 Jan 2016 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Effective Meetings</title><link>https://danny.is/writing/2015-12-18-effective-meetings/</link><guid isPermaLink="true">https://danny.is/writing/2015-12-18-effective-meetings/</guid><pubDate>Fri, 18 Dec 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>My Students have Forgotten How to be Curious</title><link>https://danny.is/writing/2015-11-03-my-students-have-forgotten-how-to-be-curious/</link><guid isPermaLink="true">https://danny.is/writing/2015-11-03-my-students-have-forgotten-how-to-be-curious/</guid><pubDate>Tue, 03 Nov 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Evaluating Training and Learning</title><link>https://danny.is/writing/2015-10-26-evaluating-training-and-learning/</link><guid isPermaLink="true">https://danny.is/writing/2015-10-26-evaluating-training-and-learning/</guid><pubDate>Mon, 26 Oct 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Getting Things Done</title><link>https://danny.is/writing/2015-08-29-getting-things-done/</link><guid isPermaLink="true">https://danny.is/writing/2015-08-29-getting-things-done/</guid><pubDate>Sat, 29 Aug 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>The Psychoactive Substances Bill 2015</title><link>https://danny.is/writing/2015-07-25-the-psychoactive-substances-bill-2015/</link><guid isPermaLink="true">https://danny.is/writing/2015-07-25-the-psychoactive-substances-bill-2015/</guid><pubDate>Sat, 25 Jul 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>The Changing Face of Software Testing</title><link>https://danny.is/writing/2015-07-22-the-changing-face-of-software-testing/</link><guid isPermaLink="true">https://danny.is/writing/2015-07-22-the-changing-face-of-software-testing/</guid><pubDate>Wed, 22 Jul 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Delivering Business Value with Kanban and Validated Learning</title><link>https://danny.is/writing/2015-03-17-delivering-business-value-with-kanban-and-validated-learning/</link><guid isPermaLink="true">https://danny.is/writing/2015-03-17-delivering-business-value-with-kanban-and-validated-learning/</guid><pubDate>Tue, 17 Mar 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Blogging with Evernote and Ruby</title><link>https://danny.is/writing/2015-02-26-blogging-with-evernote-and-ruby/</link><guid isPermaLink="true">https://danny.is/writing/2015-02-26-blogging-with-evernote-and-ruby/</guid><pubDate>Thu, 26 Feb 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Replacing Bash with ZSH on OSX</title><link>https://danny.is/writing/2015-02-26-replacing-bash-with-zsh-on-osx/</link><guid isPermaLink="true">https://danny.is/writing/2015-02-26-replacing-bash-with-zsh-on-osx/</guid><pubDate>Thu, 26 Feb 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Agile Feedback Loops</title><link>https://danny.is/writing/2015-02-24-agile-feedback-loops-by-danny-smith/</link><guid isPermaLink="true">https://danny.is/writing/2015-02-24-agile-feedback-loops-by-danny-smith/</guid><pubDate>Tue, 24 Feb 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Writing Testable Code</title><link>https://danny.is/writing/2015-02-24-writing-testable-code/</link><guid isPermaLink="true">https://danny.is/writing/2015-02-24-writing-testable-code/</guid><pubDate>Tue, 24 Feb 2015 00:00:00 GMT</pubDate><content:encoded/></item><item><title>On Sass and Other CSS Preprocessors</title><link>https://danny.is/writing/2014-05-04-on-sass-and-other-css-preprocessors/</link><guid isPermaLink="true">https://danny.is/writing/2014-05-04-on-sass-and-other-css-preprocessors/</guid><pubDate>Sun, 04 May 2014 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Controlling the Rag with Redcarpet</title><link>https://danny.is/writing/controlling-the-rag-with-redcarpet/</link><guid isPermaLink="true">https://danny.is/writing/controlling-the-rag-with-redcarpet/</guid><description>Improving web typography by preventing orphans and controlling line breaks with custom Redcarpet renderer</description><pubDate>Sun, 04 May 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Good typography is about the accumulation of small improvements. As Mark Boulton puts it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Good typographic design — on the web, in print; anywhere, in fact — relies on small, measurable improvements across an entire body of work”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’ve implemented several typographic improvements in my Ruby-based website using a custom Redcarpet renderer that automatically fixes common typography issues.&lt;/p&gt;
&lt;h2 id=&quot;the-problems-were-solving&quot;&gt;The Problems We’re Solving&lt;a href=&quot;#the-problems-were-solving&quot; aria-label=&quot;Link to “The Problems We’re Solving”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Orphans&lt;/strong&gt; - Single words hanging alone on the last line of a paragraph&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Short phrase breaks&lt;/strong&gt; - Emphasized phrases breaking across lines&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Awkward line breaks&lt;/strong&gt; - Lines breaking after prepositions, dashes, or short words&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;custom-redcarpet-renderer&quot;&gt;Custom Redcarpet Renderer&lt;a href=&quot;#custom-redcarpet-renderer&quot; aria-label=&quot;Link to “Custom Redcarpet Renderer”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s how I implemented these fixes:&lt;/p&gt;
&lt;h3 id=&quot;preventing-orphans&quot;&gt;Preventing Orphans&lt;a href=&quot;#preventing-orphans&quot; aria-label=&quot;Link to “Preventing Orphans”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;paragraph&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFA344;--0fs:italic&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Replace the space between the last two words with a non-breaking space&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;text.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;gsub!&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\s&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;+(&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\S&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;+)&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\s&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;*$/&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;&amp;amp;nbsp;\1&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;#{&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;def paragraph(text)  # Replace the space between the last two words with a non-breaking space  text.gsub!(/\s+(\S+)\s*$/, &amp;#x27;&amp;amp;nbsp;\1&amp;#x27;)  &amp;quot;&lt;p&gt;#{text}&lt;/p&gt;&amp;quot;end&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;keeping-short-phrases-together&quot;&gt;Keeping Short Phrases Together&lt;a href=&quot;#keeping-short-phrases-together&quot; aria-label=&quot;Link to “Keeping Short Phrases Together”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;do_not_break_string&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFA344;--0fs:italic&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Keep short emphasized phrases together&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;text.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;gsub!&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/&amp;lt;(em|strong)&amp;gt;([^&amp;lt;]{1,15})&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\/&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;(em|strong)&amp;gt;/&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;do&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; |match|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;match.&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;gsub&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;text&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;def do_not_break_string(text)  # Keep short emphasized phrases together  text.gsub!(/&lt;(em|strong)&gt;([^&lt;]{1,15})&lt;\/(em|strong)&gt;/) do |match|    match.gsub(&amp;#x27; &amp;#x27;, &amp;#x27;&amp;amp;nbsp;&amp;#x27;)  end  textend&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;preventing-breaks-after-short-words&quot;&gt;Preventing Breaks After Short Words&lt;a href=&quot;#preventing-breaks-after-short-words&quot; aria-label=&quot;Link to “Preventing Breaks After Short Words”&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;avoid_breaking_after_short_words&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFA344;--0fs:italic&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Complex regex to identify words that shouldn&apos;t be broken&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;text.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;gsub!&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;/(&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\s&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;)(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|vs?&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\.&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;?|via)(&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;\s&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;)/i&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;do&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; |match|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;#{&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;}#{&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;$2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;text&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;def avoid_breaking_after_short_words(text)  # Complex regex to identify words that shouldn&amp;#x27;t be broken  text.gsub!(/(\s)(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|vs?\.?|via)(\s)/i) do |match|    &amp;quot;#{$1}#{$2}&amp;amp;nbsp;&amp;quot;  end  textend&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-results&quot;&gt;The Results&lt;a href=&quot;#the-results&quot; aria-label=&quot;Link to “The Results”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These small improvements compound to create noticeably better text rendering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fewer orphans&lt;/strong&gt; - Paragraphs end more naturally&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better emphasis&lt;/strong&gt; - Short emphasized phrases stay together&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smoother reading&lt;/strong&gt; - Fewer awkward line breaks interrupt the flow&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;implementation-details&quot;&gt;Implementation Details&lt;a href=&quot;#implementation-details&quot; aria-label=&quot;Link to “Implementation Details”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The custom renderer processes Markdown content before it’s converted to HTML, applying these typographic rules automatically. This means I can write naturally in Markdown while getting professionally typeset results.&lt;/p&gt;
&lt;h2 id=&quot;typography-principles&quot;&gt;Typography Principles&lt;a href=&quot;#typography-principles&quot; aria-label=&quot;Link to “Typography Principles”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The key insight is that good typography happens at the micro level. Each individual improvement might seem small, but together they create text that’s:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;More readable&lt;/strong&gt; - Fewer distractions from poor line breaks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More professional&lt;/strong&gt; - Attention to typographic detail shows craftsmanship&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More accessible&lt;/strong&gt; - Better text flow helps all readers&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a href=&quot;#conclusion&quot; aria-label=&quot;Link to “Conclusion”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Controlling the rag (the uneven right edge of left-aligned text) and preventing orphans are foundational typography skills. By automating these improvements in my Redcarpet renderer, I ensure consistently good typography across all my content.&lt;/p&gt;
&lt;p&gt;These techniques work especially well on the web, where we have tools like non-breaking spaces to fine-tune text rendering without sacrificing the flexibility of HTML and CSS.&lt;/p&gt;
</content:encoded></item><item><title>A Pretty Readability Archive with Ruby and CSS</title><link>https://danny.is/writing/a-pretty-readability-archive-with-ruby-and-css/</link><guid isPermaLink="true">https://danny.is/writing/a-pretty-readability-archive-with-ruby-and-css/</guid><description>Building a visual bookmarks display using the Readability API, Ruby, and CSS 3D transforms</description><pubDate>Sun, 20 Apr 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve always found it interesting to see what other people read. Not just the things that they share on twitter, but the articles they read on a daily basis.&lt;/p&gt;
&lt;p&gt;I decided to build a web application to display my Readability bookmarks using Ruby, Sinatra, and CSS transitions to create an interactive card-based layout.&lt;/p&gt;
&lt;h2 id=&quot;the-technical-implementation&quot;&gt;The Technical Implementation&lt;a href=&quot;#the-technical-implementation&quot; aria-label=&quot;Link to “The Technical Implementation”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The application follows these key steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Environment variable setup&lt;/strong&gt; - Secure authentication credential storage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Readit gem integration&lt;/strong&gt; - Ruby library for the Readability API&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;XAuth authentication&lt;/strong&gt; - Secure API authentication&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bookmark fetching&lt;/strong&gt; - Retrieving archived articles&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive card display&lt;/strong&gt; - CSS 3D transforms for flippable cards&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;api-integration&quot;&gt;API Integration&lt;a href=&quot;#api-integration&quot; aria-label=&quot;Link to “API Integration”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using the Readit gem, I configured the Readability API connection:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Configure the Readit API&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;Readit&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;configure&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;do&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; |config|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;consumer_key&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;READABILITY_KEY&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;consumer_secret&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;READABILITY_SECRET&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Authenticate via XAuth&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;client &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;Readit&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1;--0fs:italic&quot;&gt;Client&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;oauth_token:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;READABILITY_TOKEN&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;oauth_token_secret:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;READABILITY_TOKEN_SECRET&lt;/span&gt;&lt;span style=&quot;--0:#FBF3DB&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Fetch archived bookmarks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;bookmarks &lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; client.&lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;bookmarks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;# Configure the Readit APIReadit.configure do |config|  config.consumer_key = ENV[&amp;#x27;READABILITY_KEY&amp;#x27;]  config.consumer_secret = ENV[&amp;#x27;READABILITY_SECRET&amp;#x27;]end# Authenticate via XAuthclient = Readit::Client.new(  oauth_token: ENV[&amp;#x27;READABILITY_TOKEN&amp;#x27;],  oauth_token_secret: ENV[&amp;#x27;READABILITY_TOKEN_SECRET&amp;#x27;])# Fetch archived bookmarksbookmarks = client.bookmarks&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;interactive-card-design&quot;&gt;Interactive Card Design&lt;a href=&quot;#interactive-card-design&quot; aria-label=&quot;Link to “Interactive Card Design”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The CSS creates an engaging card-based layout where each bookmark has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Front face&lt;/strong&gt; - Title and background image from the article&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Back face&lt;/strong&gt; - Article excerpt and metadata&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flip animation&lt;/strong&gt; - Smooth 3D transform on hover&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The cards use CSS 3D transforms to create a flipping effect that reveals additional information about each article when you hover over them.&lt;/p&gt;
&lt;h2 id=&quot;key-features&quot;&gt;Key Features&lt;a href=&quot;#key-features&quot; aria-label=&quot;Link to “Key Features”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Responsive design&lt;/strong&gt; - Cards adapt to different screen sizes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual appeal&lt;/strong&gt; - Each card shows the article’s featured image&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rich metadata&lt;/strong&gt; - Article excerpts, publication dates, and sources&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smooth animations&lt;/strong&gt; - CSS transitions for polished interactions&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;future-improvements&quot;&gt;Future Improvements&lt;a href=&quot;#future-improvements&quot; aria-label=&quot;Link to “Future Improvements”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Potential enhancements I’m considering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Infinite scrolling&lt;/strong&gt; - Load more bookmarks as you scroll&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Responsive grid&lt;/strong&gt; - Replace fixed-width layout with flexible grid&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filtering&lt;/strong&gt; - Sort by date, source, or tags&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search functionality&lt;/strong&gt; - Find specific articles quickly&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;reflection&quot;&gt;Reflection&lt;a href=&quot;#reflection&quot; aria-label=&quot;Link to “Reflection”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This project combines API integration, responsive design, and interactive CSS to create something genuinely useful. It’s a great example of how modern web technologies can transform simple data into engaging experiences.&lt;/p&gt;
&lt;p&gt;The combination of Ruby for backend processing and CSS for frontend interactions creates a fast, visually appealing way to browse through saved articles.&lt;/p&gt;
</content:encoded></item><item><title>From RVM to rbenv</title><link>https://danny.is/writing/rvm-to-rbenv/</link><guid isPermaLink="true">https://danny.is/writing/rvm-to-rbenv/</guid><description>Switching from RVM to rbenv for simpler Ruby version and gem management</description><pubDate>Tue, 08 Oct 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently switched from RVM (Ruby Version Manager) to rbenv for Ruby version and gem management. This isn’t so much of a ‘howto’, as a dump of my bash history for anyone looking to make a similar transition.&lt;/p&gt;
&lt;h2 id=&quot;why-i-left-rvm&quot;&gt;Why I Left RVM&lt;a href=&quot;#why-i-left-rvm&quot; aria-label=&quot;Link to “Why I Left RVM”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RVM had several issues that frustrated me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complicated gem and Ruby version management&lt;/strong&gt; - The system felt overly complex&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unclear storage locations&lt;/strong&gt; - I never knew where gems and Ruby versions were actually stored&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confusing gemset commands&lt;/strong&gt; - The gemset system was hard to understand and manage&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;why-rbenv-is-better&quot;&gt;Why rbenv is Better&lt;a href=&quot;#why-rbenv-is-better&quot; aria-label=&quot;Link to “Why rbenv is Better”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;rbenv offers several advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simpler to understand&lt;/strong&gt; - The system is more transparent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Works well with Bundler&lt;/strong&gt; - Better integration with modern Ruby workflows&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easier project-specific Ruby version management&lt;/strong&gt; - &lt;code&gt;.ruby-version&lt;/code&gt; files just work&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-migration-process&quot;&gt;The Migration Process&lt;a href=&quot;#the-migration-process&quot; aria-label=&quot;Link to “The Migration Process”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s the process I followed to switch:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Completely remove RVM&lt;/strong&gt; - Clean out all RVM-related files and configurations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean out old Ruby and gem installations&lt;/strong&gt; - Start fresh&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install rbenv via Homebrew&lt;/strong&gt; - Use the package manager for easy installation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install desired Ruby versions&lt;/strong&gt; - Install 1.9.3 and 2.1.4 (the versions I needed)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set up default gems&lt;/strong&gt; - Install commonly used gems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install rbenv plugins&lt;/strong&gt; - Add rbenv-bundler for better Bundler integration&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-result&quot;&gt;The Result&lt;a href=&quot;#the-result&quot; aria-label=&quot;Link to “The Result”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The goal was to create “a decent local ruby environment that I actually understand.” rbenv delivers on this promise by being:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Transparent&lt;/strong&gt; - I can see exactly what’s happening&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple&lt;/strong&gt; - No complex gemset management&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliable&lt;/strong&gt; - Works consistently across projects&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-commands&quot;&gt;Key Commands&lt;a href=&quot;#key-commands&quot; aria-label=&quot;Link to “Key Commands”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The most important rbenv commands I use daily:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;rbenv&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;versions&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# List installed Ruby versions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;rbenv&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;global&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2.1.4&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Set global Ruby version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;rbenv&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1.9.3&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;       &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Set local Ruby version for current directory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2&quot;&gt;rbenv&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFDC49&quot;&gt;rehash&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;# Refresh rbenv shims&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;rbenv versions          # List installed Ruby versionsrbenv global 2.1.4      # Set global Ruby versionrbenv local 1.9.3       # Set local Ruby version for current directoryrbenv rehash            # Refresh rbenv shims&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The switch from RVM to rbenv was one of the best development environment decisions I’ve made. If you’re frustrated with RVM’s complexity, rbenv is definitely worth considering.&lt;/p&gt;
</content:encoded></item><item><title>New Job, New Website</title><link>https://danny.is/writing/new-job-new-website/</link><guid isPermaLink="true">https://danny.is/writing/new-job-new-website/</guid><description>Transitioning from designer to Ruby developer and building a custom Sinatra-based website</description><pubDate>Wed, 02 Oct 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve recently transitioned from being a designer to a Ruby developer in London. As part of this change, I decided to build a completely custom website using Sinatra rather than relying on existing platforms.&lt;/p&gt;
&lt;h2 id=&quot;why-build-custom&quot;&gt;Why Build Custom?&lt;a href=&quot;#why-build-custom&quot; aria-label=&quot;Link to “Why Build Custom?”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After trying various blogging platforms, I couldn’t find one that met my specific requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Writing in Markdown&lt;/strong&gt; - I wanted to write everything in Markdown&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full control over HTML and CSS&lt;/strong&gt; - Complete creative freedom&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy image management&lt;/strong&gt; - Simple way to include images&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Understanding all the code&lt;/strong&gt; - I wanted to know exactly how everything worked&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-technical-setup&quot;&gt;The Technical Setup&lt;a href=&quot;#the-technical-setup&quot; aria-label=&quot;Link to “The Technical Setup”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The website is built using a custom Sinatra application with several key features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Static assets compiled locally&lt;/strong&gt; - CSS and JavaScript are processed before deployment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Redcarpet renderer&lt;/strong&gt; - Markdown is parsed with custom formatting rules&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Special tag system&lt;/strong&gt; - Images and gists can be inserted using custom tags like &lt;code&gt;{{image: 1}}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pygments syntax highlighting&lt;/strong&gt; - Code blocks are highlighted using Pygments.rb&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;deployment&quot;&gt;Deployment&lt;a href=&quot;#deployment&quot; aria-label=&quot;Link to “Deployment”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The site is deployed to Heroku with New Relic monitoring to prevent the app from going to sleep. This ensures fast loading times even during periods of low traffic.&lt;/p&gt;
&lt;h2 id=&quot;custom-styling&quot;&gt;Custom Styling&lt;a href=&quot;#custom-styling&quot; aria-label=&quot;Link to “Custom Styling”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve implemented custom styling for all standard Markdown elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lists&lt;/strong&gt; - Both ordered and unordered with custom styling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blockquotes&lt;/strong&gt; - Styled for emphasis and readability&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code blocks&lt;/strong&gt; - Syntax highlighted with clean typography&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tables&lt;/strong&gt; - Responsive and well-formatted&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;reflection&quot;&gt;Reflection&lt;a href=&quot;#reflection&quot; aria-label=&quot;Link to “Reflection”&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This project reflects my journey from designer to developer. Rather than using off-the-shelf solutions, I prefer building custom tools that give me complete creative and technical control. The site was quickly developed and deployed, proving that custom solutions don’t always require extensive development time.&lt;/p&gt;
&lt;p&gt;The key is understanding your exact requirements and building just what you need, nothing more.&lt;/p&gt;
</content:encoded></item><item><title>MoD Email Subjects with AppleScript</title><link>https://danny.is/writing/mod-email-subject-lines-with-applescript-osx/</link><guid isPermaLink="true">https://danny.is/writing/mod-email-subject-lines-with-applescript-osx/</guid><description>Automating military email subject line formatting with AppleScript for Mac Mail</description><pubDate>Fri, 28 Jun 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The work I do with the RAF and Army requires me to send emails with the date and security classification included, like this: “20130628-The Subject-U”.&lt;/p&gt;
  &lt;img src=&quot;/_astro/13-1-MoD-File-Names-Explained.B7B0rnYN_ZdBTlL.webp&quot; srcset=&quot;/_astro/13-1-MoD-File-Names-Explained.B7B0rnYN_ZdBTlL.webp 500w&quot; alt=&quot;MoD File Names Explained&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 500px) 500px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;500&quot; height=&quot;226&quot;&gt;
&lt;p&gt;I must do this at least 30 times a day, and earlier on I was that fed up with copy/pasting dates that I decided to write a script to do it for me. I did try this once before, but Mail.app’s API wasn’t very robust at the time and the action relied upon simulating keypresses which wasn’t exactly reliable.&lt;/p&gt;
&lt;p&gt;The following script adds the date and PM to the subject of the foremost Compose Mail Window. It’s a little smarter than my previous attempt too, in that it replaces the date if it’s already there and leaves the PM alone if it exists already.&lt;/p&gt;
      &lt;script src=&quot;https://gist.github.com/dannysmith/5887954.js&quot;&gt;&lt;/script&gt;   
&lt;p&gt;This one opens a reply to the selected message, with the “Re:” in the right place:&lt;/p&gt;
      &lt;script src=&quot;https://gist.github.com/dannysmith/5888072.js&quot;&gt;&lt;/script&gt;   
&lt;p&gt;Using Applescript is awkward to say the least - &lt;a href=&quot;http://www.russellbeattie.com/blog/fun-with-the-os-x-finder-and-applescript&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Russell Beattie&lt;/a&gt; puts it quite well:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using AppleScript is like being tied down in a black leather gimp-suit at a dominatrix convention, and losing the safety word… The syntax is like Erlang and ancient Greek got somehow mixed together with Visual Basic, and was translated back into English by mentally-challenged ESL students&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I did try to make a plugin for Mail.app to add some buttons to the menubar, but after discovering how &lt;a href=&quot;http://eaganj.free.fr/weblog/?post/2009/07/14/Demystifying-Mail.app-Plugins-on-Leopard&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;difficult&lt;/a&gt; it is working with Mail.app’s undocumented API, I gave up and created a Service instead.&lt;/p&gt;
&lt;p&gt;Despite AppleScript’s quirks, it’s still the most reliable way to automate Mac applications, especially when you need to interact with specific UI elements like email compose windows.&lt;/p&gt;</content:encoded></item><item><title>What is Good Design?</title><link>https://danny.is/writing/what-is-good-design/</link><guid isPermaLink="true">https://danny.is/writing/what-is-good-design/</guid><description>Exploring good design principles through the lens of the UK Government Digital Service website</description><pubDate>Mon, 21 Jan 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The UK Government Digital Service (GDS) has created something special with &lt;a href=&quot;https://www.gov.uk/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;gov.uk&lt;/a&gt;. Moving away from skeuomorphic design trends, they’ve focused on content-first, accessible design that makes government information truly usable.&lt;/p&gt;
&lt;p&gt;As John Gruber noted about skeuomorphic design:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“On retina displays… these techniques are revealed for what they truly are: an assortment of parlor tricks”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The GDS approach demonstrates that good design doesn’t need visual tricks. Instead, it comes from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Content-focused layout&lt;/strong&gt; that prioritizes information over decoration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Thoughtful typography&lt;/strong&gt; including their excellent adaptation of the Transport typeface&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accessibility&lt;/strong&gt; ensuring government services work for everyone&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flat design&lt;/strong&gt; that focuses on usability rather than visual effects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Andy Budd’s observation rings true here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The best designers and developers rarely have more talent. They simply have more time.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This echoes Teller’s insight about magic:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Sometimes magic is just someone spending more time on something than anyone else might reasonably expect.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The gov.uk website succeeds because the team invested time in getting the fundamentals right: clear typography, logical information architecture, and user-centered design principles.&lt;/p&gt;
&lt;p&gt;Good design, it turns out, is less about flashy effects and more about well-designed content, typography, and layout—combined with the time to experiment and refine until everything works seamlessly together.&lt;/p&gt;
</content:encoded></item><item><title>A Simpler Responsive Grid</title><link>https://danny.is/writing/a-simpler-responsive-grid/</link><guid isPermaLink="true">https://danny.is/writing/a-simpler-responsive-grid/</guid><description>A simple percentage-based CSS grid system that makes responsive design math easy</description><pubDate>Tue, 05 Jun 2012 00:00:00 GMT</pubDate><content:encoded>  &lt;img src=&quot;/_astro/12-1-Responsive-Grid.BHtdtGeE_2tUxbU.webp&quot; srcset=&quot;/_astro/12-1-Responsive-Grid.BHtdtGeE_2tUxbU.webp 500w&quot; alt=&quot;Grid System&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 500px) 500px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-fit=&quot;cover&quot; data-astro-image-pos=&quot;center&quot; width=&quot;500&quot; height=&quot;338&quot;&gt;
&lt;p&gt;After making use of various complicated CSS grid systems, I’ve been using a simple percentage-based grid for the last year or so. It’s based on a 1000 pixel wide container with 63 pixel columns and 20 pixel gutters. The outside gutters at the extreme left/right are 12 pixels to produce a nice round number.&lt;/p&gt;
&lt;p&gt;By using a 1000 pixel wide container, the maths is easy when using percentages. each column is 6.3%, gutters are 2% and so on…&lt;/p&gt;
&lt;p&gt;By setting a width on the container of 100% and a max-width of 1000 pixels, and thereafter using percentages for all widths, everything resizes nicely:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/ec.p6nts.css&quot;/&gt;&lt;script type=&quot;module&quot; src=&quot;/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;#container&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;margin&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;auto&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;max-width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;#my-box&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;14.6&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;/* Two columns wide - (2 * 6.3)+2 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;margin-left: &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;1.2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;/* Covering the first two columns */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;#container {    margin: 0 auto;    max-width: 1000px;    width: 100%;  }  #my-box {    width: 14.6% /* Two columns wide - (2 * 6.3)+2 */    margin-left: 1.2% /* Covering the first two columns */  }&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I’ve knocked together a quick &lt;a href=&quot;http://dasmith.co.uk/files/examples/grid.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;example page&lt;/a&gt; (&lt;a href=&quot;https://gist.github.com/2875987&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;code&lt;/a&gt;) that shows the columns. One problem with percentage-based widths is that because of the box model, any borders or padding will be added onto outside of the box. Obviously this can be a drama if you’re using pixels to declare your borders and padding.&lt;/p&gt;
&lt;p&gt;The easy solution to this is the box-sizing property:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#5BB2A2;--0fs:italic&quot;&gt;#my-other-box&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;31.2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;/* Four columns wide */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;margin-left: &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;9.5&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;/* Covering columns 2 to 5 (1.2 + 6.3 + 2) */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;box-sizing: &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;border-box&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#6AAAD1&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--0:#E981B9&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;solid&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B594E2&quot;&gt;red&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;--0:#9FA4A7&quot;&gt;/* will be rendered inside the box */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#F6F3EA&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;#my-other-box {    width: 31.2% /* Four columns wide */    margin-left: 9.5% /* Covering columns 2 to 5 (1.2 + 6.3 + 2) */    box-sizing: border-box;    border: 2px solid red; /* will be rendered inside the box */  }&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Although this property isn’t supported in IE7 and below, it’s a useful way to control the box model and make responsive design easier.&lt;/p&gt;</content:encoded></item></channel></rss>