<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://xiphe.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://xiphe.net/" rel="alternate" type="text/html" /><updated>2025-02-08T17:29:28+00:00</updated><id>https://xiphe.net/feed.xml</id><title type="html">Hannes Diercks</title><subtitle>Web developer and advisor, love tooling/automation, know how to agile, strive to improve work culture, servers are so 2010, ambassador of pattern-thinking, have a design background, pretty good musician
</subtitle><entry><title type="html">Building a company internal ui component library in 2022</title><link href="https://xiphe.net/blog/systems/component-library-in-2022.html" rel="alternate" type="text/html" title="Building a company internal ui component library in 2022" /><published>2022-07-20T00:00:00+00:00</published><updated>2022-07-20T00:00:00+00:00</updated><id>https://xiphe.net/blog/systems/component-library-in-2022</id><content type="html" xml:base="https://xiphe.net/blog/systems/component-library-in-2022.html"><![CDATA[<p>Hi I’m Hannes and a few days ago I found myself looking into a rabbit-whole
I’ve been in quite a few times.</p>

<div class="sidetrack">

<h4 id="sidetrack-why-im-writing-this">Sidetrack: Why I’m writing this</h4>

<p>About 7 years ago when component libraries were just about to become a thing
I was part of a small team that drastically increased development speed and unified
the UI/UX of <a href="https://www.jimdo.com/">Jimdo</a> by creating a component library along
with a strategy on how to get adoption and support from all product teams.<br />
After that I was able to take what we’ve learned at Jimdo and apply it to 
new and existing design systems a bunch of other companies.</p>

<p>In the past 2 Years I’ve been more on the product side of things but now
<a href="https://www.musicu.be/en/">my employer musicube</a> was acquired by <a href="https://www.songtradr.com/">songtradr</a>
and the design and development teams here are just on the verge of creating
a component library.</p>

<p>That’s the reason I thought it might be cool to take everything I’ve learned
about these systems, mix it with my current assessment of the web platform,
sprinkle a little flat-hierarchy ideology in there and write down how I’d create
a component library today, mid 2022.</p>

<p>Ready? <em>Ok let’s go!</em> 🚀</p>


</div>

<p> </p>

<h2 id="1-why">1. Why?</h2>

<blockquote>
  <p>Why should I build a component library in 2022?</p>
</blockquote>

<p>Communication between designers and developers is essential when building
software products in a bigger company. And a component library is on of the
most powerful tools they can utilize to make communication as easy as possible.</p>

<h3 id="-document-decisions">💡 Document Decisions</h3>

<p><strong>Each component documents all design and development decisions leading to it.</strong></p>

<p>The creation process may vary, but in the end there is one component
that has been approved by both development and design.
So users can confidently use it, knowing that they use the latest state and
when needed also can find out which decisions lead to it.</p>

<p>When someone is not entirely happy with a component they can use the current
state of the library as a reference point and propose improvements
based on that.</p>

<h3 id="-unify-uiux-of-your-product">🥳 Unify UI/UX of your product</h3>

<p><strong>Users of your product will subconsciously learn its UX patterns and apply it to other parts of the product. When these patterns match, the user is effective and happy.</strong></p>

<p>A widely adopted component library is the best way to get there.</p>

<p>If you fail to deliver a coherent experience, for example when the colors
of your destructive buttons vary a lot across the system, navigating requires
more cognitive affords from the user which can be exhausting and lead to deep sadness 😥.</p>

<h3 id="-speed-up-development">🏎 Speed up development</h3>

<p><strong>Users of the library should be faster when using it then they’d be without it.</strong></p>

<p><em>…“Fast” is a pretty diffuse term in product development… what I mean is</em>:</p>

<ol>
  <li>Getting a decent state of the companies UI/UX in any prototyping environment
should be a no-brainer.</li>
  <li>Round-Trips between design and development should be reduced since both
speak the same language and can refer to the library</li>
  <li>Applying new UI/UX developments/fixes to old code should be <strong>simple</strong>.</li>
</ol>

<p> </p>

<h2 id="how">How?</h2>

<blockquote>
  <p>How should I approach building a library to be effective?</p>
</blockquote>

<h3 id="-treat-it-as-if-it-were-open-source">🐙 Treat it as if it were open source</h3>

<p>I saw the greatest success when people maintain, contribute and use a company 
internal library as if were an open source project
<em>(only that this one is “just” open to the whole company instead of the whole world)</em>.</p>

<p>With that in mind it comes natural to document everything properly, to be
inclusive to everyone, to work with issues and pull requests, to be mindful with
API changes, etc…</p>

<div class="sidetrack">

<p>🧑‍🏫 Don’t worry in case you never maintained or contributed to an open source project, that’s fine.<br />
I cover the most important parts <a href="#-write-documentation-for-users">later in this article</a>. Just behave sensible,
remember <a href="#1-why">why the project exists</a> and be respectful to users and co-maintainers.</p>


</div>

<h3 id="-dont-get-too-fixated-with-nomenclature">🚓 Don’t get too fixated with nomenclature</h3>

<p>The most famous methodology for design systems is probably <a href="https://bradfrost.com/blog/post/atomic-web-design/">Atomic Design</a>. And though I think there is a great amount of guidance
you can get out of that remember that a component library is a living
system.</p>

<p>Everything can be changed, especially in the beginning.<br />
<small><em>Later on be careful when renaming API in order to keep the amount of breaking changes low<a href="-thoughtful-release-management"><sup>*</sup></a></em></small></p>

<p>Dare giving components a suboptimal name or put them in the <em>wrong</em> category.
The most important thing is that everybody (design and development) is on the
same page and uses a common language.</p>

<h3 id="️-dont-require-everything-to-be-in-the-library">❄️ Don’t require everything to be in the library</h3>

<p>You want to allow projects to have their own components and maybe even
variants of elements in the library. In a living system, evolution must also occur.
And a process that is too strict hinders product development instead of helping it.</p>

<blockquote>
  <p>I still like to call these one-of components “snowflakes”, a label coined by the team behind <a href="https://www.xing.com/en">XINGs</a> ui system.</p>
</blockquote>

<h3 id="-create-different-integration-levels">🔌 Create different integration levels</h3>

<p>In order to support rapid prototyping and to prevent locking your company in
a narrow tech choice I strongly recommend maintaining different integration
points with the library.</p>

<h4 id="level-1-css-preprocessor">Level 1: CSS Preprocessor</h4>

<p>When you have a common css preprocessor (<a href="https://sass-lang.com/">sass</a> for example)
that is widely adopted in your development this makes for a great low level
integration.</p>

<p>Provide Colors, Spacings, Typography settings and maybe also mixins on this level</p>

<h4 id="level-2-css--html">Level 2: CSS + HTML</h4>

<p><strong>🤔 Think plain <a href="https://getbootstrap.com/">Bootstrap</a>.</strong></p>

<p>Level 2 uses the values provided by the CSS Preprocessor and
provides static components that implement the <strong>look</strong> of your UI design.</p>

<p>The web platform uses CSS and HTML for UI, there’s no way around it and
therefore integrating here is tremendously valuable.</p>

<p>Create a stylesheet that unlocks your components in any page by simply adding a
<code class="language-plaintext highlighter-rouge">&lt;link /&gt;</code>.<br />
Even when your development writes no single line of plain/html and uses
ReactJS only, Marketing &amp; Support teams use low-/no-code solutions, Growth teams want to
try out some crazy new stuff, Companies get acquired that had build their
awesome product with Rails and now should adopt your look fast…</p>

<p>All of these use HTML/CSS in the end and most likely support serving a css file.</p>

<h4 id="level-3-framework">Level 3: Framework</h4>

<p>This level abstracts specific html structure and detailed class names and
should get really close to the language that is actually spoken between
design and development.<br />
When someone says “Can you please create a centered modal with a destructive button saying ‘Delete!’?”,
on the framework integration level it should boil down to something like</p>

<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">&lt;</span><span class="nc">Modal</span> <span class="na">align</span><span class="p">=</span><span class="s">"modal-center"</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nc">Button</span> <span class="na">variant</span><span class="p">=</span><span class="s">"button-destructive"</span><span class="p">&gt;</span>Delete!<span class="p">&lt;/</span><span class="nc">Button</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nc">Modal</span><span class="p">&gt;</span>
</code></pre></div></div>

<p>It uses the static components provided by the HTML + CSS level and arguments
them with <strong>behaviour</strong>, makes them accessible and easy to work with in your
most common product development environments.</p>

<div class="sidetrack">

<h4 id="sidetrack-️-component-state">Sidetrack: ⚠️ Component State</h4>

<p>Make sure that every state is accessible and changeable from outside the
component. Most state is best managed in the products using the library rather
then in the components even when it requires a little more boilerplate on the
consumer side.</p>

<blockquote>
  <p>routing is also a state</p>
</blockquote>


</div>

<h3 id="-write-documentation-for-users">📜 Write documentation for users</h3>

<p>There is this <a href="https://bradfrost.com/blog/post/the-workshop-and-the-storefront/">great article from Brad Frost about the difference between storefront and workshop documentation</a>.</p>

<p>Let’s comparing the most popular UI library tools</p>

<ul>
  <li><a href="https://github.com/styleguidist/">Styleguidist</a>
Helps you build a <strong>storefront</strong>: A place where consumers can learn about the
library concepts and browse components without needing to understand all underlying details.</li>
  <li><a href="https://storybook.js.org/">Storybook</a>
This is a <strong>workshop</strong>: It supports developers at building great components.</li>
</ul>

<p><small>ℹ️ <a href="https://figma.com/">Figma</a> is also a workshop</small></p>

<p>So with that in mind, and the fact that the library is of little worth when
nobody uses it I advise to have an up to date storefront. 
<em>(Doesn’t mean you can’t also have a workshop)</em>.</p>

<h3 id="-minimize-dependencies">👜 Minimize dependencies</h3>

<p>I personally feel like managing external dependencies is one of the most painful 
and challenging tasks in modern web development.<br />
Therefore it’s of high worth when your library is sleek, doesn’t disrupt the dependency choice
of consumers and has little to ask when dependants want to bring their packages up to date.</p>

<p>There is no golden rule here but I’d recommend using as little dependencies
that might interfere with consuming projects as possible.</p>

<div class="sidetrack">

<h4 id="sidetrack--external-css-utilities">Sidetrack: 🧰 external css utilities</h4>

<p>Users of your library will inevitably need custom styling. 
They should be free to choose if they use inline styes, css in js or 
<a href="https://tailwindcss.com/">tailwind.css</a> for that.</p>

<p>The libraries job is to provide component styles, not to serve all css needs.</p>


</div>

<h3 id="-thoughtful-release-management">🛳 Thoughtful release management</h3>

<p>Ideally whenever you release an update to the library, consumers can simply
install the latest version and ship it to users.</p>

<p>In order to achieve that be aware of the APIs consumers rely on.<br />
When you went with <a href="#-create-different-integration-levels">the integration points mentioned earlier</a> your APIs
are now:</p>

<ul>
  <li>variable names of css preprocessor integration</li>
  <li>class names and element structures of CSS + HTML integration</li>
  <li>exports and signatures (props) of framework integration</li>
</ul>

<p>Whenever one of these is modified or the look / behavior is changed significantly
you should consider it a breaking change – meaning users of the library
must be informed and provided with straight forward instructions
on how to migrate.</p>

<h3 id="-consider-tree-shaking">🌳 Consider Tree Shaking</h3>

<p>Your library will most likely include more components and helpers
than any one project will need to use. So there should be tooling at hand
to either only pick the parts needed for a project or to remove code that is not required.</p>

<div class="sidetrack">

<h4 id="sidetrack--supporting-purgecss">Sidetrack: 🧹 Supporting PurgeCSS</h4>

<p><a href="https://purgecss.com/">PurgeCSS</a> needs to statically find the css classes 
you’re using in your sourcefiles in order to know what css to keep and what to throw away 🚮</p>

<p>so its usually more effective to use class names in api’s instead of magically
creating them, at least for less common variants.</p>

<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Good:</span>
<span class="p">&lt;</span><span class="nc">Button</span> <span class="na">color</span><span class="p">=</span><span class="s">"button-blue"</span> <span class="p">/&gt;</span>
<span class="c1">// =&gt; &lt;button class="button button-blue"&gt;</span>

<span class="c1">// Problematic:</span>
<span class="p">&lt;</span><span class="nc">Button</span> <span class="na">blue</span> <span class="p">/&gt;</span>
<span class="c1">// =&gt; &lt;button class="button button-blue"&gt;</span>
</code></pre></div></div>


</div>

<p> </p>

<h2 id="what">What?</h2>

<blockquote>
  <p>What tech stack should I go with today?</p>
</blockquote>

<p>Here’s my personal list of solutions that stood the test of time,
have huge adoption rates and support the requirements I’ve set out above.</p>

<ul>
  <li>A dedicated <a href="https://slack.com/">Slack</a> channel where everyone is invited to ask questions and connect</li>
  <li>A <a href="https://github.com/new">GitHub</a> repo to organize and communicate in a structured way</li>
  <li><a href="https://sass-lang.com/">Scss</a> to create a <a href="https://github.com/twbs/bootstrap/blob/main/scss/_variables.scss">variable file similar to bootstraps</a> (but strapped down a lot) and one or more stylesheets for the component styles</li>
  <li><a href="https://github.com/styleguidist/">styleguidist</a> to create user facing documentation with example html (and later React) for all components</li>
  <li><a href="https://www.typescriptlang.org/">TypeScript</a> to implement typed components for [your framework here]</li>
  <li><a href="https://esbuild.github.io/">esbuild</a> or <a href="https://vitejs.dev/">Vite</a> to create tree shakable bundles of the javascript code</li>
  <li><a href="https://github.com/semantic-release/semantic-release">semantic release</a> to create
a nice changelog, incorporate <a href="https://semver.org/">Semver</a> and automatically release a package to either npm or some private registry.</li>
  <li>A PullRequest based workflow that creates a preview of the updated documentation
as well as a preview release of the package for each change.</li>
</ul>

<p>There are lots of other puzzle pieces required but I think these are the most important ones 
to get right in the beginning.</p>

<p> </p>

<p> </p>

<p>Alright, now let’s go build a library! 📚</p>

<p> </p>]]></content><author><name>Hannes Diercks</name></author><category term="systems" /><category term="component-library," /><category term="web" /><category term="platform" /><summary type="html"><![CDATA[Why and how I'd create a component library today]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://xiphe.net/assets/posts/2022-07-20/alfons-morales-YLSwjSy7stw-unsplash.jpeg" /><media:content medium="image" url="https://xiphe.net/assets/posts/2022-07-20/alfons-morales-YLSwjSy7stw-unsplash.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Deploy previews of branches to gh-pages</title><link href="https://xiphe.net/blog/automation/preview-every-branch-with-ghpages.html" rel="alternate" type="text/html" title="Deploy previews of branches to gh-pages" /><published>2020-10-12T00:00:00+00:00</published><updated>2020-10-12T00:00:00+00:00</updated><id>https://xiphe.net/blog/automation/preview-every-branch-with-ghpages</id><content type="html" xml:base="https://xiphe.net/blog/automation/preview-every-branch-with-ghpages.html"><![CDATA[<p>I love <a href="https://pages.github.com/">GitHub Pages</a> for it’s simplicity.<br />
I use it for simple websites and blogs (like <a href="https://github.com/Xiphe/xiphe.github.io">this page</a>). It works great for style-guides and component libraries that I build for my clients.
And it’s also great for single page applications (SPAs).</p>

<p>No additional services needed, just works, awesome!</p>

<p> </p>

<h2 id="the-problem">The Problem</h2>

<p>Normally only one Source (<code class="language-plaintext highlighter-rouge">branch</code> or <code class="language-plaintext highlighter-rouge">directory</code>) is deployed to <code class="language-plaintext highlighter-rouge">production</code>.<br />
But when you work with a team you might want to use feature branches for work in progress or spikes.</p>

<p>Wouldn’t it be great when these branches would also be deployed as a preview so that I can show my WIP to colleagues and get feedback?</p>

<blockquote>
  <p>Spoiler: The answer is “Yes of course!”</p>
</blockquote>

<p> </p>

<h2 id="tldr">TL;DR</h2>

<p>See <a href="https://github.com/Xiphe/example-ghpage-feature-preview/">example-ghpage-feature-preview repo</a>
for the complete setup.</p>

<p> </p>

<h2 id="what-well-build">What we’ll build</h2>

<p>In this article I’ll walk you through the setup of…</p>

<ul>
  <li>A GitHub repository</li>
  <li>with GitHub Actions</li>
  <li>running a build-step (jekyll in this case) (could be anything generating a deployable page)</li>
  <li>deploying it’s production branch to <code class="language-plaintext highlighter-rouge">[GITHUB_USER].github.io/[PROJECT_NAME]</code> (Can be changed with CNAME)</li>
  <li>creating previews for other branch on <code class="language-plaintext highlighter-rouge">[GITHUB_USER].github.io/[PROJECT_NAME]/preview/[branchname]</code></li>
  <li>cleaning up previews of deleted or merged branches</li>
</ul>

<p>I assume you’re familiar with <a href="https://git-scm.com/">git</a>, <a href="https://github.com/">GitHub</a> and have a brief understanding of building software in continuous integration environments.</p>

<p>Whenever you see a <code class="language-plaintext highlighter-rouge">[YOUR_...]</code> notation, that’s a placeholder that you should 
replace including the <code class="language-plaintext highlighter-rouge">[]</code> braces.</p>

<p>This example uses <code class="language-plaintext highlighter-rouge">main</code> as the production branch and <code class="language-plaintext highlighter-rouge">gh-pages</code> for GitHub Pages. It’s not required to use these branch names.</p>

<p> </p>

<h2 id="create-the-project-locally-and-push-it-to-github">Create the project locally and push it to GitHub</h2>

<ol>
  <li>Create a new jekyll project:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  gem <span class="nb">install </span>bundler jekyll
  jekyll new <span class="o">[</span>YOUR_PROJECT_NAME]
  <span class="nb">cd</span> <span class="o">[</span>YOUR_PROJECT_NAME]
  git init
  git checkout <span class="nt">-b</span> main
  git add <span class="nb">.</span>
  git commit <span class="nt">-m</span><span class="s1">'initial commit'</span>
</code></pre></div>    </div>
  </li>
  <li>Create a <a href="https://github.com/new">new empty repository on github</a></li>
  <li>Copy the remote url</li>
  <li>Push the initial project to Github:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git remote add origin <span class="o">[</span>YOUR_REMOTE_URL]
git push origin main <span class="nt">-u</span>
</code></pre></div>    </div>
  </li>
  <li>Also create a new empty branch that we’ll use for github pages
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout <span class="nt">--orphan</span> gh-pages
git <span class="nb">rm</span> <span class="nt">--cached</span> <span class="nt">-r</span> <span class="nb">.</span>
git commit <span class="nt">--allow-empty</span> <span class="nt">-m</span><span class="s1">'init'</span>
git clean <span class="nt">-df</span>
git push origin gh-pages
git checkout main
</code></pre></div>    </div>
  </li>
  <li>Enable GitHub pages for the <code class="language-plaintext highlighter-rouge">/ (root)</code> of <code class="language-plaintext highlighter-rouge">gh-pages</code> branch under the <code class="language-plaintext highlighter-rouge">Settings</code> of your repository</li>
</ol>

<p> </p>

<h2 id="create-a-github-workflow-to-build-the-sites-artifacts-in-ci">Create a github workflow to build the sites artifacts in CI</h2>

<div class="sidetrack">

<h3 id="sidetrack-why-a-custom-build">Sidetrack: why a custom build?</h3>

<p>You might now think: Aren’t we creating a jekyll page? Why should we build it ourself instead of using the <a href="https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages/setting-up-a-github-pages-site-with-jekyll">builtin jekyll that GitHub is using for pages</a>?</p>

<ol>
  <li>In this article jekyll serves as a real-life placeholder for <a href="https://gohugo.io/">hugo</a>, <a href="https://nextjs.org/">Next.js</a>, or <a href="https://github.com/myles/awesome-static-generators">any other static page generator</a>.</li>
  <li>Even with jekyll, since we’ll be hosting multiple versions of the page we need to build each one and move it to the right place on the <code class="language-plaintext highlighter-rouge">gh-pages</code> branch</li>
  <li>In fact we’ll <a href="https://github.blog/2009-12-29-bypassing-jekyll-on-github-pages/">disable the default jekyll build</a></li>
</ol>

<p>&lt;/Sidetrack&gt;</p>

</div>

<p>Add a <code class="language-plaintext highlighter-rouge">.github/workflows/deploy.yml</code> file to your project</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">deploy</span>

<span class="na">on</span><span class="pi">:</span> <span class="s">push</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v1</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-ruby@v1</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">ruby-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2.x"</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">bundle install</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">bundle config path vendor/bundle</span>
          <span class="s">bundle install --jobs 4 --retry 3</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">jekyll build</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">bundle exec jekyll build</span>
          <span class="s">touch _site/.nojekyll</span>
</code></pre></div></div>

<p>Once this is committed and pushed to github you should see a deploy workflow under 
the <code class="language-plaintext highlighter-rouge">Actions</code> tab of your repository. Currently it will only build the jekyll page
and do nothing with it…</p>

<p> </p>

<h2 id="update-gh-pages-branch-with-our-build-artifacts">Update gh-pages branch with our build artifacts</h2>

<p>jekyll builds the page to a <code class="language-plaintext highlighter-rouge">_site</code> folder. This is the folder we 
want to move to <code class="language-plaintext highlighter-rouge">gh-pages</code> branch.</p>

<p>Add the following lines to <code class="language-plaintext highlighter-rouge">.github/workflows/deploy.yml</code></p>

<div class="sidetrack">

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ... config to build your page to _site</span>

      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-node@v1</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">node-version</span><span class="pi">:</span> <span class="s">12.x</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">deploy gh-pages</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">DEST=.</span>
          <span class="s">git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/[YOUR_GITHUB_USER]/[YOUR_REPO_NAME].git</span>
          <span class="s">git config user.email "41898282+github-actions[bot]@users.noreply.github.com"</span>
          <span class="s">git config user.name "github-actions[bot]"</span>
          <span class="s">npx gh-pages\</span>
              <span class="s">--branch gh-pages\</span>
              <span class="s">--dist _site\</span>
              <span class="s">--dest $DEST\</span>
              <span class="s">--add\</span>
              <span class="s">--dotfiles</span>
</code></pre></div></div>


</div>

<blockquote>
  <p>Make sure to update <code class="language-plaintext highlighter-rouge">[YOUR_GITHUB_USER]</code> and <code class="language-plaintext highlighter-rouge">[YOUR_REPO_NAME]</code> to fit your setup.
You can also use your email and user instead of the <code class="language-plaintext highlighter-rouge">github-actions[bot]</code></p>
</blockquote>

<p>Once this is running, it will put the latest build to <code class="language-plaintext highlighter-rouge">gh-pages</code> branch.
And you should be able to visit the deployed page to see your site.</p>

<p> </p>

<h2 id="create-previews-for-feature-branches">Create previews for feature branches</h2>

<p>At this point each commit will update your page. Also WIP-features
on a unmerged branch. Leading to some sort of race-condition.</p>

<p>In order to solve this we will put the artifacts of all branches except <code class="language-plaintext highlighter-rouge">main</code> 
to a <code class="language-plaintext highlighter-rouge">preview/[BRANCHNAME]</code> subfolder.</p>

<p>For this to work we need to update the build step to make the pages aware of their
new, nested location. This will cause internal links to point to <code class="language-plaintext highlighter-rouge">/preview/[branchname]/link</code>
instead of back to the production deploy of <code class="language-plaintext highlighter-rouge">main</code>.</p>

<div class="sidetrack">

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">jekyll build</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">BRANCH=${GITHUB_HEAD_REF##*/}</span>
          <span class="s">PROD_URL=[YOUR_DEFAULT_BASE_URL]</span>
          <span class="s">BASE_URL=$([ "$BRANCH" == "main" ] &amp;&amp; echo $PROD_URL || echo "$PROD_URL/preview/$BRANCH")</span>
          <span class="s">bundle exec jekyll build --baseurl $BASE_URL</span>
          <span class="s">touch _site/.nojekyll</span>
</code></pre></div></div>


</div>

<p>Next we configure the gh-pages deploy to move the <code class="language-plaintext highlighter-rouge">_site</code> folder to different destinations
based on the current branch.</p>

<div class="sidetrack">

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">deploy gh-pages</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">BRANCH=${GITHUB_HEAD_REF##*/}</span>
          <span class="s">DEST=$([ "$BRANCH" == "main" ] &amp;&amp; echo "." || echo "preview/$BRANCH")</span>
          <span class="s"># git remote set-url ...</span>
</code></pre></div></div>


</div>

<p>Now when you branch of from <code class="language-plaintext highlighter-rouge">main</code>, call it <code class="language-plaintext highlighter-rouge">test</code>, make some changes and push the <code class="language-plaintext highlighter-rouge">test</code> branch to GitHub, it will be deployed under <code class="language-plaintext highlighter-rouge">[GITHUB_USER].github.io/[PROJECT_NAME]/preview/test</code></p>

<p> </p>

<h2 id="clean-up-previews-of-merged-or-deleted-branches">Clean up previews of merged or deleted branches</h2>

<p>Now that this is working we might want to clean up old previews once the branch has 
been deleted or merged to production.</p>

<p>For this we’ll add a <a href="https://github.com/tschaub/gh-pages#optionsbeforeadd">custom <code class="language-plaintext highlighter-rouge">beforeAdd</code> script to gh-pages</a> that checks our existing preview folders and removes those
that do not have a unmerged remote branch counterpart.</p>

<p>Replace the <code class="language-plaintext highlighter-rouge">npx gh-pages\ ...</code> call with the following:</p>

<div class="sidetrack">

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>          <span class="s">npm i cleanup-gh-pages-previews</span>
          <span class="s">npx gh-pages\</span>
              <span class="s">--branch gh-pages\</span>
              <span class="s">--beforeAdd cleanup-gh-pages-previews\</span>
              <span class="s">--dist _site\</span>
              <span class="s">--dest $DEST\</span>
              <span class="s">--add\</span>
              <span class="s">--dotfiles</span>
</code></pre></div></div>


</div>

<p>Warning: <code class="language-plaintext highlighter-rouge">cleanup-gh-pages-previews</code> is a highly specific implementation
tailored for setups like this one. <a href="https://github.com/Xiphe/example-ghpage-feature-preview/blob/main/_beforeAdd.js">See implementation</a> to check if it fits your needs.</p>

<p> </p>

<h2 id="bonus-automatically-add-preview-links-to-pull-requests">Bonus: Automatically add preview-links to pull requests</h2>

<p>Now that everything works, wouldn’t it be great to automatically add a link to 
Pull Requests?</p>

<blockquote>
  <p>Memo to self: less rhetorical questions</p>
</blockquote>

<p>We need to change the action so that it also runs on pull-requests to the <code class="language-plaintext highlighter-rouge">main</code> branch otherwise the PR-id can not be found. 
Also in order to not have duplicated runs we should also limit the builds on push to only <code class="language-plaintext highlighter-rouge">main</code>.</p>

<div class="sidetrack">

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">deploy</span>

<span class="c1"># on: push</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">main</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">main</span>

<span class="c1"># rest of the config ...</span>
</code></pre></div></div>


</div>

<p>Now we can add another step at the bottom of the config</p>

<div class="sidetrack">

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">decorate PR</span>
        <span class="na">if</span><span class="pi">:</span> <span class="s">github.event_name == 'pull_request'</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">npx decorate-gh-pr -r -c "&lt;a href=\"[YOUR_GITHUB_USER].github.io/[YOUR_PROJECT_NAME]/preview/${GITHUB_HEAD_REF##*/}\"&gt;&lt;img src=\"https://img.shields.io/badge/published-gh--pages-green\" alt=\"published to gh-pages\" /&gt;&lt;/a&gt;&lt;hr /&gt;"</span>
</code></pre></div></div>


</div>

<p> </p>

<h2 id="and-thats-it">And that’s it!</h2>

<p>See <a href="https://github.com/Xiphe/example-ghpage-feature-preview/blob/main/.github/workflows/deploy.yml"><code class="language-plaintext highlighter-rouge">deploy.yml</code> of my example repository</a>
for the optimized version that includes caching of dependencies.</p>]]></content><author><name>Hannes Diercks</name></author><category term="automation" /><category term="testing," /><category term="gh-pages," /><category term="ci," /><category term="preview," /><category term="workflows" /><summary type="html"><![CDATA[Automatically create preview pages of all your work in progress]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://xiphe.net/assets/posts/2020-10-12/drew-graham-FK0RhfEeY0w-unsplash.jpg" /><media:content medium="image" url="https://xiphe.net/assets/posts/2020-10-12/drew-graham-FK0RhfEeY0w-unsplash.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to programmatically test visual design of components</title><link href="https://xiphe.net/blog/testing/component-design-testing.html" rel="alternate" type="text/html" title="How to programmatically test visual design of components" /><published>2020-09-28T00:00:00+00:00</published><updated>2020-09-28T00:00:00+00:00</updated><id>https://xiphe.net/blog/testing/component-design-testing</id><content type="html" xml:base="https://xiphe.net/blog/testing/component-design-testing.html"><![CDATA[<h2 id="the-problem">The Problem</h2>

<p>When writing component based applications or libraries we often create components
where part of their API only changes the appearance.</p>

<div id="ex1" class="sidetrack">
  <style>
  .ex1-hidden {
    display: none;
  }
  #ex1 label {
    user-select: none;
  }
  #ex1-button {
    font-weight: bold;
    pointer-events: none;
    border: none;
    border-radius: 3px;
    padding: 0.5em 1em;
    background: lightgray;
  }
  #ex1-button.ex1-primary {
    background: fuchsia;
    color: white;
  }
  </style>

  <div id="ex1-code" class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">&lt;</span><span class="nc">Button</span> <span class="nt">primary</span><span class="p">&gt;</span>Hello World<span class="p">&lt;/</span><span class="nc">Button</span><span class="p">&gt;</span></code></pre></div></div>

  <template id="ex1-default-code"><div class="highlight"><pre class="highlight"><code><span class="p">&lt;</span><span class="nc">Button</span><span class="p">&gt;</span>Hello World<span class="p">&lt;/</span><span class="nc">Button</span><span class="p">&gt;</span></code></pre></div></template>

  <button id="ex1-button" class="ex1-primary">Hello World</button>
  &nbsp; | <label>
    <input type="checkbox" id="ex1-toggle" checked="" />
    toggle primary prop
  </label>

  <script type="text/javascript">
    const toggle = document.getElementById('ex1-toggle');
    const codeContainer = document.getElementById('ex1-code');
    const primaryCode = codeContainer.innerHTML;
    const defaultCode = document.getElementById('ex1-default-code').innerHTML;
    const button = document.getElementById('ex1-button');
    let isPrimary = toggle.checked;
    function update() {
      requestAnimationFrame(() => {
        codeContainer.innerHTML = isPrimary ? primaryCode : defaultCode;
        button.classList[isPrimary ? 'add' : 'remove']('ex1-primary');
      });
    };
    toggle.addEventListener('change', function(ev) {
      isPrimary = ev.target.checked;
      update();
    });
  </script>
</div>

<p>How should we best test this?
Lets find out!</p>

<p> </p>

<h2 id="first-identify-the-interfaces">First Identify the interface(s)</h2>

<p>Before writing any test we should be clear about the interfaces of an abstraction and their consumers.<br />
<em>This is a big topic for another post… jumping to conclusions:</em></p>

<ul>
  <li>The <strong>abstraction</strong> is the <code class="language-plaintext highlighter-rouge">Button</code> component.</li>
  <li>It’s <strong>interfaces</strong> are:
    <ol>
      <li>The programming Interface.<br />
<em>(In react: props + context + return value)</em></li>
      <li>It’s UI + UX</li>
    </ol>
  </li>
  <li>Their <strong>consumers</strong> are:
    <ol>
      <li>Fellow programmers using the component in their app</li>
      <li>The users of this app</li>
    </ol>
  </li>
</ul>

<blockquote>
  <p>Most abstractions in frontend applications have two kinds of consumers: Engineers &amp; App-Users</p>
</blockquote>

<p> </p>

<h2 id="then-find-the-cause--effect-you-want-to-test">Then find the cause + effect you want to test</h2>

<p>For our <code class="language-plaintext highlighter-rouge">Button</code>:</p>
<ul>
  <li>When the <code class="language-plaintext highlighter-rouge">primary</code> prop is set to true by an engineer [<em>via complex hooks and stuff</em>] <strong>(cause)</strong></li>
  <li>Then the background of the app should appear <code class="language-plaintext highlighter-rouge">fuchsia</code> to the app-user <strong>(effect)</strong></li>
</ul>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">fuchsia</code> is the main brand color!</p>
</blockquote>

<p> </p>

<h2 id="now-decide-on-the-best-tooling-to-write-an-optimal-programmatic-test-with">Now decide on the best tooling to write an optimal programmatic test with</h2>

<p>This is where things get tricky! Most tools I see being used today don’t support writing optimal tests. So let’s compare them!</p>

<div class="sidetrack">

<h4 id="sidetrack-characteristics-of-an-optimal-test">Sidetrack: Characteristics of an optimal test</h4>

<ol>
  <li>The test <strong>acts</strong> on the <strong>cause</strong> and <strong>asserts</strong> on the <strong>effect</strong> (see <a href="https://duckduckgo.com/?q=arrange+act+assert">AAA</a> &amp; <a href="#then-find-the-cause--effect-you-want-to-test">previous section</a>)</li>
  <li>The test does not cover implementation details (see: <a href="https://kentcdodds.com/blog/testing-implementation-details">Testing Implementation Details</a>)</li>
  <li>It <strong>only</strong> breaks when the covered effect changes.</li>
</ol>


</div>

<p> </p>

<p>So what are the options?</p>

<h3 id="option-0-just-dont-test">Option 0: Just don’t test</h3>

<p>I think it’s fully valid to just not test visual design programmatically. Manual QA will 
most likely be enough.</p>

<p>Still there are cases where automated tests are really valuable. Most primarily
in <strong>design systems</strong>/<strong>component libraries</strong> where the mission is to provide
abstractions that are easy to use for engineers and look as intended to the user.</p>

<p> </p>

<p>As a rule of thumb ask yourself: <em>“Am I (or my team) the only one using this component?”</em></p>

<p><strong>Yes</strong>: Only Add tests if you feel the component is unstable.<br />
<strong>No</strong>: Add tests unless the component is dead simple.</p>

<p> </p>

<h3 id="option-1-snapshot-testing">Option 1: Snapshot Testing</h3>

<p>A simplified snapshot test would look like this:</p>

<div class="sidetrack">

<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">expect</span><span class="p">(</span><span class="nx">renderToHTML</span><span class="p">(&lt;</span><span class="nc">Button</span> <span class="na">primary</span><span class="p">&gt;</span>Hello<span class="p">&lt;/</span><span class="nc">Button</span><span class="p">&gt;))</span>
  <span class="p">.</span><span class="nx">toMatchInlineSnapshot</span><span class="p">(</span><span class="s2">`
    "&lt;button class="primary"&gt;Hello&lt;button&gt;"
  `</span><span class="p">);</span>
</code></pre></div></div>


</div>

<p>From my experience tests like this have little to no value for appearance testing
because they <strong>assert</strong> on an <strong>implementation detail</strong> (namely the class name).</p>

<ul>
  <li>👎 the <code class="language-plaintext highlighter-rouge">primary</code> class could set <code class="language-plaintext highlighter-rouge">background: olive;</code> but the test would be green.</li>
  <li>👎 refactoring to <code class="language-plaintext highlighter-rouge">&lt;button style="background: fuchsia;"&gt;</code> would have the same <strong>effect</strong> but the test would fail.</li>
  <li>👎 refactoring the class name or adding others would make the test fail.</li>
</ul>

<blockquote>
  <p>This does not mean snapshot tests are bad. I recommend reading <a href="https://kentcdodds.com/blog/effective-snapshot-testing">Effective Snapshot Testing</a>.</p>
</blockquote>

<p> </p>

<h3 id="option-2-visual-regression-testing">Option 2: Visual Regression Testing</h3>

<p>A simplified visual regression test would create a screenshot of the button rendered in a browser
and compare it to a stored screenshot. If any pixels have changed, the test would fail.</p>

<figure><img src="/assets/posts/2020-09-28/screenshot-difference.png" />
<figcaption class="center">Image Source: <a href="https://qanish.wordpress.com/2018/12/12/visual-regression-testing-tools/">Visual Regression Testing Tools</a></figcaption></figure>

<p> </p>

<p>While this perfectly covers the <strong>effect</strong> without depending on implementation
details, it fails on focussing on the single <strong>effect</strong> we want to test.
Thereby <a href="#sidetrack-characteristics-of-an-optimal-test">violating point 3 of an optimal test</a>.</p>

<ul>
  <li>👎 <strong>Any</strong> change to the looks of the button will now cause the test to fail.
All buttons now have a border? All button tests fail!</li>
  <li>👎 Even changing the button <strong>label</strong> will fail the test.</li>
  <li>
    <p>👎 Visual regression tests also fully cover <strong>the browsers rendering/painting process.</strong></p>

    <p><strong>Assertions</strong> have the highest value when they cover <strong>effects</strong> as short as possible after they
leave your <strong>abstraction</strong>. If we let the effects flow though a bunch of other systems, chances are they get blurry.</p>

    <p>For example we will not test that the primary background color is actually <em>perceived</em> as “fuchsia” by the <strong>user</strong>. (<em>That should have been done beforehand by user- and a11y-tests</em>).</p>

    <p>Why should we test that the browser actually renders a DOM-node with a <code class="language-plaintext highlighter-rouge">backgroundColor</code> of <code class="language-plaintext highlighter-rouge">fuchsia</code> accordingly? That is something that the maintainers of the browser should test, not us.</p>
  </li>
</ul>

<blockquote>
  <p>This also does not mean visual regression tests are bad in general. I recommend reading <a href="https://medium.com/loftbr/visual-regression-testing-eb74050f3366">Visual Regression Testing</a>.</p>
</blockquote>

<p> </p>

<h3 id="option-3-style-testing">Option 3: Style testing</h3>

<p>The closes point where the <strong>effect</strong> leaves our domain (the point after which we can not accidentally introduce bugs anymore) is the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle">computed styles</a> of the DOM-Node in the browser.</p>

<p>Let’s test that!</p>

<div class="sidetrack">

<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">getRealStyles</span><span class="p">,</span> <span class="nx">toCss</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">test-real-styles</span><span class="dl">'</span><span class="p">;</span>

<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">primary prop</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">makes the background fuchsia</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">button</span> <span class="o">=</span> <span class="nx">renderToDomNode</span><span class="p">(&lt;</span><span class="nc">Button</span> <span class="na">primary</span><span class="p">&gt;</span>Hello<span class="p">&lt;/</span><span class="nc">Button</span><span class="p">&gt;);</span>

    <span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">getRealStyles</span><span class="p">({</span>
      <span class="na">css</span><span class="p">:</span> <span class="nx">readFileSync</span><span class="p">(</span><span class="dl">'</span><span class="s1">button.css</span><span class="dl">'</span><span class="p">),</span> 
      <span class="na">doc</span><span class="p">:</span> <span class="nx">button</span><span class="p">,</span>
      <span class="na">getStyles</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">backgroundColor</span><span class="dl">'</span><span class="p">],</span>
    <span class="p">});</span>
    <span class="nx">expect</span><span class="p">(</span><span class="nx">toCss</span><span class="p">(</span><span class="nx">styles</span><span class="p">)).</span><span class="nx">toMatchInlineSnapshot</span><span class="p">(</span><span class="s2">`
      "background-color: fuchsia;"
    `</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>


</div>

<ul>
  <li>👍 Acts on the <strong>cause</strong> (primary prop) and asserts on the <strong>effect</strong> (background-color)</li>
  <li>👍 Doesn’t cover implementation details on how the style is applied to the element</li>
  <li>👎 Still covers implementation details when it comes to element composition.
(refactoring the button to be wrapped in a <code class="language-plaintext highlighter-rouge">span</code> will fail the test)</li>
  <li>👍 Adding other styles or changing the label will not break the test.</li>
</ul>

<p>I shamelessly recommend using <a href="https://github.com/Xiphe/test-real-styles"><code class="language-plaintext highlighter-rouge">test-real-styles</code></a>
because it uses real browsers and supports using your full page css.</p>

<p>There is also <a href="https://github.com/testing-library/jest-dom#tohavestyle"><code class="language-plaintext highlighter-rouge">toHaveStyle</code> of <code class="language-plaintext highlighter-rouge">jest-dom</code></a> but it did not work for me because it keeps the element in <a href="https://github.com/jsdom/jsdom">jsdom</a> which has <a href="https://github.com/jsdom/jsdom/labels/css">lots of issues emulating CSSOM</a></p>

<p>Most css-in-js solutions provide something like <a href="https://github.com/styled-components/jest-styled-components"><code class="language-plaintext highlighter-rouge">jest-styled-components</code></a> which is pretty neat, too. 
(<em>A downside is that these will also not evaluate the styles in a real browser and therefore will not capture bugs caused by the cascade.</em>)</p>

<p> </p>

<h2 id="finally-write-the-actual-test">Finally write the actual test</h2>

<p>Given we decided on a <strong>style-testing</strong> solution, I want to share a few thoughts 
that might make the tests even more valuable.</p>

<p>Writing <code class="language-plaintext highlighter-rouge">.button { background: fuchsia; }</code> in the implementation and
<code class="language-plaintext highlighter-rouge">expect(getStyle('background'))</code> <code class="language-plaintext highlighter-rouge">.toBe('fuchsia')</code> in the test feels tedious.</p>

<p>Though in bigger components this will be more meaningful and stabilizing, there
is a potential gem to be created:</p>

<h4 id="a-machine-readable-spec-of-low-level-design-patterns">A machine-readable spec of low level design patterns</h4>

<p>Sit down with your designers and not only talk about how the button should
look like but also which underlying <strong>patterns</strong> lead to this look.</p>

<p>This might surface things like:</p>

<p>“We want all intractable elements to have a <code class="language-plaintext highlighter-rouge">border-radius</code> of <code class="language-plaintext highlighter-rouge">5px</code>”<br />
“The main solid interaction element should use our brand-color as background”</p>

<p>Which can be translated to:</p>

<div class="sidetrack">

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// designIntentions.js</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">brandColor</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./atoms</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">const</span> <span class="nx">interactable</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">borderRadius</span><span class="p">:</span> <span class="dl">'</span><span class="s1">5px</span><span class="dl">'</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">primaryInteractable</span> <span class="o">=</span> <span class="p">{</span>
  <span class="p">...</span><span class="nx">interactable</span><span class="p">,</span>
  <span class="na">backgroundColor</span><span class="p">:</span> <span class="nx">brandColor</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Which then can be used in style-tests like:</p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">getRealStyles</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">test-real-styles</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">primaryInteractable</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">designIntentions.js</span><span class="dl">'</span><span class="p">;</span>

<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">primary prop</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">applies primary interactable styles</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">buttonElement</span> <span class="o">=</span> <span class="nx">renderToHtmlElement</span><span class="p">(&lt;</span><span class="nc">Button</span> <span class="na">primary</span><span class="p">&gt;</span>Hello<span class="p">&lt;/</span><span class="nc">Button</span><span class="p">&gt;);</span>

    <span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">getRealStyles</span><span class="p">({</span>
      <span class="na">css</span><span class="p">:</span> <span class="nx">readFileSync</span><span class="p">(</span><span class="dl">'</span><span class="s1">button.css</span><span class="dl">'</span><span class="p">),</span> 
      <span class="na">doc</span><span class="p">:</span> <span class="nx">buttonElement</span><span class="p">,</span>
      <span class="na">getStyles</span><span class="p">:</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">primaryIntractable</span><span class="p">),</span>
    <span class="p">});</span>
    <span class="nx">expect</span><span class="p">(</span><span class="nx">styles</span><span class="p">).</span><span class="nx">toEqual</span><span class="p">(</span><span class="nx">primaryIntractable</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>


</div>

<p>And I think that’s <strong>beautiful</strong>.</p>

<p> </p>

<h3 id="do-you-think-your-project-could-benefit-from-style-testing">Do you think your project could benefit from style testing?</h3>

<p>I’m available to help you set everything up technically, help dev and design
teams to get the most value of this and also <a href="/lmttfy/">write tests for you</a>.</p>

<p><a href="#footer-contact">get in touch ❣️</a></p>]]></content><author><name>Hannes Diercks</name></author><category term="testing" /><category term="testing," /><category term="components," /><category term="design," /><category term="UI," /><category term="test-real-styles" /><summary type="html"><![CDATA[Learn to write optimal tests for the looks of your app]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://xiphe.net/assets/posts/2020-09-28/vitor-pinto-M_fFGxg8Zhk-unsplash.jpg" /><media:content medium="image" url="https://xiphe.net/assets/posts/2020-09-28/vitor-pinto-M_fFGxg8Zhk-unsplash.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>