<?xml-stylesheet href="/pretty-feed-v2.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Trys Mudford's Blog</title>
    <link>https://www.trysmudford.com/year/2025/</link>
    <description>Posts, thoughts, links and photos from Trys</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 19 Aug 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.trysmudford.com/blog/index.xml" rel="self" type="application/rss+xml"/>
    
    <item>
      <title>Learning when to stop</title>
      <link>https://www.trysmudford.com/blog/learning-when-to-stop/</link>
      <pubDate>Tue, 19 Aug 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/learning-when-to-stop/</guid>
      <description><![CDATA[
<p>Cricket is a funny old game. Many find it dreadfully boring, particularly the test variety. To be fair, any game that can be played for five full days and not produce a result does sound like a practical joke that&rsquo;s gone a bit too far.</p>
<p>It&rsquo;s also a deeply frustrating game to play. It&rsquo;s a team sport, but one where abject individual failure is almost guaranteed, particularly as a batter. The first scenario is you won&rsquo;t actually be required to bat, because the batters ahead of you were good enough not to get out within the allotted number of overs. This is frustrating when you&rsquo;ve spent the week practising or thinking about how you are going to play differently to the last game. The most common option is you get out to the middle, then get out, and have to trudge with a number of runs that you&rsquo;re either content, or almost always, disappointed with. And in some very rare cases, you may still be in at the end of the innings, where you&rsquo;ll likely be rue-ing not taking more risks and scoring more runs.</p>
<p>And yet, thousands of individuals put themselves through this pain each summery Saturday afternoon because there&rsquo;s something addictive about bettering your score. It&rsquo;s a sport that&rsquo;s instinctively gamified, chock full of stats &amp; averages.</p>
<p>Before Jacob was born, I was roped into playing for the local team. I was pretty terrible, but lucked one or two <em>okay</em> scores in the first season. The team weren&rsquo;t too fussed as I was, and I quote, &ldquo;another body on the pitch&rdquo;. Despite a winter of practising and weekly summer net sessions, the second season was not any better.</p>
<p>After three years of absence, I was roped into another game on Saturday. I spent the week fretting, then preparing, then looking forward to it. I visualised exactly how I&rsquo;d defend those first 10 or so balls to make sure I got my head back into the game. I went to the local nets with my wife and was very pleased with how solid my forward defense was, and even managed to showcase a few attacking shots with confidence and low risk.</p>
<p>And then the moment came where I strode out to bat. Expectations low, but with a real hunger to get off the mark and contribute something to the team. In came the first ball; a nice straight one. &ldquo;Forward to it&rdquo;, I thought; &ldquo;head over the ball&rdquo;. The bat came down to prod it back gently to the bowler, and all I heard was the rattle of the wooden bails behind me. To say I felt dejected would be an understatement. It wasn&rsquo;t the game situation, nor the fact that family and friends were watching. It was the crushing realisation that cricket is not the game for me. A game I love to watch and dig into the nerdy details, and a game I <em>so</em> want to be good at. But it&rsquo;s not happening.</p>
<p>I walked off and looked at my little family sitting on the boundary rope. My wife and I shrugged at each other and I headed back to get changed. I spent the rest of the innings in the park with Jacob, playing hide and seek and helping him up the climbing frame. It was wonderful.</p>
<p>I&rsquo;m a much better Dad than I am cricketer, and that&rsquo;s okay. I don&rsquo;t need to spend my (and my family&rsquo;s) Saturday afternoons getting mentally crushed by a game. Hobbies are good, particularly active ones, and I need to find a replacement. But I&rsquo;ve got to the point of acceptance. Cricket&rsquo;s not for me.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>The elephant in the room</title>
      <link>https://www.trysmudford.com/blog/the-elephant-in-the-room/</link>
      <pubDate>Fri, 08 Aug 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/the-elephant-in-the-room/</guid>
      <description><![CDATA[
<p>Colton Voege has penned an excellent <a href="https://colton.dev/blog/curing-your-ai-10x-engineer-imposter-syndrome/">debunk of the 10x AI engineer</a>. Their account of rapidly diminishing returns mirrors my experience with agentic coding. Yes, it can create 10x the code that you or I could create in the same time period, but it certainly can&rsquo;t create 10x the value or impact.</p>
<p>Through lunchtime chats, off-hand comments and a growing number of DM&rsquo;s, it&rsquo;s becoming clear that, although the AI propaganda is loud, it&rsquo;s not landing with a great deal of engineers on the ground. Many I&rsquo;ve spoken to appreciate its use within a niche set of circumstances, but I could count the number who&rsquo;ve actually drunk the koolaid on one hand.</p>
<p>And yet, within a tech company, it&rsquo;s taboo to voice any form of healthy scepticism.</p>
<blockquote class="longquote"><p>If you are an engineer and your boss asks you:</p>
<p>Hey, you&rsquo;re getting 10x the productivity thanks to AI, just like all the other engineers, right?</p>
<p>You are strongly incentivized to say yes. And when every other engineer also says yes for the same reason, that CEO isn&rsquo;t lying, they are just relaying what they heard.</p>
<p><cite><a href="https://colton.dev/blog/curing-your-ai-10x-engineer-imposter-syndrome/">No, AI is not Making Engineers 10x as Productive</a>, Colton Voege</cite></p>

  
</blockquote>

<p>But when the rhetoric is &ldquo;your very livelihood is on the line if you don&rsquo;t subscribe to this brave new world&rdquo;, we <strong>really should</strong> be able to discuss this in the open.</p>
<p>Regardless of whether you believe AI is coming for your job or not, navigating any existential threat alone is exhausting. It&rsquo;s time to re-read <a href="https://ethanmarcotte.com/books/you-deserve-a-tech-union/">You Deserve a Tech Union</a> and plan a forum for these discussions&hellip;</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Better max-width queries</title>
      <link>https://www.trysmudford.com/blog/max-width-queries/</link>
      <pubDate>Thu, 24 Jul 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/max-width-queries/</guid>
      <description><![CDATA[
<p>A friend shared this tip the other day and it was too useful not to reshare. If you&rsquo;re a mobile-up kind of developer, it&rsquo;s easy to forget that <code>max-width</code> media queries are &ldquo;less than or equal to&rdquo; by default. It can play havoc with your nice round numbers:</p>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">min-width</span><span class="o">:</span> <span class="nt">60em</span><span class="o">)</span> <span class="p">{</span>
  <span class="c">/* above and including 60em */</span>
<span class="p">}</span>

<span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">max-width</span><span class="o">:</span> <span class="nt">59</span><span class="p">.</span><span class="nc">9375em</span><span class="o">)</span> <span class="p">{</span>
  <span class="c">/* below 60em, but not including 60em exactly */</span>
<span class="p">}</span>
</code></pre></div><p>If you want to tidy that up, and continue to <a href="https://caniuse.com/css-media-range-syntax">support &lt;= Safari 16.3</a>, you can use a query like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">max-width</span><span class="o">:</span> <span class="nt">60em</span><span class="o">)</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">not</span> <span class="o">(</span><span class="nt">width</span><span class="o">:</span> <span class="nt">60em</span><span class="o">))</span> <span class="p">{</span>
  <span class="c">/* below 60em, but not including 60em exactly */</span>
<span class="p">}</span>
</code></pre></div><p>However, if you&rsquo;re bought into <a href="https://caniuse.com/css-media-range-syntax">range queries</a>, it all becomes much, much simpler:</p>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">width</span> <span class="o">&lt;</span> <span class="nt">60em</span><span class="o">)</span> <span class="p">{}</span>
</code></pre></div>]]>
      </description>
    </item>
    
    <item>
      <title>A human review</title>
      <link>https://www.trysmudford.com/blog/a-human-review/</link>
      <pubDate>Tue, 22 Jul 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/a-human-review/</guid>
      <description><![CDATA[
<p>Mid-year reviews have just come around in the ol' 9-5. It&rsquo;s an opportunity to reflect on the first six months (H1 if you&rsquo;re into all that business speak) and set some goals for the end of the year. I personally find the process quite cathartic. Not only does it make the end of year review simpler (I can barely remember what I had for breakfast, let alone what business metrics I contributed towards in January), I find that as I start reflecting &amp; writing, it quickly becomes a gratitude list of the smart colleagues I&rsquo;ve worked with, and the interesting features I&rsquo;ve been able to work on.</p>
<p>I spent a couple of hours working through my notes and writing up a review before sending it to my manager, awaiting their equivalent review for me.</p>
<p>However, the review I received back was, quite simply, quintessential AI slop. All the tell-tail signs were there—words like &ldquo;pioneered&rdquo; &amp; &ldquo;spearheaded&rdquo; peppered throughout, no personal detail, and most of all, a complete regurgitation of what I originally wrote in my review. You see, managers ask to see your review before you submit so &ldquo;we can submit a unified message to the business&rdquo;, and you can quite quickly join the dots on what happened next (hint: it involved a bot called Claude &amp; some <code>cmd-c</code> and <code>cmd-v</code>).</p>
<p>Without any context of me as a person, AI decided the best way to set personalised goals for me, was to fire back what I&rsquo;d written but on steroids. When I wrote about &ldquo;component migrations ahead of a React 19 upgrade&rdquo;, AI wrote: &ldquo;Trys should spearhead the React 19 upgrade project&rdquo;, missing the fact that the project already has a feature lead. When I wrote about having an interest in improving our frontend stack, AI wrote &ldquo;Trys should write a frontend strategy document for the company&rdquo; - not exactly a task for a lone engineer, eh? There was no insightful feedback or contextual suggestions, just an A4 page of vagueries.</p>
<p>This is not a pop at the individual manager. I get it, if you have 15 reviews to deliver in a week and there&rsquo;s a tool that takes all that pain away, that tool&rsquo;s going to look pretty seductive. And I <em>know</em> it&rsquo;s not just the managers. When the review deadline looms and you&rsquo;ve not had a minute to reflect, let alone write, I&rsquo;d wager a hefty sum that a good proportion of employees are submitting <em>their</em> reviews from bullet points via an OpenAI server.</p>
<p>But when you get to the point where both parties are using AI, you might as well pull the facade and send bullet points to one another. I&rsquo;d rather read that than a wall of slop. <a href="https://claytonwramsey.com/blog/prompt/">I&rsquo;d rather read the prompt</a>. And I&rsquo;d take a single ounce of humanity over a full time AI manager, any day of the week.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Generate all pair permutations in Utopia SCSS</title>
      <link>https://www.trysmudford.com/blog/generate-all-pairs/</link>
      <pubDate>Tue, 20 May 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/generate-all-pairs/</guid>
      <description><![CDATA[
<a href="https://utopia.fyi/blog/generate-all-pairs/" class="button">Read the article</a>

]]>
      </description>
    </item>
    
    <item>
      <title>The joy is in the craft</title>
      <link>https://www.trysmudford.com/blog/the-joy-is-in-the-craft/</link>
      <pubDate>Fri, 09 May 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/the-joy-is-in-the-craft/</guid>
      <description><![CDATA[
<p>If you&rsquo;re a maker, like I am on the web, you&rsquo;ll no doubt know the joy and wonder of creating <a href="https://joshcollinsworth.com/blog/the-blissful-zen-of-a-good-side-project">something from scratch</a>. That you can assemble something useful, useless, beautiful or brilliant from a few pixels, phrases or physical objects. To <a href="https://themaxim.com/find-the-angel-in-the-marble"><em>&ldquo;see the angel in the marble, and carve to set it free&rdquo;</em></a> is a feeling unique to those who create.</p>
<p>As <a href="https://en.wikipedia.org/wiki/Enshittification">enshittification</a> increases, and slop becomes the norm, my feelings towards so-called AI are not ones of fear, but of pity and disappointment. These models claim to be able to create, or to increase our creativity. They can spin up a wonky clone of a song, picture or site in a matter of seconds, which without basic scrutiny might appear <a href="https://buttondown.com/practicaltips/archive/the-dancing-bear-part-1/">initially impressive</a>. Heck, you might even be able to <a href="https://tomblomfield.com/post/778601470234918912/vibecoding-a-production-app">vibe-code an app</a> in a weekend (although best of luck maintaining that), but in doing so, you&rsquo;re missing the entire point. The joy is not in the product or the end result, but in the craft.</p>
<p>It&rsquo;s in the <code>prefers-reduced-motion</code> query, or the elegant custom property that removes a bulk of nesting. It&rsquo;s in knowing that all users will get the best experience for their device &amp; environment, because you put the time in to make it so. It&rsquo;s in winning the fight for server-side over client-side AB testing, because you know it makes a difference. The joy is in the well-planned &amp; anticlimactic go live, the microinteractions you snuck in, the encapsulated &amp; deletable component, and the extensible grid system you know will be a joy to work with well in the future. It&rsquo;s in the craft. The <a href="https://www.seantucker.photography/the-meaning-in-the-making">meaning is in the making</a>.</p>
<p>No prompt or koolaid-infused-workflow will bring that joy.</p>
<p>Sure, it&rsquo;s quicker to e-bike a parkrun, and if you&rsquo;re as unfit as I am, will be a darn-side easier. You&rsquo;ll finish 10 minutes ahead of the pack. You might even feel the most fleeting sense of unearned achievement. But you won&rsquo;t feel that same satisfaction as the person who put in the hours, pushed themselves to their limit and crossed the line under their own steam.</p>
<p>That&rsquo;s what makes me want to create. It&rsquo;s what makes me want to build &amp; share, to learn &amp; teach. It&rsquo;s the web that I fell in love with, that captivated me as junior developer, and what keeps me fired up to this day.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>National Trusts</title>
      <link>https://www.trysmudford.com/blog/national-trusts/</link>
      <pubDate>Thu, 24 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/national-trusts/</guid>
      <description><![CDATA[
<p>Growing up, my family had life membership for the National Trust, and thus, much of my childhood was spent traipsing in leafy gardens and around stately homes. A couple of years ago, before the financial responsibilities/burden (delete as appropriate) of a toddler, we decided to make the same investment.</p>
<p>Spreadsheets are <a href="/blog/virtual-games-yahtzee">a</a> <a href="/blog/virtual-games-boggle">passion</a> of ours, so of course, we began tracking our visits, most importantly capturing how much it would have cost if we paid for each visit. Last week, after only 39 months, we broke even.</p>
<p>Breaking even is a little tenuous, as it&rsquo;s pretty unlikely we&rsquo;d have visited all these properties if we didn&rsquo;t have membership, and even if we did, we&rsquo;d have bought annual memberships (which takes about 12 years to pay off). But it&rsquo;s a bit of fun to see how much we&rsquo;ve saved.</p>
<table class="table">
<thead>
<tr>
<th>Property</th>
<th>Visits</th>
<th>Total</th>
<th>%</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sheffield Park</td>
<td>56</td>
<td>£1,291.00</td>
<td>59.73%</td>
</tr>
<tr>
<td>Nymans</td>
<td>7</td>
<td>£184.40</td>
<td>8.53%</td>
</tr>
<tr>
<td>Dyrham Park</td>
<td>2</td>
<td>£84.00</td>
<td>3.89%</td>
</tr>
<tr>
<td>Batemans</td>
<td>4</td>
<td>£81.00</td>
<td>3.75%</td>
</tr>
<tr>
<td>Petworth</td>
<td>2</td>
<td>£72.00</td>
<td>3.33%</td>
</tr>
<tr>
<td>Standen</td>
<td>2</td>
<td>£58.00</td>
<td>2.68%</td>
</tr>
<tr>
<td>Wakehurst Place</td>
<td>2</td>
<td>£58.00</td>
<td>2.68%</td>
</tr>
<tr>
<td>The Vyne</td>
<td>2</td>
<td>£56.00</td>
<td>2.59%</td>
</tr>
<tr>
<td>Parking</td>
<td>8</td>
<td>£36.00</td>
<td>1.67%</td>
</tr>
<tr>
<td>Hardwick Hall</td>
<td>1</td>
<td>£32.00</td>
<td>1.48%</td>
</tr>
<tr>
<td>Hinton Ampner</td>
<td>1</td>
<td>£32.00</td>
<td>1.48%</td>
</tr>
<tr>
<td>Polesden Lacey</td>
<td>1</td>
<td>£30.00</td>
<td>1.39%</td>
</tr>
<tr>
<td>Dunham Massey</td>
<td>1</td>
<td>£28.00</td>
<td>1.30%</td>
</tr>
<tr>
<td>Sissinghurst</td>
<td>1</td>
<td>£24.00</td>
<td>1.11%</td>
</tr>
<tr>
<td>Scotney Castle</td>
<td>1</td>
<td>£24.00</td>
<td>1.11%</td>
</tr>
<tr>
<td>Dyffryn Gardens</td>
<td>1</td>
<td>£22.00</td>
<td>1.02%</td>
</tr>
<tr>
<td>Emmetts Garden</td>
<td>1</td>
<td>£20.00</td>
<td>0.93%</td>
</tr>
<tr>
<td>Bodiam Castle</td>
<td>1</td>
<td>£20.00</td>
<td>0.93%</td>
</tr>
<tr>
<td>Longshaw</td>
<td>2</td>
<td>£9.00</td>
<td>0.42%</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>Total</th>
<td>94</td>
<td>£2,162.40</td>
<td></td>
</tr>
</table>
<style>
.table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--space-l);
}

.table th, .table td {
  border: 1px solid var(--table-border);
  padding: 8px;
}
.table th {
  background-color: var(--table-bg);
  text-align: left;
}
</style>
<div class="alert alert--with-eyebrow">
  
    <span class="alert__eyebrow">Note</span>
  
  This does not include how much we&rsquo;ve spent on scones, which mercifully, we have not captured.
</div>

<p><img src="/images/blog/national-trusts.jpg" alt="A selfie of Jacob, Lauren and Trys crouching in a field of tulips, all looking very happy"></p>
]]>
      </description>
    </item>
    
    <item>
      <title>Legacy guitar gear</title>
      <link>https://www.trysmudford.com/blog/legacy-guitar-gear/</link>
      <pubDate>Tue, 15 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/legacy-guitar-gear/</guid>
      <description><![CDATA[
<p>I recently became the owner of an exciting piece of guitar gear. Well, exciting to me, anyway. Winding back the clock to 2010, I distinctly remember pining after a TC Electronic G-System. It was <em>the</em> guitar effects processor to have; all the pro&rsquo;s were using it, but I was a poor student and couldn&rsquo;t come close to affording one.</p>
<p><img src="/images/blog/g-system.jpg" alt="TC Electronic G-System"></p>
<p>A couple of months ago, I spotted a used one for a very reasonable cost and I just couldn&rsquo;t resist. It arrived, and it was immediately clear why professionals used this—it&rsquo;s an absolute tank of a unit. It&rsquo;s 8KG and made of thick aluminium. The processor is removable and rackmountable. And it looks like a Christmas tree. I love it.</p>
<p>What&rsquo;s super interesting is how it has clearly inspired the design of modern processors. Sure, there&rsquo;s no touch screen and the LCD strip is dated, but there are also no buttons or switches to get broken on the road. You navigate the menu by twisting the footswitches themselves, which was way ahead of it&rsquo;s time.</p>
<p>It doesn&rsquo;t have any amp/cab modelling or drive effects, so amazingly, the sounds haven&rsquo;t dated at all. Chorus hasn&rsquo;t really changed since the 80&rsquo;s, so they still sound great on a unit from the 00&rsquo;s. It merrily fires out relay switches to change channels on my amp, has four powered effect loops for drive pedals, and is effortlessly controllable via MIDI, which I used in a gig at Christmas.</p>
<p>I&rsquo;ve ended up selling my Strymon &amp; Walrus Audio gear and have fully committed to this now historic (and in my opinion, timeless) piece of kit.</p>
<p>In doing so, I now realise that I&rsquo;m very much getting old and nostalgic, not for things I had in my youth, but for things I couldn&rsquo;t afford at the time. Next thing you know, I&rsquo;ll be driving a <a href="https://en.wikipedia.org/wiki/Renault_Avantime">Renault Avantime</a>&hellip;</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Rounded triangular boxes in CSS/SVG</title>
      <link>https://www.trysmudford.com/blog/rounded-triangular-boxes-in-css/</link>
      <pubDate>Thu, 10 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/rounded-triangular-boxes-in-css/</guid>
      <description><![CDATA[
<p>In the <a href="/blog/lessons-from-a-brand-refresh/">Motorway brand refresh</a>, I had to build <em>a lot</em> of rounded rectangular &amp; triangular shapes (I don&rsquo;t know what they&rsquo;re officially called), and got quite good and understanding the pro&rsquo;s and con&rsquo;s of each approach. Depending on your use-case, there&rsquo;s a few possible options available to you.</p>
<h2 id="the-filled-box">The filled box</h2>
<p><img src="/images/blog/motorway-cvt.jpg" alt="A screenshot from the Motorway product, showing a hero image on top of several rounded triangular rectangles"></p>
<p>The &lsquo;simpler&rsquo; use-case is the filled box. Bennett Feely&rsquo;s <a href="https://bennettfeely.com/clippy/">Clippy</a> continues to be the best tool for the job of generating <code>clip-path</code> shapes, and even has  &lsquo;left/right point&rsquo; options. Clip-path gives us a pointy edge, but doesn&rsquo;t give us rounded edges, or a particularly responsive solution out of the box - wider screens will have a more pronounced point than smaller screens when using percentages:</p>
<style>
  .pointy-demo {
    background: var(--site-chrome);
    padding: var(--space-l);
    border: 1px solid var(--blog-bg);
    margin-bottom: var(--space-l);
  }

  .pointy-controller {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2xs) var(--space-l);
    align-items: center;
    justify-content: center;
  }

  .pointy-controls {
    display: flex;
    flex-direction: column;
    gap: var(--space-3xs);
    justify-content: center;
    align-items: center;
    padding-top: var(--space-s);
    font-size: var(--step--1);
    font-weight: 700;
  }

  .pointy-controls code {
    font-size: var(--step--2);
    font-weight: 400;
  }

  .pointy-demo + .highlight {
    margin-top: calc(-1 * var(--space-l));
  }

  .box {
    background: linear-gradient(90deg, var(--gradient-blue), var(--gradient-green));
    height: 200px;
  }

  .box1 {
    --point-inset: calc(1% * var(--value0, 90));
    clip-path: polygon(0% 0%, var(--point-inset) 0%, 100% 50%, var(--point-inset) 100%, 0% 100%);
  }
</style>
<p><svg height="0" style="position:absolute;visibility:hidden" version="1.1" width="0" xmlns="http://www.w3.org/2000/svg"><defs><filter id="rounding-filter"><feGaussianBlur in="SourceGraphic" result="blur" stdDeviation="6"></feGaussianBlur><feColorMatrix in="blur" mode="matrix" result="goo" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 25 -9"></feColorMatrix><feComposite in="SourceGraphic" in2="goo" operator="atop"></feComposite></filter></defs></svg></p>
<div class="pointy-demo">
  <div class="box box1"></div>
  <div class="pointy-controls">
    <label for="box1-slider">Clip path: <output>90</output>%</label>
    <input type="range" id="box1-slider" min="50" max="99" value="90" />
  </div>
</div>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">box</span> <span class="p">{</span>
  <span class="k">clip-path</span><span class="p">:</span> <span class="nb">polygon</span><span class="p">(</span><span class="mi">0</span><span class="kt">%</span> <span class="mi">0</span><span class="kt">%</span><span class="p">,</span> <span class="mi">75</span><span class="kt">%</span> <span class="mi">0</span><span class="kt">%</span><span class="p">,</span> <span class="mi">100</span><span class="kt">%</span> <span class="mi">50</span><span class="kt">%</span><span class="p">,</span> <span class="mi">75</span><span class="kt">%</span> <span class="mi">100</span><span class="kt">%</span><span class="p">,</span> <span class="mi">0</span><span class="kt">%</span> <span class="mi">100</span><span class="kt">%</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div><p>To improve the point, you can use <code>calc</code> with <code>clip-path</code> to determine where the point starts/ends. <code>border-radius</code> gets us some roundedness, but only on the flat edge, not on the pointy edge:</p>
<style>
  .box2 {
    --point-inset: calc(100% - (1px * var(--value0, 32)));
    clip-path: polygon(0% 0%, var(--point-inset) 0%, 100% 50%, var(--point-inset) 100%, 0% 100%);
    border-radius: calc(1px * var(--value1, 16));
  }
</style>
<div class="pointy-demo">
  <div class="box box2"></div>
  <div class="pointy-controller">
    <div class="pointy-controls">
      <label for="box2-slider">Clip path offset: <output>32</output>px</label>
      <input type="range" id="box2-slider" min="1" max="120" value="32" />
    </div>
    <div class="pointy-controls">
      <label for="box2-slider1">Border radius: <output>16</output>px</label>
      <input type="range" id="box2-slider1" min="1" max="48" value="16" />
    </div>
  </div>
</div>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">box</span> <span class="p">{</span>
  <span class="nv">--point-inset</span><span class="p">:</span> <span class="nb">calc</span><span class="p">(</span><span class="mi">100</span><span class="kt">%</span> <span class="o">-</span> <span class="mi">2</span><span class="kt">rem</span><span class="p">);</span>
  <span class="k">clip-path</span><span class="p">:</span> <span class="nb">polygon</span><span class="p">(</span><span class="mi">0</span><span class="kt">%</span> <span class="mi">0</span><span class="kt">%</span><span class="p">,</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">point</span><span class="o">-</span><span class="kc">inset</span><span class="p">)</span> <span class="mi">0</span><span class="kt">%</span><span class="p">,</span> <span class="mi">100</span><span class="kt">%</span> <span class="mi">50</span><span class="kt">%</span><span class="p">,</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">point</span><span class="o">-</span><span class="kc">inset</span><span class="p">)</span> <span class="mi">100</span><span class="kt">%</span><span class="p">,</span> <span class="mi">0</span><span class="kt">%</span> <span class="mi">100</span><span class="kt">%</span><span class="p">);</span>
  <span class="k">border-radius</span><span class="p">:</span> <span class="mi">1</span><span class="kt">rem</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>To properly round this out, we need to employ a lesser-used bit of SVG: <code>&lt;feGaussianBlur&gt;</code>. There&rsquo;s a <a href="https://dev.to/afif/css-shapes-with-rounded-corners-56h">wonderful article</a> by Temani Afif that explains the technique. This snippet can be popped at the bottom of your page, and sits there as an invisible SVG.</p>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="p">&lt;</span><span class="nt">svg</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;0&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;position:absolute; visibility:hidden&#34;</span> <span class="na">version</span><span class="o">=</span><span class="s">&#34;1.1&#34;</span> <span class="na">width</span><span class="o">=</span><span class="s">&#34;0&#34;</span> <span class="na">xmlns</span><span class="o">=</span><span class="s">&#34;http://www.w3.org/2000/svg&#34;</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">defs</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">filter</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;rounding-filter&#34;</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">feGaussianBlur</span> <span class="na">in</span><span class="o">=</span><span class="s">&#34;SourceGraphic&#34;</span> <span class="na">result</span><span class="o">=</span><span class="s">&#34;blur&#34;</span> <span class="na">stdDeviation</span><span class="o">=</span><span class="s">&#34;5&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">feGaussianBlur</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">feColorMatrix</span> <span class="na">in</span><span class="o">=</span><span class="s">&#34;blur&#34;</span> <span class="na">mode</span><span class="o">=</span><span class="s">&#34;matrix&#34;</span> <span class="na">result</span><span class="o">=</span><span class="s">&#34;goo&#34;</span> <span class="na">values</span><span class="o">=</span><span class="s">&#34;1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 25 -9&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">feColorMatrix</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">feComposite</span> <span class="na">in</span><span class="o">=</span><span class="s">&#34;SourceGraphic&#34;</span> <span class="na">in2</span><span class="o">=</span><span class="s">&#34;goo&#34;</span> <span class="na">operator</span><span class="o">=</span><span class="s">&#34;atop&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">feComposite</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">filter</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">defs</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">svg</span><span class="p">&gt;</span>
</code></pre></div><p>We can then apply this filter to our box and it will round the corners of child elements. Change <code>stdDeviation</code> to increase/decrease the amount of rounding.</p>
<style>
  .box3 {
    filter: url(#rounding-filter);
  }

  .box3 > * {
    --point-inset: calc(100% - (1px * var(--value0, 32)));
    clip-path: polygon(0% 0%, var(--point-inset) 0%, 100% 50%, var(--point-inset) 100%, 0% 100%);
  }
</style>
<div class="pointy-demo">
  <div class="box3">
    <div class="box"></div>
  </div>
  <div class="pointy-controller">
    <div class="pointy-controls">
      <label for="box3-slider">Clip path offset: <output>32</output>px</label>
      <input type="range" id="box3-slider" min="1" max="120" value="32" />
    </div>
    <div class="pointy-controls">
      <label for="box3-slider1">stdDeviation: <output>6</output></label>
      <input type="range" id="box3-slider1" data-deviation="1" min="1" max="30" value="5" />
    </div>
  </div>
</div>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">box</span> <span class="p">{</span>
  <span class="k">filter</span><span class="p">:</span> <span class="nb">url</span><span class="p">(</span><span class="sx">#rounding-filter</span><span class="p">);</span>
<span class="p">}</span>

<span class="p">.</span><span class="nc">box</span> <span class="o">&gt;</span> <span class="o">*</span> <span class="p">{</span>
  <span class="nv">--point-inset</span><span class="p">:</span> <span class="nb">calc</span><span class="p">(</span><span class="mi">100</span><span class="kt">%</span> <span class="o">-</span> <span class="mi">2</span><span class="kt">rem</span><span class="p">);</span>
  <span class="k">clip-path</span><span class="p">:</span> <span class="nb">polygon</span><span class="p">(</span><span class="mi">0</span><span class="kt">%</span> <span class="mi">0</span><span class="kt">%</span><span class="p">,</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">point</span><span class="o">-</span><span class="kc">inset</span><span class="p">)</span> <span class="mi">0</span><span class="kt">%</span><span class="p">,</span> <span class="mi">100</span><span class="kt">%</span> <span class="mi">50</span><span class="kt">%</span><span class="p">,</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">point</span><span class="o">-</span><span class="kc">inset</span><span class="p">)</span> <span class="mi">100</span><span class="kt">%</span><span class="p">,</span> <span class="mi">0</span><span class="kt">%</span> <span class="mi">100</span><span class="kt">%</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div><div class="alert alert--with-eyebrow">
  
    <span class="alert__eyebrow">Note</span>
  
  <p><code>filter: url()</code> will only apply to child elements including <code>:before/:after</code>, so you need a bit more markup to make this work.</p>
<p>In addition, you&rsquo;ll want to avoid putting any text/images that don&rsquo;t want to be blurred into pulp as direct descendants of the box. This filter works (I believe) by blurring the child elements and the lopping off the blurred edges.</p>

</div>

<h2 id="the-outlined-box">The outlined box</h2>
<p><img src="/images/blog/motorway-outline.jpg" alt="A screenshot from the Motorway product, showing an outlined triangular box"></p>
<p>Outlined boxes are a bit more tricky. You&rsquo;d think it would just be a case of swapping background for a border, but alas not. Again, because this filter works by blurring the edges of the component, all borders effectively disappear.  To compound the problem, <code>box-shadow: inset</code>, <code>filter: drop-shadow()</code> also get blurred, and <code>clip-path</code> and <code>border</code> do not play nicely (subtle background added for effect):</p>
<div class="pointy-demo">
  <div class="box box4"></div>
  <div class="pointy-controls">
    <label for="box4-slider">Clip path offset: <output>32</output>px</label>
    <input type="range" id="box4-slider" min="0" max="120" value="32" />
</div>
</div>
<style>
  .box4 {
    --point-inset: calc(100% - (1px * var(--value0, 32)));
    clip-path: polygon(0% 0%, var(--point-inset) 0%, 100% 50%, var(--point-inset) 100%, 0% 100%);
    border: 1px solid var(--gradient-green);
    background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.1));
  }
</style>
<p>The best solution I found was to employ an additional SVG element an append it to the right-side of the box (as demonstrated by the orange flashing line).</p>
<div class="pointy-demo">
  <div class="box-wrapper">
    <div class="box box5"></div>
    <svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" fill="none" viewBox="0 0 127 478">
      <path stroke="var(--gradient-green)" stroke-width="1" d="M0 477h11.33a15.5 15.5 0 0 0 14.17-9.22l98.67-222.51a15.5 15.5 0 0 0 0-12.56L25.51 10.22A15.5 15.5 0 0 0 11.34 1H0" vector-effect="non-scaling-stroke"/>
    </svg>
  </div>
  <div class="pointy-controller">
    <div class="pointy-controls">
      <label for="box5-slider1">Border radius: <output>16</output>px</label>
      <input type="range" id="box5-slider1" min="1" max="48" value="16" />
    </div>
    <div class="pointy-controls">
      <label for="box5-slider2">Triangle size: <output>48</output>px</label>
      <input type="range" id="box5-slider2" min="12" max="160" value="48" />
    </div>
  </div>
</div>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;box-wrapper&#34;</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;box&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">svg</span> <span class="na">xmlns</span><span class="o">=</span><span class="s">&#34;http://www.w3.org/2000/svg&#34;</span> <span class="na">preserveAspectRatio</span><span class="o">=</span><span class="s">&#34;none&#34;</span> <span class="na">fill</span><span class="o">=</span><span class="s">&#34;none&#34;</span> <span class="na">viewBox</span><span class="o">=</span><span class="s">&#34;0 0 127 478&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">path</span> <span class="na">stroke</span><span class="o">=</span><span class="s">&#34;var(--theme)&#34;</span> <span class="na">stroke-width</span><span class="o">=</span><span class="s">&#34;1&#34;</span> <span class="na">d</span><span class="o">=</span><span class="s">&#34;M0 477h11.33a15.5 15.5 0 0 0 14.17-9.22l98.67-222.51a15.5 15.5 0 0 0 0-12.56L25.51 10.22A15.5 15.5 0 0 0 11.34 1H0&#34;</span> <span class="na">vector-effect</span><span class="o">=</span><span class="s">&#34;non-scaling-stroke&#34;</span><span class="p">/&gt;</span>
  <span class="p">&lt;/</span><span class="nt">svg</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</code></pre></div><div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">box-wrapper</span> <span class="p">{</span>
  <span class="nv">--theme</span><span class="p">:</span> <span class="kc">rebeccapurple</span><span class="p">;</span>
  <span class="k">display</span><span class="p">:</span> <span class="k">grid</span><span class="p">;</span>
  <span class="k">grid-template-columns</span><span class="p">:</span> <span class="kc">auto</span> <span class="mi">3</span><span class="kt">rem</span><span class="p">;</span>
<span class="p">}</span>

<span class="p">.</span><span class="nc">box</span> <span class="p">{</span>
  <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">theme</span><span class="p">);</span>
  <span class="n">border-inline-end</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
  <span class="k">border-radius</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><style>
  .box-wrapper {
    display: grid;
    grid-template-columns: 1fr auto;
  }

  .box5 {
    border: 1px solid var(--gradient-green);
    border-inline-end-style: dashed;
    border-inline-end-color: rgba(255, 60, 5, var(--dash-opacity));
    background: none;
    border-radius: calc(1px * var(--value0, 16)) 0 0 calc(1px * var(--value0, 16));
    animation: pulse 1.5s infinite;
  }

  @keyframes pulse {
    0%,
    100% {
      --dash-opacity: 1;
    }
    
    50% {
      --dash-opacity: 0;
    }
  }

  .box-wrapper > svg {
    height: 200px;
    width: calc(1px * var(--value1, 48));
  }
</style>
<div class="alert alert--with-eyebrow">
  
    <span class="alert__eyebrow">Post requisites</span>
  
  <p>Firstly, the SVG needs to be pre-rounded for this to work. You also need to decide your <code>stroke-width</code> beforehand to get this to work consisently:</p>
<div class="pointy-demo" style="display: flex; justify-content: center;">
  <svg width="80" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" fill="none" viewBox="0 0 127 478"><path stroke="var(--gradient-green)" stroke-width="1" d="M0 477h11.33a15.5 15.5 0 0 0 14.17-9.22l98.67-222.51a15.5 15.5 0 0 0 0-12.56L25.51 10.22A15.5 15.5 0 0 0 11.34 1H0" vector-effect="non-scaling-stroke"/></svg>
</div>
<p>Next, it&rsquo;s essential that you use <code>preserveAspectRatio=&quot;none&quot;</code> on the SVG. This allows you to squash and squeeze the SVG into whatever shape you require.</p>
<p>Finally, you need to set <code>vector-effect=&quot;non-scaling-stroke&quot;</code> on the <code>&lt;path /&gt;</code> element. This ensures that the stroke width remains the consistent with the box border, regardless of the size &amp; shape of the SVG. If you don&rsquo;t do this, you&rsquo;ll end up with a very thin line when the SVG is scaled down.</p>

</div>

<script>
  const demos = document.querySelectorAll('.pointy-demo');
  const deviation = document.querySelector('#rounding-filter feGaussianBlur');
  (() => {
    demos.forEach(demo => {
      const controls = demo.querySelectorAll('.pointy-controls');
      controls.forEach((controls, index) => {
        const slider = controls.querySelector('[type="range"]');
        const outputs = controls.querySelectorAll('output');
        if (!slider) return;
        slider.addEventListener('input', (e) => {
          demo.style.setProperty(`--value${index}`, e.target.value);
          outputs.forEach(output => output.innerText = e.target.value);

          if (slider.dataset.deviation && deviation) {
            deviation.setAttribute('stdDeviation', e.target.value);
          }
        });
      });
    });
  })();
</script>
<h2 id="closing-note">Closing note</h2>
<p>You may be asking yourself, why not just set an SVG background image and have done with it? Well, if you want control over the corner rounding, triangle size, and most importantly, stay responsive to differing viewports and quantities of content, then this is probably the way to go. If the requirement is for a simple empty box, then an SVG directly in the DOM is a simpler bet.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Lessons from a Brand Refresh</title>
      <link>https://www.trysmudford.com/blog/lessons-from-a-brand-refresh/</link>
      <pubDate>Wed, 09 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/lessons-from-a-brand-refresh/</guid>
      <description><![CDATA[
<p>I&rsquo;ve had the pleasure of leading the engineering rollout of a pretty exciting brand refresh for <a href="https://motorway.co.uk/">Motorway</a>. Called onto the project in early February, the task was to manage and implement the rollout across our entire digital platform. Motorway is a complex car marketplace, with multiple consumer-, dealer-, driver-, and internal-facing products.</p>
<p><img src="/images/blog/motorway-refresh.jpg" alt="A banner graphic with the updated Motorway logo, and a blue hatchback car on top of some triangular shapes"></p>
<p>On March 17th, we successfully and smoothly launched the refresh to 16 web and native applications. I wanted to share some lessons learned along the way.</p>
<h2 id="the-importance-of-a-design-system">The importance of a design system</h2>
<p>Unsurprisingly, the biggest asset in this project was our solid design system: The Highway Code. Since 2022, a small team of dedicated designers &amp; engineers have cultivated this system, ensuring it does <em>just enough, but not too much</em>. This atomic and foundational system allowed us to fundamentally refresh our wider visual language and update our components.</p>
<p>I cannot stress how impossible and painful a task this would have been without this design system. Speaking of&hellip;</p>
<h2 id="the-pain-of-frontend-tech-debt">The pain of frontend tech debt</h2>
<p>Design systems can only be the panacean &ldquo;single source of truth&rdquo; <em>if</em> they are the only source of truth. Our reality is three design systems: The Highway Code, and two legacy component libraries that have never properly been deprecated. Totally removing these libraries wasn&rsquo;t feasible in the time we had, so compromises had to be made.</p>
<p>Firstly, all legacy colours &amp; typographic references within consuming applications were removed and replaced with references from The Highway Code. We could release this change long before go-live day safe in the knowledge that when the design system was updated, all colours &amp; typography would also be updated. We also updated as many legacy components to use The Highway Code as time allowed, again, reducing the risk of functional changes on go-live day.</p>
<p>In a bid to reduce the number of application launches needing to be synced up, I opted to use CSS custom properties &amp; fallbacks to control the legacy library components from The Highway Code. For example, a legacy multi-select component needed a <code>border-radius</code> update, so I wrapped the existing value in a custom property:</p>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">multiselect</span> <span class="p">{</span>
   <span class="k">border-radius</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">thc</span><span class="o">-</span><span class="n">input</span><span class="o">-</span><span class="n">border</span><span class="o">-</span><span class="n">radius</span><span class="p">,</span> <span class="mi">4</span><span class="kt">px</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div><p>This meant I could safely pre-release the (admittedly quite flaky) legacy systems, safe in the knowledge that no visual changes would occur until The Highway Code updated that custom property. It&rsquo;s not perfect to form this tie, but as a stopgap until the legacy systems are deprecated, it worked pretty well in reducing launch risk. Along the way, we also removed 20% of the legacy components, which was a nice bonus.</p>
<h2 id="the-challenge-of-ambition">The challenge of ambition</h2>
<p>The &ldquo;easy&rdquo; option in a brand refresh is to just update colours &amp; typography, slap on a new logo, and call it a day. But that didn&rsquo;t sit right with us. Brand refreshes are pretty unique opportunities to focus on UI over user flows &amp; product features. Although we didn&rsquo;t want to touch fundamental UX patterns, we did want to use this opportunity to improve accessibility, performance and usability through good UI practices.</p>
<p>We improved colour contrast, typographical hierarchy and legibility, reduced page weights, and simplified a good amount of visual clutter. We sweated the details on animations, iconography and micro-interactions, and even made some fundamental and scary changes to our space &amp; typographical design token naming conventions, knowing that the opportunity to touch every CSS file doesn&rsquo;t come around often.</p>
<p>Naturally, I also used it as a trojan horse for some <a href="https://utopia.fyi">fluid CSS</a>.</p>
<p><img src="/images/blog/motorway-cvt.jpg" alt="A screenshot from the Motorway product, showing a hero image & MOT checker form"></p>
<h2 id="the-benefits-of-prioritisation">The benefits of prioritisation</h2>
<p>The design team was given a divergent brief to apply this brand refresh, which led to a huge quantity of design ideas that gradually converged to a consistent but ambitious design language update. In a product as large as Motorway, we had to be ruthless in the rollout prioritisation.</p>
<p>Estimating on an evolving design across 16 applications is a tricky task. Understandably, executives like to know <em>what</em> would be shipping on the 17th March. We knew we couldn&rsquo;t build <em>everything</em>, but, as mentioned before, also had the ambition to go well beyond just colours and typography.</p>
<p>Using a <a href="/blog/prioritising-requirements/#exercise-moscow">&lsquo;MoSCoW&rsquo;-esque</a> exercise (Must do, Should do, Could do, Won&rsquo;t do), we set about categorising the biggest design changes into P1&rsquo;s, P2&rsquo;s &amp; P3&rsquo;s. We committed to completing all P1&rsquo;s by the 17th, building from there if time allowed.</p>
<p>Foundational design system updates were all P1&rsquo;s, as was the application of the updated system to all consuming services. It was important to me that no system got left behind, including internal ones. For consumer-facing services, we committed to updating home, account and vehicle profiling flows, and for dealer-facing services we updated all logged out pages, and the key bidding screen. I&rsquo;ve seen enough brand refreshes in my time to know that patchy or half-baked design rollouts are glaring and abrasive.</p>
<p>We felt quietly confident that we could complete the P1&rsquo;s and make good headway through the P2&rsquo;s before go-live. Under-promising and over-delivering always pays off in these scenarios.</p>
<p>However, the challenge with a list of categorised line-items, is its still pretty hard for executives to <em>see</em> what will actually be going live&hellip;</p>
<p><img src="/images/blog/motorway-outline.jpg" alt="A screenshot from the Motorway product, showing a banner advertising Motorway Pay"></p>
<h2 id="the-power-of-a-good-demo--release-candidates">The power of a good demo &amp; release candidates</h2>
<p>With the design team creating the &ldquo;visual north star&rdquo;, we didn&rsquo;t want to spend precious design hours mocking up interim views, especially if we felt we could get through the P1&rsquo;s and onto P2&rsquo;s &amp; P3&rsquo;s. Thanks to the design system &amp; our CI setup, we were able to produce release candidate versions of the system on every commit, and apply that to our key consuming applications. So rather than Figma-ise a make-believe middle step, we could render exactly what the product would look like if we did nothing more to the product, other than apply the design system update.</p>
<p>We could then side-by-side the &ldquo;north star&rdquo; and the &ldquo;worst case scenario&rdquo; and present it to the executive team, explaining that all P1&rsquo;s would be at the north star level, and P2/3&rsquo;s would look <em>at least</em> as good as the release candidate application, and almost certainly better. This calmed all nerves and got us the green light to proceed without wasting any design effort.</p>
<p>These release candidates then served as consistent staging applications that could be shared widely with the team so there were no surprises come go-live day.</p>
<h2 id="the-guard-against-conversion">The guard against conversion</h2>
<p>We were naturally very cautious about the potential for conversion impact in this project. With the best design intent, user research and data in the world, there&rsquo;s still no guarantee that a design refresh won&rsquo;t have a negative impact on conversion. To mitigate this, we ran a number of A/B tests ahead of the launch, testing any functional changes before the &lsquo;big day&rsquo; so we could be sure the new designs were at least at parity with the old ones.</p>
<h2 id="the-importance-of-a-solid-release-process">The importance of a solid release process</h2>
<p>As we approached go-live, we&rsquo;d completed all P1&rsquo;s, all P2&rsquo;s and a good number of P3&rsquo;s. Our release candidates were in a great place, and we&rsquo;d reduced risk as much as possible. The final step was to create a solid release plan.</p>
<p>No-one likes big-bang releases, but for a project like this (one that used so much SCSS &amp; CSS), it was impossible to use feature flags. We spent time with product managers to understand what features were being rolled out as we were building to ensure they were all visually compatible, and we merged <code>main</code> into the 16 release branches daily to reduce conflicts. Through All Hands, and regular Slack messages, we communicated the go-live plan with the whole company, informing when code freezes would come into place, when the release would happen, where they should report bugs/incidents, and when code thaws would take place.</p>
<p>Each application had a bespoke go-live checklist, with links to the pull request, release candidate and relevant product dashboards, and a visual ticklist of each step that would need to take place on the day.</p>
<p>On the big day, we methodically worked through each checklist, and got through all 16 applications in a matter of hours. There were no hiccups, challenges or downtime—it was all incredibly calm and smooth. We watched the numbers like hawks over the coming days and saw no negative conversion impact whatsoever. A week on from the launch and we all breathed a sigh of relief and celebrated a job well done 🍾</p>
<h2 id="the-ideal-job-for-a-design-engineer">The ideal job for a design engineer</h2>
<p>This project was <strong>perfect</strong> for me (and would be for any other design engineer). There was a huge degree of design ambiguity, as well as a lot of plates to spin. Touching every CSS file and changing every page is no small beast, and potentially one that a &ldquo;software engineer&rdquo; would shy away from, or wish to break into smaller chunks.</p>
<p>But for a design engineer, it was an absolute dream. Designers and I were able to rapidly iterate in the browser, using design &amp; engineering sensibilities to quickly make decisions and avoid wasted Figma time. I absolutely love being the glue between design &amp; engineering, and this project was a perfect example of how that can work in practice.</p>
<p>This post is getting long so I might follow up with another going into some of the design details and fun engineering challenges we faced along the way.</p>
]]>
      </description>
    </item>
    
  </channel>
</rss>