<?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/2022/</link>
    <description>Posts, thoughts, links and photos from Trys</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 23 Nov 2022 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.trysmudford.com/blog/index.xml" rel="self" type="application/rss+xml"/>
    
    <item>
      <title>It&#39;s all about that space</title>
      <link>https://www.trysmudford.com/blog/its-all-about-that-space/</link>
      <pubDate>Wed, 23 Nov 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/its-all-about-that-space/</guid>
      <description><![CDATA[
<p>I&rsquo;ve been working with <a href="https://www.hustlersquad.net/">James</a> on the new <a href="https://utopia.fyi/grid/calculator/">Utopia Grid Calculator</a>, the latest tool in the Utopia arsenal. It asks for four pieces of information:</p>
<ul>
<li>Gutter width @min viewport</li>
<li>Gutter width @max viewport</li>
<li>Column width @max viewport</li>
<li>Number of columns</li>
</ul>
<p>Combining these four values creates a fluid grid:</p>
<p><img src="/images/blog/utopia-grid-calculator.jpg" alt="A screenshot of the Utopia Grid Calculator, with a table of results, and a live visualisation of the grid."></p>
<p>Under the visualisation, there&rsquo;s a CSS generator. On the first iteration, it included the line:</p>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">u-grid</span> <span class="p">{</span>
  <span class="k">grid-template-columns</span><span class="p">:</span> <span class="nf">repeat</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">grid</span><span class="o">-</span><span class="n">columns</span><span class="p">),</span> <span class="mi">1</span><span class="n">fr</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div><p>That might seem reasonable given the user has just asked for X columns. But something didn&rsquo;t feel at all right about that line.</p>
<p>When I said earlier that those four values created a fluid grid, that wasn&rsquo;t entirely true. What we&rsquo;ve created is a fluid container that can be used as a grid with fluid gutters. The columns inform the size of the container. But what you choose to do with that container is entirely up to you. A grid is a complex and nuanced beast that is so context &amp; content dependent, that making assumptions within a tool like this is fruitless.</p>
<p>I&rsquo;ve always avoided boot-windy grid systems. They&rsquo;re heavy and designed for the &lsquo;average&rsquo; website, never truly catering for the intricacies of your design. They provide all the classes in the world to push/pull columns at whatever breakpoints they deem &lsquo;correct&rsquo;, but without knowing what lives inside your rectangles, they are ultimately blunt tools.</p>
<h2 id="the-12-column-illusion">The 12 column illusion</h2>
<p>The grid is a designer&rsquo;s tool. It assists in getting things to be the right size, and as Lex reminded those at FFConf, <a href="https://noti.st/loftio/wDxO6z/design-for-developers#sagiuf4">in balance</a>. But the 12 column grid itself is an illusion, or at best a fading artefact of the emergent design.</p>
<p>We&rsquo;ve settled on 12 columns, not because we necessarily need <span style="white-space: nowrap;">1-3-7-1</span> layouts, but because it divides neatly and in multiple ways. It&rsquo;s flexible. What we&rsquo;re <em>really</em> looking for is halves, thirds and quarters to be rendered in context of the available space.</p>
<p>It all comes down to: <strong>fractions and the spaces between</strong>.</p>
<p>Utopia is concerned about the latter, about how your fractions slot together, and whether they feel in harmony with the overarching design. It&rsquo;s why, by default, our <a href="https://utopia.fyi/grid/calculator/">grid calculator</a> visualises gutters, not columns. Get the gutters right, and the rest will follow. Or as <a href="https://medium.com/@alansmlxl/the-space-between-the-lines-6ed1a78059a8">Alan Moore quotes</a>:</p>
<blockquote>
<p>‘I start with the space between the lines.’</p>
</blockquote>
<h2 id="required-further-reading">Required further reading</h2>
<p>If you&rsquo;ve got this far, do read the entirety of <a href="https://gridless.design/">gridless.design</a>. <a href="https://twitter.com/donniedamato">Donnie D&rsquo;Amato</a> has done a remarkable job in articulating the flaws in the grid systems we&rsquo;ve come to accept. <a href="https://bradfrost.com/blog/post/layout-grid-in-design-systems/">Brad Frost has also recently written on the subject</a>, specifically from a design systems' perspective. It&rsquo;s well worth your time.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Getting started with Utopia Figma plugins</title>
      <link>https://www.trysmudford.com/blog/getting-started-with-utopia-figma-plugins/</link>
      <pubDate>Wed, 16 Nov 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/getting-started-with-utopia-figma-plugins/</guid>
      <description><![CDATA[
<a href="https://utopia.fyi/blog/get-started-with-utopia-figma-plugins/" class="button">Read the article</a>

]]>
      </description>
    </item>
    
    <item>
      <title>Building the new Utopia homepage</title>
      <link>https://www.trysmudford.com/blog/utopia-home/</link>
      <pubDate>Wed, 19 Oct 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/utopia-home/</guid>
      <description><![CDATA[
<p><a href="https://utopia.fyi">Utopia</a> has a new home page with a nifty demo to (hopefully) show what the fuss is about. This is a quick write up on how it works under the hood&hellip;</p>
<div style="position: relative; padding-bottom: 73.77%; padding-top: 30px; margin-bottom: 30px; height: 0; overflow: hidden;">
  <video src="https://trysmudford.com/images/blog/utopia-home.mp4" poster="https://trysmudford.com/images/blog/utopia-home.jpg" loop style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" preload="none" controls></video>
</div>
<p><a href="https://twitter.com/j98">James</a> designed the beautiful new component, which acts as a &lsquo;website within a website&rsquo;. It could&rsquo;ve been a <code>&lt;video&gt;</code> file. This would&rsquo;ve been easy to add to the site, but not at all interactive. Alternatively, an <code>&lt;iframe&gt;</code> pointing at a page with the demo, with controls to resize the iframe itself would&rsquo;ve worked, but with one distinct disadvantage specific to this implementation. This component also needed to provide the homepage <code>&lt;h1&gt;</code>, and putting it in an iframe would&rsquo;ve removed the content from the page.</p>
<p>So I attacked this as a standalone component on the page. Markup-wise, it was all pretty standard stuff. The interesting bit is in how the type/space scales interact with the form controls and pretend to be rendered on small/large screens.</p>
<p>Most sites are designed at a minimum of <code>~320px</code>, but this needed to be smaller. So I created a <a href="https://utopia.fyi/type/calculator/?c=210,12.5,1.2,686,16,1.25,5,0,&amp;s=0.75%7C0.5%7C0.25,1.5%7C2%7C3%7C4%7C6,s-l">scale on Utopia</a> running from <code>210px</code> to <code>686px</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
  <span class="nv">--fluid-min-width</span><span class="p">:</span> <span class="mi">210</span><span class="p">;</span>
  <span class="nv">--fluid-max-width</span><span class="p">:</span> <span class="mi">686</span><span class="p">;</span>

  <span class="nv">--fluid-screen</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vw</span><span class="p">;</span>
  <span class="nv">--fluid-bp</span><span class="p">:</span> <span class="nb">calc</span><span class="p">(</span>
    <span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="kc">screen</span><span class="p">)</span> <span class="o">-</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="n">min</span><span class="o">-</span><span class="n">width</span><span class="p">)</span> <span class="o">/</span> <span class="mi">16</span> <span class="o">*</span> <span class="mi">1</span><span class="kt">rem</span><span class="p">)</span> <span class="o">/</span>
      <span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="n">max</span><span class="o">-</span><span class="n">width</span><span class="p">)</span> <span class="o">-</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="n">min</span><span class="o">-</span><span class="n">width</span><span class="p">))</span>
  <span class="p">);</span>
<span class="p">}</span>

<span class="p">@</span><span class="k">media</span> <span class="nt">screen</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">min-width</span><span class="o">:</span> <span class="nt">686px</span><span class="o">)</span> <span class="p">{</span>
  <span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
    <span class="nv">--fluid-screen</span><span class="p">:</span> <span class="nb">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="n">max</span><span class="o">-</span><span class="n">width</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1</span><span class="kt">px</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div><p>Instead of applying those values to the <code>:root</code> element, where the whole website would be affected, I changed the selector to <code>.demo</code>, so it cascades inside this component. Thanks to the <a href="https://www.trysmudford.com/blog/refactoring-css-locks/">CSS Lock Refactor</a>, <code>--fluid-screen</code> is a variable we can control; usually set to <code>100vw</code> before capping all the type/space units with a single media query.</p>
<p>But for this component, we remove the <code>--fluid-screen: 100vw</code> line, and the whole media query, so we can control it via an <code>&lt;input type=&quot;range&quot; /&gt;</code>. In Vue, it looks a bit like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="p">&lt;</span><span class="nt">template</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;demo&#34;</span> <span class="na">:style</span><span class="o">=</span><span class="s">&#34;{
</span><span class="s">    &#39;--fluid-screen&#39;: `${screen}px`
</span><span class="s">  }&#34;</span><span class="p">&gt;</span>
    ...
    <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;range&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;screen&#34;</span> <span class="na">min</span><span class="o">=</span><span class="s">&#34;210&#34;</span> <span class="na">max</span><span class="o">=</span><span class="s">&#34;686&#34;</span> <span class="p">/&gt;</span>
  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="nx">screen</span><span class="o">:</span> <span class="mi">686</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>

<span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
  <span class="p">.</span><span class="nc">demo</span> <span class="p">{</span>
    <span class="nv">--fluid-min-width</span><span class="p">:</span> <span class="mi">210</span><span class="p">;</span>
    <span class="nv">--fluid-max-width</span><span class="p">:</span> <span class="mi">686</span><span class="p">;</span>
    <span class="nv">--fluid-bp</span><span class="p">:</span> <span class="nb">calc</span><span class="p">(</span>
      <span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="kc">screen</span><span class="p">)</span> <span class="o">-</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="n">min</span><span class="o">-</span><span class="n">width</span><span class="p">)</span> <span class="o">/</span> <span class="mi">16</span> <span class="o">*</span> <span class="mi">1</span><span class="kt">rem</span><span class="p">)</span> <span class="o">/</span>
        <span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="n">max</span><span class="o">-</span><span class="n">width</span><span class="p">)</span> <span class="o">-</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="n">min</span><span class="o">-</span><span class="n">width</span><span class="p">))</span>
    <span class="p">);</span>

    <span class="nv">--step-0</span><span class="p">:</span> <span class="o">...</span>
    <span class="c">/* ... all the type &amp; space units */</span>

    <span class="k">width</span><span class="o">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">fluid</span><span class="o">-</span><span class="kc">screen</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</code></pre></div><p>Now we can paint on the type/space units onto the markup as we would on any other website. When the user interacts with the input and sets it to somewhere in the middle, say <code>448</code>, the browser renders the demo as if it was <code>448px</code>, even if your viewport is <code>1440px</code> or <code>320px</code>, and all the space/type reacts accordingly! I love it so much ❤️</p>
<p>If you&rsquo;d like to read more about this approach, check out my <a href="https://www.trysmudford.com/blog/space-aware-components/">previous blog post on space aware components</a>. This is all stuff that will become trivial when container queries arrive, but for now this works pretty well.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>A good reset</title>
      <link>https://www.trysmudford.com/blog/a-good-reset/</link>
      <pubDate>Tue, 26 Jul 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/a-good-reset/</guid>
      <description><![CDATA[
<p>In a <a href="https://adactio.com/journal/19315">recent post</a>, Jeremy discussed a possible reason for developers not reaching for native HTML elements like <code>button</code> or <code>select</code>, and instead recreating them with <code>div</code>s, JavaScript and ARIA:</p>
<blockquote>
<p>But I now understand that someone else might hear that there’s a native HTML element—like button or select—that comes with built-in behaviours around interaction and accessibility, and think “Uh-oh! What if there unexpected side-effects of these built-in behaviours that might bite me on the ass?</p>
</blockquote>
<p>But I wonder if there&rsquo;s a simpler explanation.</p>
<p>The one common feature between every codebase I&rsquo;ve encountered on that doesn&rsquo;t use <code>button</code>s well, is a <strong>bad CSS reset</strong>. Developers try to use a <code>button</code>, and find that it still looks like a native browser button, so they grab a plain old, blank canvas <code>div</code>, and build from there.</p>
<p>Adding a good CSS reset to an established codebase <em>is</em> tricky; the side effects of changing very global element styles can be unknown and far-reaching. But if you can get your <code>button</code>s looking as plain as a <code>div</code> from the start of a project, there&rsquo;s very little reason not to use the proper element.</p>
<p>I tend to reset my <code>button</code>s to look like any other text element, then layer on styling with classes. But by having the blank canvas to begin with, you won&rsquo;t be fighting against yourself when additional <code>button</code> variants come along.</p>
<div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="nt">button</span> <span class="p">{</span>
  <span class="kp">-webkit-</span><span class="k">appearance</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="mi">0</span><span class="p">;</span>
  <span class="k">text-align</span><span class="p">:</span> <span class="kc">inherit</span><span class="p">;</span>
  <span class="k">background</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
  <span class="k">box-shadow</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
  <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">cursor</span><span class="p">:</span> <span class="kc">pointer</span><span class="p">;</span>
  <span class="k">border</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
  <span class="k">color</span><span class="p">:</span> <span class="kc">inherit</span><span class="p">;</span>
  <span class="k">font</span><span class="p">:</span> <span class="kc">inherit</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>]]>
      </description>
    </item>
    
    <item>
      <title>Building a design system with Eleventy</title>
      <link>https://www.trysmudford.com/blog/eleventy-design-system/</link>
      <pubDate>Mon, 16 May 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/eleventy-design-system/</guid>
      <description><![CDATA[
<p>One of the standout projects while working at <a href="https://www.clearleft.com">Clearleft</a> was collaborating with Malvern Panalytical to build the foundations of <a href="https://brand.malvernpanalytical.com/">their design system</a>. Four tech stacks within the business dictated that this system would centre around CSS, rather than language-driven component markup.</p>
<p>This decision gave us freedom to pick the most flexible stack for the design system, not forcing our hand to choose an off-the-shelf platform with specific language/framework support. Furthermore, we wanted a system that was beyond a component library; a true design system that could be used to articulate and accommodate brand, content and wider design standards.</p>
<p>Eleventy was the obvious candidate. Generating &ldquo;pages from data&rdquo; was the most compelling feature, and add to that the flexibility in templating and the rapid build times, there really was no other stack that came close.</p>
<p>Taking inspiration from <a href="https://fractal.build/">Fractal</a>; a project born from design systems projects at Clearleft, I wanted to build this system around component &ldquo;context&rdquo;. Context is a fancy word for the data that gets passed to a component to render it, and it allows you to render a component, and any number of variants by providing different data.</p>
<p>Mixed with the <a href="/blog/encapsulated-11ty-components/">encapsulated macro pattern</a>, we can demonstrate these  components with ease, and with a consistent interface.</p>
<h2 id="demo-design-system">Demo design system</h2>
<p>I&rsquo;ve created a <a href="https://eleventy-design-system.netlify.app/">demonstration design system</a> to articulate how this can work in practice. Feel free to fork <a href="https://github.com/trys/eleventy-design-system/">the repository</a> and tweak to your heart&rsquo;s content.</p>
<a href="https://eleventy-design-system.netlify.app/" class="button">Demo Design System</a>

<h3 id="screenshots-from-the-system">Screenshots from the system</h3>
<figure>
  <img src="/images/blog/e-ds-1.jpg">
  <figcaption>A button component within the system</figcaption>
</figure>
<figure>
  <img src="/images/blog/e-ds-5.jpg">
  <figcaption>A simple example of a variant: a button with a different background</figcaption>
</figure>
<figure>
  <img src="/images/blog/e-ds-2.jpg">
  <figcaption>The generated `.html` code for the button</figcaption>
</figure>
<figure>
  <img src="/images/blog/e-ds-3.jpg">
  <figcaption>The component macro and the context data passed to it</figcaption>
</figure>
<figure>
  <img src="/images/blog/e-ds-6.jpg">
  <figcaption>A quotation component rendered in a centred, limited-width preview</figcaption>
</figure>
<h2 id="how-it-works">How it works</h2>
<p>There are three key files in this design system (the links below go to the files in the repository on GitHub):</p>
<ol>
<li><a href="https://github.com/trys/eleventy-design-system/blob/main/src/_includes/components/button/button.config.js"><code>_includes/components/**/*.config.js</code></a></li>
<li><a href="https://github.com/trys/eleventy-design-system/blob/main/src/_data/components.js"><code>_data/components.js</code></a></li>
<li><a href="https://github.com/trys/eleventy-design-system/blob/main/src/components-full-pages.njk"><code>components-full-pages.njk</code></a></li>
</ol>
<h3 id="key-file-1-_includescomponentsconfigjshttpsgithubcomtryseleventy-design-systemblobmainsrc_includescomponentsbuttonbuttonconfigjs">Key file 1: <a href="https://github.com/trys/eleventy-design-system/blob/main/src/_includes/components/button/button.config.js">_includes/components/**/*.config.js</a></h3>
<p>A config file is what flags each component to the design system. Here&rsquo;s an example configuration for the quote component rendered in the screenshots above:</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
  <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;Quote&#39;</span><span class="p">,</span>
  <span class="nx">context</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">body</span><span class="o">:</span> <span class="s1">&#39;&lt;p&gt;Maecenas sed diam eget risus varius blandit...&lt;/p&gt;&#39;</span><span class="p">,</span>
    <span class="nx">citation</span><span class="o">:</span> <span class="s1">&#39;Trys Mudford, Design Engineer&#39;</span>
  <span class="p">},</span>
  <span class="nx">preview</span><span class="o">:</span> <span class="s1">&#39;wrapper&#39;</span>
<span class="p">}</span>
</code></pre></div><p>The <code>title</code> field is used in the sidebar menu. The <code>context</code> object is the data that&rsquo;s passed into the component at the time of rendering. The <code>preview</code> field references the file that should be used to render the component. Most components are rendered in a full-width wrapper, but it can be useful to pop the component into a more realistically limited-width, or multi-column wrapper.</p>
<p>Now we&rsquo;ve got this data format, it&rsquo;s trivial to create variants of it. Here&rsquo;s the button configuration:</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
  <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;Button&#39;</span><span class="p">,</span>
  <span class="nx">context</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">label</span><span class="o">:</span> <span class="s1">&#39;Hello&#39;</span><span class="p">,</span>
  <span class="p">},</span>
  <span class="nx">variants</span><span class="o">:</span> <span class="p">[</span>
    <span class="p">{</span>
      <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;Different label&#39;</span><span class="p">,</span>
      <span class="nx">context</span><span class="o">:</span> <span class="p">{</span>
        <span class="nx">label</span><span class="o">:</span> <span class="s1">&#39;Hello, world&#39;</span>
      <span class="p">}</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;Blue background&#39;</span><span class="p">,</span>
      <span class="nx">context</span><span class="o">:</span> <span class="p">{</span>
        <span class="nx">colour</span><span class="o">:</span> <span class="s1">&#39;blue&#39;</span>
      <span class="p">}</span>
    <span class="p">},</span>
  <span class="p">]</span>
<span class="p">}</span>
</code></pre></div><p>You&rsquo;ll see the <code>variants</code> field format is a repeat of the parent component, but in an array, with only the differences in data from the parent passed in. In this case, either a different <code>label</code>, or a <code>colour</code> parameter. These pieces of data are then available within the component <code>params</code> variable to adapt to accordingly.</p>
<div class="highlight"><pre class="chroma"><code class="language-twig" data-lang="twig"><span class="c">{# button.njk #}</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span>- <span class="k">set</span> <span class="nv">classNames</span> <span class="o">=</span> <span class="s2">&#34;c-button&#34;</span> -<span class="cp">%}</span><span class="x">
</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span>- <span class="k">if</span> <span class="nv">params.colour</span> -<span class="cp">%}</span><span class="x">
</span><span class="x">  </span><span class="cp">{%</span> <span class="k">set</span> <span class="nv">classNames</span> <span class="o">=</span> <span class="nv">classNames</span> <span class="o">+</span> <span class="s2">&#34; c-button--&#34;</span> <span class="o">+</span> <span class="nv">params.colour</span> <span class="cp">%}</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span> <span class="k">endif</span> -<span class="cp">%}</span><span class="x">
</span><span class="x">
</span><span class="x">&lt;button type=&#34;</span><span class="cp">{{</span> <span class="nv">params.type</span> <span class="k">or</span> <span class="s1">&#39;button&#39;</span> <span class="cp">}}</span><span class="x">&#34; class=&#34;</span><span class="cp">{{</span> <span class="nv">classNames</span> <span class="cp">}}</span><span class="x">&#34;&gt;
</span><span class="x">  </span><span class="cp">{{</span> <span class="nv">params.label</span> <span class="cp">}}</span><span class="x">
</span><span class="x">&lt;/button&gt;
</span></code></pre></div><h3 id="key-file-2-_datacomponentsjshttpsgithubcomtryseleventy-design-systemblobmainsrc_datacomponentsjs">Key file 2: <a href="https://github.com/trys/eleventy-design-system/blob/main/src/_data/components.js">_data/components.js</a></h3>
<p>JavaScript files in the <code>_data</code> folder are called at build time by Eleventy, to provide global data to templates. Usually, this would be used for API calls, gathering content before generating the website. But we can perform other asynchronous actions in this space, namely, reading from the file system:</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kr">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// Pull in all the config files
</span><span class="c1"></span>  <span class="kr">const</span> <span class="nx">modules</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">requireGlob</span><span class="p">(</span><span class="s1">&#39;../_includes/**/*.config.js&#39;</span><span class="p">,</span> <span class="p">{</span>
    <span class="nx">reducer</span><span class="p">,</span>
    <span class="nx">bustCache</span><span class="o">:</span> <span class="kc">true</span>
  <span class="p">});</span>

  <span class="c1">// Convert the components into our required format
</span><span class="c1"></span>  <span class="kr">const</span> <span class="nx">componentGroups</span> <span class="o">=</span> <span class="nx">modules</span><span class="p">.</span><span class="nx">components</span>
    <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">convertComponent</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nb">Boolean</span><span class="p">);</span>
  
  <span class="c1">// Return the components and the menu
</span><span class="c1"></span>  <span class="k">return</span> <span class="p">{</span>
    <span class="nx">components</span><span class="o">:</span> <span class="nx">componentGroups</span><span class="p">.</span><span class="nx">flat</span><span class="p">(),</span>
    <span class="nx">menu</span><span class="o">:</span> <span class="nx">prepareMenu</span><span class="p">(</span><span class="nx">componentGroups</span><span class="p">)</span>
  <span class="p">};</span>
<span class="p">}</span>
</code></pre></div><p>The function <code>convertComponent</code> moulds the data from <code>*.config.js</code> into a usable format:</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">convertComponent</span><span class="p">(</span><span class="nx">component</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Extract variants from component and remove them
</span><span class="c1"></span>  <span class="kd">let</span> <span class="p">{</span> <span class="nx">variants</span> <span class="o">=</span> <span class="p">[]</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">component</span><span class="p">;</span>
  <span class="k">delete</span> <span class="nx">component</span><span class="p">.</span><span class="nx">variants</span><span class="p">;</span>

  <span class="c1">// Back out if the component isn&#39;t valid
</span><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">component</span> <span class="o">||</span> <span class="o">!</span><span class="nx">component</span><span class="p">.</span><span class="nx">title</span><span class="p">)</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>

  <span class="c1">// Set sensible defaults for previews &amp; slugs
</span><span class="c1"></span>  <span class="nx">component</span><span class="p">.</span><span class="nx">preview</span> <span class="o">=</span> <span class="nx">component</span><span class="p">.</span><span class="nx">preview</span> <span class="o">||</span> <span class="s1">&#39;default&#39;</span><span class="p">;</span>
  <span class="kr">const</span> <span class="nx">parentSlug</span> <span class="o">=</span> <span class="nx">component</span><span class="p">.</span><span class="nx">slug</span> <span class="o">||</span> <span class="nx">slugify</span><span class="p">(</span><span class="nx">component</span><span class="p">.</span><span class="nx">title</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">());</span>
  
  <span class="c1">// Loop the variants, returning a merged combo of component, then variant
</span><span class="c1"></span>  <span class="nx">variants</span> <span class="o">=</span> <span class="nx">variants</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">variant</span> <span class="p">=&gt;</span> <span class="p">{</span>
    <span class="kr">const</span> <span class="nx">variantSlug</span> <span class="o">=</span> <span class="nx">slugify</span><span class="p">(</span><span class="nx">variant</span><span class="p">.</span><span class="nx">title</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">());</span>
    <span class="kr">const</span> <span class="nx">preview</span> <span class="o">=</span> <span class="o">!!</span><span class="nx">variant</span><span class="p">.</span><span class="nx">preview</span> <span class="o">?</span> <span class="nx">variant</span><span class="p">.</span><span class="nx">preview</span> <span class="o">||</span> <span class="s1">&#39;default&#39;</span> <span class="o">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">preview</span> <span class="o">||</span> <span class="s1">&#39;default&#39;</span><span class="p">;</span>

    <span class="k">return</span> <span class="p">{</span>
      <span class="p">...</span><span class="nx">component</span><span class="p">,</span>
      <span class="p">...</span><span class="nx">variant</span><span class="p">,</span>
      <span class="nx">context</span><span class="o">:</span> <span class="p">{</span>
        <span class="p">...</span><span class="nx">component</span><span class="p">.</span><span class="nx">context</span><span class="p">,</span>
        <span class="p">...</span><span class="nx">variant</span><span class="p">.</span><span class="nx">context</span>
      <span class="p">},</span>
      <span class="nx">variant</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="nx">preview</span><span class="p">,</span>
      <span class="nx">originalTitle</span><span class="o">:</span> <span class="nx">variant</span><span class="p">.</span><span class="nx">title</span><span class="p">,</span>
      <span class="nx">title</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">component</span><span class="p">.</span><span class="nx">title</span><span class="si">}</span><span class="sb"> - </span><span class="si">${</span><span class="nx">variant</span><span class="p">.</span><span class="nx">title</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
      <span class="nx">slug</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">parentSlug</span><span class="si">}</span><span class="sb">-</span><span class="si">${</span><span class="nx">variantSlug</span><span class="si">}</span><span class="sb">`</span>
    <span class="p">}</span>
  <span class="p">});</span>
    
  <span class="c1">// Return the main component and any variants
</span><span class="c1"></span>  <span class="k">return</span> <span class="p">[</span>
    <span class="p">{</span>
      <span class="nx">slug</span><span class="o">:</span> <span class="nx">parentSlug</span><span class="p">,</span>
      <span class="p">...</span><span class="nx">component</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="p">...</span><span class="nx">variants</span>
  <span class="p">]</span>
<span class="p">}</span>
</code></pre></div><p>So what we&rsquo;ve done here is gather up all the config files, and serve them as one massive global array for Eleventy to work with.</p>
<h3 id="key-file-3-components-full-pagesnjkhttpsgithubcomtryseleventy-design-systemblobmainsrccomponents-full-pagesnjk">Key file 3: <a href="https://github.com/trys/eleventy-design-system/blob/main/src/components-full-pages.njk">components-full-pages.njk</a></h3>
<p>This ingenious little template from Eleventy loops around the data we&rsquo;ve exposed in <code>components.components</code> (sorry, poor choice of variable naming there), and creates a page for every component <em>and</em> variant! It renders the component using the <code>{{ c() }}</code> macro, passing in the context, and it pops it within a preview wrapper.</p>
<div class="highlight"><pre class="chroma"><code class="language-twig" data-lang="twig"><span class="x">---
</span><span class="x">pagination:
</span><span class="x">    data: components.components
</span><span class="x">    size: 1
</span><span class="x">    alias: component
</span><span class="x">permalink: &#34;components/full/</span><span class="cp">{{</span> <span class="nv">component.slug</span> <span class="cp">}}</span><span class="x">/&#34;
</span><span class="x">title: &#39;Components&#39;
</span><span class="x">renderData:
</span><span class="x">  title: &#34;</span><span class="cp">{{</span> <span class="nv">component.title</span> <span class="cp">}}</span><span class="x">&#34;
</span><span class="x">---
</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&#34;layout.njk&#34;</span> <span class="cp">%}</span><span class="x">
</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span> <span class="k">block</span> <span class="nv">inner</span> <span class="cp">%}</span><span class="x">
</span><span class="x">  </span><span class="cp">{%</span> <span class="k">set</span> <span class="nv">comp</span> <span class="cp">%}</span><span class="x">
</span><span class="x">    </span><span class="cp">{{</span> <span class="nv">c</span><span class="o">(</span><span class="nv">component.name</span><span class="o">,</span> <span class="nv">component.context</span><span class="o">)</span> <span class="cp">}}</span><span class="x">
</span><span class="x">  </span><span class="cp">{%</span> <span class="k">endset</span> <span class="cp">%}</span><span class="x">
</span><span class="x">  </span><span class="cp">{%</span> <span class="k">include</span> <span class="s2">&#34;design-system/previews/preview-&#34;</span> <span class="o">+</span> <span class="nv">component.preview</span> <span class="o">+</span> <span class="s2">&#34;.njk&#34;</span> <span class="cp">%}</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span><span class="x">
</span></code></pre></div><p>And that&rsquo;s it! Here&rsquo;s what it looks like:</p>
<p><img src="/images/blog/e-ds-4.jpg" alt=""></p>
<h2 id="designing-the-design-system">Designing the design system</h2>
<p>Hang on; you might be thinking, that looks pretty different to the earlier screenshots? Well yes, but that&rsquo;s for good reason. See, instead of rendering the component directly within a page in the design system, we render it on its own, and then include it via an <code>&lt;iframe&gt;</code>. This renders the component at the window size of the iframe, not at the size of the whole page. It&rsquo;s a subtle difference, but when you&rsquo;re working with viewport units or <a href="https://utopia.fyi">fluid type &amp; space</a>, it&rsquo;s an important distinction.</p>
<p>The solution is another <code>-pages.njk</code> <a href="https://github.com/trys/eleventy-design-system/blob/main/src/components-pages.njk">file</a>. It starts off in the same way as the previous template, reading in the <code>components.components</code> data and rendering a page for each component/variant. For each page, we then output some tabs for the various views we want for the component.</p>
<div class="highlight"><pre class="chroma"><code class="language-twig" data-lang="twig"><span class="c">{# components-pages.njk #}</span><span class="x">
</span><span class="x">---
</span><span class="x">pagination:
</span><span class="x">    data: components.components
</span><span class="x">    size: 1
</span><span class="x">    alias: component
</span><span class="x">permalink: &#34;components/</span><span class="cp">{{</span> <span class="nv">component.slug</span> <span class="cp">}}</span><span class="x">/&#34;
</span><span class="x">title: &#34;Components&#34;
</span><span class="x">sidebar: components
</span><span class="x">renderData:
</span><span class="x">  title: &#34;</span><span class="cp">{{</span> <span class="nv">component.title</span> <span class="cp">}}</span><span class="x">&#34;
</span><span class="x">---
</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&#34;./_includes/design-system/library.njk&#34;</span> <span class="cp">%}</span><span class="x">
</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span> <span class="k">from</span> <span class="s2">&#34;design-system/component.njk&#34;</span> <span class="nv">import</span> <span class="nv">code</span><span class="o">,</span> <span class="nv">context</span> <span class="cp">%}</span><span class="x">
</span><span class="x">
</span><span class="x"></span><span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}</span><span class="x">
</span><span class="x">  &lt;seven-minute-tabs class=&#34;ds-stretch&#34;&gt;
</span><span class="x">    &lt;ol role=&#34;tablist&#34; class=&#34;ds-tabs&#34; aria-label=&#34;What does this tab chooser do?&#34;&gt;
</span><span class="x">      &lt;li&gt;&lt;a class=&#34;ds-link&#34; href=&#34;#example-tab&#34; role=&#34;tab&#34;&gt;Demo&lt;/a&gt;&lt;/li&gt;
</span><span class="x">      &lt;li&gt;&lt;a class=&#34;ds-link&#34; href=&#34;#code-tab&#34; role=&#34;tab&#34;&gt;.html&lt;/a&gt;&lt;/li&gt;
</span><span class="x">      &lt;li&gt;&lt;a class=&#34;ds-link&#34; href=&#34;#context-tab&#34; role=&#34;tab&#34;&gt;.njk &amp; context&lt;/a&gt;&lt;/li&gt;
</span><span class="x">      &lt;li&gt;&lt;a class=&#34;ds-link&#34; href=&#34;/components/full/</span><span class="cp">{{</span> <span class="nv">component.slug</span> <span class="cp">}}</span><span class="x">&#34;&gt;Full Screen ↗&lt;/a&gt;&lt;/li&gt;
</span><span class="x">    &lt;/ol&gt;
</span><span class="x">
</span><span class="x">    &lt;div id=&#34;example-tab&#34; role=&#34;tabpanel&#34; class=&#34;ds-stretch&#34;&gt;
</span><span class="x">      &lt;div class=&#34;ds-frame ds-stretch&#34;&gt;
</span><span class="x">        &lt;iframe class=&#34;ds-stretch&#34; src=&#34;/components/full/</span><span class="cp">{{</span> <span class="nv">component.slug</span> <span class="cp">}}</span><span class="x">&#34; sandbox=&#34;allow-same-origin allow-scripts allow-forms allow-modals&#34; style=&#34;&#34; marginwidth=&#34;0&#34; marginheight=&#34;0&#34; frameborder=&#34;0&#34; vspace=&#34;0&#34; hspace=&#34;0&#34; scrolling=&#34;yes&#34;&gt;&lt;/iframe&gt;
</span><span class="x">      &lt;/div&gt;
</span><span class="x">    &lt;/div&gt;
</span><span class="x">
</span><span class="x">    &lt;div id=&#34;code-tab&#34; role=&#34;tabpanel&#34; class=&#34;ds-code ds-stretch&#34;&gt;
</span><span class="x">      </span><span class="cp">{{</span> <span class="nv">code</span><span class="o">(</span><span class="nv">component.name</span><span class="o">,</span> <span class="nv">component.context</span><span class="o">)</span> <span class="cp">}}</span><span class="x">
</span><span class="x">    &lt;/div&gt;
</span><span class="x">
</span><span class="x">    &lt;div id=&#34;context-tab&#34; role=&#34;tabpanel&#34; class=&#34;ds-code ds-stretch&#34;&gt;
</span><span class="x">      </span><span class="cp">{{</span> <span class="nv">context</span><span class="o">(</span><span class="nv">component.name</span><span class="o">,</span> <span class="nv">component.context</span><span class="o">)</span> <span class="cp">}}</span><span class="x">
</span><span class="x">    &lt;/div&gt;
</span><span class="x">  &lt;/seven-minute-tabs&gt;
</span><span class="x"></span><span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span><span class="x">
</span></code></pre></div><p>In this case, I&rsquo;ve got a tab for the visual demo of the component (housing the aforementioned <code>&lt;iframe&gt;</code>), a view of the generated HTML, a print-out of the context in use for the variant, and an option to see the component full screen. But this is only the start, we can render markdown documentation, prop tables, version numbers, stability labels, links to Figma, or whatever else takes your fancy.</p>
<p>The beauty of the <code>*.config.js</code> format, is you can pop in whatever data you fancy, and it&rsquo;ll be available in this template. If your team has other requirements, you can easily adapt!</p>
<h3 id="styling-the-system">Styling the system</h3>
<p>The final part of this example is a <a href="https://github.com/trys/eleventy-design-system/blob/main/src/static/design-system/css/design-system.css">sprinkling of CSS</a> to give the design system some form, and a sidebar to render the list of components, and any other associated pages of documentation. In the past, I&rsquo;ve written about the <a href="/blog/design-foundations/">systemised foundations</a>, how to release the design system on NPM, and the decisions behind the CSS methodology, along with brand guidelines, tone of voice guides and anything else required to make this the home of digital design within a company.</p>
<p>You can get <em>really</em> meta, and use the components <em>within</em> the design system, to design the design system! If you&rsquo;ve already designed buttons &amp; tabs for your website, why generate another load of them for the system, right?!</p>
<p>If you have any questions on how this all works, or if you&rsquo;d be interested in how I&rsquo;d extend the system further, please <a href="https://www.twitter.com/trysmudford">pop me a tweet</a>.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>A video introduction to Utopia</title>
      <link>https://www.trysmudford.com/blog/an-introduction-to-utopia/</link>
      <pubDate>Tue, 08 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/an-introduction-to-utopia/</guid>
      <description><![CDATA[
<div class="video-embed">
	
		<div style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;">
			<iframe src="//www.youtube-nocookie.com/embed/DDuGtN-GakA" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" allowfullscreen="1" frameborder="0" title="YouTube Video"></iframe>
		</div>
	
</div>

<p>James and I were invited to speak at <a href="https://twitter.com/uxghent">UX Ghent</a> for their frontend themed evening. We spoke about the origins of Utopia, and how you can begin to use it in your projects.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>A video introduction to Utopia</title>
      <link>https://www.trysmudford.com/blog/utopia-introduction-video/</link>
      <pubDate>Tue, 08 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/utopia-introduction-video/</guid>
      <description><![CDATA[
<a href="https://utopia.fyi/blog/an-introduction-video/" class="button">Read the article</a>

]]>
      </description>
    </item>
    
  </channel>
</rss>